pax_global_header00006660000000000000000000000064113754454710014525gustar00rootroot0000000000000052 comment=b2c0b992d8358507f595b03e4f7ca908781fe914 tkrat_2.2cvs20100105-dfsg.orig/000077500000000000000000000000001137544547100160045ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/.cvsignore000066400000000000000000000001221137544547100177770ustar00rootroot00000000000000Makefile config.log config.cache config.status config.h confdefs.h autom4te.cache tkrat_2.2cvs20100105-dfsg.orig/BLURB000066400000000000000000000052721137544547100166030ustar00rootroot00000000000000TkRat is a graphical Mail User Agent (MUA) which handles MIME. It is mainly written in C but the user interface is done in tcl/tk. The following is a non exhaustive list of the capabilities: * Supports disconnected IMAP folders A disconnected mailbox is one where TkRat keeps a local copy of each message in the mailbox. The user can then work against this copy offline. Reply to messages, copy messages or delete them. At some later time the user may synchronize the mailboxes and the changes done to the local copy are applied to the master IMAP-folder. * Keeps track of number of unread messages in multiple folders One can instruct TkRat to monitor certain folders and always show the number of new/total number of messages in them. TkRat can also alert the user of new messages arriving. * Multi-lingual interface Currently English, Swedish, Italian, French and Serbian are supported but it is not hard to add more languages. * Message database Messages can be stored in a database. When inserting you add keywords, expiration time and what to do when the expiration time is reached. Internally the messages are stored as flat text-files. * Virtual folders A virtual folder is a name that has been set on an ordinary folder (mbox, mh, IMAP or POP) or a database search expression. The user can define a menu structure which holds all the virtual folders and can then move messages to a folder or open a folder via the menu. * Message hold You can suspend the composing of a message by putting the message in the hold. The composing can then be continued at a later time. You can stop the program in the meantime. Multiple messages can be in the hold at the same time. * Watcher TkRat can be configured to monitor a number of mailboxes and notify the user when new messages arrives in them. If a new message arrives a small window with a list of all messages (or only the new ones) in the mailbox is opened. The user can then either press the right mouse button in this window to make it go away and continue watching for new messages. Or press the left button to make the watcher window go away and the main window to deiconify. * Interface to the rest of the mail world The program currently understands unix mailboxes, POP, IMAP and mh folders. Messages are sent via SMTP or any user configured program (for example sendmail). * Composing Messages are composed with the built in editor (tk's text widget plus many extensions) or an external editor of your choice. * MIME support Understands MIME both in bodies and headers. * Supports PGP/MIME Generates PGP/MIME messages. There is also support for receiving old style PGP messages. /MaF tkrat_2.2cvs20100105-dfsg.orig/CONFIGURATION000066400000000000000000000127561137544547100177510ustar00rootroot00000000000000 Onsala 2002-04-17 How to configure TkRat TkRat software and its included text is Copyright 1996-2004 by Martin Forssn. The full text of the legal notices is contained in the file called COPYRIGHT, included with this distribution. WHAT GOES INTO THE FILES You can set a lot of options for TkRat via a couple of configuration files, but you don't have to. All options should have reasonable default values. It is however recommended that the site administrator changes the site defaults in the configuration file in the SITE directory (see below). The user can then later do their own customizations from inside the program. These user changes are saved in the users '~/.ratatosk/ratatoskrc' file. LOCATION OF CONFIGURATION FILES The configuration files are first read from the SITE directory and then from the users ~/.ratatosk directory. The configuration file is called 'ratatoskrc'. The default SITE directory is /usr/local/etc. This will change if you give a --prefix (or --sysconfdir) argument to configure. SYNTAX OF CONFIGURATION FILES The configuration files are read via the tcl source command which means that they can have any valid tcl syntax. TkRat defines a 'RatLock' command which locks a given variable so that it may not be changed anymore. An example of an configuration file may look like this: # This is a comment line. set option(create_sender) 1 RatLock option(create_sender) # Set the default editor set option(editor) "xterm -e vi %s" OPTIONS Below follows a list of the options a site administrator normally might want to set in the site-specific configuration file. To set option NAME in the file you should use 'set option(NAME) VALUE'. This is just a subset of the options. For a full list check the options.tcl file in the tkrat subdirectory. Beware that many of the options have special syntax and meanings. It is strongly recommended that you use the preferences window in TkRat to change the options. NAME DESCRIPTION language The default language of the user interface. Currently the only possible values are 'en', 'sv', 'it', 'fr' and 'sr' (English, Swedish, Italian, French and Serbian). The default is English. default_folder The definition of the folder that gets opened when the program is started for the first time. It will then be written into the users vfolderlist and this variable does not modify it any more. See FOLDER DEFINITIONS below for more details. editor The default external editor. The editor will NOT be run in a terminal window. A '%s' must be included and will be expanded to the name of the file to edit. This defaults to 'emacs %s'. lookup_name If this is set to '1' then TkRat will look in the local passwd-file for the full names of users when addressing messages. This might be time-consuming so some sites might want to disable it by default. The default is '1'. mail_steal If this is true then we should check for mail that netscape has stolen from the inbox every time we start the application. The default is '1'. icon This defines which icon bitmap the program should install. The possible values are: "normal" (a 64x64 bitmap), "small" (50x50) and none. Default is normal. system_aliases The identification of a system-wide address book. This should be a list with three elements: {NAME tkrat PATH} NAME is the name of the address book the user should see, tkrat is a keyword which says this file is in the tkrat-format (other possible values are pine, mail and elm). The last value is the path to the address book FOLDER DEFINITIONS Each folder is defined by a tcl list. For example the default value of default_ folder is "INBOX file {} /usr/spool/mail/$env(USER)". This is a list of four elements, the first is the symbolic name of the folder, the second is the type of folder, the third is a flag-field and the fourth is the actual file. To set a value like this in a configuration file you write: set option(default_folder) "INBOX file {} INBOX" There are four different types of folders one might have as inboxes. The syntax for them are (values written in capital letters should be customized, other characters must be exactly as written): File folders: "NAME file {} FILENAME" POP3 folders: "NAME pop3 {} HOST" IMAP folders: "NAME imap {} HOST MAILBOX" Disconnected: "NAME dis {} HOST MAILBOX" For network based folders (pop3, imap and disconnected) there is an entry HOST which should refer to a host definition. A host is defined by the following command: set mailServer(HOST) {SERVER PORT FLAGS USER} Where HOST should match the corresponding entry in the folder definition. The PORT part may be left emtpy. Valid flags are: 'pop3', 'ssl' and 'validate-cert'. Pop3-folders must have the 'pop3' flag set. CHARACTER SET ALIASES You can instruct TkRat that one character set name really means another character set. This is useful when dealing with software that is improperly configured and therefore emits strange character set names. TkRat contains a number of such aliases when shipped but each site may want to extend the list to cover more names. To add more aliases you must create a charsetAliases file in any of the configuration directories. This file should have lines in the following format: set charsetAlias(ALIAS) NAME This line says that ALIAS is an alias and the real name is NAME. I would also appreciate if you notified me (maf@dtek.chalmers.se) of any aliases you think should be built in into the release. tkrat_2.2cvs20100105-dfsg.orig/COPYRIGHT000066400000000000000000000035651137544547100173100ustar00rootroot00000000000000 Copyright (c) 1996-2001 Martin Forssn, Gteborg Sweden. All rights reserved. TkRat is a graphical mail user agent. The author of this software is Martin Forssen and can be reached via the following email address: maf@dtek.chalmers.se. He is hereafter refered to as the author. Redistribution and use in source and binary forms, with or without modification, for commercial or non-commercial use, is 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. 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 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. Notice that included in this release is software copyrighted by others (possibly under other conditions). The fact that it is present here does not alter the copyright of the original authors. See each file for details. tkrat_2.2cvs20100105-dfsg.orig/DEBUGGING000066400000000000000000000015431137544547100172250ustar00rootroot00000000000000Some random notes on how to develop with the TkRat code The TkRat library provides a number of commands to the tcl environment. These commands are documented in doc/interface. It is easy to use a debugger on TkRat, when you know the trick:-) Personally I run gdb on the relevant wish-binary while standing in the tkrat lib directory. In that directory I have a .gdbinit which contains: set args ../tkrat/tkrat -confdir /home/maf/.test -appname tkrattest This lets me have a completely separate tkrat directory for testing. Unfortunately you can not just start 'gdb wish8.4' and then set breakpoints inside the TkRat code. Since it is loaded dynamically you must wait until it has been loaded. It gets loaded by the call to 'RatGetId' in start.tcl. One can add a small delay anyplace after that to be able to break into teh debugger and set the desired breakpoints. tkrat_2.2cvs20100105-dfsg.orig/INSTALL000066400000000000000000000170511137544547100170410ustar00rootroot00000000000000Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. tkrat_2.2cvs20100105-dfsg.orig/Makefile.in000066400000000000000000000037711137544547100200610ustar00rootroot00000000000000# # This is the toplevel makefile for ratatosk # ############################################################################# # TkRat software and its included text is Copyright 1996-2004 by # # Martin Forssn. # # # # The full text of the legal notice is contained in the file called # # COPYRIGHT, included with this distribution. # ############################################################################# @SET_MAKE@ OSTYPE = @OSTYPE@ SHELL = /bin/sh EXTRACFLAGS=@NATIVE_SHLIB_CFLAGS@ @MEM_DEBUG_FLAGS@ @TCL_INCLUDE@ @CCLIENT_FLAGS@ IMAPFLAGS = @IMAPFLAGS@ ETAGS = @etags@ all: build build: if test -r imap/OSTYPE ; then \ if test "Makefile" = `ls -t Makefile imap/OSTYPE|head -1` ; then \ cd imap; ${MAKE} clean; \ fi; \ fi cd imap; \ if test "`cat OSTYPE 2>/dev/null || true`" = "${OSTYPE}" ; then \ ${MAKE} ${IMAPFLAGS} EXTRACFLAGS="${EXTRACFLAGS}"; \ else \ ${MAKE} ${OSTYPE} ${IMAPFLAGS} EXTRACFLAGS="${EXTRACFLAGS}"; \ fi cd lib; ${MAKE} cd util; ${MAKE} cd tkrat; ${MAKE} install.bin: cd lib; ${MAKE} install.bin cd util; ${MAKE} install.bin cd tkrat; ${MAKE} install.bin install.shared: cd util; ${MAKE} install.shared cd tkrat; ${MAKE} install.shared cd misc; ${MAKE} install.shared cd doc; ${MAKE} install.shared install: install.bin install.shared clean: cd imap; ${MAKE} clean cd lib; ${MAKE} clean cd util; ${MAKE} clean cd tkrat; ${MAKE} clean tags: -rm -f TAGS cd lib; ${MAKE} tags-internal cd util; ${MAKE} tags-internal cd tkrat; ${MAKE} tags-internal ${ETAGS} --append --no-globals --output=TAGS imap/src/osdep/unix/*.[ch] ${ETAGS} --append --no-globals --output=TAGS imap/src/*/*.[ch] distclean: clean rm -f config.status config.log config.cache config.h \ Makefile lib/Makefile tkrat/Makefile script/Makefile \ misc/Makefile util/Makefile test/run tkrat/tkrat tkrat_2.2cvs20100105-dfsg.orig/README000066400000000000000000000137031137544547100166700ustar00rootroot00000000000000 Onsala 2002-08-12 TkRat software and its included text is Copyright 1996-2004 by Martin Forssn. The full text of the legal notices is contained in the file called COPYRIGHT, included with this distribution. PORTABILITY This software should work on any Unix-system where you can build tcl/tk. BUILDING THE SOFTWARE TkRat depends on tcl/tk 8.3 or later so these must be installed. Tcl/tk 8.3.4 is recommended. run './configure' This will configure the makefiles etc. See INSTALL for options to configure. Configure depends on you having tclsh8.3 and wish8.3 or later in your path. If configure fails to locate the tcl/tk include-files you might have to help it by providing a --with-tcl-include=DIR argument. do a 'make' This should build the software. Ignore any errors you get about not being able to build mtest. You might want to test the software before installing it. Change directory to either the lib/ or tkrat/ directories ('cd tkrat') and run '../tkrat/tkrat'. If everything seems to work you can install it. do a 'make install' This will install the program in /usr/local/bin. The destination can be overridden with the --prefix argument to configure in the first step. The makefiles will install one small tcl-program in /usr/local/bin, some dynamically loadable modules in /usr/local/lib/tkrat2.1 and a set of tcl files in /usr/local/share/tkrat2.1. The paths to those two later directories are written into the small tkrat program in /usr/local/bin. A small manpage is also installed into /usr/local/man/man1. You can change the name of the program and the directory for the library files by using the --program-prefix and --program-suffix arguments to configure. SSL TkRat will now include ssl-support if the openssl library is found by configure. One can help configure on the way by using the --with-ssl argument. To be able to verify server certificates you must have the relevant CA-certificates installed on your system. Exactly where those should be stored depends upon your ssl- configuration. But usually they should be found in $OPENSSLDIR/cert.pem HTML TkRat will show html-emails if TkHtml version 2.0 or later is installed. TkHtml can be fetched from http://www.hwaci.com/sw/tkhtml/index.html. CONFIGURATION You can have site-wide configuration files. Configuration issues are covered in the CONFIGURATION file. PERMISSIONS of /var/spool/mail TkRat expects /var/spool/mail to have permissions drwxrwxrwt. That is everybody has write access but the sticky bit is on. It is NOT safe to make TkRat setgid or setuid. DELIVERY STATUS NOTIFICATIONS (DSN) TkRat supports DSN as defined in rfc1891-4. This requires that messages are sent via ESMTP and that the MTA (mail transfer agent) also supports DSN (otherwise you will not get DSN support, TkRat will still work though). Actually all MTA's the message will pass through must talk DSN for it to work fully (otherwise you will just get DSN's with the action field set to forwarded). CURRENT INFORMATION This software has a webpage at http://www.tkrat.org/ This page should always contain up to date information abut tkrat. There is also a mailing list on which announcements of new versions are posted. To subscribe send a mail to tkrat-announce-request@tkrat.org, or visit the list page at http://www.tkrat.org/mailman/listinfo/tkrat-announce There is also a discussion list which you can subscribe to at tkrat-request@tkrat.org or http://www.tkrat.org/mailman/listinfo/tkrat (announcements are posted to both lists). To send messages to this list send them to tkrat@tkrat.org ACKNOWLEDGEMENTS I would like to thank all those people who has helped me test TkRat. I would also like to thank Tristan Tarrant who did the Italian version of all the texts, thanks also goes to Christope Martin, Stphane Gourmelen, Nol Giraud, and ric Simon for the french texts. As well as to Srdjan Pokorni for the Serbian translation. POSTCARDS TkRat is free to use, I do not require anything from any of its users. But I do not mind getting postcards. In fact I think it is a great way to keep track of how far it has spread. So if you like TkRat (and if you want to) you can send me a postcard from where you live. I have the following address: Martin Forssen Fyrmstarevgen 48 439 94 Onsala SWEDEN BUGS (aka UNDOCUMENTED FEATURES) Please report any found bugs to maf@dtek.chalmers.se AUTHOR's ADDRESS The author (thats me) can be reached at maf@tkrat.org I live in south of the city of Gteborg in Sweden. MISC NOTES I have had reports that the tcl/tk included in Solaris 8 have some serious problems which leads to the usage of excessive amounts of CPU. I recommend upgrading to 8.3.4 or later. INCLUDED SOFTWARE This package includes software from both University of Washington (the c-client library) and from Lucent Technologies (blt_busy command). The blt_busy package is covered by the following copyright. /* * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies 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 tortuous action, arising * out of or in connection with the use or performance of this * software. * * The "busy" command was created by George Howlett. */ tkrat_2.2cvs20100105-dfsg.orig/config.h.in000066400000000000000000000033111137544547100200250ustar00rootroot00000000000000/* config.h.in. Generated automatically from configure.in by autoheader. */ /* Define to empty if the keyword does not work. */ #undef const /* Define if you have that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H /* Define if utime(file, NULL) sets file's timestamp to the present. */ #undef HAVE_UTIME_NULL /* Define if you need to in order for stat and other things to work. */ #undef _POSIX_SOURCE /* Define if your declares struct tm. */ #undef TM_IN_SYS_TIME /* Define if you have the gethostname function. */ #undef HAVE_GETHOSTNAME /* Define if you have the mkdir function. */ #undef HAVE_MKDIR /* Define if you have the mktime function. */ #undef HAVE_MKTIME /* Define if you have the strdup function. */ #undef HAVE_STRDUP /* Define if you have the strstr function. */ #undef HAVE_STRSTR /* Define if you have the header file. */ #undef HAVE_DIRENT_H /* Define if you have the header file. */ #undef HAVE_FCNTL_H /* Define if you have the header file. */ #undef HAVE_NDIR_H /* Define if you have the header file. */ #undef HAVE_SYS_DIR_H /* Define if you have the header file. */ #undef HAVE_SYS_NDIR_H /* Define if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define if you have the header file. */ #undef HAVE_UNISTD_H /* Define if you have the snprintf function. */ #undef HAVE_SNPRINTF /* Timezone defines */ #undef HAVE_TM_TZADJ #undef HAVE_TM_GMTOFF #undef HAVE_TIMEZONE_VAR /* Define if you have the openssl libraries */ #undef HAVE_OPENSSL /* Define if the system has strlcpy */ #undef HAVE_STRLCPY /* Define if the system has strlcat */ #undef HAVE_STRLCAT tkrat_2.2cvs20100105-dfsg.orig/configure000077500000000000000000007431561137544547100177330ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) as_nl=' ' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH if test "x$CONFIG_SHELL" = x; then if (eval ":") 2>/dev/null; then as_have_required=yes else as_have_required=no fi if test $as_have_required = yes && (eval ": (as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=\$LINENO as_lineno_2=\$LINENO test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } ") 2> /dev/null; then : else as_candidate_shells= as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. case $as_dir in /*) for as_base in sh bash ksh sh5; do as_candidate_shells="$as_candidate_shells $as_dir/$as_base" done;; esac done IFS=$as_save_IFS for as_shell in $as_candidate_shells $SHELL; do # Try only shells that exist, to save several forks. if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { ("$as_shell") 2> /dev/null <<\_ASEOF if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi : _ASEOF }; then CONFIG_SHELL=$as_shell as_have_required=yes if { "$as_shell" 2> /dev/null <<\_ASEOF if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi : (as_func_return () { (exit $1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = "$1" ); then : else exitcode=1 echo positional parameters were not saved. fi test $exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } _ASEOF }; then break fi fi done if test "x$CONFIG_SHELL" != x; then for as_var in BASH_ENV ENV do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done export CONFIG_SHELL exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} fi if test $as_have_required = no; then echo This script requires a shell more modern than all the echo shells that I found on your system. Please install a echo modern shell, or manually run the script under such a echo shell if you do have one. { (exit 1); exit 1; } fi fi fi (eval "as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0") || { echo No shell found that supports shell functions. echo Please tell autoconf@gnu.org about your system, echo including any error possibly output before this echo message } as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir fi echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= ac_unique_file="/lib/rat.h" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_list= ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datarootdir datadir sysconfdir sharedstatedir localstatedir includedir oldincludedir docdir infodir htmldir dvidir pdfdir psdir libdir localedir mandir DEFS ECHO_C ECHO_N ECHO_T LIBS build_alias host_alias target_alias VERSION BUSYLIB_VERSION compress ssh tclsh wish etags CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SET_MAKE INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S AWK cp csuffix CCLIENT_FLAGS MEM_DEBUG_FLAGS TCL_INCLUDE TK_DEFS XMKMF CPP XINCLUDES SSLDIR IMAPFLAGS INSTALL_PREFIX GREP EGREP EXTRA_LIBS SHLIB_CFLAGS NATIVE_SHLIB_CFLAGS SHLIB_LD SHLIB_LD_LIBS SHLIB_SUFFIX RATLIB_SUFFIX BUSYLIB_SUFFIX OSTYPE TEXTFILES LIBOBJS LTLIBOBJS' ac_subst_files='' ac_precious_vars='build_alias host_alias target_alias compress ssh tclsh wish etags CC CFLAGS LDFLAGS LIBS CPPFLAGS XMKMF CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` eval enable_$ac_feature=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` eval enable_$ac_feature=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/[-.]/_/g'` eval with_$ac_package=\$ac_optarg ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/[-.]/_/g'` eval with_$ac_package=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute directory names. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; } done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || { echo "$as_me: error: Working directory cannot be determined" >&2 { (exit 1); exit 1; }; } test "X$ac_ls_di" = "X$ac_pwd_ls_di" || { echo "$as_me: error: pwd does not report name of working directory" >&2 { (exit 1); exit 1; }; } # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$0" || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 { (exit 1); exit 1; }; } pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names X features: --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-tcl=PATH use this version of tclsh (path to binary or bindir) --with-tcl-include=DIR directory to look for tcl.h and tk.h in --with-tkconfig=DIR directory to look for tkConfig.sh in --with-x use the X Window System --with-ssl=PATH where to look for SSL, PATH points to the SSL installation (default: many) --without-ssl disable SSL --with-install-prefix=DIR prefix to use when installing files Some influential environment variables: compress Path to compress command ssh Path to ssh command tclsh Path to tclsh command wish Path to wish command etags Path to etags command CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if you have headers in a nonstandard directory XMKMF Path to xmkmf, Makefile generator for X Window System CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.61 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.61. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------------- ## ## File substitutions. ## ## ------------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo cat confdefs.h echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then set x "$CONFIG_SITE" elif test "x$prefix" != xNONE; then set x "$prefix/share/config.site" "$prefix/etc/config.site" else set x "$ac_default_prefix/share/config.site" \ "$ac_default_prefix/etc/config.site" fi shift for ac_site_file do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi ac_header_list="$ac_header_list utime.h" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" # Version numbers of tkrat VERSION=2.2 BUSYLIB_VERSION=1.0 ac_aux_dir= for ac_dir in misc "$srcdir"/misc; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in misc \"$srcdir\"/misc" >&5 echo "$as_me: error: cannot find install-sh or install.sh in misc \"$srcdir\"/misc" >&2;} { (exit 1); exit 1; }; } fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. echo might interpret backslashes. # By default was `s,x,x', remove it if useless. cat <<\_ACEOF >conftest.sed s/[\\$]/&&/g;s/;s,x,x,$// _ACEOF program_transform_name=`echo $program_transform_name | sed -f conftest.sed` rm -f conftest.sed # Define precious variables ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO: checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (ac_try="$ac_compiler --version >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler --version >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -v >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -v >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -V >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -V >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # # List of possible output files, starting from the most likely. # The algorithm is not robust to junk in `.', hence go to wildcards (a.*) # only as a last resort. b.out is created by i960 compilers. ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' # # The IRIX 6 linker writes into existing files which may not be # executable, retaining their permissions. Remove them first so a # subsequent execution test works. ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { (ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link_default") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi { echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6; } if test -z "$ac_file"; then echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } { echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6; } { echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext { echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT { echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 CFLAGS="" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } if test "${ac_cv_prog_cc_c89+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_c89=$ac_arg else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6; } ;; xno) { echo "$as_me:$LINENO: result: unsupported" >&5 echo "${ECHO_T}unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6; } set x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } SET_MAKE= else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. { echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in ./ | .// | /cC/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi done done ;; esac done IFS=$as_save_IFS fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { echo "$as_me:$LINENO: result: $INSTALL" >&5 echo "${ECHO_T}$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { echo "$as_me:$LINENO: checking whether ln -s works" >&5 echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } else { echo "$as_me:$LINENO: result: no, using $LN_S" >&5 echo "${ECHO_T}no, using $LN_S" >&6; } fi for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_AWK+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AWK="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { echo "$as_me:$LINENO: result: $AWK" >&5 echo "${ECHO_T}$AWK" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$AWK" && break done # Extract the first word of "gzip", so it can be a program name with args. set dummy gzip; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_compress+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $compress in [\\/]* | ?:[\\/]*) ac_cv_path_compress="$compress" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_compress="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi compress=$ac_cv_path_compress if test -n "$compress"; then { echo "$as_me:$LINENO: result: $compress" >&5 echo "${ECHO_T}$compress" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi if test "x$compress" = "x"; then # Extract the first word of "compress", so it can be a program name with args. set dummy compress; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_compress+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $compress in [\\/]* | ?:[\\/]*) ac_cv_path_compress="$compress" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_compress="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi compress=$ac_cv_path_compress if test -n "$compress"; then { echo "$as_me:$LINENO: result: $compress" >&5 echo "${ECHO_T}$compress" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi csuffix=.Z else csuffix=.gz fi # Extract the first word of "ssh", so it can be a program name with args. set dummy ssh; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_ssh+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $ssh in [\\/]* | ?:[\\/]*) ac_cv_path_ssh="$ssh" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ssh="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ssh=$ac_cv_path_ssh if test -n "$ssh"; then { echo "$as_me:$LINENO: result: $ssh" >&5 echo "${ECHO_T}$ssh" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi if test "${GCC}" = "yes"; then { echo "$as_me:$LINENO: checking version of GCC" >&5 echo $ECHO_N "checking version of GCC... $ECHO_C" >&6; } GCC_version=`${CC} --version | sed -n '1s/^[^ ]* ([^(]*) //;s/ .*$//;1p'` { echo "$as_me:$LINENO: result: ${GCC_version}" >&5 echo "${ECHO_T}${GCC_version}" >&6; } fi case "${GCC_version}" in 4*) CCLIENT_FLAGS="-Wno-pointer-sign" ;; esac # # If users has explicitly specified a tclsh-command, then force us to use # that version. # # Check whether --with-tcl was given. if test "${with_tcl+set}" = set; then withval=$with_tcl; tclpath=$withval fi if test "x$tclpath" != "x"; then if test -x "$tclpath"; then forced_tclsh=$tclpath required_tcl_version=`echo 'puts $tcl_version' | $forced_tclsh` PATH=`dirname $forced_tclsh`:$PATH else PATH=$tclpath:$PATH fi fi # # See if user has specified tcl/tk include directory. If he has then check # files there to see which versions of tclsh and wish are needed. # # Check whether --with-tcl-include was given. if test "${with_tcl_include+set}" = set; then withval=$with_tcl_include; tcl_with_include=$withval fi if test "x$tcl_with_include" != "x"; then if test "x$forced_tclsh" != "x"; then { { echo "$as_me:$LINENO: error: '--with-tcl-include can not be used together with --with-tclsh'" >&5 echo "$as_me: error: '--with-tcl-include can not be used together with --with-tclsh'" >&2;} { (exit 1); exit 1; }; } fi for i in $tcl_with_include ; do if test -r $i/tcl.h ; then MAJOR=`grep TCL_MAJOR_VERSION $i/tcl.h | awk '{print $3}'` MINOR=`grep TCL_MINOR_VERSION $i/tcl.h | awk '{print $3}'` if test $MAJOR$MINOR -lt 83 ; then { { echo "$as_me:$LINENO: error: 'Specified --with-tcl-include directory contains version $MAJOR.$MINOR. 8.3 or later required'" >&5 echo "$as_me: error: 'Specified --with-tcl-include directory contains version $MAJOR.$MINOR. 8.3 or later required'" >&2;} { (exit 1); exit 1; }; } fi required_tcl_version=$MAJOR.$MINOR fi done fi if test "x$required_tcl_version" != "x"; then # Extract the first word of "tclsh$required_tcl_version", so it can be a program name with args. set dummy tclsh$required_tcl_version; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_tclsh+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $tclsh in [\\/]* | ?:[\\/]*) ac_cv_path_tclsh="$tclsh" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_tclsh="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi tclsh=$ac_cv_path_tclsh if test -n "$tclsh"; then { echo "$as_me:$LINENO: result: $tclsh" >&5 echo "${ECHO_T}$tclsh" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi tv=$required_tcl_version if test "x$tclsh" = "x"; then { { echo "$as_me:$LINENO: error: 'Can not find tcl/tk $required_tcl_version in path'" >&5 echo "$as_me: error: 'Can not find tcl/tk $required_tcl_version in path'" >&2;} { (exit 1); exit 1; }; } fi else # Extract the first word of "tclsh8.6", so it can be a program name with args. set dummy tclsh8.6; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_tclsh+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $tclsh in [\\/]* | ?:[\\/]*) ac_cv_path_tclsh="$tclsh" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_tclsh="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi tclsh=$ac_cv_path_tclsh if test -n "$tclsh"; then { echo "$as_me:$LINENO: result: $tclsh" >&5 echo "${ECHO_T}$tclsh" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi tv=8.6 if test "x$tclsh" = "x"; then # Extract the first word of "tclsh8.5", so it can be a program name with args. set dummy tclsh8.5; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_tclsh+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $tclsh in [\\/]* | ?:[\\/]*) ac_cv_path_tclsh="$tclsh" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_tclsh="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi tclsh=$ac_cv_path_tclsh if test -n "$tclsh"; then { echo "$as_me:$LINENO: result: $tclsh" >&5 echo "${ECHO_T}$tclsh" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi tv=8.5 fi if test "x$tclsh" = "x"; then # Extract the first word of "tclsh8.4", so it can be a program name with args. set dummy tclsh8.4; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_tclsh+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $tclsh in [\\/]* | ?:[\\/]*) ac_cv_path_tclsh="$tclsh" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_tclsh="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi tclsh=$ac_cv_path_tclsh if test -n "$tclsh"; then { echo "$as_me:$LINENO: result: $tclsh" >&5 echo "${ECHO_T}$tclsh" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi tv=8.4 fi if test "x$tclsh" = "x"; then # Extract the first word of "tclsh8.3", so it can be a program name with args. set dummy tclsh8.3; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_tclsh+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $tclsh in [\\/]* | ?:[\\/]*) ac_cv_path_tclsh="$tclsh" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_tclsh="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi tclsh=$ac_cv_path_tclsh if test -n "$tclsh"; then { echo "$as_me:$LINENO: result: $tclsh" >&5 echo "${ECHO_T}$tclsh" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi tv=8.3 fi if test "x$tclsh" = "x"; then # Extract the first word of "tclsh", so it can be a program name with args. set dummy tclsh; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_tclsh+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $tclsh in [\\/]* | ?:[\\/]*) ac_cv_path_tclsh="$tclsh" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_tclsh="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi tclsh=$ac_cv_path_tclsh if test -n "$tclsh"; then { echo "$as_me:$LINENO: result: $tclsh" >&5 echo "${ECHO_T}$tclsh" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi tv=`echo 'puts $tcl_version' | $tclsh` case $tv in 8.[3456789]*) ;; *) tclsh= ;; esac cmd_without_version=ok fi if test "x$tclsh" = "x"; then { { echo "$as_me:$LINENO: error: 'Can not find tcl/tk 8.3 or later in path'" >&5 echo "$as_me: error: 'Can not find tcl/tk 8.3 or later in path'" >&2;} { (exit 1); exit 1; }; } fi fi PATH=`dirname $tclsh`:$PATH # Extract the first word of "wish$tv", so it can be a program name with args. set dummy wish$tv; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_wish+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $wish in [\\/]* | ?:[\\/]*) ac_cv_path_wish="$wish" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_wish="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi wish=$ac_cv_path_wish if test -n "$wish"; then { echo "$as_me:$LINENO: result: $wish" >&5 echo "${ECHO_T}$wish" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi if test "x$wish" = "x" -a "x$cmd_without_version" = "xok"; then # Extract the first word of "wish", so it can be a program name with args. set dummy wish; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_wish+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $wish in [\\/]* | ?:[\\/]*) ac_cv_path_wish="$wish" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_wish="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi wish=$ac_cv_path_wish if test -n "$wish"; then { echo "$as_me:$LINENO: result: $wish" >&5 echo "${ECHO_T}$wish" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test "x$wish" = "x"; then { { echo "$as_me:$LINENO: error: 'Can't find matching wish'" >&5 echo "$as_me: error: 'Can't find matching wish'" >&2;} { (exit 1); exit 1; }; } fi # # Check if we are using memory debugging # { echo "$as_me:$LINENO: checking if tclsh is compiled with memory debugging" >&5 echo $ECHO_N "checking if tclsh is compiled with memory debugging... $ECHO_C" >&6; } out=`echo 'puts [info commands memory]' | $tclsh` if test "xmemory" = "x$out"; then MEM_DEBUG_FLAGS=-DTCL_MEM_DEBUG { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } else MEM_DEBUG_FLAGS= { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # # Locate the tcl/tk libraries and include files. # { echo "$as_me:$LINENO: checking tcl.h" >&5 echo $ECHO_N "checking tcl.h... $ECHO_C" >&6; } tcl_bin_dir=`dirname $tclsh` tcl_bin_dir=`dirname $tcl_bin_dir` tcl_lib_dir=`echo 'puts [file dirname [file dirname $tcl_library]]' | $tclsh` if test "x$DISPLAY" != "x"; then tk_lib_dir=`echo 'puts [file dirname [file dirname $tk_library]];destroy .'| $wish` fi tk_bin_dir=`dirname $wish` tk_bin_dir=`dirname $tk_bin_dir` tcl_dirs="$tcl_lib_dir $tk_lib_dir $tcl_bin_dir $tk_bin_dir" tcl_dirs="$tcl_dirs /usr/local /usr/tcl /usr/pd/tcl /usr" for i in $tcl_dirs ; do tcl_include_dirs="$tcl_include_dirs $i/include" done tcl_include_dirs="$tcl_with_include $tcl_include_dirs" tcl_include_dirs="$tcl_include_dirs /usr/include/tcl /usr/include" tcl_include_dirs="$tcl_include_dirs /usr/local/include/tcl" tcl_include_dirs="$tcl_include_dirs /usr/include/tcl$tv" tcl_include_dirs="$tcl_include_dirs /usr/include/tk$tv" tcl_include_dirs="$tcl_include_dirs /usr/local/include/tcl$tv" tcl_include_dirs="$tcl_include_dirs /usr/local/include/tk$tv" tcl_dir=0 for i in $tcl_include_dirs ; do if test -r $i/tcl.h ; then MAJOR=`grep TCL_MAJOR_VERSION $i/tcl.h | awk '{print $3}'` MINOR=`grep TCL_MINOR_VERSION $i/tcl.h | awk '{print $3}'` if test $MAJOR.$MINOR = $tv ; then tcl_dir=$i break fi fi done if test $tcl_dir = 0 ; then { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } { echo "$as_me:$LINENO: WARNING: WARNING can't find tcl include files version $tv." >&5 echo "$as_me: WARNING: WARNING can't find tcl include files version $tv." >&2;} tcl_dir= fi { echo "$as_me:$LINENO: result: $tcl_dir" >&5 echo "${ECHO_T}$tcl_dir" >&6; } tk_dir=0 { echo "$as_me:$LINENO: checking tk.h" >&5 echo $ECHO_N "checking tk.h... $ECHO_C" >&6; } for i in $tcl_include_dirs ; do if test -r $i/tk.h ; then MAJOR=`grep TK_MAJOR_VERSION $i/tk.h | awk '{print $3}'` MINOR=`grep TK_MINOR_VERSION $i/tk.h | awk '{print $3}'` if test $MAJOR.$MINOR = $tv; then tk_dir=$i break fi fi done if test $tk_dir = 0 ; then { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } { echo "$as_me:$LINENO: WARNING: WARNING can't find tk include files version $tv." >&5 echo "$as_me: WARNING: WARNING can't find tk include files version $tv." >&2;} tk_dir= fi { echo "$as_me:$LINENO: result: $tk_dir" >&5 echo "${ECHO_T}$tk_dir" >&6; } if test "$tcl_dir" = "$tk_dir" ; then TCL_INCLUDE=-I$tcl_dir else TCL_INCLUDE="-I$tcl_dir -I$tk_dir" fi # # Try to locate tkConfig.sh # # Check whether --with-tkconfig was given. if test "${with_tkconfig+set}" = set; then withval=$with_tkconfig; tkconfig_with=$withval fi { echo "$as_me:$LINENO: checking tkConfig.sh" >&5 echo $ECHO_N "checking tkConfig.sh... $ECHO_C" >&6; } if test "x$tkconfig_with" != "x"; then for i in $tkconfig_with ; do if test -r $i/tkConfig.sh ; then tk_config_test=$i/tkConfig.sh else { { echo "$as_me:$LINENO: error: No tkConfig.sh in $tkconfig_with" >&5 echo "$as_me: error: No tkConfig.sh in $tkconfig_with" >&2;} { (exit 1); exit 1; }; } fi done else for i in $tk_dir/../lib $prefix/lib/tk$tv $tk_dir/../../lib/tk$tv; do if test -r $i/tkConfig.sh ; then tk_config_test=$i/tkConfig.sh break fi done fi if test "x$tk_config_test" = "x"; then { { echo "$as_me:$LINENO: error: No tkConfig.sh found" >&5 echo "$as_me: error: No tkConfig.sh found" >&2;} { (exit 1); exit 1; }; } fi TK_VERSION=`. $tk_config_test ; echo $TK_VERSION` if test $TK_VERSION != $tv; then { { echo "$as_me:$LINENO: error: Found tkConfig.sh (in $tk_config_test) is version $TK_VERSION while I expected $tv." >&5 echo "$as_me: error: Found tkConfig.sh (in $tk_config_test) is version $TK_VERSION while I expected $tv." >&2;} { (exit 1); exit 1; }; } fi { echo "$as_me:$LINENO: result: $tk_config_test" >&5 echo "${ECHO_T}$tk_config_test" >&6; } #-------------------------------------------------------------------- # Locate the X11 header files and the X11 library archive. Try # the ac_path_x macro first, but if it doesn't find the X stuff # (e.g. because there's no xmkmf program) then check through # a list of possible directories. Under some conditions the # autoconf macro will return an include directory that contains # no include files, so double-check its result just to be safe. #-------------------------------------------------------------------- ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { echo "$as_me:$LINENO: result: $CPP" >&5 echo "${ECHO_T}$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { echo "$as_me:$LINENO: checking for X" >&5 echo $ECHO_N "checking for X... $ECHO_C" >&6; } # Check whether --with-x was given. if test "${with_x+set}" = set; then withval=$with_x; fi # $have_x is `yes', `no', `disabled', or empty when we do not yet know. if test "x$with_x" = xno; then # The user explicitly disabled X. have_x=disabled else case $x_includes,$x_libraries in #( *\'*) { { echo "$as_me:$LINENO: error: Cannot use X directory names containing '" >&5 echo "$as_me: error: Cannot use X directory names containing '" >&2;} { (exit 1); exit 1; }; };; #( *,NONE | NONE,*) if test "${ac_cv_have_x+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # One or both of the vars are not set, and there is no cached value. ac_x_includes=no ac_x_libraries=no rm -f -r conftest.dir if mkdir conftest.dir; then cd conftest.dir cat >Imakefile <<'_ACEOF' incroot: @echo incroot='${INCROOT}' usrlibdir: @echo usrlibdir='${USRLIBDIR}' libdir: @echo libdir='${LIBDIR}' _ACEOF if (export CC; ${XMKMF-xmkmf}) >/dev/null 2>/dev/null && test -f Makefile; then # GNU make sometimes prints "make[1]: Entering...", which would confuse us. for ac_var in incroot usrlibdir libdir; do eval "ac_im_$ac_var=\`\${MAKE-make} $ac_var 2>/dev/null | sed -n 's/^$ac_var=//p'\`" done # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. for ac_extension in a so sl; do if test ! -f "$ac_im_usrlibdir/libX11.$ac_extension" && test -f "$ac_im_libdir/libX11.$ac_extension"; then ac_im_usrlibdir=$ac_im_libdir; break fi done # Screen out bogus values from the imake configuration. They are # bogus both because they are the default anyway, and because # using them would break gcc on systems where it needs fixed includes. case $ac_im_incroot in /usr/include) ac_x_includes= ;; *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; esac case $ac_im_usrlibdir in /usr/lib | /lib) ;; *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; esac fi cd .. rm -f -r conftest.dir fi # Standard set of common directories for X headers. # Check X11 before X11Rn because it is often a symlink to the current release. ac_x_header_dirs=' /usr/X11/include /usr/X11R6/include /usr/X11R5/include /usr/X11R4/include /usr/include/X11 /usr/include/X11R6 /usr/include/X11R5 /usr/include/X11R4 /usr/local/X11/include /usr/local/X11R6/include /usr/local/X11R5/include /usr/local/X11R4/include /usr/local/include/X11 /usr/local/include/X11R6 /usr/local/include/X11R5 /usr/local/include/X11R4 /usr/X386/include /usr/x386/include /usr/XFree86/include/X11 /usr/include /usr/local/include /usr/unsupported/include /usr/athena/include /usr/local/x11r5/include /usr/lpp/Xamples/include /usr/openwin/include /usr/openwin/share/include' if test "$ac_x_includes" = no; then # Guess where to find include files, by looking for Xlib.h. # First, try using that file with no special directory specified. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then # We can compile using X headers with no special include directory. ac_x_includes= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 for ac_dir in $ac_x_header_dirs; do if test -r "$ac_dir/X11/Xlib.h"; then ac_x_includes=$ac_dir break fi done fi rm -f conftest.err conftest.$ac_ext fi # $ac_x_includes = no if test "$ac_x_libraries" = no; then # Check for the libraries. # See if we find them without any special options. # Don't add to $LIBS permanently. ac_save_LIBS=$LIBS LIBS="-lX11 $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { XrmInitialize () ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then LIBS=$ac_save_LIBS # We can link X programs with no special library path. ac_x_libraries= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 LIBS=$ac_save_LIBS for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` do # Don't even attempt the hair of trying to link an X program! for ac_extension in a so sl; do if test -r "$ac_dir/libX11.$ac_extension"; then ac_x_libraries=$ac_dir break 2 fi done done fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext fi # $ac_x_libraries = no case $ac_x_includes,$ac_x_libraries in #( no,* | *,no | *\'*) # Didn't find X, or a directory has "'" in its name. ac_cv_have_x="have_x=no";; #( *) # Record where we found X for the cache. ac_cv_have_x="have_x=yes\ ac_x_includes='$ac_x_includes'\ ac_x_libraries='$ac_x_libraries'" esac fi ;; #( *) have_x=yes;; esac eval "$ac_cv_have_x" fi # $with_x != no if test "$have_x" != yes; then { echo "$as_me:$LINENO: result: $have_x" >&5 echo "${ECHO_T}$have_x" >&6; } no_x=yes else # If each of the values was on the command line, it overrides each guess. test "x$x_includes" = xNONE && x_includes=$ac_x_includes test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries # Update the cache value to reflect the command line values. ac_cv_have_x="have_x=yes\ ac_x_includes='$x_includes'\ ac_x_libraries='$x_libraries'" { echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5 echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6; } fi not_really_there="" if test "$no_x" = ""; then if test "$x_includes" = ""; then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 not_really_there="yes" fi rm -f conftest.err conftest.$ac_ext else if test ! -r $x_includes/X11/Intrinsic.h; then not_really_there="yes" fi fi fi if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then { echo "$as_me:$LINENO: checking for X11 header files" >&5 echo $ECHO_N "checking for X11 header files... $ECHO_C" >&6; } XINCLUDES="# no special path needed" xinc="no special path needed" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 XINCLUDES="nope" fi rm -f conftest.err conftest.$ac_ext if test "$XINCLUDES" = nope; then dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr /X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/ope nwin/include /usr/X11/include /usr/sww/include" for i in $dirs ; do if test -r $i/X11/Intrinsic.h; then { echo "$as_me:$LINENO: result: $i" >&5 echo "${ECHO_T}$i" >&6; } XINCLUDES=" -I$i" break fi done else { echo "$as_me:$LINENO: result: ok" >&5 echo "${ECHO_T}ok" >&6; } fi if test "$XINCLUDES" = nope; then { echo "$as_me:$LINENO: result: couldn't find any!" >&5 echo "${ECHO_T}couldn't find any!" >&6; } XINCLUDES="# no include files found" fi else if test "$x_includes" != ""; then XINCLUDES=-I$x_includes else XINCLUDES="# no special path needed" fi fi # # Check for openssl installation # If found the build the imap toolkit with ssl-support # This code was based on code found in the portable distribution of OpenSSH # [MH] Enhanced by some parts from cURL http://curl.haxx.se/. To be continued. # default on tryssl=yes # Check whether --with-ssl was given. if test "${with_ssl+set}" = set; then withval=$with_ssl; tryssl=$withval fi if test X"$tryssl" = X"no"; then { echo "$as_me:$LINENO: WARNING: SSL support disabled" >&5 echo "$as_me: WARNING: SSL support disabled" >&2;} IMAPFLAGS="SSLTYPE=none" else if test X"$tryssl" != Xyes; then tryssldir=$tryssl fi saved_LIBS="$LIBS" saved_LDFLAGS="$LDFLAGS" saved_CFLAGS="$CFLAGS" if test "x$prefix" != "xNONE" ; then tryssldir="$tryssldir $prefix" fi { echo "$as_me:$LINENO: checking for OpenSSL" >&5 echo $ECHO_N "checking for OpenSSL... $ECHO_C" >&6; } if test "${ac_cv_openssldir+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ssldir in $tryssldir "" /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/share/ssl /usr/local /usr/pkg /opt /opt/openssl /sw ; do if test ! -z "$ssldir" ; then if test ! -f "$ssldir/include/openssl/rand.h" ; then continue fi LDFLAGS="$saved_LDFLAGS -L$ssldir/lib" CFLAGS="$saved_CFLAGS -I$ssldir/include" else LDFLAGS="$saved_LDFLAGS" fi LIBS="$saved_LIBS -lcrypto" # Basic test to check for compatible version and correct linking if test "$cross_compiling" = yes; then placeholder=0 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main(void) { char a[2048]; memset(a, 0, sizeof(a)); RAND_add(a, sizeof(a), sizeof(a)); return(RAND_status() <= 0); } _ACEOF rm -f conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then found_crypto=1 break; else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) placeholder=0 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi if test ! -z "$found_crypto" ; then break; fi done if test -z "$ssldir" ; then ssldir="(system)" fi if test -z "$found_crypto" ; then ac_cv_openssldir=no else ac_cv_openssldir=$ssldir fi fi { echo "$as_me:$LINENO: result: $ac_cv_openssldir" >&5 echo "${ECHO_T}$ac_cv_openssldir" >&6; } if test "x$ac_cv_openssldir" != "xno"; then cat >>confdefs.h <<\_ACEOF #define HAVE_OPENSSL 1 _ACEOF SSLDIR=$ac_cv_openssldir if (test "x$ac_cv_openssldir" = "x(system)") ; then SSLDIR=/usr fi EXTRA_LIBS="$EXTRA_LIBS -L$SSLDIR/lib -lssl -lcrypto" IMAPFLAGS="SSLTYPE=unix SSLDIR=$SSLDIR" else { { echo "$as_me:$LINENO: error: cannot find openssl; try --without-ssl?" >&5 echo "$as_me: error: cannot find openssl; try --without-ssl?" >&2;} { (exit 1); exit 1; }; } fi fi # # Find installation prefix (if any) # # Check whether --with-install-prefix was given. if test "${with_install_prefix+set}" = set; then withval=$with_install_prefix; INSTALL_PREFIX=$withval fi # Etags is needed for build environment only # Extract the first word of "etags", so it can be a program name with args. set dummy etags; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_etags+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $etags in [\\/]* | ?:[\\/]*) ac_cv_path_etags="$etags" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_etags="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi etags=$ac_cv_path_etags if test -n "$etags"; then { echo "$as_me:$LINENO: result: $etags" >&5 echo "${ECHO_T}$etags" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # NetBSD have crypt in a special library { echo "$as_me:$LINENO: checking for crypt in -lc" >&5 echo $ECHO_N "checking for crypt in -lc... $ECHO_C" >&6; } if test "${ac_cv_lib_c_crypt+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lc $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char crypt (); int main () { return crypt (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_c_crypt=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_c_crypt=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_c_crypt" >&5 echo "${ECHO_T}$ac_cv_lib_c_crypt" >&6; } if test $ac_cv_lib_c_crypt = yes; then true else have_libcrypt=yes { echo "$as_me:$LINENO: checking for crypt in -lcrypt" >&5 echo $ECHO_N "checking for crypt in -lcrypt... $ECHO_C" >&6; } if test "${ac_cv_lib_crypt_crypt+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypt $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char crypt (); int main () { return crypt (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_crypt_crypt=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_crypt_crypt=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_crypt_crypt" >&5 echo "${ECHO_T}$ac_cv_lib_crypt_crypt" >&6; } if test $ac_cv_lib_crypt_crypt = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBCRYPT 1 _ACEOF LIBS="-lcrypt $LIBS" fi fi # AIX needs the s library { echo "$as_me:$LINENO: checking for getuserattr in -ls" >&5 echo $ECHO_N "checking for getuserattr in -ls... $ECHO_C" >&6; } if test "${ac_cv_lib_s_getuserattr+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ls $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char getuserattr (); int main () { return getuserattr (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_s_getuserattr=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_s_getuserattr=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_s_getuserattr" >&5 echo "${ECHO_T}$ac_cv_lib_s_getuserattr" >&6; } if test $ac_cv_lib_s_getuserattr = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBS 1 _ACEOF LIBS="-ls $LIBS" fi { echo "$as_me:$LINENO: checking for library containing strerror" >&5 echo $ECHO_N "checking for library containing strerror... $ECHO_C" >&6; } if test "${ac_cv_search_strerror+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char strerror (); int main () { return strerror (); ; return 0; } _ACEOF for ac_lib in '' cposix; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_search_strerror=$ac_res else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext if test "${ac_cv_search_strerror+set}" = set; then break fi done if test "${ac_cv_search_strerror+set}" = set; then : else ac_cv_search_strerror=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_search_strerror" >&5 echo "${ECHO_T}$ac_cv_search_strerror" >&6; } ac_res=$ac_cv_search_strerror if test "$ac_res" != no; then test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_header_dirent=no for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5 echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include <$ac_hdr> int main () { if ((DIR *) 0) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 _ACEOF ac_header_dirent=$ac_hdr; break fi done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then { echo "$as_me:$LINENO: checking for library containing opendir" >&5 echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6; } if test "${ac_cv_search_opendir+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char opendir (); int main () { return opendir (); ; return 0; } _ACEOF for ac_lib in '' dir; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_search_opendir=$ac_res else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext if test "${ac_cv_search_opendir+set}" = set; then break fi done if test "${ac_cv_search_opendir+set}" = set; then : else ac_cv_search_opendir=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 echo "${ECHO_T}$ac_cv_search_opendir" >&6; } ac_res=$ac_cv_search_opendir if test "$ac_res" != no; then test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi else { echo "$as_me:$LINENO: checking for library containing opendir" >&5 echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6; } if test "${ac_cv_search_opendir+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char opendir (); int main () { return opendir (); ; return 0; } _ACEOF for ac_lib in '' x; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_search_opendir=$ac_res else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext if test "${ac_cv_search_opendir+set}" = set; then break fi done if test "${ac_cv_search_opendir+set}" = set; then : else ac_cv_search_opendir=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 echo "${ECHO_T}$ac_cv_search_opendir" >&6; } ac_res=$ac_cv_search_opendir if test "$ac_res" != no; then test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi fi { echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6; } if test "${ac_cv_header_sys_wait_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int main () { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_header_sys_wait_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_sys_wait_h=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6; } if test $ac_cv_header_sys_wait_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_SYS_WAIT_H 1 _ACEOF fi { echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } if test "${ac_cv_path_GREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Extract the first word of "grep ggrep" to use in msg output if test -z "$GREP"; then set dummy grep ggrep; ac_prog_name=$2 if test "${ac_cv_path_GREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break ac_count=`expr $ac_count + 1` if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS fi GREP="$ac_cv_path_GREP" if test -z "$GREP"; then { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} { (exit 1); exit 1; }; } fi else ac_cv_path_GREP=$GREP fi fi { echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 echo "${ECHO_T}$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } if test "${ac_cv_path_EGREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else # Extract the first word of "egrep" to use in msg output if test -z "$EGREP"; then set dummy egrep; ac_prog_name=$2 if test "${ac_cv_path_EGREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break ac_count=`expr $ac_count + 1` if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS fi EGREP="$ac_cv_path_EGREP" if test -z "$EGREP"; then { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} { (exit 1); exit 1; }; } fi else ac_cv_path_EGREP=$EGREP fi fi fi { echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF rm -f conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi { echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in fcntl.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } else # Is the header compilable? { echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6; } # Is the header present? { echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ;; esac { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; } if test "${ac_cv_c_const+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { /* FIXME: Include the comments suggested by Paul. */ #ifndef __cplusplus /* Ultrix mips cc rejects this. */ typedef int charset[2]; const charset cs; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* AIX XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; pcpcc = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; { /* SCO 3.2v4 cc rejects this. */ char *t; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; if (s) return 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* AIX XL C 1.02.0.0 rejects this saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; }; struct s *b; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; if (!foo) return 0; } return !cs[0] && !zero.x; #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_c_const=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_const=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 echo "${ECHO_T}$ac_cv_c_const" >&6; } if test $ac_cv_c_const = no; then cat >>confdefs.h <<\_ACEOF #define const _ACEOF fi for ac_header in $ac_header_list do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } else # Is the header compilable? { echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6; } # Is the header present? { echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ;; esac { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { echo "$as_me:$LINENO: checking whether utime accepts a null argument" >&5 echo $ECHO_N "checking whether utime accepts a null argument... $ECHO_C" >&6; } if test "${ac_cv_func_utime_null+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else rm -f conftest.data; >conftest.data # Sequent interprets utime(file, 0) to mean use start of epoch. Wrong. if test "$cross_compiling" = yes; then ac_cv_func_utime_null=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #ifdef HAVE_UTIME_H # include #endif int main () { struct stat s, t; return ! (stat ("conftest.data", &s) == 0 && utime ("conftest.data", 0) == 0 && stat ("conftest.data", &t) == 0 && t.st_mtime >= s.st_mtime && t.st_mtime - s.st_mtime < 120); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_utime_null=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_utime_null=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi { echo "$as_me:$LINENO: result: $ac_cv_func_utime_null" >&5 echo "${ECHO_T}$ac_cv_func_utime_null" >&6; } if test $ac_cv_func_utime_null = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_UTIME_NULL 1 _ACEOF fi rm -f conftest.data for ac_func in snprintf do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$ac_func || defined __stub___$ac_func choke me #endif int main () { return $ac_func (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_var'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in strlcpy do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$ac_func || defined __stub___$ac_func choke me #endif int main () { return $ac_func (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_var'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in strlcat do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$ac_func || defined __stub___$ac_func choke me #endif int main () { return $ac_func (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_var'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done #------------------------------------------------------------------------------ # Find out all about time handling differences. #------------------------------------------------------------------------------ for ac_header in sys/time.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } else # Is the header compilable? { echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6; } # Is the header present? { echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ;; esac { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6; } if test "${ac_cv_header_time+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_header_time=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_time=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 echo "${ECHO_T}$ac_cv_header_time" >&6; } if test $ac_cv_header_time = yes; then cat >>confdefs.h <<\_ACEOF #define TIME_WITH_SYS_TIME 1 _ACEOF fi { echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6; } if test "${ac_cv_struct_tm+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { struct tm tm; int *p = &tm.tm_sec; return !p; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_struct_tm=time.h else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_struct_tm=sys/time.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 echo "${ECHO_T}$ac_cv_struct_tm" >&6; } if test $ac_cv_struct_tm = sys/time.h; then cat >>confdefs.h <<\_ACEOF #define TM_IN_SYS_TIME 1 _ACEOF fi { echo "$as_me:$LINENO: checking for struct tm.tm_zone" >&5 echo $ECHO_N "checking for struct tm.tm_zone... $ECHO_C" >&6; } if test "${ac_cv_member_struct_tm_tm_zone+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include <$ac_cv_struct_tm> int main () { static struct tm ac_aggr; if (ac_aggr.tm_zone) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_member_struct_tm_tm_zone=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include <$ac_cv_struct_tm> int main () { static struct tm ac_aggr; if (sizeof ac_aggr.tm_zone) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_member_struct_tm_tm_zone=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_member_struct_tm_tm_zone=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_member_struct_tm_tm_zone" >&5 echo "${ECHO_T}$ac_cv_member_struct_tm_tm_zone" >&6; } if test $ac_cv_member_struct_tm_tm_zone = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_TM_TM_ZONE 1 _ACEOF fi if test "$ac_cv_member_struct_tm_tm_zone" = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_TM_ZONE 1 _ACEOF else { echo "$as_me:$LINENO: checking whether tzname is declared" >&5 echo $ECHO_N "checking whether tzname is declared... $ECHO_C" >&6; } if test "${ac_cv_have_decl_tzname+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { #ifndef tzname (void) tzname; #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_have_decl_tzname=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_have_decl_tzname=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_have_decl_tzname" >&5 echo "${ECHO_T}$ac_cv_have_decl_tzname" >&6; } if test $ac_cv_have_decl_tzname = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_DECL_TZNAME 1 _ACEOF else cat >>confdefs.h <<_ACEOF #define HAVE_DECL_TZNAME 0 _ACEOF fi { echo "$as_me:$LINENO: checking for tzname" >&5 echo $ECHO_N "checking for tzname... $ECHO_C" >&6; } if test "${ac_cv_var_tzname+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if !HAVE_DECL_TZNAME extern char *tzname[]; #endif int main () { return tzname[0][0]; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_var_tzname=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_var_tzname=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_var_tzname" >&5 echo "${ECHO_T}$ac_cv_var_tzname" >&6; } if test $ac_cv_var_tzname = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_TZNAME 1 _ACEOF fi fi { echo "$as_me:$LINENO: checking tm_tzadj in struct tm" >&5 echo $ECHO_N "checking tm_tzadj in struct tm... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { struct tm tm; tm.tm_tzadj; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then cat >>confdefs.h <<\_ACEOF #define HAVE_TM_TZADJ 1 _ACEOF { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { echo "$as_me:$LINENO: checking tm_gmtoff in struct tm" >&5 echo $ECHO_N "checking tm_gmtoff in struct tm... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { struct tm tm; tm.tm_gmtoff; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then cat >>confdefs.h <<\_ACEOF #define HAVE_TM_GMTOFF 1 _ACEOF { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # # Its important to include time.h in this check, as some systems (like convex) # have timezone functions, etc. # have_timezone=no { echo "$as_me:$LINENO: checking long timezone variable" >&5 echo $ECHO_N "checking long timezone variable... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { extern long timezone; timezone += 1; exit (0); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then have_timezone=yes cat >>confdefs.h <<\_ACEOF #define HAVE_TIMEZONE_VAR 1 _ACEOF { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # # On some systems (eg IRIX 6.2), timezone is a time_t and not a long. # if test "$have_timezone" = no; then { echo "$as_me:$LINENO: checking time_t timezone variable" >&5 echo $ECHO_N "checking time_t timezone variable... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { extern time_t timezone; timezone += 1; exit (0); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then cat >>confdefs.h <<\_ACEOF #define HAVE_TIMEZONE_VAR 1 _ACEOF { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi # # AIX does not have a timezone field in struct tm. When the AIX bsd # library is used, the timezone global and the gettimeofday methods are # to be avoided for timezone deduction instead, we deduce the timezone # by comparing the localtime result on a known GMT value. # if test "`uname -s`" = "AIX" ; then { echo "$as_me:$LINENO: checking for gettimeofday in -lbsd" >&5 echo $ECHO_N "checking for gettimeofday in -lbsd... $ECHO_C" >&6; } if test "${ac_cv_lib_bsd_gettimeofday+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbsd $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gettimeofday (); int main () { return gettimeofday (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_bsd_gettimeofday=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_bsd_gettimeofday=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gettimeofday" >&5 echo "${ECHO_T}$ac_cv_lib_bsd_gettimeofday" >&6; } if test $ac_cv_lib_bsd_gettimeofday = yes; then libbsd=yes fi if test $libbsd = yes ; then cat >>confdefs.h <<\_ACEOF #define USE_DELTA_FOR_TZ 1 _ACEOF fi fi # # Some systems require a -lcrypt # if test "$have_libcrypt" = yes ; then EXTRA_LIBS="$EXTRA_LIBS -lcrypt" fi ######################################################################### # The following code checks the system type and defines variables for # # building of shared libraries and imap. # # It is mostly taken from tcl8.0p2 # ######################################################################### # Step 1: set the variable "system" to hold the name and version number # for the system. This can usually be done via the "uname" command, but # there are a few systems, like Next, where this doesn't work. { echo "$as_me:$LINENO: checking system version (for imap & dynamic loading)" >&5 echo $ECHO_N "checking system version (for imap & dynamic loading)... $ECHO_C" >&6; } if test -f /usr/lib/NextStep/software_version; then system=NEXTSTEP-`awk '/3/,/3/' /usr/lib/NextStep/software_version` else system=`uname -s`-`uname -r` if test "$?" -ne 0 ; then { echo "$as_me:$LINENO: result: unknown (can't find uname command)" >&5 echo "${ECHO_T}unknown (can't find uname command)" >&6; } system=unknown else # Special check for weird MP-RAS system (uname returns weird # results, and the version is kept in special file). if test -r /etc/.relid -a "X`uname -n`" = "X`uname -s`" ; then system=MP-RAS-`awk '{print $3}' /etc/.relid` fi if test "`uname -s`" = "AIX" ; then system=AIX-`uname -v`.`uname -r` fi { echo "$as_me:$LINENO: result: $system" >&5 echo "${ECHO_T}$system" >&6; } fi fi # Step 2: check for existence of -ldl library. This is needed because # Linux can use either -ldl or -ldld for dynamic loading. { echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6; } if test "${ac_cv_lib_dl_dlopen+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_dl_dlopen=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6; } if test $ac_cv_lib_dl_dlopen = yes; then have_dl=yes else have_dl=no fi # Step 3: set configuration options based on system name and version. fullSrcDir=`cd $srcdir; pwd` RATLIB_SUFFIX="" BUSYLIB_SUFFIX="" TCL_UNSHARED_LIB_SUFFIX="" TCL_LIB_VERSIONS_OK=ok case $system in AIX-4.[[2-9]]) SHLIB_CFLAGS="" SHLIB_LD="$fullSrcDir/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" AIX=yes RATLIB_SUFFIX='${VERSION}.a' BUSYLIB_SUFFIX='${BUSYLIB_VERSION}.a' OSTYPE=Unkown ;; AIX-*) SHLIB_CFLAGS="" SHLIB_LD="$fullSrcDir/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" RATLIB_SUFFIX='${VERSION}.a' BUSYLIB_SUFFIX='${BUSYLIB_VERSION}.a' case "`uname -v`.`uname -r`" in 4.1) OSTYPE=a41 ;; 3.2) OSTYPE=a32 ;; *) OSTYPE=aix ;; esac ;; BSD/OS-2.1*) SHLIB_CFLAGS="" SHLIB_LD="shlicc -r" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" OSTYPE=bsd ;; BSD/OS-3*|BSD/OS-4*) SHLIB_CFLAGS="" SHLIB_LD="shlicc -r" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" OSTYPE=bs3 ;; Darwin-7.*) SHLIB_CFLAGS="" SHLIB_LD="libtool -dynamic -single_module -flat_namespace -undefined suppress -multiply_defined suppress" SHLIB_LD_LIBS='-lSystem -lcc_dynamic' SHLIB_SUFFIX=".dylib" test -e /sw/include/openssl && OSTYPE=fnk || OSTYPE=osx ;; Darwin-8.*) SHLIB_CFLAGS="" SHLIB_LD="libtool -dynamic -single_module -flat_namespace -undefined suppress -multiply_defined suppress" SHLIB_LD_LIBS='-lSystemStubs' SHLIB_SUFFIX=".dylib" test -e /sw/include/openssl && OSTYPE=fnk || OSTYPE=osx ;; dgux*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" RATLIB_SUFFIX=".so" BUSYLIB_SUFFIX=".so" OSTYPE=d-g ;; HP-UX-*.08.*|HP-UX-*.09.*) { echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6; } if test "${ac_cv_lib_dld_shl_load+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_dld_shl_load=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6; } if test $ac_cv_lib_dld_shl_load = yes; then have_dld=yes else have_dld=no fi if test "$have_dld" = yes ; then SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".sl" fi OSTYPE=hpp ;; HP-UX-*.10.*|HP-UX-*.11.*) { echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6; } if test "${ac_cv_lib_dld_shl_load+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_dld_shl_load=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6; } if test $ac_cv_lib_dld_shl_load = yes; then have_dld=yes else have_dld=no fi if test "$have_dld" = yes ; then SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".sl" fi OSTYPE=hpx ;; IRIX-4.*) SHLIB_CFLAGS="-G 0" SHLIB_SUFFIX=".a" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" SHLIB_LD_LIBS='${LIBS}' RATLIB_SUFFIX='${VERSION}.a' BUSYLIB_SUFFIX='${BUSYLIB_VERSION}.a' OSTYPE=sgi ;; IRIX-5.*) SHLIB_CFLAGS="" SHLIB_LD="ld -shared -rdata_shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=sgi ;; IRIX-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -shared -rdata_shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then OSTYPE=gsg else OSTYPE=sg6 fi ;; IRIX64-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -32 -shared -rdata_shared -rpath /usr/local/lib" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=sg6 ;; Linux*) SHLIB_CFLAGS="-fPIC" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" if test "$have_dl" = yes ; then SHLIB_LD="${CC} -shared" else SHLIB_LD="ld -shared" fi { echo "$as_me:$LINENO: checking for pam_start in -lpam" >&5 echo $ECHO_N "checking for pam_start in -lpam... $ECHO_C" >&6; } if test "${ac_cv_lib_pam_pam_start+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpam -lpam_misc $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pam_start (); int main () { return pam_start (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then ac_cv_lib_pam_pam_start=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_pam_pam_start=no fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { echo "$as_me:$LINENO: result: $ac_cv_lib_pam_pam_start" >&5 echo "${ECHO_T}$ac_cv_lib_pam_pam_start" >&6; } if test $ac_cv_lib_pam_pam_start = yes; then have_pam=yes else have_pam=no fi if test "$have_pam" = yes ; then OSTYPE=lnp EXTRA_LIBS="$EXTRA_LIBS -L$SSLDIR/lib -lpam -lpam_misc" else if test "$have_libcrypt" = yes ; then OSTYPE=slx else OSTYPE=lnx fi fi ;; MP-RAS-02*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=Unkown ;; MP-RAS-*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=Unkown ;; NetBSD-*|FreeBSD-[12].*) # Not available on all versions: check for include file. if test "${ac_cv_header_dlfcn_h+set}" = set; then { echo "$as_me:$LINENO: checking for dlfcn.h" >&5 echo $ECHO_N "checking for dlfcn.h... $ECHO_C" >&6; } if test "${ac_cv_header_dlfcn_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi { echo "$as_me:$LINENO: result: $ac_cv_header_dlfcn_h" >&5 echo "${ECHO_T}$ac_cv_header_dlfcn_h" >&6; } else # Is the header compilable? { echo "$as_me:$LINENO: checking dlfcn.h usability" >&5 echo $ECHO_N "checking dlfcn.h usability... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6; } # Is the header present? { echo "$as_me:$LINENO: checking dlfcn.h presence" >&5 echo $ECHO_N "checking dlfcn.h presence... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: dlfcn.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: dlfcn.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: dlfcn.h: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: dlfcn.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: dlfcn.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: dlfcn.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: dlfcn.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: dlfcn.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: dlfcn.h: see the Autoconf documentation" >&5 echo "$as_me: WARNING: dlfcn.h: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: dlfcn.h: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: dlfcn.h: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: dlfcn.h: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: dlfcn.h: in the future, the compiler will take precedence" >&2;} ;; esac { echo "$as_me:$LINENO: checking for dlfcn.h" >&5 echo $ECHO_N "checking for dlfcn.h... $ECHO_C" >&6; } if test "${ac_cv_header_dlfcn_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_dlfcn_h=$ac_header_preproc fi { echo "$as_me:$LINENO: result: $ac_cv_header_dlfcn_h" >&5 echo "${ECHO_T}$ac_cv_header_dlfcn_h" >&6; } fi if test $ac_cv_header_dlfcn_h = yes; then SHLIB_CFLAGS="-fpic" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.so.1.0' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.so.1.0' else SHLIB_CFLAGS="" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".a" RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.a' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.a' fi # FreeBSD doesn't handle version numbers with dots. TCL_UNSHARED_LIB_SUFFIX='`echo ${VERSION} | tr -d .`.a' TCL_LIB_VERSIONS_OK=nodots OSTYPE=neb ;; FreeBSD-*) # FreeBSD 3.* and greater have ELF. SHLIB_CFLAGS="-fpic" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=bsf ;; OpenBSD-*) # OpenBSD/SPARC needs -fPIC, -fpic should be fine for all others. case `machine` in sparc*) SHLIB_CFLAGS="-fPIC" ;; *) SHLIB_CFLAGS="-fpic" ;; esac SHLIB_LD="${CC} -shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.so' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.so' TCL_UNSHARED_LIB_SUFFIX=`echo ${VERSION} | tr -d .`.a TCL_LIB_VERSIONS_OK=nodots OSTYPE=bso ;; NEXTSTEP-*) SHLIB_CFLAGS="" SHLIB_LD="cc -nostdlib -r" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=nxt ;; OSF1-1.0|OSF1-1.1|OSF1-1.2) # OSF/1 1.[012] from OSF, and derivatives, including Paragon OSF/1 SHLIB_CFLAGS="" # Hack: make package name same as library name SHLIB_LD='ld -R -export $@:' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=osf ;; OSF1-1.*) # OSF/1 1.3 from OSF using ELF, and derivatives, including AD2 SHLIB_CFLAGS="-fpic" SHLIB_LD="ld -shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=osf ;; OSF1-[VXT][45]*) # Digital OSF/4 and later SHLIB_CFLAGS="" SHLIB_LD='ld -shared' SHLIB_LD_LIBS="-lc -lsecurity" SHLIB_SUFFIX=".so" OSTYPE=os4 ;; OSF1-[VXT]*) # Digital OSF/1 SHLIB_CFLAGS="" SHLIB_LD='ld -shared -expect_unresolved "*"' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=osf ;; RISCos-*) SHLIB_CFLAGS="-G 0" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".a" OSTYPE=Unkown ;; SCO_SV-3.2*) # Note, dlopen is available only on SCO 3.2.5 and greater. However, # this test works, since "uname -s" was non-standard in 3.2.4 and # below. SHLIB_CFLAGS="-Kpic -belf" SHLIB_LD="ld -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" if uname -X | fgrep 3.2v5 > /dev/null then OSTYPE=sc5 else OSTYPE=sco fi ;; SINIX*5.4*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=snx ;; SunOS-4*) SHLIB_CFLAGS="-PIC" SHLIB_LD="ld" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" # SunOS can't handle version numbers with dots in them in library # specs, like -ltcl7.5, so use -ltcl75 instead. Also, it # requires an extra version number at the end of .so file names. # So, the library has to have a name like libtcl75.so.1.0 RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.so.1.0' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.so.1.0' TCL_UNSHARED_LIB_SUFFIX='`echo ${VERSION} | tr -d .`.a' TCL_LIB_VERSIONS_OK=nodots if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then OSTYPE=gsu else OSTYPE=sun fi ;; SunOS-5*) SHLIB_LD="/usr/ccs/bin/ld -G -z text" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then SHLIB_CFLAGS="-fPIC" OSTYPE=gso else SHLIB_CFLAGS="-KPIC" OSTYPE=sol fi ;; ULTRIX-4.*) SHLIB_CFLAGS="-G 0" SHLIB_SUFFIX=".a" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" SHLIB_LD_LIBS='${LIBS}' OSTYPE=ult ;; UNIX_SV*) SHLIB_CFLAGS="-KPIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=ult ;; esac # If we're running gcc, then change the C flags for compiling shared # libraries to the right flags for gcc, instead of those for the # standard manufacturer compiler. if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then case $system in AIX-*) ;; BSD/OS*) ;; IRIX*) ;; NetBSD-*|FreeBSD-*|OpenBSD-*) ;; RISCos-*) ;; ULTRIX-4.*) ;; *) NATIVE_SHLIB_CFLAGS=$SHLIB_CFLAGS SHLIB_CFLAGS="-fPIC" ;; esac fi if test "X$NATIVE_SHLIB_CFLAGS" = "X" ; then NATIVE_SHLIB_CFLAGS=$SHLIB_CFLAGS fi if test "X$RATLIB_SUFFIX" = "X" ; then RATLIB_SUFFIX='${VERSION}${SHLIB_SUFFIX}' fi if test "X$BUSYLIB_SUFFIX" = "X" ; then BUSYLIB_SUFFIX='${BUSYLIB_VERSION}${SHLIB_SUFFIX}' fi ############################################################################ { echo "$as_me:$LINENO: checking Names of generated text files" >&5 echo $ECHO_N "checking Names of generated text files... $ECHO_C" >&6; } cat >conftest.tcl << EOF cd \$argv source defs.tcl proc variable {v} { global languages foreach l \$languages { puts -nonewline ".messages/text_\${v}_[lindex \$l 0].tcl " } } proc label {a} {} foreach l \$languages { proc [lindex \$l 0] m {} } foreach f [glob *.text] { source \$f } EOF TEXTFILES=`$tclsh conftest.tcl tkrat/Text` rm -fr conftest.tcl { echo "$as_me:$LINENO: result: done" >&5 echo "${ECHO_T}done" >&6; } ############################################################################ ac_config_files="$ac_config_files Makefile tkrat/Makefile lib/Makefile misc/Makefile util/Makefile doc/Makefile" ac_config_files="$ac_config_files test/run" ac_config_files="$ac_config_files tkrat/tkrat" if test "$error" = 1 ; then { echo "$as_me:$LINENO: WARNING: Encountered errors while configuring. You might" >&5 echo "$as_me: WARNING: Encountered errors while configuring. You might" >&2;} { echo "$as_me:$LINENO: WARNING: have to modify lib/Makefile by hand." >&5 echo "$as_me: WARNING: have to modify lib/Makefile by hand." >&2;} fi cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then test "x$cache_file" != "x/dev/null" && { echo "$as_me:$LINENO: updating cache $cache_file" >&5 echo "$as_me: updating cache $cache_file" >&6;} cat confcache >$cache_file else { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) as_nl=' ' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir fi echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.61. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.61, with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2006 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) echo "$ac_cs_version"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header { echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 CONFIG_SHELL=$SHELL export CONFIG_SHELL exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "tkrat/Makefile") CONFIG_FILES="$CONFIG_FILES tkrat/Makefile" ;; "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; "misc/Makefile") CONFIG_FILES="$CONFIG_FILES misc/Makefile" ;; "util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "test/run") CONFIG_FILES="$CONFIG_FILES test/run" ;; "tkrat/tkrat") CONFIG_FILES="$CONFIG_FILES tkrat/tkrat" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } # # Set up the sed scripts for CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "$CONFIG_FILES"; then _ACEOF ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF SHELL!$SHELL$ac_delim PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim PACKAGE_NAME!$PACKAGE_NAME$ac_delim PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim PACKAGE_STRING!$PACKAGE_STRING$ac_delim PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim exec_prefix!$exec_prefix$ac_delim prefix!$prefix$ac_delim program_transform_name!$program_transform_name$ac_delim bindir!$bindir$ac_delim sbindir!$sbindir$ac_delim libexecdir!$libexecdir$ac_delim datarootdir!$datarootdir$ac_delim datadir!$datadir$ac_delim sysconfdir!$sysconfdir$ac_delim sharedstatedir!$sharedstatedir$ac_delim localstatedir!$localstatedir$ac_delim includedir!$includedir$ac_delim oldincludedir!$oldincludedir$ac_delim docdir!$docdir$ac_delim infodir!$infodir$ac_delim htmldir!$htmldir$ac_delim dvidir!$dvidir$ac_delim pdfdir!$pdfdir$ac_delim psdir!$psdir$ac_delim libdir!$libdir$ac_delim localedir!$localedir$ac_delim mandir!$mandir$ac_delim DEFS!$DEFS$ac_delim ECHO_C!$ECHO_C$ac_delim ECHO_N!$ECHO_N$ac_delim ECHO_T!$ECHO_T$ac_delim LIBS!$LIBS$ac_delim build_alias!$build_alias$ac_delim host_alias!$host_alias$ac_delim target_alias!$target_alias$ac_delim VERSION!$VERSION$ac_delim BUSYLIB_VERSION!$BUSYLIB_VERSION$ac_delim compress!$compress$ac_delim ssh!$ssh$ac_delim tclsh!$tclsh$ac_delim wish!$wish$ac_delim etags!$etags$ac_delim CC!$CC$ac_delim CFLAGS!$CFLAGS$ac_delim LDFLAGS!$LDFLAGS$ac_delim CPPFLAGS!$CPPFLAGS$ac_delim ac_ct_CC!$ac_ct_CC$ac_delim EXEEXT!$EXEEXT$ac_delim OBJEXT!$OBJEXT$ac_delim SET_MAKE!$SET_MAKE$ac_delim INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim INSTALL_DATA!$INSTALL_DATA$ac_delim LN_S!$LN_S$ac_delim AWK!$AWK$ac_delim cp!$cp$ac_delim csuffix!$csuffix$ac_delim CCLIENT_FLAGS!$CCLIENT_FLAGS$ac_delim MEM_DEBUG_FLAGS!$MEM_DEBUG_FLAGS$ac_delim TCL_INCLUDE!$TCL_INCLUDE$ac_delim TK_DEFS!$TK_DEFS$ac_delim XMKMF!$XMKMF$ac_delim CPP!$CPP$ac_delim XINCLUDES!$XINCLUDES$ac_delim SSLDIR!$SSLDIR$ac_delim IMAPFLAGS!$IMAPFLAGS$ac_delim INSTALL_PREFIX!$INSTALL_PREFIX$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim EXTRA_LIBS!$EXTRA_LIBS$ac_delim SHLIB_CFLAGS!$SHLIB_CFLAGS$ac_delim NATIVE_SHLIB_CFLAGS!$NATIVE_SHLIB_CFLAGS$ac_delim SHLIB_LD!$SHLIB_LD$ac_delim SHLIB_LD_LIBS!$SHLIB_LD_LIBS$ac_delim SHLIB_SUFFIX!$SHLIB_SUFFIX$ac_delim RATLIB_SUFFIX!$RATLIB_SUFFIX$ac_delim BUSYLIB_SUFFIX!$BUSYLIB_SUFFIX$ac_delim OSTYPE!$OSTYPE$ac_delim TEXTFILES!$TEXTFILES$ac_delim LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 83; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` if test -n "$ac_eof"; then ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` ac_eof=`expr $ac_eof + 1` fi cat >>$CONFIG_STATUS <<_ACEOF cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof /@[a-zA-Z_][a-zA-Z_0-9]*@/!b end _ACEOF sed ' s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g s/^/s,@/; s/!/@,|#_!!_#|/ :n t n s/'"$ac_delim"'$/,g/; t s/$/\\/; p N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n ' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF :end s/|#_!!_#|//g CEOF$ac_eof _ACEOF # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/ s/:*\${srcdir}:*/:/ s/:*@srcdir@:*/:/ s/^\([^=]*=[ ]*\):*/\1/ s/:*$// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF fi # test -n "$CONFIG_FILES" for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 echo "$as_me: error: Invalid tag $ac_tag." >&2;} { (exit 1); exit 1; }; };; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 echo "$as_me: error: cannot find input file: $ac_f" >&2;} { (exit 1); exit 1; }; };; esac ac_file_inputs="$ac_file_inputs $ac_f" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input="Generated from "`IFS=: echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} fi case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin";; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` { as_dir="$ac_dir" case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 echo "$as_me: error: cannot create directory $as_dir" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= case `sed -n '/datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p ' $ac_file_inputs` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s&@configure_input@&$configure_input&;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&5 echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out"; rm -f "$tmp/out";; *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; esac ;; :H) # # CONFIG_HEADER # _ACEOF # Transform confdefs.h into a sed script `conftest.defines', that # substitutes the proper values into config.h.in to produce config.h. rm -f conftest.defines conftest.tail # First, append a space to every undef/define line, to ease matching. echo 's/$/ /' >conftest.defines # Then, protect against being on the right side of a sed subst, or in # an unquoted here document, in config.status. If some macros were # called several times there might be several #defines for the same # symbol, which is useless. But do not sort them, since the last # AC_DEFINE must be honored. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* # These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where # NAME is the cpp macro being defined, VALUE is the value it is being given. # PARAMS is the parameter list in the macro definition--in most cases, it's # just an empty string. ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' ac_dB='\\)[ (].*,\\1define\\2' ac_dC=' ' ac_dD=' ,' uniq confdefs.h | sed -n ' t rset :rset s/^[ ]*#[ ]*define[ ][ ]*// t ok d :ok s/[\\&,]/\\&/g s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p ' >>conftest.defines # Remove the space that was appended to ease matching. # Then replace #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. # (The regexp can be short, since the line contains either #define or #undef.) echo 's/ $// s,^[ #]*u.*,/* & */,' >>conftest.defines # Break up conftest.defines: ac_max_sed_lines=50 # First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" # Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" # Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" # et cetera. ac_in='$ac_file_inputs' ac_out='"$tmp/out1"' ac_nxt='"$tmp/out2"' while : do # Write a here document: cat >>$CONFIG_STATUS <<_ACEOF # First, check the format of the line: cat >"\$tmp/defines.sed" <<\\CEOF /^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def /^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def b :def _ACEOF sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS echo 'CEOF sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail grep . conftest.tail >/dev/null || break rm -f conftest.defines mv conftest.tail conftest.defines done rm -f conftest.defines conftest.tail echo "ac_result=$ac_in" >>$CONFIG_STATUS cat >>$CONFIG_STATUS <<\_ACEOF if test x"$ac_file" != x-; then echo "/* $configure_input */" >"$tmp/config.h" cat "$ac_result" >>"$tmp/config.h" if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 echo "$as_me: $ac_file is unchanged" >&6;} else rm -f $ac_file mv "$tmp/config.h" $ac_file fi else echo "/* $configure_input */" cat "$ac_result" fi rm -f "$tmp/out12" ;; esac case $ac_file$ac_mode in "test/run":F) chmod +x test/run ;; "tkrat/tkrat":F) chmod +x tkrat/tkrat ;; esac done # for ac_tag { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi tkrat_2.2cvs20100105-dfsg.orig/configure.in000066400000000000000000000623521137544547100203250ustar00rootroot00000000000000dnl This file should be processed by autoconf to generate a configuration dnl script. It has leant heavily from other similar scripts, most from dnl tcl/tk. dnl dnl TkRat software and its included text is Copyright 1996-2004 by dnl Martin Forssn. dnl dnl The full text of the legal notices is contained in the file called dnl COPYRIGHT, included with this distribution. # This varaible tests if we need to warn the user that he/she might have # to modify the generated files error=0 AC_INIT(/lib/rat.h) AC_CONFIG_HEADER(config.h) # Version numbers of tkrat VERSION=2.2 BUSYLIB_VERSION=1.0 AC_SUBST(VERSION) AC_SUBST(BUSYLIB_VERSION) AC_CONFIG_AUX_DIR(misc) AC_ARG_PROGRAM # Define precious variables AC_ARG_VAR(compress, [Path to compress command]) AC_ARG_VAR(ssh, [Path to ssh command]) AC_ARG_VAR(tclsh, [Path to tclsh command]) AC_ARG_VAR(wish, [Path to wish command]) AC_ARG_VAR(etags, [Path to etags command]) dnl Checks for programs AC_PROG_CC AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_AWK AC_PATH_PROG(compress, gzip) if test "x$compress" = "x"; then AC_PATH_PROG(compress, compress) csuffix=.Z else csuffix=.gz fi AC_PATH_PROG(ssh, ssh) AC_SUBST(ssh) AC_SUBST(compress) AC_SUBST(cp) AC_SUBST(csuffix) AC_SUBST(CFLAGS) AC_SUBST(CCLIENT_FLAGS) dnl Check for gcc > 4.0 if test "${GCC}" = "yes"; then AC_MSG_CHECKING(version of GCC) GCC_version=`${CC} --version | sed -n '1s/^[[^ ]]* ([[^(]]*) //;s/ .*$//;1p'` AC_MSG_RESULT(${GCC_version}) fi case "${GCC_version}" in 4*) CCLIENT_FLAGS="-Wno-pointer-sign" ;; esac # # If users has explicitly specified a tclsh-command, then force us to use # that version. # AC_ARG_WITH(tcl, [ --with-tcl=PATH use this version of tclsh (path to binary or bindir)], tclpath=$withval) if test "x$tclpath" != "x"; then if test -x "$tclpath"; then forced_tclsh=$tclpath required_tcl_version=`echo 'puts $tcl_version' | $forced_tclsh` PATH=`dirname $forced_tclsh`:$PATH else PATH=$tclpath:$PATH fi fi # # See if user has specified tcl/tk include directory. If he has then check # files there to see which versions of tclsh and wish are needed. # AC_ARG_WITH(tcl-include, [ --with-tcl-include=DIR directory to look for tcl.h and tk.h in], tcl_with_include=$withval) if test "x$tcl_with_include" != "x"; then if test "x$forced_tclsh" != "x"; then AC_MSG_ERROR('--with-tcl-include can not be used together with --with-tclsh') fi for i in $tcl_with_include ; do if test -r $i/tcl.h ; then MAJOR=`grep TCL_MAJOR_VERSION $i/tcl.h | awk '{print $3}'` MINOR=`grep TCL_MINOR_VERSION $i/tcl.h | awk '{print $3}'` if test $MAJOR$MINOR -lt 83 ; then AC_MSG_ERROR('Specified --with-tcl-include directory contains version $MAJOR.$MINOR. 8.3 or later required') fi required_tcl_version=$MAJOR.$MINOR fi done fi if test "x$required_tcl_version" != "x"; then AC_PATH_PROG(tclsh, tclsh$required_tcl_version, , $PATH) tv=$required_tcl_version if test "x$tclsh" = "x"; then AC_MSG_ERROR('Can not find tcl/tk $required_tcl_version in path') fi else AC_PATH_PROG(tclsh, tclsh8.6, , $PATH) tv=8.6 if test "x$tclsh" = "x"; then AC_PATH_PROG(tclsh, tclsh8.5, , $PATH) tv=8.5 fi if test "x$tclsh" = "x"; then AC_PATH_PROG(tclsh, tclsh8.4, , $PATH) tv=8.4 fi if test "x$tclsh" = "x"; then AC_PATH_PROG(tclsh, tclsh8.3, , $PATH) tv=8.3 fi if test "x$tclsh" = "x"; then AC_PATH_PROG(tclsh, tclsh, , $PATH) tv=`echo 'puts $tcl_version' | $tclsh` changequote(<<, >>)dnl case $tv in 8.[3456789]*) ;; *) tclsh= ;; esac changequote([, ])dnl cmd_without_version=ok fi if test "x$tclsh" = "x"; then AC_MSG_ERROR('Can not find tcl/tk 8.3 or later in path') fi fi PATH=`dirname $tclsh`:$PATH AC_PATH_PROG(wish, wish$tv, , $PATH) if test "x$wish" = "x" -a "x$cmd_without_version" = "xok"; then AC_PATH_PROG(wish, wish, , $PATH) fi if test "x$wish" = "x"; then AC_MSG_ERROR('Can't find matching wish') fi # # Check if we are using memory debugging # AC_MSG_CHECKING(if tclsh is compiled with memory debugging) changequote(<<, >>)dnl out=`echo 'puts [info commands memory]' | $tclsh` changequote([, ])dnl if test "xmemory" = "x$out"; then MEM_DEBUG_FLAGS=-DTCL_MEM_DEBUG AC_MSG_RESULT(yes) else MEM_DEBUG_FLAGS= AC_MSG_RESULT(no) fi AC_SUBST(MEM_DEBUG_FLAGS) # # Locate the tcl/tk libraries and include files. # AC_MSG_CHECKING(tcl.h) tcl_bin_dir=`dirname $tclsh` tcl_bin_dir=`dirname $tcl_bin_dir` changequote(<<, >>)dnl tcl_lib_dir=`echo 'puts [file dirname [file dirname $tcl_library]]' | $tclsh` if test "x$DISPLAY" != "x"; then tk_lib_dir=`echo 'puts [file dirname [file dirname $tk_library]];destroy .'| $wish` fi changequote([, ])dnl tk_bin_dir=`dirname $wish` tk_bin_dir=`dirname $tk_bin_dir` tcl_dirs="$tcl_lib_dir $tk_lib_dir $tcl_bin_dir $tk_bin_dir" tcl_dirs="$tcl_dirs /usr/local /usr/tcl /usr/pd/tcl /usr" for i in $tcl_dirs ; do tcl_include_dirs="$tcl_include_dirs $i/include" done tcl_include_dirs="$tcl_with_include $tcl_include_dirs" tcl_include_dirs="$tcl_include_dirs /usr/include/tcl /usr/include" tcl_include_dirs="$tcl_include_dirs /usr/local/include/tcl" tcl_include_dirs="$tcl_include_dirs /usr/include/tcl$tv" tcl_include_dirs="$tcl_include_dirs /usr/include/tk$tv" tcl_include_dirs="$tcl_include_dirs /usr/local/include/tcl$tv" tcl_include_dirs="$tcl_include_dirs /usr/local/include/tk$tv" tcl_dir=0 for i in $tcl_include_dirs ; do if test -r $i/tcl.h ; then MAJOR=`grep TCL_MAJOR_VERSION $i/tcl.h | awk '{print $3}'` MINOR=`grep TCL_MINOR_VERSION $i/tcl.h | awk '{print $3}'` if test $MAJOR.$MINOR = $tv ; then tcl_dir=$i break fi fi done if test $tcl_dir = 0 ; then AC_MSG_RESULT(no) AC_MSG_WARN([WARNING can't find tcl include files version $tv.]) tcl_dir= fi AC_MSG_RESULT($tcl_dir) tk_dir=0 AC_MSG_CHECKING(tk.h) for i in $tcl_include_dirs ; do if test -r $i/tk.h ; then MAJOR=`grep TK_MAJOR_VERSION $i/tk.h | awk '{print $3}'` MINOR=`grep TK_MINOR_VERSION $i/tk.h | awk '{print $3}'` if test $MAJOR.$MINOR = $tv; then tk_dir=$i break fi fi done if test $tk_dir = 0 ; then AC_MSG_RESULT(no) AC_MSG_WARN([WARNING can't find tk include files version $tv.]) tk_dir= fi AC_MSG_RESULT($tk_dir) if test "$tcl_dir" = "$tk_dir" ; then TCL_INCLUDE=-I$tcl_dir else TCL_INCLUDE="-I$tcl_dir -I$tk_dir" fi AC_SUBST(TCL_INCLUDE) # # Try to locate tkConfig.sh # AC_ARG_WITH(tkconfig, [ --with-tkconfig=DIR directory to look for tkConfig.sh in], tkconfig_with=$withval) AC_MSG_CHECKING(tkConfig.sh) if test "x$tkconfig_with" != "x"; then for i in $tkconfig_with ; do if test -r $i/tkConfig.sh ; then tk_config_test=$i/tkConfig.sh else AC_MSG_ERROR([No tkConfig.sh in $tkconfig_with]) fi done else for i in $tk_dir/../lib $prefix/lib/tk$tv $tk_dir/../../lib/tk$tv; do if test -r $i/tkConfig.sh ; then tk_config_test=$i/tkConfig.sh break fi done fi if test "x$tk_config_test" = "x"; then AC_MSG_ERROR([No tkConfig.sh found]) fi TK_VERSION=`. $tk_config_test ; echo $TK_VERSION` if test $TK_VERSION != $tv; then AC_MSG_ERROR([Found tkConfig.sh (in $tk_config_test) is version $TK_VERSION while I expected $tv.]) fi AC_MSG_RESULT($tk_config_test) AC_SUBST(TK_DEFS) #-------------------------------------------------------------------- # Locate the X11 header files and the X11 library archive. Try # the ac_path_x macro first, but if it doesn't find the X stuff # (e.g. because there's no xmkmf program) then check through # a list of possible directories. Under some conditions the # autoconf macro will return an include directory that contains # no include files, so double-check its result just to be safe. #-------------------------------------------------------------------- AC_PATH_X not_really_there="" if test "$no_x" = ""; then if test "$x_includes" = ""; then AC_TRY_CPP([#include ], , not_really_there="yes") else if test ! -r $x_includes/X11/Intrinsic.h; then not_really_there="yes" fi fi fi if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then AC_MSG_CHECKING(for X11 header files) XINCLUDES="# no special path needed" xinc="no special path needed" AC_TRY_CPP([#include ], , XINCLUDES="nope") if test "$XINCLUDES" = nope; then dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr /X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/ope nwin/include /usr/X11/include /usr/sww/include" for i in $dirs ; do if test -r $i/X11/Intrinsic.h; then AC_MSG_RESULT($i) XINCLUDES=" -I$i" break fi done else AC_MSG_RESULT(ok) fi if test "$XINCLUDES" = nope; then AC_MSG_RESULT(couldn't find any!) XINCLUDES="# no include files found" fi else if test "$x_includes" != ""; then XINCLUDES=-I$x_includes else XINCLUDES="# no special path needed" fi fi AC_SUBST(XINCLUDES) # # Check for openssl installation # If found the build the imap toolkit with ssl-support # This code was based on code found in the portable distribution of OpenSSH # [MH] Enhanced by some parts from cURL http://curl.haxx.se/. To be continued. # default on tryssl=yes AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=PATH], [where to look for SSL, PATH points to the SSL installation (default: many)]) AC_HELP_STRING([--without-ssl], [disable SSL]), tryssl=$withval) if test X"$tryssl" = X"no"; then AC_MSG_WARN(SSL support disabled) IMAPFLAGS="SSLTYPE=none" else if test X"$tryssl" != Xyes; then tryssldir=$tryssl fi saved_LIBS="$LIBS" saved_LDFLAGS="$LDFLAGS" saved_CFLAGS="$CFLAGS" if test "x$prefix" != "xNONE" ; then tryssldir="$tryssldir $prefix" fi AC_CACHE_CHECK([for OpenSSL], ac_cv_openssldir, [ for ssldir in $tryssldir "" /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/share/ssl /usr/local /usr/pkg /opt /opt/openssl /sw ; do if test ! -z "$ssldir" ; then if test ! -f "$ssldir/include/openssl/rand.h" ; then continue fi LDFLAGS="$saved_LDFLAGS -L$ssldir/lib" CFLAGS="$saved_CFLAGS -I$ssldir/include" else LDFLAGS="$saved_LDFLAGS" fi LIBS="$saved_LIBS -lcrypto" # Basic test to check for compatible version and correct linking AC_TRY_RUN( [ #include #include int main(void) { char a[2048]; memset(a, 0, sizeof(a)); RAND_add(a, sizeof(a), sizeof(a)); return(RAND_status() <= 0); } ], [ found_crypto=1 break; ], [ placeholder=0 ], [ placeholder=0 ] ) if test ! -z "$found_crypto" ; then break; fi done if test -z "$ssldir" ; then ssldir="(system)" fi if test -z "$found_crypto" ; then ac_cv_openssldir=no else ac_cv_openssldir=$ssldir fi ]) if test "x$ac_cv_openssldir" != "xno"; then AC_DEFINE(HAVE_OPENSSL) dnl Need to recover ssldir - test above runs in subshell SSLDIR=$ac_cv_openssldir if (test "x$ac_cv_openssldir" = "x(system)") ; then SSLDIR=/usr fi EXTRA_LIBS="$EXTRA_LIBS -L$SSLDIR/lib -lssl -lcrypto" IMAPFLAGS="SSLTYPE=unix SSLDIR=$SSLDIR" AC_SUBST(SSLDIR) else AC_MSG_ERROR(cannot find openssl; try --without-ssl?) fi fi AC_SUBST(IMAPFLAGS) # # Find installation prefix (if any) # AC_ARG_WITH(install-prefix, [ --with-install-prefix=DIR prefix to use when installing files], INSTALL_PREFIX=$withval) AC_SUBST(INSTALL_PREFIX) # Etags is needed for build environment only AC_PATH_PROG(etags, etags) AC_SUBST(etags) dnl check for special libraries # NetBSD have crypt in a special library AC_CHECK_LIB(c, crypt, [true], [have_libcrypt=yes AC_CHECK_LIB(crypt, crypt)]) # AIX needs the s library AC_CHECK_LIB(s, getuserattr) dnl Check for header files AC_ISC_POSIX AC_HEADER_DIRENT AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(fcntl.h unistd.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST dnl Checks for library functions. AC_FUNC_UTIME_NULL AC_CHECK_FUNCS(snprintf) AC_CHECK_FUNCS(strlcpy) AC_CHECK_FUNCS(strlcat) #------------------------------------------------------------------------------ # Find out all about time handling differences. #------------------------------------------------------------------------------ AC_CHECK_HEADERS(sys/time.h) AC_HEADER_TIME AC_STRUCT_TIMEZONE AC_MSG_CHECKING([tm_tzadj in struct tm]) AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_tzadj;], [AC_DEFINE(HAVE_TM_TZADJ) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) AC_MSG_CHECKING([tm_gmtoff in struct tm]) AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_gmtoff;], [AC_DEFINE(HAVE_TM_GMTOFF) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) # # Its important to include time.h in this check, as some systems (like convex) # have timezone functions, etc. # have_timezone=no AC_MSG_CHECKING([long timezone variable]) AC_TRY_COMPILE([#include ], [extern long timezone; timezone += 1; exit (0);], [have_timezone=yes AC_DEFINE(HAVE_TIMEZONE_VAR) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) # # On some systems (eg IRIX 6.2), timezone is a time_t and not a long. # if test "$have_timezone" = no; then AC_MSG_CHECKING([time_t timezone variable]) AC_TRY_COMPILE([#include ], [extern time_t timezone; timezone += 1; exit (0);], [AC_DEFINE(HAVE_TIMEZONE_VAR) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) fi # # AIX does not have a timezone field in struct tm. When the AIX bsd # library is used, the timezone global and the gettimeofday methods are # to be avoided for timezone deduction instead, we deduce the timezone # by comparing the localtime result on a known GMT value. # if test "`uname -s`" = "AIX" ; then AC_CHECK_LIB(bsd, gettimeofday, libbsd=yes) if test $libbsd = yes ; then AC_DEFINE(USE_DELTA_FOR_TZ) fi fi # # Some systems require a -lcrypt # if test "$have_libcrypt" = yes ; then EXTRA_LIBS="$EXTRA_LIBS -lcrypt" fi AC_SUBST(EXTRA_LIBS) ######################################################################### # The following code checks the system type and defines variables for # # building of shared libraries and imap. # # It is mostly taken from tcl8.0p2 # ######################################################################### # Step 1: set the variable "system" to hold the name and version number # for the system. This can usually be done via the "uname" command, but # there are a few systems, like Next, where this doesn't work. AC_MSG_CHECKING([system version (for imap & dynamic loading)]) if test -f /usr/lib/NextStep/software_version; then system=NEXTSTEP-`awk '/3/,/3/' /usr/lib/NextStep/software_version` else system=`uname -s`-`uname -r` if test "$?" -ne 0 ; then AC_MSG_RESULT([unknown (can't find uname command)]) system=unknown else # Special check for weird MP-RAS system (uname returns weird # results, and the version is kept in special file). if test -r /etc/.relid -a "X`uname -n`" = "X`uname -s`" ; then system=MP-RAS-`awk '{print $3}' /etc/.relid` fi if test "`uname -s`" = "AIX" ; then system=AIX-`uname -v`.`uname -r` fi AC_MSG_RESULT($system) fi fi # Step 2: check for existence of -ldl library. This is needed because # Linux can use either -ldl or -ldld for dynamic loading. AC_CHECK_LIB(dl, dlopen, have_dl=yes, have_dl=no) # Step 3: set configuration options based on system name and version. fullSrcDir=`cd $srcdir; pwd` RATLIB_SUFFIX="" BUSYLIB_SUFFIX="" TCL_UNSHARED_LIB_SUFFIX="" TCL_LIB_VERSIONS_OK=ok case $system in changequote(<<, >>)dnl AIX-4.[[2-9]]) changequote([, ])dnl SHLIB_CFLAGS="" SHLIB_LD="$fullSrcDir/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" AIX=yes RATLIB_SUFFIX='${VERSION}.a' BUSYLIB_SUFFIX='${BUSYLIB_VERSION}.a' OSTYPE=Unkown ;; AIX-*) SHLIB_CFLAGS="" SHLIB_LD="$fullSrcDir/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" RATLIB_SUFFIX='${VERSION}.a' BUSYLIB_SUFFIX='${BUSYLIB_VERSION}.a' case "`uname -v`.`uname -r`" in 4.1) OSTYPE=a41 ;; 3.2) OSTYPE=a32 ;; *) OSTYPE=aix ;; esac ;; BSD/OS-2.1*) SHLIB_CFLAGS="" SHLIB_LD="shlicc -r" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" OSTYPE=bsd ;; BSD/OS-3*|BSD/OS-4*) SHLIB_CFLAGS="" SHLIB_LD="shlicc -r" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" OSTYPE=bs3 ;; Darwin-7.*) SHLIB_CFLAGS="" SHLIB_LD="libtool -dynamic -single_module -flat_namespace -undefined suppress -multiply_defined suppress" SHLIB_LD_LIBS='-lSystem -lcc_dynamic' SHLIB_SUFFIX=".dylib" test -e /sw/include/openssl && OSTYPE=fnk || OSTYPE=osx ;; Darwin-8.*) SHLIB_CFLAGS="" SHLIB_LD="libtool -dynamic -single_module -flat_namespace -undefined suppress -multiply_defined suppress" SHLIB_LD_LIBS='-lSystemStubs' SHLIB_SUFFIX=".dylib" test -e /sw/include/openssl && OSTYPE=fnk || OSTYPE=osx ;; dgux*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" RATLIB_SUFFIX=".so" BUSYLIB_SUFFIX=".so" OSTYPE=d-g ;; HP-UX-*.08.*|HP-UX-*.09.*) AC_CHECK_LIB(dld, shl_load, have_dld=yes, have_dld=no) if test "$have_dld" = yes ; then SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".sl" fi OSTYPE=hpp ;; HP-UX-*.10.*|HP-UX-*.11.*) AC_CHECK_LIB(dld, shl_load, have_dld=yes, have_dld=no) if test "$have_dld" = yes ; then SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".sl" fi OSTYPE=hpx ;; IRIX-4.*) SHLIB_CFLAGS="-G 0" SHLIB_SUFFIX=".a" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" SHLIB_LD_LIBS='${LIBS}' RATLIB_SUFFIX='${VERSION}.a' BUSYLIB_SUFFIX='${BUSYLIB_VERSION}.a' OSTYPE=sgi ;; IRIX-5.*) SHLIB_CFLAGS="" SHLIB_LD="ld -shared -rdata_shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=sgi ;; IRIX-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -shared -rdata_shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then OSTYPE=gsg else OSTYPE=sg6 fi ;; IRIX64-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -32 -shared -rdata_shared -rpath /usr/local/lib" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=sg6 ;; Linux*) SHLIB_CFLAGS="-fPIC" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" if test "$have_dl" = yes ; then SHLIB_LD="${CC} -shared" else SHLIB_LD="ld -shared" fi AC_CHECK_LIB(pam, pam_start, have_pam=yes, have_pam=no, -lpam_misc) if test "$have_pam" = yes ; then OSTYPE=lnp EXTRA_LIBS="$EXTRA_LIBS -L$SSLDIR/lib -lpam -lpam_misc" else if test "$have_libcrypt" = yes ; then OSTYPE=slx else OSTYPE=lnx fi fi ;; MP-RAS-02*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=Unkown ;; MP-RAS-*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=Unkown ;; NetBSD-*|FreeBSD-[[12]].*) # Not available on all versions: check for include file. AC_CHECK_HEADER(dlfcn.h, [ SHLIB_CFLAGS="-fpic" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.so.1.0' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.so.1.0' ], [ SHLIB_CFLAGS="" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".a" RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.a' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.a' ]) # FreeBSD doesn't handle version numbers with dots. TCL_UNSHARED_LIB_SUFFIX='`echo ${VERSION} | tr -d .`.a' TCL_LIB_VERSIONS_OK=nodots OSTYPE=neb ;; FreeBSD-*) # FreeBSD 3.* and greater have ELF. SHLIB_CFLAGS="-fpic" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=bsf ;; OpenBSD-*) # OpenBSD/SPARC needs -fPIC, -fpic should be fine for all others. case `machine` in sparc*) SHLIB_CFLAGS="-fPIC" ;; *) SHLIB_CFLAGS="-fpic" ;; esac SHLIB_LD="${CC} -shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.so' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.so' TCL_UNSHARED_LIB_SUFFIX=`echo ${VERSION} | tr -d .`.a TCL_LIB_VERSIONS_OK=nodots OSTYPE=bso ;; NEXTSTEP-*) SHLIB_CFLAGS="" SHLIB_LD="cc -nostdlib -r" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=nxt ;; OSF1-1.0|OSF1-1.1|OSF1-1.2) # OSF/1 1.[012] from OSF, and derivatives, including Paragon OSF/1 SHLIB_CFLAGS="" # Hack: make package name same as library name SHLIB_LD='ld -R -export $@:' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=osf ;; OSF1-1.*) # OSF/1 1.3 from OSF using ELF, and derivatives, including AD2 SHLIB_CFLAGS="-fpic" SHLIB_LD="ld -shared" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=osf ;; changequote(<<, >>)dnl OSF1-[VXT][45]*) changequote([, ])dnl # Digital OSF/4 and later SHLIB_CFLAGS="" SHLIB_LD='ld -shared' SHLIB_LD_LIBS="-lc -lsecurity" SHLIB_SUFFIX=".so" OSTYPE=os4 ;; changequote(<<, >>)dnl OSF1-[VXT]*) changequote([, ])dnl # Digital OSF/1 SHLIB_CFLAGS="" SHLIB_LD='ld -shared -expect_unresolved "*"' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=osf ;; RISCos-*) SHLIB_CFLAGS="-G 0" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".a" OSTYPE=Unkown ;; SCO_SV-3.2*) # Note, dlopen is available only on SCO 3.2.5 and greater. However, # this test works, since "uname -s" was non-standard in 3.2.4 and # below. SHLIB_CFLAGS="-Kpic -belf" SHLIB_LD="ld -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" if uname -X | fgrep 3.2v5 > /dev/null then OSTYPE=sc5 else OSTYPE=sco fi ;; SINIX*5.4*) SHLIB_CFLAGS="-K PIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=snx ;; SunOS-4*) SHLIB_CFLAGS="-PIC" SHLIB_LD="ld" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" # SunOS can't handle version numbers with dots in them in library # specs, like -ltcl7.5, so use -ltcl75 instead. Also, it # requires an extra version number at the end of .so file names. # So, the library has to have a name like libtcl75.so.1.0 RATLIB_SUFFIX='`echo ${VERSION} | tr -d .`.so.1.0' BUSYLIB_SUFFIX='`echo ${BUSYLIB_VERSION} | tr -d .`.so.1.0' TCL_UNSHARED_LIB_SUFFIX='`echo ${VERSION} | tr -d .`.a' TCL_LIB_VERSIONS_OK=nodots if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then OSTYPE=gsu else OSTYPE=sun fi ;; SunOS-5*) SHLIB_LD="/usr/ccs/bin/ld -G -z text" SHLIB_LD_LIBS='${LIBS}' SHLIB_SUFFIX=".so" if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then SHLIB_CFLAGS="-fPIC" OSTYPE=gso else SHLIB_CFLAGS="-KPIC" OSTYPE=sol fi ;; ULTRIX-4.*) SHLIB_CFLAGS="-G 0" SHLIB_SUFFIX=".a" SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" SHLIB_LD_LIBS='${LIBS}' OSTYPE=ult ;; UNIX_SV*) SHLIB_CFLAGS="-KPIC" SHLIB_LD="cc -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" OSTYPE=ult ;; esac # If we're running gcc, then change the C flags for compiling shared # libraries to the right flags for gcc, instead of those for the # standard manufacturer compiler. if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then case $system in AIX-*) ;; BSD/OS*) ;; IRIX*) ;; NetBSD-*|FreeBSD-*|OpenBSD-*) ;; RISCos-*) ;; ULTRIX-4.*) ;; *) NATIVE_SHLIB_CFLAGS=$SHLIB_CFLAGS SHLIB_CFLAGS="-fPIC" ;; esac fi if test "X$NATIVE_SHLIB_CFLAGS" = "X" ; then NATIVE_SHLIB_CFLAGS=$SHLIB_CFLAGS fi if test "X$RATLIB_SUFFIX" = "X" ; then RATLIB_SUFFIX='${VERSION}${SHLIB_SUFFIX}' fi if test "X$BUSYLIB_SUFFIX" = "X" ; then BUSYLIB_SUFFIX='${BUSYLIB_VERSION}${SHLIB_SUFFIX}' fi AC_SUBST(SHLIB_CFLAGS) AC_SUBST(NATIVE_SHLIB_CFLAGS) AC_SUBST(SHLIB_LD) AC_SUBST(SHLIB_LD_LIBS) AC_SUBST(SHLIB_SUFFIX) AC_SUBST(RATLIB_SUFFIX) AC_SUBST(BUSYLIB_SUFFIX) AC_SUBST(OSTYPE) ############################################################################ dnl Get names of text files AC_MSG_CHECKING([Names of generated text files]) changequote(<<>>)dnl cat >conftest.tcl << EOF cd \$argv source defs.tcl proc variable {v} { global languages foreach l \$languages { puts -nonewline ".messages/text_\${v}_[lindex \$l 0].tcl " } } proc label {a} {} foreach l \$languages { proc [lindex \$l 0] m {} } foreach f [glob *.text] { source \$f } EOF changequote([, ])dnl TEXTFILES=`$tclsh conftest.tcl tkrat/Text` rm -fr conftest.tcl AC_MSG_RESULT(done) AC_SUBST(TEXTFILES) ############################################################################ dnl Generate output AC_CONFIG_FILES(Makefile tkrat/Makefile lib/Makefile misc/Makefile util/Makefile doc/Makefile) AC_CONFIG_FILES(test/run, [chmod +x test/run]) AC_CONFIG_FILES(tkrat/tkrat, [chmod +x tkrat/tkrat]) if test "$error" = 1 ; then AC_MSG_WARN([ Encountered errors while configuring. You might]) AC_MSG_WARN([ have to modify lib/Makefile by hand.]) fi AC_OUTPUT tkrat_2.2cvs20100105-dfsg.orig/doc/000077500000000000000000000000001137544547100165515ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/doc/.cvsignore000066400000000000000000000000111137544547100205410ustar00rootroot00000000000000Makefile tkrat_2.2cvs20100105-dfsg.orig/doc/LSM000066400000000000000000000011531137544547100171270ustar00rootroot00000000000000Begin3 Title: TkRat - an X11 based Mail User Agent Version: 1.1 Entered-date: 13JAN98 Description: TkRat is a Mail User Agent that speaks MIME. It has an interface built in tcl/tk and a kernel written in C. Keywords: MUA, mail, IMAP, POP, MIME, tcl/tk, X11, pgp Author: maf@dtek.chalmers.se (Martin Forssen) Maintained-by: Primary-site: ftp.md.chalmers.se /pub/tkrat 1361k tkrat-1.1.tar.gz Alternate-site: ftp.sunet.se /pub/unix/mail/tkrat 1361k tkrat-1.1.tar.gz Original-site: Platforms: X11 Unix with tcl7.5/tk4.1 or later Copying-policy: Copyrighted but free. End tkrat_2.2cvs20100105-dfsg.orig/doc/Makefile.in000066400000000000000000000025331137544547100206210ustar00rootroot00000000000000############################################################################# # TkRat software and its included text is Copyright 1996-2004 by # # Martin Forssen. # # # # The full text of the legal notices is contained in the file called # # COPYRIGHT, included with this distribution. # ############################################################################# # Installation directories datarootdir = @datarootdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ MAN_DIR = @mandir@ BIN_DIR = @bindir@ DATA_DIR = @datadir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` LIB_DIR = @libdir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` INSTALL = @INSTALL@ INSTALL_PREFIX = @INSTALL_PREFIX@ #-------- No changes should be done below -------- TARGETS = tkrat.1 all: ${TARGETS} install.shared: ${TARGETS} if test ! -d ${INSTALL_PREFIX}${MAN_DIR} ; then \ ${INSTALL} -m 0755 -d ${INSTALL_PREFIX}${MAN_DIR} ;\ fi if test ! -d ${INSTALL_PREFIX}${MAN_DIR}/man1 ; then \ ${INSTALL} -m 0755 -d ${INSTALL_PREFIX}${MAN_DIR}/man1 ;\ fi ${INSTALL} -m 0644 tkrat.1 ${INSTALL_PREFIX}${MAN_DIR}/man1/tkrat.1 install: install.shared clean: tkrat_2.2cvs20100105-dfsg.orig/doc/changes000066400000000000000000004704531137544547100201210ustar00rootroot00000000000000This file lists the changes made to TkRat between versions. It is much more detailed than the changes shown to the user when starting a new version. 100105: (bug fix) Fixed various issues with how bad passphrases where handled when signing or decrypting messages. 100105: (bug fix) Fixed bug in advanced pop mode of first use wizard. 090630: (bug fix) Fixed a number of compilation warnings. 071230: (bug fix) Handle new configure events generated by tcl/tk 8.5. 070917: (bug fix) Do not dump core if getpwuid() fails. 070711: (bug fix) Sending process could hang. 070711: (bug fix) Did not handle non us-ascii characters in subject when pretty printing page heading. 070711: (enhancement) Now also parses expressions like "subject not has foo". 070711: (bug fix) Group by expression often ignored 'not'. 070618: (bug fix) Could hang when sending to program and the sending program exited before reading the whole message. 070417: (bug fix) Reverted to using $env(USER) instead of $env(LOGNAME) since the c-code make sure the previous is set. 070416: (enhancement) Now builds on MacOSX 10.4 (thanks to Tony Stout for the patch). 070209: (bug fix) PGP bug fixes. TkRat did not recongnize clearsigned messages. And it was not possible to see the output from pgp again after having aborted the operation. 070129: (bug fix) Make sure that a message is selected after a filter is cleared. If no messages were selected then select the first message. 070129: (feaure) Added horisontal scrollbar to show text widget which appears if an embedded html message or image is too wide. 070129: (feature) Added support for Tkhtml 3 061115: (bug fix) Continue composing a draft message could forget all but the first line of recipients. 061113: (enhancement) Removed 'Mail regarding' text in printouts and made addresses print one per line. 061110: (bug fix) Fixed problems with mime type detection when attaching stuff to emails. The routine failed if teh filename contained a ','. This could also lead to a crash of TkRat. 061110: (enhancement) Recognize the german aw: in subjects 061024: (bug fix) User $env(LOGNAME) instead of $env(USER) since not all shells generate the latter. 061020: (bug fix) Current version of tnef can not read from stdin. 060915: (bug fix) When searching the database in "and"-mode require all of the specified keywords to be present. 060905: (bug fix) Made database times be relative to midnight (00:00 and 24:00) so that searches with start "yesterday" actually means "yesterday 00:00" 060730: (feature) Added entry to message menu which flags all messages with the same subject. 060730: (feature) It is now possible to drag-select when creating a group in a separate window. 060727: (bug fix) Grouping messages could mark too many messages. This could happen if one marked more than one message via window or by expression in a large folder (more than 1000 messages). The nasty thing about this bug was that the extra flagged messages did not show up as flagged unless one reopened the folder. 060719: (feature) Added a database info dialog which shows which keywords, expiration time and action a message has. It is also possible to change the values for a message, or a group of messages, via this dialog. 060713: (feature) A new database information window which shows number of messages, totoal size, start and end-date as well as the used jeywords (and usage count). It is also possible to open a folder with the messages which have a certain keyword via this dialog. 060707: (feature) Made escape act as cancel/close in most windows. 060707: (feature) Added mailto URL handling. It is now possible to give an mailto link on the command line and it will open a compose window with the relevant fields filled in. 060706: (bug fix) Show text part if html part has been deleted. 060706: (feature) Support html images embedded in a multipart/related bodypart. 060705: (feature) Include aliases in address completion list. 060705: (bug fix/feature) Numerous changes to PGP handling. - Error message windows did not always show up - Improved handling of status from gnupg - Allow the user to retry an aborted PGP operation later (by pressing the sig button) 060515: (bug fix) Fixed a number of bugs in the auto-wrap code. 060412: (bug fix) TkRat could drop part of the address list when postponing a message. 060412: (feature) Speeded up mail copy again. We do not need to actually open the destination folder, which is a potentially time-consuming operations. The new code does not open the destination folder. 060330: (bug fix) Fixed insidious syntax error in configure-script. 060328: (bug fix) Fixed issue with address fields when bouncing messages. 060320: (bug fix) Tidied up handling of external editors. 060315: (bug fix) Fixed error on apply in preferences/compose 060116: (bug fix) Decoding an encoded text bodypart containing a multibyte encoding (like utf-8) could mangle some characters. 060115: (bug fix) The locally saved copy of an outgoing message could be truncated if the message was encoded in utf-8. 060127: (bug fix) Inserting certain messages into a disconnected folder made it confused. The end result was that TkRat dowloaded all messages again. 060125: (bug fix) Bounced messages were always sent with the default role. 060124: (bug fix) Show generated headers did not show the real generated addresses and header lines. 060124: (bug fix) Fixed problem with continuing composing messages with bcc address only. 060117: (bug fix) The day of week was always sunday in the list of messages. 060116: (bug fix) Check that list format entered in preferences is valid. 051130: (bug fix) Could crash when pretty printing unusual headers. 051130: (feature) Added entries to the "New Folder window" menu which searches teh database for messages with teh same subject as the current message or involving the current messages sender. 051128: (change) Try to keep internal date intact when moving messages. 051123: (change) Speed up copy of messages to a not-open folder. 051121: (feature) Set charset parameter on text attachments. 051121: (change) Show exact number of messages in folder. 051116: (bug fix) Do not change role when moving to a new folder unless the new folder has an explicit role set. 050930: (bug fix) Check filenames for valid characters. 050929: (bug fix) Use provided filename, if any, when laumching mailcap viewer. 050928: (bug fix) TkRat did send all messages in the outgoing folder, even if they were marked for deletion. 050927: (feature) Now automatically tries to add a '--mime' to the mimeprog (default file) to see if that generates mime-type output. Falls back to the old style if it is not supported. Also removed mimeprog from preferences GUI. 050926: (bug fix) When changing role the from addresses did not always get updated since the check got confused by teh address rewriting. 050926: (bug fix) Handle (i.e. show error message) the case where a message is being deleted while there is a print dialog open for it. The same issue existed when bouncing and deleting attachments. 050922: (feature) It is now possible to specify arguments to the spell-checking command. 050919: (bug fix) Attachments could seem to dissapear when copying a message from a database folder to an mbx folder. 050919: (bug fix) Fixed minor bug in address field where it did not always break lines when needed. 050919: (bug fix) RatUp_IsMe was not called for a lot of addresses. 050914: (change) Updated embedded c-client library to version 2004f 050908: (bug fix) Changed how aliases are exapnded for display. Now the alias key is kept as the address portion. 050902: (bug fix) Do not accept aliases with empty name or address. 050825: (feature) Extract addresses from a group of messages. 050825: (feature) Reworked address book window. Now addresses are edited in a popup dialog. 050819: (bug fix) Reimport all folders could leave old orphaned folders in the structures. 050818: (bug fix) Include angle brackets in message-id. 050816: (bug fix) Wrapping of replies could cut off the last line. 050815: (change) Terminology change: changed "disconnected" to "offline". 050815: (feature) Modified expiration code to ask for confirmation if enough time has passed since the last expire. 050812: (feature) Made group menu mimic message menu. 050809: (bug fix) Made it possible to apply an empty filter (if another filter is active). Also made it possible to move the focus by just pressing left mousebutton outside filter entry field. 050809: (feature) Improved synchronization code so disconnected folders should synchronize faster. 050807: (bug fix) IMAP toolkit was not properly rebuilt after a reconfigure. 050724: (bug fix) Eliminate warnings from gcc 4.0. 050722: (feature) List how many flagged messages there are in the group menu. 050720: (change) Added /usr/share/ssl as a location where configure looks for ssl. 050718: (change) Always draw a horizontal line between bodyparts when showing a message. 050717: (feature) Made it possible to create clones of messages with the attachments deleted. 050715: (change) Changed build process so that generated text-files are placed in a subdirectory during build. 050714: (feature) TkRat can now list and save files contained in winmai..dat attachments if the tnef command is installed. 050713: (bug fix) A big message stored in the database could get corrupted when copied to another folder. 050713: (bug fix) A saerch in thye database might have missed some early messages since the start-date calculation might be wrong. 050713: (feature) It is now possible to change the way dates are formatted in the list of messages. 050710: (feature) Changed most numerical preferences entries to spinboxes (if running tk8.4 or later). Also added validation functions to them which prevents invalid values. 050710: (removed feature) Removed code for taking mail from netscape. I think it is obsolete. 050710: (feature) Reinstated smtp logging. 050709: (feature) Reorganized preferences window. Removed a lot of settings from the GUI. Simplified others. 050706: (feature) Automatically set charset option. 050705: (bug fix) Added missing keys to compose keydef window. 050705: (bug fix) Delete the local copy of a disconnected folder when it is converted to a normal imap-folder. 050704: (feature) Remember last used directory between sessions. 050703: (change) Moved 'Compose' and 'Find...' from message menu to TkRat menu. This made it possible to unify the 'Message' and context menu code. 050703: (feature) Made it possible to toggle the A-flag of messages. 050701: (bug fix) Fixed behavior when no spell-check program was found. 050630: (feature) Now generates message-id. 050630: (bug fix) Fixed saving of window positions. 050608: (bug fix) Do not add domains to addressesp in history list. 050531: (bug fix) Address history misshandled addresses with special characters in them. 050527: (bug fix) The spell checker occasionally missed words. 050526: (feature) Translate header names when pretty printing. 050525: (bug fix) Improved behavior of address auto-completion window. 050511: (feature) Autocomplete of addresses. TkRat remembers the addresses you send email to and when sending it pops up a esmall window with suggested completions. 050511: (feature) Retain name part of addresses when replying. 050413: (bug fix) Fixed problemn with aliases when postponing messages. 050411: (change) Changed how the accelerators are represented in the menus. 050408: (bug fix) Mark backup messages as backups and check if any of them should be deleted one minute after startup. This shoudl clean out any old stale backups. 050406: (feature) Applied patch from anonymous which adds a list of recently used folders to the folder list and also highlights folders with unread messages in them. 050406: (bug fix) Disregard case in alias extract dialog when checking if an alias already exists. Patch from anonymous contributor. 050405: (bug fix) Added patch by Gernot Zander which provides a references header for replies. 050404: (bug fix) Fixed problems which made it impossible to add a new POP folder. 050324: (feature) Make a backup copy of the current message whenever the compose window is closed (even when sending) and keep it for 60 seconds (adjustable). 050322: (bug fix) Could fail to copy/move a message from an unix format mailbox to an mbx format mailbox. 050321: (feature) Made ^W close all windows. 050321: (bug fix) Fix problem with struct menu when a content-type contained a space. 050308: (bug fix) ^W did sometimes work even if only one window was open. 050308: (bug fix) Fixed various bugs with bcc: - Changing role did reset the bcc field - Bcc was not preserved if the message was postponed - TkRat would crash when resuming composition of a message with only bcc recpients. 050305: (bug fix) Fixed numerous problems with sending: - Save outgoing failed for the next message if sending failed - User was not notified when a save of a sent message failed - Manual network sync only sent one message 050305: (bug fix) Pressing enter in the filter window caused focus to move to the message list and therefore arrows scrolled both the message list and teh current message. 050303: (bug fix) Fixed various problems related to changing teh spell checker in teh preferences window. 050301: (feature) Copy message does not advance the current message any longer. Added copy to context-menu. Fixed problem where context-menu always moved the same message. 050228: (feature) Changed some menu settings in teh compose window (wrap and autospell) to not only change teh current behavior but also the default for future composes. 050223: (feature) Changed sort-order entry in folder menu to also update the default sort order. Therefore I could remove the sort order settings from preferences. 050221: (bug fix) Fixed problem in file selector which sometimes made backspace not work. 050218: (feature) Added doubleclick handling in message list. The action is either reply to all or continue compose depending on which folder is open. 050214: (bug fix) Fixed misc bugs regarding filtered folders. 050213: (bug fix) Fixed potential "Index out of bounds" error which often struck in the 'Drafts' folder. 050203: (feature) Added patches from Stefan Richter to build on MacOSX with Fink. 050127: (feature) Made it possible to store a snapshot of a message while composing it. 050127: (change) Changed text in dialog which appears when there is more than one folder window open when quitting. 050126: (feature) Made text search be interactive. 050120: (bug fix) TkRat could fail to show the first small part of the text when showing a multipart message in a database folder. 050119: (bug fix) Improved algorithm for wrapping messages we are replying to. It now handles tabs and enumerated lists better. 050112: (feature) Now makes priodic backups when composing a message. The backup is intended for use in case of a crash. 050111: (change) Renamed "Held Messages" to "Drafts". 050109: (feature) Increased speed of inline text spell checking by caching words. 050109: (feature) Reorganized menus. 050107: (bug fix) Fixed core dump which could happen when a database folder was updated. 041221: (bug fix) Make sender child process ignore SIGINT so it does not die when using a debugger. 041221: (feature) Now remembers size and position of file dialog. 041220: (feature) Made it possible to filter the list of messages. 041220: (bug fix) Changed spell-checking preferences setting to be useful. That is teh list of dictionaries is updated when the command is changed and the command is checked. 041202: (feature) Moved folder information part into menu-bar and compacted it. This is needed to make room for the coming filtering. 041123: (feature) Added support for aspell as spell check command. And made automatic spellchecking faster by only checking complete words. 041117: (feature) Implemented 'N' transformation in list command. This make it possible to avoid gettin 'To:' in the attribution line when answering your own messages. 041116: (feature) It is now possible to copy an embedded message into any folder. Use the structure menu (or right click). 041115: (bug fix) Do not include smtp_passwd in bug-reports. 041112: (feature) Changed a lot of preferences from being true/false list selections to be checkbuttons. 041108: (bug fix) Fixed illegal double negation syntax. 041108: (feature) Changed database searches so teh interval is specified outside the other search parameters. This means that it is also usable with "or"-searches. 041108: (bug fix) When searching the database only require one matching word per line even if using "and"-mode. 041018: (bug fix) Removed last character when inserting messages into database. 041013: (bug fix) Rewrote re;-handling to handle more variants and also handle mailing lists where it may appear both before and after the list identifier [listname]. 041011: (bug fix) Could crash if a folder was deleted outside TkRat while TkRat had it open. 041007: (bug fix) Corrected German spelling. Thanks goes to Volker Reichelt . 041005: (bug fix) Printing of non us-ascii characters gave strange results if the machines native charset was not iso8859-1. 041004: (feature) Added automatic spell-checking to compose window. 041004: (feature) Rewrote undo handling in compose window to actually work and added redo functionality. 040927: (bug fix) Mangled port numbers when editing IMAP servers. 040927: (bug fix) PrettyPrinting sometimes used a very big font. 040924: (bug fix) gave tcl error when entering invalid addresses. 040910: (bug fix) More fixes for erros which could cocur when folder menus were torn off. 040830: (bug fix) Gave error message when creating a new folder when a folder menu had been torn off. 040827: (bug fix) Sometimes when deleting the last message the message list scrolled horizontally to the right. 040824: (bug fix) Fixed problem with printing messages which contained the tab character. Also made resulting postscript a little bit smaller. 040824: (feature) Now continue composing appears in the to of the message menu (when in held or outgoing). 040819: (bug fix) The message count of open folders did not get updated in detached folder menus. 040818: (bug fix) Would crash if an address book was added under another name than it was initially created. Also did not update list of aliases when adding adddress books. 040818: (bug fix) Fixed setting of content-description and content-id when creating attachments. 040812: (bug fix) Warn if one tries to define a new alias with an old name. 040811: (bug fix) Added compatibility version fo labelframe command. 040805: (feature) Multi-language spell checking. 040803: (bug fix) Fixed error in vfolderdef window when one tried to use the default value on teh first screen. 040725: (feature) It is now posssible to configure the title of the watcher window. 040725: (bug fix) Failed to properly encode non us-ascii characters in outgoing address lines. 040725: (feature) Added option which makes makes html the last choice when selecting which of a number of alternate bodies to show. 040725: (bug fix) Remember scroll position in message bodies (within one session). 040724: (feature) Make configure prefer version 8.5 of tcl/tk. 040724: (bug fix) Fixed handling of expressions when creating groups. Backslashes were removed before the regexp was applied. 040721: (feature) If you use tcl/tk 8.5 then images and html text is shown without vertical scrollbars since the text widget has been fixed. 040712: (enhancement) Show popup struct menu on right-click in message headers as well. 040712: (feature) Show provided filenames of bodyparts in struct menu. 040712: (feature) List sizes of attachments while composing. 040712: (enhancement) Handle disk-full conditions with disconnected folders. 040711: (enhancement) Made short popup messages look better. 040710: (bug fix) Added missing bind to message list so that 1-drag does not select text in the message list. 040710: (feature) Changed color sets so data areas have different background. 040709: (bug fix) Plugged variable leaking when composing or using file selector. 040709: (feature) It is now possible to forward (separately or in one message) and bounce a group of messages. 040708: (feature) Now creates an icon window so there should be an color icon (depending on the window manager). 040707: (feature) Added copy operations alongside move. 040707: (feature) Improved database search window. Changes: * Easier to use * A line which searches all address fields * One can now specify a time interval to search within 040707: (bug fix) It is now also possible to remove the flagging from messages i the "create in window" grouping window. 040706: (bug fix) Creating group in window sometimes failed to flag the messages. 040706: (bug fix) Remember PGP settings when holding messages. 040706: (feature) It is now possible to set which pgp key to use and the default pgp actions for each entry in the address book. 040705: (feature) New pgp details window where one can see which pgp keys will be used for a message. 040705: (cleanup) Changed how aliases are expanded. 040629: (bug fix) Did crash when creating new aliases. 040510: (bug fix) Fixed problem where running the tkrat command in the build directory did not pick up the libs from the build directory. This happened if the package had been installed in the tcl/tk lib directory. 040317: (bug fix) Avoid core dump when moving messages to mh-folders. 040225: (bug fix) Removed setgrid arg so that the main folder window is resized in pixels rather than characters. This works around a bug in tk which otherwise forces a rather large minimum size of the window. 040218: (bug fix) Ignore control characters and newlines when drawing list of messages. 040218: (feature) Did internal changes to the RatAlias commands. Address book entries can now specify which pgp key to use for the recipient. 040127: (feature) Removed ability to change how much aliases are expanded while in the compose window. 040115: (bug fix) Now closes all open instances of a folder if you change the definition of the underlying folder. 040113: (feature) Fixed so the grouping window does not forget the selection state when new emails arrive. This makes it much easier to quickly delete lots of email (i.e. spam). 040112: (bug fix) The periodic checking for new email closed any open balloon help text. 040105: (bug fix) Fixed core dump which could occur if you converted an open imap mailbox to a disconnected one and reopened it. 031217: (feature) Changed behavior of address (formerly alias) window. Also added support for pgp operations and key per alias. However this is commented out until compose.tcl supports it. 031123: (feature) PGP signing is now controlled per role. Each role can select the default pgp signing status (on or off). It is also possible to specify which key the message should be signed with. 031119: (feature) Improved PGP key listing code. Now works much better when using GnuPG. You can now also select to list either secret or public keys. 031116: (bug fix) Be more tolerant towards bad folder definitions. 031113: (feature change) Moved the saving of outgoing messages to after they have been sent. 031106: (bug fix) Fixed encoding of line-endings when messages were encoded in quoted-printable. 031105: (feature) Removed the option watcher_name, now uses the localized string instead. 031105: (bug fix) Removed extra sender and reply-to headers when sending. 031103: (bug fix) Incorporated OpenBSD changes from Stuart Cassoff. 031103: (feature) Changed new folder wizard to autmatically figure out the mode (create, use or import). 030929: (feature) Added new german text from Thomas Fetcke. 030905: (bug fix) Escape more dangerous characters in URLs. 030813: (bug fix) Require at least version 2.0 of TkHtml. Also added note of this in the README-file. 030811: (bug fix) Removed the -verbose flag to pkg_mkIndex. This avoids a bug in tcl8.4 which prevented build. 030811: (feature) Changed flagging behavior so when flagging multiple messages all gets set to the same flag value as the first one. 030805: (feature) Reinstated old functionality. Shift-B2 now toggles the flagging for a range of messages. 030805: (feature) Changed from nanosleep() to usleep(). Should improve portability. 030701: (bug fix) Handle aspell (which behaves subtly different from ispell) 030624: (feature) Can now build with tcl/tk 8.5. 030624: (bug fix) Upgraded included c-client to version 2002d. 030617: (bug fix) Some header fields could get corrupted when a pgp operation failed when sending. 030611: (bug fix) Handle localized characters as well as () in subject when doing pretty-print. 030606: (feature) When importing, prune empty subfolders. This mainly effects Cyrus-users. 030605: (feature) Added "Clear selection" button to alias extract window. 030604: (feature) Do not open folders before appending a group of messages to it. This avoids one potentially expensive operation but assumes that IMAP connection caching keeps us from reconnecting for each message and that the underlying driver does not parse the entire mailbox for each append. Normally yhis is not a problem. 030604: (bug fix) Imap caching did not always work which affected IMAP performance negatively. 030528: (bug fix) Escape non us-ascii characters when pretty-printing. 030527: (feature) Improved speed of some group operations (flagging and deletion). This should be most visible when operating on a lot of messages in an IMAP-folder. 030523: (bug fix) Fixed problem where the database warned about inconsistent number of messages in database. it turned out that no messages were lost, but the error message was annoying. 030523: (bug fix) Changed the way parameters are encoded. Now there is an option which controls how to encode them (rfc2047, rfc2231 or both). Rfc2231 is the standard and rfc2047 is illegal. But some mailers (for example Outlook) does not understand rfc2231 but works ok with rfc2047. The default is both. 030516: (bug fix) Correctly handle both rfc2231 and rfc2047 encoded parameters. 030511: (feature) Added keyboard shortcut (Alt-d) for moving message to database. 030501: (feature) Subscribe to all created folders. 030428: (bug fix) The balloon help in the menus did show incorrect texts. 030428: (feature) Added context menu when pressing the third mousebutton over a message in the message-list. 030425: (feature) Writes/reads a file in the tmpdir once every day so that the directory is not removed by /tmp-cleaning programs. 030410: (feature) Applied speedup patch by Don Lewis. 030327: (bug fix) Repaired configure.in-file which the last update broke:-( Replaced --with-tclsh and --with-tcl-bin whith --with-tcl which covers both cases. 030325: (feature) Rewrote the busy-code to use facilities provided by modern versions of tcl/tk. 030321: (feature) Now looks for 8.4 by default (after tclsh). Also added support for 8.5. 030321: (feature) New configuration option --with-tclsh which forces TkRat to use the specified tclsh. 030321: (bug fix) Remove local copy of disconnected folder when deleting the folder. 030307: (bug fix) Handle dying of the sender a bit more gracefully. 030307: (bug fix) Reinstated header-line wrapping of outgoing emails. 030304: (feature) Added proxy support to the html handling. Code from Laurent Duperval. 030203: (feature) Added touched up xpm-version of logo provided by Robin Roe. 030203: (bug fix) Fixed problem where it was possible to drag a pane oustide the window thus making the display kind of confusing. 030128: (bug fix) Fixed problem where horizontal scrollbars appeared even though not really needed (Tk has some confusing definitions of width). 030121: (bug fix) Do not delete files when they are detached (unless they were created by Tkrat). 030114: (gui) Merged menus in New/edit folder window into one. 030113: (feature) Added wizard helping people to set thing up the first time they start TkRat. 021229: (bug fix) Converted preferences pane and vfolderdef pane to rat_scrollframe so scrollbars appears when needed. 021228: (bug fix) Fix so that messages in the preferences window adjust to the width of the window. 021228: (bug fix) Added patch from Marc Herbert which changed the SSL parameters to configure (making them saner). 021224: (feature) Implemented rfc2231 encoding of parameters. 021224: (feature) Changed c-client library to break lines when outputting parameters (if needed). 021221: (bug fix) Duplicated messages when moving multiple messages into a disconnected folder. 021220: (improvement) Improved the way PGP passphrases are cleared out of memory. 021219: (bug fix) The copy you may save of an outgoing message shoudl be marked as read. 021218: (feature) Upograded to imap-2002a toolkit. 021216: (bug fix) Fixed problem where inserting a message into an open db-folder crashed TkRat. 021211: (bug check) Added sanity check to disconnected folder sync. 021126: (bug fix) Fixed problem where the inbox might not be opened on start but rather another folder with the same name. 021029: (bug fix) Fixed problem with sending where a message could be sent twice and then you got an error message from mail_elt. 021001: (bug fix) Fixed subtle problem in configure which hit systems whith the openssl include-files installed but not the libraries. 020930: (feature) A small change with big impact. Changed the default font from bold to non-bold. Also shrunk the padding in all buttons. This gives IMHO a much sleeker look. 020925: (bug fix) Fixed pgp-test to work without a ~/.gnupg. 020925: (bug fix) Made sure every Tcl_DecrRefCount() in the code has a matching Tcl_IncrRefCount(). 020925: (enhancement) Reset the balloon help timer not only on mouse motion events but also button and key events. 020924: (bug fix) Fixed problem when imap-folders does not exist. 020921: (nitfix) Lock interface while creating new folders. 020915: (bug fix) Had problems with pgp/mime messages. 020912: (bug fix) If you had an alias with the same key as a local user name then TkRat did show the local users full name in the address field. The message was however sent to the correct address. 020909: (bug fix) Update new-message count in database folders when reading messages. Unpdate new message count when marking messages as unread. 020909: (feature) Increased delay for balloon help (from .5s to 1.5s). This will hopefully make it less obnoxious. 020908: (bug fix) Fixed core-dump problem when compiled with wish8.4. There was a problem in the busy routines. 020906: (bug fix) Cleaned up file permissions. Now relies almost entirely on the umask. The only exception is the tmp-directory which is still created 0700. The permissions option became obsolete and is now removed. 020904: (bug fix) Replaced most occurrences for "regsub -all" and "SubstArg" with calls to "string map". 020902: (feature) Added support for SMTP authentication. 020830: (feature) Can now force all roles to use the same set of sending preferences. 020827: (feature) TkRat does now enable TLS (if compiled with encryption) when sending email over SMTP. There is a new option to enable/ disable the verification of the server certificate. 020826: (feature) Keep keyboard focus when adding new header entries in the compose window. 020825: (bug fix) URLs which contained '%u' could send tkrat into an eternal loop when pressed. Thanks goes to Marc Herbert. 020823: (feature) Removed baloon help from the message list and message body areas. 020822: (feature) Changed the way new features are presented. Now there will be a popup window whenever a new feature is added, not just when switching to a new release. 020821: (feature) Added support for Mozilla and Galeon as URL-viewers. Also added option which controls if URLs should be opened in new windows etc. 020819: (feature) Changed folder update code so now all folders are updated at once, this should reduce the flickering some p}eople see. The new code also immediately sees when the watcher interval is changed. 020819: (feature) Added buttons to increase/decrease the size of all fonts in the preferences window. 020819: (feature) Changed default keyboard bindings to inlude "Pg down", "Pg up" and "Home". 020814: (bug fix) '"' should act as an URL terminator. 020813: (bug fix) Should now handle '~' and localized characters in all file names. 020809: (bug fix) Changed configure & README to reflect the fact that tcl/tk 8.3 or later is required. 020807: (bug fix) Make it impossible to input space in alias names when extracting addresses. 020805: (bug fix) Decode description of bodyparts. 020725: (feature) Added "utf-8" as valid charset for outgoing emails. 020722: (feature) Applied patch from Laurent Duperval to show filenames of attachments. 020722: (feature-) Removed the support for DSN. 020720: (bug fix) Fixed potential buffer overflow in RatDecodeHeader(). Spotted by Don Lewis. 020719: (feature) Upgraded to autoconf 2.50. Now tkrat/tkrat and test/run will be executable also after calling config.status. 020719: (feature) The first big step for the version after 2.1 * Rewrote message holding to use a normal folder To continue composing you now doubleclock on a message in this folder. This mechanism needs to be improved. * Rewrote message sending, this lead to: - The outgoing queue is now a normal folder - PGP operations now happens immediately when send is pressed - Save to operations are also performed when send is pressed - it will be easy to support smtp authentication * The old hold mechanism is now obsolete * Moved obsolete library calls to a new library 'ratlib' which is dynamically loaded when needed * Set option to c-client to avoid status-message in local file-folders * Give balloon help also for disabled menu entries * Lots of other minor cleanups 020808: ***** Released 2.1 ***** 020628: (bug fix) Avoid adding extra blank line when using external editor. Thanks goes to Stefan Richter. 020628: (bug fix) Part of the text in the version window was editable. 020623: ***** Released 2.1b3 ***** 020617: (bug fix) Incorporated lots of grammatical corrections to the English messages. Provided by Chris Tubutis. 020613: (bug fix) If you had discarded changes in the preferences window then the discard dialog came up every time you switched pane. 020610: (bug fix) Saving a copy of an outgoing message to an open folder caused this folder to stay open (and monitored) until tkrat is closed. 020603: (bug fix) Fixed possible coredump on importing file-folders. 020530: ***** Released 2.1b2 ***** 020529: (bug fix) Fixed problem on Solaris which made the build process fail the first time. 020529: (bug fix) It was possible to abort halfway through the creation of a new folder and leave things in a flux. 020527: (bug fix) Fixed problem with deleting the first role. 020526: (bug fix) Added extra newline to messages when holding (and sending). 020526: (bug fix) Could not drag&drop folders in the vfolderdef window. 020521: (bug fix) Did not handle single-letter addresses to address fields. 020521: (bug fix) Do not fix CRLF of attached files if they already are correct. Fixed PGP signing of dos-encoded text attachments. 020515: (bug fix) Changed Mailcap handling so tempfiles are deleted after 30 seconds instead of immediately. This should allow applications to load them. 020515: (bug fix) Fixed bug-report address to point to maf@tkrat.org. 020515: (bug fix) Do not die on corrupt alias-files. 020510: (bug fix) TkRat no longer works with tcl/tk 8.1 minimum version is 8.2.0. Made it work with that version and updated documentation. 020510: (bug fix) Removed obsolete field Return-path. This was actually not used in sent emails. It was used to control the 'MAIL FROM' dialog in SMTP and to specify key to use when signing. Both these cases now use the from address. 020508: (feature) Added Portuguese translations by Rui Lus Pires. 020506: (feature) Accept both ispell 3.1 and 3.2. 020505: (bug fix) The check for pam on Linux was broken and the c-client lib was never built for pam. 020503: (bug fix) Make Return in dialogs invoke the selected button rather than always the default one. 020503: (bug fix) Fixed cleanup after failed folder creation. 020502: (enhancement) Changed configure to look in more places for files. This makes it work on FreeBSD-4.3. Also added missing result print for tkConfig.sh search. 020429: (bug fix) Import of subscribed folders created directories for all folders and could create duplicate menu entries. 020429: (bug fix) Increased margins on printouts. 020427: (bug fix) Improved ability of configure to find tkConfig.sh 020424: (feature) Removed ability to automatically import all mailboxes when creating a new IMAP-server. Feature was too dangerous. 020423: (bug fix) Fixed bug which sometimes gave errors when using the struct menu. 020423: (reuse) Applied patch from Laurent Duperval which improves code reuse in html.tcl 020423: (bug fix) Fixed problem with creating new IMAP-servers. 020423: (bug fix) Watch for ridiculous values of watcher_time. 020422: (feature) Added new fields to role. One which specifies the domain to add to unqualified addresses and one to control the name we use when presenting ourselves to SMTP-servers. Both of these can be left empty in which case they default to the domain of your from address. 020421: (bug fix) Added balloon-help to the online/offline icon 020418: ***** Released 2.1b ***** 020418: (bug fix) Fixed so it works with the latest version of 8.4. It will now break with earlier versions than 8.4a5. 020417: (bug fix) Improved PGP address matching 020414: (bug fix) Enabling monitor of a folder now takes effect directly. 020414: (feature) Show number of messages in folder-menu for all open folders, not just monitored. 020414: (bug fix) Size was '???' for empty mailboxes. 020414: (bug fix) Fixed various memory leaks 020407: (bug fix) Avoid main window flashing when started in iconic mode. 020407: (bug fix) Should try the selected charset as possible encoding as the first one after us-ascii. 020405: (bug fix) Could miss new messages when an empty folder was open. 020404: (bug fix) Failed to launch browser when no browser instance was running. 020402: (bug fix) Consider digits as separator characters but "'" which is embedded in words is not. 020402: (bug fix) Fixed spurios "can't create mailbox, already exists" message which appeared when moving messages to INBOX. 020402: (bug fix) Source view was writable. 020402: (bug fix) Fixed problem with attaching files with non us-ascii characters in their names. 020401: (bug fix) Handle message ID's with quotation and backslahses in them. 020329: (bug fix) You could create an address book without a filename to save it in. Also alias menus got confused when adding address books. 020327: (bug fix) The test for more than one open folder window when quitting was broken. 020327: (feature) Added keyboard shortcut ^W for closing a window (the old ^C still works). 020326: (feature) Removed headers 'Comment' 'Keywords' and 'Content-Description' from the compose window since they were not actually implemeneted. 020324: (bug fix) Now compiles cleanly with the latests cvs-version of tcl/tk (8.4). 020319: (bug fix) Made more resistent against grab-errors. 020319: (bug fix) Gave error if one closed the compose window while spell-checking. 020319: (bug fix) Should finally handle folders with non us-ascii charcters correctly. 020305: (bug fix) Misc fixes to folder importion code. 020304: (bug fix) Added code which verifies and repairs the vfolderdef- structure. 020226: (bug fix) Significantly relaxed the requirements on URLs by allowing any character except space inside them. 020225: (bug fix) Check that alias-files exists before trying to read them. 020224: (bug fix) Make sure changes to the vfolderlist are applied immediately. 020220: (bug fix) Fixed problem where double commas in an address field confused the alias expansion. 020220: (feature) Made it possible to drag the dividing line in the new/edit folder window. 020219: (bug fix) Only insert the hierarchy separator if needed when importing mailboxes. 020216: (bug fix) Change number mangling to assume 1024 bytes per kilobyte rather than 1000. 020216: (bug fix) Ignore the '<>' surrounding some URLs. 020212: (bug fix) Misc small rearrangements of stuff in preferences window. 020212: (bug fix) Number of new/unread messages was off when DSN arrived. 020211: (bug fix) Running the external editor did not pick up the written text if you deliberatly set the encoding to "system". 020211: (bug fix) Fixed problem where images could end up being shown in the wrong message when quickly moving throug messages. 020210: (bug fix) Changed rules for which characters are considered word separators for spell-checker. Now uses alnum. 020209: (bug fix) The watcher often missed new messages. 020207: (feature) Increased left margin on printouts. 020207: (feature) Improved support for recognizing PDF-files. Patch from Gran Larsson. 020206: (bug fix) Did not remember size of folder window. 020206: (feature) Addded missing German texts. Thanks goes to Thomas Fetcke. 020204: ***** Made snapshot 20020204 ***** 020203: (feature) Added patch from Laurent Duperval which tries to determine the real type of application/octet-stream attachments by checking the filename. 020201: (bug fix) Treat '-' as a separator character when spellchecking. 020201: (bug fix) Changed behavior of mailbox-importion. Should now avoid extra inclusion of INBOX folder every now and then. 020127: (bug fix) Fixed problems with references extraction noted by Christophe.Martin@sdt.univ-brest.fr. 020127: (feature) Now reassembles split header-lines. 020127: (bug fix) Completely rewrote the header-wrapping/encoding logic. The old version did not correctly handle escaped character sets like iso-2022-jp, nor did it break lines correctly. 020122: (bug fix) Did wrap when inserting things in the beginning of a pasted paragraph. 020122: (bug fix) Fixed problems spell checking words like "journaling" and "isn't" 020120: (bug fix) Fixed serious problem in new DSN-parsing code. 020115: (feature) Cleaned up quitting code. Now quit from the window- manager works. 020115: (bug fix) Complained about syntax-errors in address lists if they contained newlines or tabs, even if they are legal. 020115: (bug fix) Fixed DSN-handling which did not work at all. 020113: (bug fix) Fixed core-generating bugs in the dsn-handling. 020112: (bug fix) Tmpdir-cleaner should ignore signals so it triggers even when the main process is terminated by SIGINT etc. 020110: (bug fix) Corrected names of iso-2022 charsets. 020110: (bug fix) Initialize user language before showing upgrade message. 020109: (bug fix) Show dialog to user when mailbox stream is lost during an automatic update. Also avoid errors if the user clicks in the cleareed message-list. 020108: (feature) Now sends deferred messages on startup in online mode. 020107: (bug fix) Fixed focus problem after compose window was closed. 020105: (bug fix) The external web-browser did not always get the right URL passed when the URL contained some magic characters. Also improved the URL-finder. All this is thanks to a patch from Marc.Herbert@ens-lyon.fr. 020105: (feature) Now refuses to send while you have syntax errors in any of the address fields. 020101: (bug fix) Restored the address checking stuff which marks invalid addresses in red when composing. 020101: (behaviour) You can now press BackSpace when the cursor is at the start of the buffer and it does the right things. 020101: (behaviour) Made the text edited in an external editor not wrap automatically. 020101: (bug fix) Fixed problem where you got a charset error when trying to launch an external editor. 020101: (bug fix) Fixed "Wrap paragraph" feature so it now works. It now wraps the entire paragraph in which the selection starts and ends. 011230: (bug fix) Fixed configure so that if it finds a tclsh without a version number attached then accept a wish without version. 011230: (feature) Added window which may show the details why ispell fails. 011221: (feature) Converted online/offline indicator to button which toggles the state. 011221: (feature) Now goes offline when all network connection fails. 011219: ***** Made snapshot 20011219 ***** 011215: (feature) Keep the destination folder open during a group- move so we do not need to reopen it for each message. 011215: (bug fix) Fixed problem where tkrat internally missed that a copy had faield and actually mareked the original for deletion. 011212: (bug fix) Fixed wobbling cursor problem. Thanks to TEX who pointed me in the right direction. 011210: (bug fix) Always lock GUI when synchronizing. 011209: (bug fix) Fixed misc focus problems where modal popup windows did not get focus when they should. 011209: (bug fix) Actually create disconnected folders locally when importing disconnected. 011209: (bug fix) Do not generate an empty Subject:-header if no subject has been given. 011207: (feature) TkRat will now include the fullname you have in your aliases in the sent messages. 011207: (bug fix) Sort the iso-character sets so lower numbers are preferred (they are more standard). 011207: (bug fix) Fixed annoying right-scrolling bug if the last message was had a too long subject. 011207: (feature) Obsoleted the masquerade_as option. This option turned out to be in conflict with the domain specified in the From:-option. It was very unclear which option controleld what. Now everything is controlled by the From-option. 011207: (feature) Enhanced "Show generated headers" window to also show which host we are going to claim to be in EHLO. 011205: (bug fix) Got error when changing roles if the signature had been deleted. 011203: (bug fix) Include personal-name in headers. 011203: ***** Made snapshot 20011203 ***** 011202: (feature) Added keyboard shortcut 'o' to toggle online/offline mode. 011202: (feature) Added missing texts in German and French. Thanks goes to Thomas Fetcke and Christophe Martin. 011130: (bug fix) Fixed some potential address variable corruptions and potential memory leaks. 011130: (bug fix) Could lose lock on mailbox when fiddling with monitor flag. 011130: (bug fix) The apply button in the folder edit window could give inconsistent results when flags were changed. 011130: (bug fix) Retore in the folder edit window did not always restore correctly after an Apply. 011130: (bug fix) Fixed potential core-dump in check&fix dbase. 011129: (bug fix) Fixed problem with "View source". 011129: (bug fix) Fixed "Send bug report". 011129: (feature) Removed configrmation-dialog when you closed the last window. Instead made ^C just beep on the last window. Use ^Q to quit instead. 011129: (bug fix) Fixed core-dump in sending process. This only occurred in abnormal situations. 011127: ***** Made snapshot 20011127 ***** 011127: (feature) Now asks for confirmation before quitting (when closing the last window). 011125: (feature) Now understands all iso8859 charsets supported by tcl (more appeared in 8.4). 011125: (feature) Added a button, right of address field, to popup alias selection list in the compose window. 011125: (feature) Now seems to work with tcl/tk-8.4 (todays version in cvs). 011124: (feature) Add host-name to the appname registered with the X-server. This makes it possible to run two different instances on different machines, but with the same X-server, without having to modify the appname. 011124: (feature) You can now specify which PGP key to use for signing for each role. 011124: (behaviour) Implemented a common function which generates the headers for sending. This function is used both when sending and showing generated headers. Also changed the default configuration so that forcing the generation of Sender: is turned off. 011121: (bug fix) Fixes to the font selection window. Now the buttons gets disabled when illegal etc. It is now also more error resistant. 011121: (bug fix) Finally did a complete fix of the drag&drop problems in the New/Edit folder window. 011117: (bug fix) Enabling monitoring of a folder did not update the vfolderdef window. And it did not take effect until after reboot. 011117: (bug fix) Fix to mime.tcl so we have file-commands which are stupid and prints stuff on stderr. 011116: (bug fix) Instead of using the actual host and user-names use the values specified in the role. 011110: (bug fix) The new message counter counted delivery notifications double. 011026: (bug fix) Toggling the visibility of embedded text-parts could affect the visibility of all following parts as well. 011017: (feature) Now remembers text properties (wrapping etc.) when holding messages. 011013: (bug fix) Handle errors in external editor better. 011013: (bug fix) Show old font name when editing fonts. 011013: (bug fix) Return value to indicate compose error. Patch from Mikhail Teterin . 011012: (bug fix) Fixed some layout problems in preferences window. 011012: (bug fix) Cleaned up browser start function and fixed opera start. 011012: (bug fix) Always put the tkrat libraries first in auto_path so we prefer the right versions. 011010: (bug fix) Fixed numerous problems with dragging&dropping of folders: * Moving did often not "take" (undone next time) * The folder-list could get corrupted when items where moved in and out of directories * You could not move directories at all * You can now place things directly after a subdirectory without placing it in the subdirectory 011003: (bug fix) Fixed problems in Mailcap handling. 011003: (bug fix) Mailcap path changes in the preferences window did not always take. 011003: (feature) Can now change roles in the compose window. 011001: (bug fix) Now handles corrupt messages better. 010928: (bug fix) Fixed misc problems with threaded sorting. 010917: (bug fix) Fixed bug with charset of external editors. 010912: (bug fix) Fixed walking watcher problem. For some people the watcher window would move slightly every now and then to the right. This has now been fixed. 010910: (bug fix) Changed auto-scrolling algorithm in folder window so now it shouldn't move the view unless acceptable. 010906: (feature) Now handles buggy MTAs which encode PGP-messages. 010906: (bug fix) The window to manually insert messages into the database was broken. The keywords and expiration-type were exchanged in the database. 010905: (bug fix) One can now use non us-ascii characters in database keywords. 010903: (feature) Removed the check for the micalg parameter when reading signed messages, some other MUAs do not generate this. 010830: (bug fix) Fixed bug where the message list would scroll whenever the folder was synchronized or the window unmapped. 010829: (bug fix) Spell checker would remove the space between two adjacent corrected words. 010827: (feature) Added polish texts by tri10o@poland.com. 010825: (bug fix) Fixed pgp signing & encrypting. 010824: (bug fix) Fixed problem where the list of messages to choose from (when replying to a message with embedded messages) did miss the scrollbar. 010822: (feature) Incorporated big patch from Laurent Duperval. It adds the following features: - Ability to modify font sizes for HTML. - Inline display of HTML images (configurable, defaults to off). - Better handling of MIME types for attachments. The "file" program to use is now configurable. - Keybinding to do network sync (Ctrl-Y by default) - Keybinding to open a new file (Ctrl-O) - A few more types in the MIME table - HTML and Image code has been moved to a separate file 010822: (feature) Added support for --with-tkconfig to configure. Patch by Laurent Duperval 010821: (bug fix) Replace all $ and , in urls for all browsers. 010817: (bug fix) Fixed "Send bug report function" 010817: (bug fix) Restore all '=' at the end of enriched encoding. 010817: (feature) Now default save folders are defined per role. 010814: (feature) Disconnected folders now handles network connectivity losses gracefully. 010814: (feature) Removed our directory in /tmp when being killed. 010814: (feature) Removed the deferred sending option. This option was obsoleted by the online/offline mode stuff and actually now only served to confuse people. 010812: (feature) Added support for roles. The role determines which address to use, which signature-file to use and how to send messages. One can switch roles manually and also have different default-roles for different folders. 010808: (feature) Radically changed layout of preferences window. This was done to partly make it easier to use and partially to prepare for the roles feature. 010808: (feature) Improved layout of tree widget by making it denser. It now also only adds the scrollbars when needed. 010803: (feature) Cleaned up a lot of code. Converted all proces to use the Tcl_Obj iterfaces and also converted lots of other things to Tcl_Obj. This should make tkrat marginally faster (probably not noticeable though). 010731: (feature) Now tries harder to show something sensible in the attachment list of the compose window. 010730: (feature) Added icon indicating online/offline status. 010729: (feature) Now checks for errors when going online and aborts if all disconnected folders fails to sync. 010728: (feature) Misc cleanups in folder definition window. 010728: (feature) Improved handling of folder with non ascii characters in their name. 010716: (feature) Improved password caching key comparison. 010711: (bug fix) Now defaults to online-mode. 010703: (feature) Now allow all known encodings for the external editor. Default to the system default encoding. 010703: (feature) Added minimal support for reading text/enriched messages 010628: (bug fix) Build on linux systems without pam. 010628; (bug fix) Dumped core when debugging connections. 010628: (bug fix) Could not create pop3 folders in folder window. 010626: ***** make snapshot 20010626 ***** 010619: (feature) Now create a directory to store all the temporary files in. 010617: (bug fix) Now better handles if current directory becomes unreadable. 010617: (bug fix) Fixed misc stupid problems with pgp integration where it did hang when working with big files. 010606: (feature) Updated french texts from Christophe Martin and Marc Herbert. 010606: (feature) Search path for editor command before complaining. 010601: (feature) Can now handle ~ in all filenames. 010531: (feature) Added Opera to builtin list of browsers. 010528: (bug fix) Made tests work against cyrus imap-server. 010509: (bug fix) Fixed several problems with wrapping of replies. 010502: (feature) Double-clicking in the suggestion list of the spell- checker now does a replace as well. 010502: (feature) Spell-checking: Changed learn feature to use ispell itself instead of directly mucking around with the file. This also means we can remove the ispell_dictionary option. 010430: (bug fix) Removed text listing which languages are implemented since it was outdated. 010423: (bug fix) Fixed problem where tkrat could hand while spell- checking a message. 010413: (bug fix) Cleaned up code so everything now runs through frink -J (syntax checker) whithout generating any real warnings. 010410: (feature) Improved the show generated headers function to show both SMTP FROM and RCPT TO entries. Also fix the addresses shown to match reality. 010405: (feature) One can now leave the to:-field empty and just use cc and/or bcc. 010404: (bug fix) Fixed problem with scrolling message list on update. 010324: (bug fix) Fixed potential core-dump in pgp code. 010324: (bug fix) Rewrote word finding algorithm in rat_ispell.tcl to match the one in ispell. This avoids the bug with multiple-words. 010317: (feature) Can now modify the colors of the Balloon help as well. 010302: (bug fix) Fixed references to option(simple_data) which were misspelled. 001227: (feature) Added framework for automated testing. Also implemented tests for online/offline mode. 001227: (feature) Implemented online/offline mode. This only affects disconnected folders and message sending. 001225: (feature) Added code to control the ssh-preferences in c-client. This means that one can now set it up to use SSH to contact imap and pop severs. 001223: (feature) Be more forgiving when using strange charsets while composing. 010220: ***** released version 2.0.1 ***** 010212: (bug fix) Did not include all heades when forwarding a message as attachment. 010212: (bug fix) Fixed ballon help in compose window. Thanks goes to Thomas Fetcke. 010212: (feature) Added german language texts. Thanks goes to Daniel Egger , Jochen Bern , and Thomas Fetcke . 010126: (bug fix) The watcher did not always trig on new messages. Thanks goes to rgm@cadence.com who spotted the exact problem. 010112: (bug fix) The local copies of imported disconnected folders did not get created automatically and therefore never filled up. 010112: (bug fix) Importing disconnected folder did not give usable folders. 010112: (bug fix) Importing folders did not work at all under some circumstances. 001228: (feature) Added code which checks the vfolderdef file version and aborts if it is too high. 001226: (bug fix) Fixed potential header corruption internally. 001222: (bug fix) It has turned out that the definition of the GB charsets in tcl is not the same as used in MIME-email. A quick workaround is to treat them as us-ascii instead. This is not a solution which is statisfactory in the long run. 001216: (bug fix) Could generate lines ened with bare newlines when sending attached messages. 001216: (bug fix) Fixed handling of embedded '&' in urls when using other browsers than netscape. 001204: ***** released version 2.0 ***** 001204: (bug fix) One could get an error if one closed a window while tkrat was "busy". 001202: (bug fix) Could not close the keydef window using the window manager. 001201: (bug fix) Corrected encoding of attachment file names. 001201: (bug fix) Did not handle non us-ascii characters in filenames when attaching files. 001129: (bug fix) Alias chooser did not work. 001122: ***** released version 2.0rc8 ***** 001122: (bug fix) Expressions could get saved twice. 001120: (bug fix) Misc bugfixes to the Define Editors window. 001115: (bug fix) The newline between header and body could dissappear under some circumstances when saving outgoing messages. 001112: (bug fix) Do not give error message when printing via keyboard when no message is selected. 001112: (bug fix) React for list selections from the keyboard in some dialogs. 001112: (bug fix) wm transient should be called before the window is mapped. 001112: (bug fix) The alias window: the "Move to" button did not get enabled if one selected only one alias. 001111: (bug fix) Fixed handling of long directory names and of those containing LWSP in the file selector. 001110: (bug fix) Could dump on certain database searches. 001107: (feature) Added tags target to Makefiles 001103: (bug fix) The configure script now looks a little harder for tkConfig.sh. Thanks goes to Alexander Leidinger. 001101: (bug fix) Small fixes to make it work better on 64-bit systems. Thanks goes to larry.gensch@compaq.com. 001030: ***** released version 2.0rc7 ***** 001029: (bug fix) Fixed problem with focus which got lost after a popup window was closed. 001029: (feature) Removed integration of tcl-minimizers in Makefiles since they do not really have any effect when running tcl/tk 8.* 001025: (bug fix) The spell-check window now immediately closes when one presses the dismiss-button. 001017: (feature) Added charset aliases for a number of windows-fonts so tkrat should now understand most windows codepages. Also made tkrat try the encoding-name directly to see if tcl supports it. 001013: (bug fix) The modifiers 'R' and 'r' in the folder list did the same "To:" transposing as 'n' and 'm', which it should not do. 001013: (bug fix) The structure of folders could become corrupted after an import was done. 001010: (feature) Removed browse button from mh-dialogs. MH-folders are always specified relative the users mh-path and not from the root as the file-browser did. 001009: (bug fix) Do not generate invalid vfolderlist-file when the user deletes all folders. 001006: (bug fix) Selection in address fields of the compose windod did dissapear when focus was move out of the text field. 001004: (bug fix) Misc fixes to message backup algorithm (when expiring). 000926: (bug fix) Database could forget to delete messages. 000921: (bug fix) Check database could wrongly accuse the index.info to contain the wrong number of messages. 000920: (bug fix) Encrypting and signing with pgp 5 did not work. 000917: (bug fix) Saving outgoing messages did not always reuse the open connection to an mailbox and thus resulted in delays before the new messge was seen in the folder. 000915: (bug fix) Fixed handling of line-endings when saving messages. This bug could lead to errors (message contains bare newlines) when saving mail to picky imap-servers. 000911: (feature) Include version of lib in bug-reports. 000908: (bug fix) Added focus command into ModalGrab so it is run before the grab. This should help with some window managers. 000908: (bug fix) Could hang when trying to wrap a cited message. 000908: (bug fix) Allow exlamation marks inside URLs. 000906: (bug fix) Certain edit-operations in the header fields of the compose window could be mirrored in the body part. 000906: (bug fix) A <> into aheader field of the compose window also pasted into the body. 000905: (feature) Should now build on IRIX-6.5 with gcc as well. 000905: (bug fix) Fixed minor bug in doc/Makefile.in which could cause an, harmless, error message during installation. 000904: (bug fix) Expression saving did not work. 000904: (bug fix) Could fail to resend a deferred message which had already failed to send once. 000902: (bug fix) Removed -u flag to gpg when signing. 000829: ***** released version 2.0rc6 ***** 000828: (bug fix) Got a stray character on position 129 in folder-list when making any flag changes. 000828: (bug fix) Message-attachements does not have to be binary. 000828: (bug fix) Added -u argument to gpg when signing. 000826: (bug fix) Fixed problems in parsing and generating modified utf7. This led to poblems with localized imap folder names. 000824: (bug fix) Did crash on IMAP import if no mailbox path was given. 000823: (bug fix) Changed configure-script to work with IRIX-6.5. Also fixed bad output when locating X11 header-files. 000822: (bug fix) Finally fixed the extract addresses function for messages with lots fo addresses. 000822: (feature) Also set the name parameter on the content-type for attachments. Thanks goes to "Stefan Richter " 000822: (clarified) Rearranged the order of the entries in the "Setup Network Sync" window so they reflect the actual order the items are performed. Thanks goes to "Stefan Richter 000822: (bug fix) Passed to short buffer lengths to snprintf in ratDbase.c when checking dbase. Big thanks to "Peter TEX Weigand " for finding this. 000819: (bug fix) A number of minor tweaks in the reply-to line-wrapping algorithm. - if it should only wrap a single character then an extra newlin was inserted. - Now does not join lines which starts with a non-alnum charcter (ignoring LWSP). - Now does not break a line if there are no characters in the part about to be wrapped. 000817: (bug fix) The Extract Addresses dialog did generate an error when you already had aliases for all addresses in the message. 000812: (bug fix) Handle single quotes in mailcap files. 000812: (bug fix) Handle delete requests from wm for list windows. This could lead to errors in the Saved Expressions list. 000807: ***** released version 2.0rc5 ***** 000806: (feature) Restored handling of pgp minimizers. 000806: (bug fix) Removed wrapping of unencoded header-lines. 000805: (bug fix) The listing of pgp-keys on the keyring was riddled with bugs (this is used when attaching pgp-keys). 000805: (bug fix) Changing some pgp-prefrences might not "take" until the next restart. 000805: (bug fix) Changed behavious of file-selector. Now you can not write patterns on the filename line and have the ok/save button resolve them. The problem with this feature was that if the suggested filename contained any glob characters (* and ?) then the save button appeared not to work. 000805: (bug fix) Editing the inbox could clear the "Save outgoing" marking of another folder. 000804: (bug fix) It turned out that pgp-6.5.1i does not return a usable exit-code when checking signatures. Therefore I changed the code to always show the questionmark for that one so as to force the user to press the pgp-button and read the output. 000729: (bug fix) Fixed watcher to not beep when a message arrives in a monitored but not watched folder. 000728: (bug fix) Fixed alias extract window to add a scrollbar when it needs to fit on the screen. 000728: (bug fix) Moving messages between open folders now work much better. 000728: (bug fix) Removed extraneous newline inserted when moving messages. This newline also caused problems with some picky IMAP-servers. 000728: (bug fix) Better arguments to gpg when checking signatures. Also change action when verifying old type signatures, there seems to be no way of checking the return-values of the different pgp-versions and get reliable results. So I changed the sig button to always read "?" in those cases and you have to check the message yourself. Thanks goes to R Horn . 000728: (bug fix) Fixed bug where pgp signing generated a bad signature on all messages except the first message signed in a session. Also added support for the sha1 algorithm. Big thanks goes to Walter Truitt for this patch. 000728: (bug fix) One could not save outgoing messages to an open IMAP- folder without losing the connection. While fixing this bug I uncovered a number of other problems relating to the watcher and other things which also were fixed. 000714: (bug fix) Fixed serious bug in insert function which could cause core-dumps. Big thanks to "Derek B. Noonburg" who submitted a patch. 000713: (bug fix) Fixed a couple of typos in alias.tcl. Thanks goes to Nic Bernstein . 000711: (change) Changed the default folder location from being $MAIL to being the default compiled into c-client. This only applies to people who start tkrat for the first time at sites which haven't overridden the default start folder in some global ratatoskrc folder. 000710: (bug fix) Allow people to upgrade attachments encoding from 8bit to binary. 000710: (bug fix) Always treat non-text attachments as binary to avoid problems with lineending conversions. 000708: (bug fix) Group expression could appear twice in the list. 000708: (feature) Added check of tcl-version to avoid bug reports from people who run it with tcl/tk < 8.1. 000708: (bug fix) Garbage got added to the end of the message when sending. 000703: (bug fix) BackSpace delete characters to the right of the cursor when positioned at the top left corner. 000703: (bug fix) Fixed small problem with line wrapping which struck people who are replying above the quoted text. 000702: ***** released version 2.0rc4 ***** 000629: (bug fix) Folder list could get out of sync on folders which were not monitored when new messages arrived. 000628: (bug fix) Replaced unportable snprintf define with my own function (only used if snprintf is not already available. Thanks goes to Lloyd.Parkes@vuw.ac.nz for the tip. 000628: (bug fix) The delete button did not ligth up if only one alias was selected in the alias-list. 000627: (bug fix) Changed order show and group menus were created to fix keyboard navigation. 000626: (bug fix) Removed +batchmode=1 flag from pgp5 when signing. Patch from Mike Marion . 000619: (bug fix) More fixes to make --with-tcl-include be more tolerant in what it accepts. 000618: ***** released version 2.0rc3 ***** 000617: (bug fix) Fixed --with-tcl-include option to configure. Thanks goes to Laurent Duperval for the patch. 000617: (bug fix) Fixed linking flags on SunOS systems. 000615: ***** released version 2.0rc2 ***** 000613: (bug fix) Fixed potential deadlock when a pgp error appeared at the same time as an imap login window. 000610: (feature) Can now change keyboard shortcut for compose_key_copy. 000610: (bug fix) Could get tcl error when closing a window for some window managers. Added sanity checks. 000607: (feature) Changed the order things are done when synchronizing with the network. The new order is to start by running an external program, then send deferred and finally send deferred. 000607: (bug fix) Fixed problems with triggered when one turned off monitoring of an open folder. 000531: (bug fix) Fixed problem with quitting when tk was compiled with threads. 000530: (bug fix) Check that fopen really succeeds when holding a message. Also replace the ':' in the filename with '_'. Patch submitted by "Peter 'Luna' Runestig" . 000527: (bug fix) Fixed so that the disconnected folders no longer fails if a message lacks Message-Id. 000525: (bug fix) Now places more limits on the end character of urls. 000524: (bug fix) Did not always exit when started in client mode (when sending messages to existing tkrat instance). 000524: (bug fix) Fixed handling of encrypted messages when we fail to decrypt them. 000523: (bug fix) Fixed configure-script to take version from --with-tcl-include if specified. Also made it do more sanity checks for versions. 000522: (bug fix) Got error when pressing ^y without a selection. 000522: (bug fix) Fixed an error where some database folders always claimed that the database was corrupt. 000522: (feature) Added support for APOP. 000519: (bug fix) Fixed potential core-dump problem in pgp code. 000517: (bug fix) Fixed problems with initial dots in body when using sendmail to send messages. 000517: (bug fix) Fixed coredump when synchronizing. 000517: (bug fix) Import of disconnected folders should now work better. 000516: (bug fix) Small fixes to line-wrapping code. 000516: (bug fix) Should also escape \ when generating postscript. 000512: (bug fix) Fixed so that disconnected folders works with cyrus IMAP-server as well. 000509: (bug fix) Added checks to avoid tcl-error when closing window. 000508: (bug fix) Removed debugging output from PGP-code. 000508: (bug fix) Return did indent even if wrapping was disabled. 000507: (bug fix) Fixed doubleclick on read-only aliases in the alias window. 000505: (bug fix) Disable more menu entries when no messages are shown. 000505: (feature) Added tcl/tk versions to version dialog. 000504: (bug fix) Made alias command accept even older formats of aliases. 000503: (bug fix) Fixed configure script for HP-UX. 000502: (enhancement) Automatically generate list of generated textfiles for Makefile. 000502: (bug fix) Fixed bug so that changes to "Use input method" takes effect on apply. 000501: (bug fix) Make sure libraries are installed 0755 instead of 0644. 000420: ***** released version 2.0rc1 ***** 000420: (bug fix) Fixed problem in print command generation which did not work if there was no %s substitution (patch from cmartin@ipnl.in2p3.fr). 000419: (cleanup) Removed minimizer support. 000417: (bug fix) More fixes for grabbing of Modal dialogs. 000416: (bug fix) Fixed insidious bug in move to database code. 000414: (bug fix) Wrap of cited message could result in a mess. 000413: (bug fix) More sanity checks when inserting into database. 000412: (bug fix) Now handles foldernames with localized characters correctly. 000406: (bug fix) Now uses plain newlines when sending to program. 000323: (bug fix) Rewrote parts of the makefiles. They are now more consistent and have separate install targets for binary and shared files. Also moved the blt_busy-library to the arch- dependent lib directory instead. 000320: (bug fix) Fixed error in generic list-window scrollbar. 000320: (bug fix) Could leave flagged and deleted flags set on message even when moving it. 000315: (bug fix) Could leave lots of defunct processes lying around. 000314: (bug fix) The threaded folder sort could sometimes hide messages. 000310: (feature) Added ispell integration. Feature provided by Bryan Schofield . 000306: (bug fix) Fixed misc grammatical errors in balloon help. (patch from Jan Martin ) 000305: (bug fix) Fixed problem with message indexes (as shown in the message list). 000302: (bug fix) Now processes malformed DSNs better. Patch from Lou Ruppert . 000301: (bug fix) Restored old behaviour where dynamic import did not happen until the actual submenu was opened. 000229: (feature) Now prefers tcl/tk8.3 when configuring. 000229: (feature) Added support for input methods. I.e. they can be enabled from the preferences. This option defaults to off and requires tk 8.3. 000226: (bug fix) Could crash when one tried to send a really big message. 000226: (bug fix) Watcher failed to show message when one new message arrived in an empty folder. 000226: (bug fix) Crashed when the first message arrived to a file folder which did not previously exist. 000226: (bug fix) Fixed gpg signing (thanks to Erik Stenvall). 000225: (bug fix) IMAP import should now better with servers without the NAMESPACE extenstion. 000225: (feature) Added a new keybinding for a quit key. 000224: (bug fix) More url matching problems (did not match @ in host part). 000223: (bug fix) Explicitly set protection on installed files. 000223: (bug fix) Fixes for the command list of the compose window from Peter TEX Weigand . 000222: (bug fix) One could not define keys with modifiers (like shift). 000222: (bug fix) Fixed problems with displaying of big images. Thanks goes to Peter TEX Weigand for the patch. 000221: (bug fix) Make install did not install the manpage. 000221: (bug fix) If your inbox was a disconnected folder, the dbase expire would cause another local copy of it. 000217: (feature) Added sort by size patch from Laurent Duperval laurent.duperval@cgi.ca. 000217: (bug fix) Fixed url matching problem (did not match ~). 000216: ***** released version 2.0b9 ***** 000216: (bug fix) Fixed potential problem ine SMTP error reporting. 000216: (bug fix) Import of mh-folders did not work. 000215: (bug fix) Fixed tcl error when opening a new folder window with a file folder. 000215: (bug fix) Spelled out dependencies in Makefile instead of relying on file glob expressions. 000215: (bug fix) Fixed blt stuff to work with tk8.3.0. 000214: (bug fix) The font display in the preferences window did not show the correct fonts. Also made the font name be printed in the actual font. 000214: (bug fix) Split Modal into two parts to handle focus better. 000214: (bug fix) New URL-searching expression from Peter TEX Weigand 000213: (bug fix) Moved package requirements to later to avoid problems with loading the rat_textlist package. 000213: (bug fix) Fixed expression problem (fix from Per Johansson ). 000210: (bug fix) Fixed crash when moving a message to another folder. 000210: (bug fix) Improved autowrap routine, it should now not treat numbers in the middle of a paragraph as starts of enumerated lists. 000209: (bug fix) The watcher window had no handler for WM_DELETE_WINDOW, patch provided by dcurtiss@ge-harris.com. 000209: (bug fix) Fixed handling of fonts with spaces in their names. 000209: (bug fix) Fixed submenu creation and deletion in new/edit folder. 000208: (bug fix) Fixed interoperability with pgp 6.5 000207: ***** released version 2.0b8 ***** 000207: (bug fix) Moving messages to an already open folder confused that folder window. 000207: (change) Changed ^C to close current folder window instead of quitting the entire application. 000206: (bug fix) The url-parser now disallows ']' and ')' in urls. 000206: (bug fix) Should now compile better on OpenBSD. 000206: (bug fix) Do not include from address if a reply-to address is present when replying to all. 000206: (bug fix) Rewrote folder importion code (and changed algorithm). 000131: (bug fix) Now asks only once if it should delete the actual imap folders during a folder struct deletion. 000131: (feature) Ask for confirmation before quitting when there are more than one folder window. 000130: (feature) Added feature to file selector to hide dotfiles, enabled by default. 000130: (bug fix) Fixed bug where a new message could get marked as read when synchronizing a folder manually. 000130: (bug fix) Changing sort order from threaded gave unpredicatble results (in that session). 000128: (bug fix) Now handles symbolic links when attaching files. 000127: (feature) Added %x substitution to external editing command. 000127: (bug fix) Fixed save outgoing to disconnected. 000125: (bug fix) The show header now shows all instances of the selected headers if any of them appears multiple times in the message. 000124: (bug fix) Fixed misc problems with expression window. 000124: (bug fix) Could get 'm window name already exists in parent' when working with folder menus which were to big to fit. 000117: (feature) Changed default wrap-mode to word. Now also remembers the wrapping mode. 000114: (bug fix) Prettyprint (almost) always inserted a linebreak before non us-ascii characters. Also fixed message which appeared when an unprintable bodypart was encountered. 000112: (feature) Add %f subsstitution in icon name as well (patch from Richard Meitzler). 000111: (bug fix) Fixed problem where all parts of an multipart-alternative were shown the second time a message was selected. 000110: (bug fix) Rever to Shift-Tab if the keysym ISO_Left_Tab does not exist. 000109: (feature) Changes to modal windows. They now try to always stay on top. 000108: (feature) Now also reads ~/.ratataosk/ratatoskrc.tcl where one can make final overrides. 000108: (feature) Added option for placement file 000104: (kludge) Fixed so we can read messages saved by y2k-buggy tkrat-1.2. 000104: (feature) Added builtin imap-debugging. Can be enable from the preferences window. 000104: (bug fix) The previous director button in the fileselector did not work. 000102: (bug fix) Fixed a potential core-dump in threaded folder sort. 000101: ***** released version 2.0b7 ***** 000101: (bug fix) Crashed if it had to expire the database and the inbox was a disconnected folder. 000101: (bug fix) Did not start if the configuration directory did not exist. 000101: (bug fix) Fixed "Saved Expressions" dialog. 000101: (bug fix) Reintroduced texts to get the PrintSetup window to work again. 991229: (bug fix) Make sure the font family menu is split if it is to big to fit on screen. 991228: ***** released version 2.0b6 ***** 991228: (bug fix) Fixed bug in line-wrapping code which would insert spaces into words and join parts of different words. 991228: (feature) Added limited support for content-disposition headers. 991227: (bug fix) Expire expected the inbox to have internal index '0' but should have used $vFolderInbox instead. 991227: (bug fix) Remove threading information when sort order is changed from threaded to something different. 991227: (bug fix) Improved logic for when to convert an old expressions-file to the new format. 991227: (bug fix) Now works with tk8.3b1. 991226: (feature) Changed font handlig to by default override all X-resources, also made font changing dialog to modify font selection. 991219: (bug fix) Now all toplevel windows have class TkRat. 991218: (bug fix) Override any potential X-resources specifying background and foreground colors. 991217: (bug fix) The 'Run through command' entry in the edit menu of the compose window did only sjow up the first time the menu was shown. 991217: (bug fix) Did not find the correct contents for deeply nested bodyparts under embedded rfc822-messages. 991214: (bug fix) Fixed problem with file selector. 991212: (bug fix) Changed text "Mailbox name" in IMAP mailbox definition dialog to "Mailbox path". 991208: (bug fix) Signing with gpg caused the message body to be included twice. 991207: (bug fix) Could dump on some systems if one had non us-ascii characters in the username. 991206: (bug fix) Saving outgoing to an IMAP mailbox caused a crash. 991206: (bug fix) Fixed option defaults to use the definition of ratatosk_dir instead of ~/.ratatosk (makes -confdir option work much better). 991206: (bug fix) Fixed problem with fileselector where it complained that it could not chdir to "". 991206: (bug fix) Fixed scrolling problem with embedded images, also some enhancements to html display code (from laurent.duperval@cgi.ca) 991205: (bug fix) Was (under some circumstances) incompatible with the plus and dash-patches. 991202: (bug fix) More fixes to the threaded sort algorithm. But finally I am statisfied with the algorithm. 991130: (bug fix) Shift tab did seldom work. 991130: (bug fix) Misc fixes and addendums to ballon help texts. Big thanks to Martin@ipnl.in2p3.fr. 991124: (bug fix) Always use external editor did not work 991123: (bug fix) Added missing balloon texts 991123: (bug fix) Changed test-tkrat to use builtin pwd function. 991121: ***** released version 2.0b5 ***** 991121: (bug fix) Fixed a buffer overflow error in the sender handling routine. 991119: (feature) Added functionality change wrapping of shown messages. This functionality and the show header set are placed in a new "Show" menu in the folder window" 991119: (feature) Added options to control cursor placement and signature inclusion when composing replies. Patch from Richard Meitzler. 991119: (bug fix) Added "do not edit"-warning at the end of the generated text-files as well. 991118: (bug fix) Fixed error when pasting into a header-field when there was nothing t paste. 991116: (bug fix) Fixed problem with PGP-keys. It was not possible to attach gpg-keys to messages. 991115: (bug fix) Fixed problem where monitored folders sometimes were closed even tough the should not be. 991112: (bug fix) Encrypted and/or signed bodyparts did get sent with the wrong end of line marking. 991111: (bug fix) The PGP-code did crash on empty keyrings. 991111: (bug fix) More checks on input values in alias window. 991110: (bug fix) Changed order of items in TkRat menu to make keyboard traversal easier. 991110: (bug fix) Could coredump if one when sending messages where one had attached a multipart attachment. 991109: (bug fix) Fixed sorting problem which generated core under some circumstances. 991108: (bug fix) Fixed problems when running multiple IMAP-servers on the same host (different ports though). 991104: (bug fix) Fixed bugs where tkrat did not always close folders when leaving them. 991103: (bug fix) Did not close folders correctly. This made it fail to expunge on close and eventually to run out of file-descriptors. 991103: (bug fix) Fixed problems in compose command editing window. 991103: (bug fix) Fixed buffer overflows which mainly occurred when sending messages via SMTP. 991101: (bug fix) Really abort an IMAP login when user presses cancel. 991101: (bug fix) Window close of send bug report window did not work. 991101: (bug fix) It is now possible to turn off pgp-support. 991101: (bug fix) Enabling pgp-support gave bugs in that session. 991101: (feature) Added -blank flag for startups without opening any folders in the folder window. 991029: (bug fix) Fixed ballon help for folder sort order option in admin menu. 991029: (bug fix) Recall button did not always get enabled in the fileselector. 991029: (bug fix) Do sanity checking of folder handlers when syncing network. 991029: (bug fix) The Makefile now actually prints the error encountered when building pkgIndex.tcl files. 991028: (bug fix) Rely on tk.h to define TK_USE_INPUT_METHODS in blt_window.c 991028: (feature) Added option for default bcc. 991027: (bug fix) Added --batch flag for gnupg. 991027: (bug fix) Make clean in lib did not remove the dummy library. 991026: ***** released version 2.0b4 ***** 991025: (bug fix) Added missing objects to clean target in util/Makefile.in 991025: (bug fix) Fixed problem with unread message count when opening an IMAP folder. 991025: (bug fix) Under some circumstances did tkrat not show all bodyparts 991023: (bug fix) Fixed problem with pasting text into the compose window. 991022: (bug fix) Support for messages in charsets incompatible with iso8859-1 was broken. 991022: (feature) Added Serbian texts from spok@beotel.yu 991022: (feature) Added a text file merge program to make inclusion of new texts easier. 991022: (bug fix) Behave sanely even if the expire interval is less than one day (by internally setting the interval to one day after running the first expire). 991022: (bug fix) Import of unix directories now works a bit better. 991021: (bug fix) The preferences window now warns if there is no '%p' in the print command. 991021: (bug fix) Could crash when wrapping cited message if the given regexp was invalid. 991020: (bug fix) Is now much better at returning the focus after a dialog window has taken it. 991019: (bug fix) TkRat does not now try to reopen the inbox immediately if it failed the first time it opened. Also Be more smart when reopening a monitored folder. 991019: (bug fix) Misc fixes to wrapping code: * Make sure the insertion cursor always is visible after a wrap. * Text read from file is marked as non wrappable * Enable the user to see which parts of text are non wrappable * Remove non wrappable markings from selection when wrapping paragraph (if insert cursor is within selection). 991018: (bug fix) Tkrat could crash while synchronizing disconnected folders. 991018: (bug fix) Do not mark pasted text as not wrappable if it does not contain any line breaks. 991018: (bug fix) Fixed bug in threaded sorting where messages with the same subject was not correctly sorted according to date. 991013: (bug fix) You could not change the print mode to plain text. 991012: (feature) Now supresses the errors about messages missing in certain languages during build (see tkrat/Text/README for how to turn them on again). 991012: (bug fix) Fixed problem with bouncing or forwarding multipart messages which contained no valid parts. 991011: (feature) Added option to always use the external editor (provided by Todd J Martin ). 991011: (feature) Added support for BSD/OS-4 in configure. 991011: (bug fix) If one supplies a pth to tclsh then use that before checking the ordinary path (was after). 991011: (feature) Improved file selector by adding recall button (recalls suggested filename) and popup menu for old directories. 991009: (bug fix) Should show busy icon when opening monitored folders as well 991009: (bug fix) Do not show error messages when the user presses cancel in a login dialog 991009: (bug fix) Could still dump core when it lost the lock on a mailbox 991008: (feature) Now deletes all occurrences of mailto: when pasting into an address field while composing. 991008: (bug fix) Added %f escape for folder window name which gets replaced by the current folder name. 991007: (bug fix) Modified Makefile to fail if the index did not build correctly. 990928: (bug fix) Fixed problems with reset button in preferences window. 990928: (bug fix) Fixed problem where under some circumstances the number of held messages indicator did show 9 messages when there were no held messages. 990928: (feature) Improved startup time by directly sourcing the needed pkgIndex.tcl-files. 990928: (bug fix) Fixed problems with changing folders from database 990928: (bug fix) The images got installed into the wrong directory. 990926: (bug fix) Fixed tons of pontential internal buffer overflows. 990924: (bug fix) Fixed misc problems which happened when folders died. 990924: (bug fix) Sending did never work after it had encountered one hard error. 990922: (bug fix) Often failed to identify postscript files when attaching types. 990921: (bug fix) Clarified the ballon help texts over the different sort order choices. 990921: (bug fix) Now uses dynamically allocated buffers for all addresses so we should get no more buffer overflows from big addresses. 990919: (bug fix) Correct the undef's in rat.h to statisfy picky compilers. 990919: (bug fix) It was impossible to quit once a quit had been aborted (due to active compose windows). 990919: (feature) Added -confdir argument 990919: (bug fix) The From:-field should act as an address field when composing messages. 990919: (bug fix) Now more robust against badly formed message/rfc822 bodyparts. 990919: (feature) Added support for Gnu Privacy Guard (patch by Bart Hartgers ). 990919: (bug fix) Password caching did not work if one entered an alias for the hostname of the server. 990919: (bug fix) rat.h contained a #inclide which got called on some systems. 990917: (bug fix) Fixes resizing of img and/or html container when the tkrat window is resized (patch from laurent.duperval@cgi.ca) 990917: (bug fix) Did not recognize https:-urls 990916: (bug fix) Import of IMAP folders now works again. Also made tkrat prune empty directories when importing (option which defaults to prune). This keeps the crud down when connecting to a cyrus IMAP-server. 990913: ***** released version 2.0b3 ***** 990913: (bug fix) More resistance to failed imap connections when creating folders. 990913: (bug fix) Fixed problems with embedded '$' in uls. 990913: (bug fix) Fixed problems where the watcher sometimes did not pop up for new messages. 990912: (bug fix) Imap import should now work also with servers using another hierarchy delimiter than '/'. 990912: (bug fix) Printing of messages from the dbase locked the database. 990912: (bug fix) Really close all folders when we get the kiss of death. 990912: (bug fix) "Expunge on closes" did not have any effect on monitored folders since they are never actually closed (except when one quits tkrat). This is now fixed. 990911: (bug fix) Added arrow cursor patch from Laurent Duperval. 990911: (bug fix) Could crash when checking and/or fixing the database. 990911: (bug fix) Made tkrat/tkrat (development running script) use the wish configure found. 990911: (bug fix) Removed warnings about redefinition of ckrealloc when compiling with tcl/tk < 8.2. 990910: (bug fix) Disconnected folders do not rely on the message-id to uniquely identify a message (use UID's instead). 990908: (bug fix) Got an error when clearing the display after a folder open failed. 990907: (bug fix) Added further checking that the folder really is open to the sync proc. 990906: (cleanup) Do not try to build the bundled c-client utils like mtest, imapd and ipopd. 990906: (bug fix) External editonames with embedded spaces caused problems. 990906: (bug fix) Fixed documentation on RatUP_Signature it gets an array name as argument. 990906: (bug fix) Fixed problem with reply_to in expressions. 990906: (bug fix) Added ballon help text for vd_watch. 990906: (feature) Changed default folder specification to use environment variable MAIL as pathname for default mailbox. 990905: (bug fix) Made nearly all toplevel windows belong to the TkRat class. 990905: (bug fix) Got error when destroying a torn-off folder menu. 990904: (feature) Added support for FreeBSD-3 in the configure-file. 990903: (bug fix) TkRat failed when the folder menu got too big. 990903: (bug fix) Import of imap or disconnected folders did not work. 990903: (bug fix) Adding or importing address books did not work wery well. 990903: (bug fix) Got an error when trying to edit an imported folder. 990830: (bug fix) Replaced some tabs in util/Makefile.in with spaces to keep picky makes happy. 990829: (bug fix) In the new/edit folder window: if one dragged a submenu and dropped it on its original position, then it dissapeared. 990829: (feature) Added build support for Tru64 Unix V5.0 990829: (bug fix) Do not open folders to monitor until the first folder window is created. 990829: (bug fix) Is now able to save outgoing messages to disconnected folders 990828: (bug fix) Too big imported directories gave an error when any folder menu was opened. 990828: (bug fix) Added catch around the last unprotected grabs. 990828: (bug fix) Did crash when one copied messages to a local monitored folder. 990828: ***** released version 2.0b2 ***** 990827: (bug fix) Fixed compilation warnings about types to Tcl_Free et al. 990827: (bug fix) Crashed when moving some messages from disconnected folder to dbase. 990827: (bug fix) Message sequence numbers did not change when messages were deleted from a folder. 990826: (bug fix) Opening the folder menu gave an error after the connection to a monitored folder had died. 990826: (bug fix) One could get crashes while closing a folder. Also removed extraneous messages while closing folder. 990826: (bug fix) TkRat beeped when you got to the first unread message in a folder. 990825: (bug fix) Fixed evil bug in threaded sorting code which hid some messages. 990824: (bug fix) Password caching did not work. 990824: (bug fix) Could not save the vfolder structures if you had imported folders in a subfolder. 990823: (bug fix) Fixed bug in rat_edit module which could give errors in initUndo. 990823: (bug fix) Handle messages missing charset parameter better. 990822: (bug fix) Could not open pop-folders. 990822: (feature) All folder menus in the vfolderdef window are now closed by default. 990821: (feature) Now able to show HTML-text if the TkHtml extension is installed. (Feature provided by laurent.duperval@cgi.ca). 990821: (bug fix) Added print-key to keydef window. 990821: (bug fix) Fixed problem with deleting the compose window. 990821: (bug fix) Fixed ballon help over "Checkpoint on unmap" item. 990821: (bug fix) Made the configuration script really check the tcl/tk version and refuse to run if no recent-enough version is found. 990821: (feature) Changed the title of folder windows to be the folder name. 990821: (bug fix) The balloon help did not know of the num_deferred and num_held displays in the folder window. 990821: (bug fix) The options initialization expect there to be an env(USER) before the c-code had made sure there was one. 990821: (bug fix) Got errors when showing a message containing embedded images. 990820: (bug fix) Fixed problems with importing folders 990819: ***** released version 2.0b1 ***** 990818: (feature) Parametrized the dispatching of various mime-types when showing them. This makes it easier to add local code to show certain types. 990817: (feature) Can now monitor an arbitrary numbe rof folders. This means that the folder menu shows the number of unseen and total number of messages in each monitored folder. One can also enable/disable the watcher individually for each monitored folder. 990802: (feature) Now more intelligent when sending fails on a hard error. The message is no longer returned to the compose window and you only get one error message, regardless of how many messages you have queued. 990802: (feature) Added keyboard shortcut for printing. 990722: (feature) The configure script now checks if tclsh is compiled with memory debugging and sets the appropriate flags. 990722: (feature) Added display of number of deferred and held messages. 990720: (feature) Replaced badly-working busy outine with routines copied from BLT2.4i. 990718: (feature) Added patch for scrolling images by lduperval@sprint.ca. 990718: (feature) Added a simple prettyprinting routine 990710: (feature) Added "Wrap cited text" entry to edit menu in compose window. 990708: (feature) Now accepts some command line arguments (folder to open, start composing and netsync). Also checks for already running instance and resuses that so one can control an already runnign tkrat from the command line 990707: (feature) Now uses regular expression to find Re: in subjects 990707: (big fix) Now saves aliases before quitting (if needed). 990706: (feature) Now shows hourglass during some (probably lengthy) operations. Does not work perfectly though (shows default cursor in some parts). 990706: (feature) Rewrote the line wrapping algorithm. Should now handle list etc. 990614: (bug fix) Expressions where one searched for an exact match might cause a core-dump. 990609: (bug fix) The database code now handles '/' in the to address of messages inserted. 990607: (bug fix) One could not switch back to the original color once one had changed it. 990530: (feature) Replaced the internal file selector with a specially modified copy of the tk selector. 990522: (bug fix) Do not recreate the child bodypart commands each time they are asked for. This makes tkrat faster when handling multipart messages. 990518: (bug fix) The code which divided a menu which would otherwise be to big to fit on screen did now work correctly. It ignored submenus in the broken off part, thus making for example the message structure menu pretty unusable if one had to many attachments. 990518: (feature) Now uses the img package (if available) thanks to a patch sent by laurent.duperval@cgi.ca. 990517: (feature) Added disconnected IMAP-folders. 990517: (feature) Changed behaviour of log messages so those with an explicit time have precedence. 990424: (bug fix) Blush mode on: Fixed bugs where dates will get written incorrectly after 1999. 990423: (bug fix) The database code crashed when one inserted a message with a to-line longer than 1024 bytes. 990421: (feature) The balloon help now only triggers once while the cursor is within each trigging area. That means that if it pops up once and the user moves the mouse to amke it dissapear it will not reappear until the cursor has left the area. 990408: (feature) Now clears the 'deleted' and 'flagged' flags on messages when moving them between folders. 990330: (feature) Added default values for remote host and remote user (used when defiing new remote folders) 990330: (feature) You can now mark messages as unread 990329: (feature) Print all control-key shortcuts in menus with capital letter. 990329: (feature) Changed wrap-selection to wrap paragraph and added keyboard-shortcut (^J) 990325: (feature) If select "Send bug report" you now get to a dialog which asks for subject and description. This wil hopefully cut down on the number of bug reports with no explaining text whatsoever I get. 990323: (bug fix) Now only greys out the "attach pgp keys" entry in the "attach special" menu (in the compose window. Earlier it disabled the entire menu. 990323: (feature) Added --with-tcl-bin and --with-tcl-include options to configure. 990317: (bug fix) Make sure the balloon help text always is black. 990305: (bug fix) Could get error when sending deferred 990301: (bug fix) Aliases weren't properly saved after an extract. Thanks to baccala@freesoft.org for the patch. 990206: (bug fix) Added -verbose flag to lib/Makefile when building the package index file to see any errors. 990115: (bug fix) Now replaces commas in URLs with %2c before handing the off to netscape since NS can not handle commas in URLs passed on the command line. Also handle URLs with multiple '%' in them. 990115: (bug fix) Now imports aliases from mailrc-files better. 990113: (feature) Added --with-install-prefix option to configure. 990112: (bug fix) Now handles most possible boolean values 990104: (feature) Now only sorts the folder if really needed. 990104: (bug fix) Fixed cases where files got created with bad modes. 990104: (feature) Added double-click handling to alias window. 990104: (feature) Added some internal caching to spped up sorting. 990104: (feature) Added threaded message sorting. 990104: (feature) Added support for pgp5. 981206: (bug fix) Alias expansion depended on the lookup_name option. 981206: (bug fix) Added guarding tkwait visibility before all grabs 981202: (bug fix) Added the delete_group text which was missing 981128: (feature) Improved the speed by which folders are read. 980929: (bug fix) Now checks that the structure menu doesn't get too big. 980910: (bug fix) You could move the selection past the end of the list in the alias chooser window. 980905: (feature) Now wraps lines in the included part of original messages when composing replies. Also introduced new userproc which may give the citation to use. Added options to contorl line length and if we should include the signature separator. 980826: (feature) Changed handling of address header lines in the show window. If it conatins multiple addresses they are now shown one on each line. 980823: (bug fix) Code in alias expansion which detected loop used bad logic. Now silently ignores the looped entries instead of generating bad errors. 980823: (feature) Changed behaviour of address entry fields. They now try to fit all addresses into one line. If that is not possible it tries to have two addresses per line. If any address is longer than half a line it uses only one address per line. 980823: (feature) Moved balloon help to separate package. Also changed balloon help behaviour somewhat. Now it only pops up once in a widget until you reenter it again. 980822: (tcl bug workaround) Added extra space in argv argument to Tcl_OpenCommandChannel() to work around tcl bug. Thanks to baccala@freesoft.org for the patch. 980706: (bug fix) Now handles multiline alias when importing .mailrc aliases. 980706: (bug fix) Should now handle imap login ids containing whitespace 980706: (bug fix) If there was one bodypart in amessage in an unkown charset. Then no bodyparts after this was displayed. 980705: (feature) Can now cache passwords on disk. 980628: (bug fix) One could get missing text errors if one had added nonstandard headers to the selected headers list. 980628: (bug fix) Added missing texts 980628: (feature) Added french texts. Thanks gos to Christope Martin (Christophe.Martin@ipnl.in2p3.fr), Stphane Gourmelen, Nol Giraud, and ric Simon 980625: (bug fix) Fixed problem with grab when redefining keys. 980621: (feature) Fixed so that the charset handling in text source files works as expected. 980621: (bug fix) Removed extra pair of braces around the folder specification for all imap and pop folders. 980620: (bug fix) TkRat could get internal errors when syncing an empty folder. 980620: (bug fix) It was possibel to get "index out of bounds" when right clicking in an empty folder 980620: (feature) Added message bouncing 980607: (feature) Added handler for WM_DELETE_WINDOW for all windows. 980601: (bug fix) The compose window now check that all of To:, Cc: and Bcc: are empty before complaining about no recipients. 980601: (bug fix) Could dump core when retreiving name of recipient. 980531: (feature) Made submenus default o not have a tearoff entry, added option to control this feature. 980531: (feature) Added more url browsers and moved netscape startup to background 980530: (feature) Rewrote the new/edit folder window completely. Many new features in it. 980406: (feature) It is now possible to control the level of alias expansion in the compose window. 980403: (feature) Rewrote RatDecodeHeader to use a custom function instead of an regexp. 980322: (feature) Made the folder window paned. 980318: (bug fix) The compose window got out of sync when a send failed due to lack of recipient. 980316: (feature) You can now define multiple external editors, which may operate on different character sets. 980314: (feature) Changed the OkButtons routine to use the -default argument instead of an extra frame. 980309: (bug fix) Dumped core when moving expired messages to inbox. 980309: (bug fix) The inbox folder was created with an empty name when tkrat was started for the first time. 980308: (code change) Created a package rat_list which handles lists of things. 980227: (feature) Reorganized the sending part of the preferences window. There are now options for setting the default return_path and to control if the sender header should be generated. There is also a new window available from the compose window which shows which address headers will be generated for a message. 980224: (bug fix) You got the wrong balloon help texts for the attach buttons. 980222: (feature) Just withdraw the preferences window when the user selects dismiss (instead of actually deleting it). This makes it kind of faster the second time around. 980222: (feature) Now configure works better on all kinds of OSF-systems. 980222: (feature) Converted to tcl/tk8.1. All support for older versions is dropped. The internal representation of characters is now utf-8 which made it possible to rip out a good portion of kludgy code. This also led to a completely changed font handling. 980130: (code change) Broke out the tab code into a separate package. 980125: (feature) You can now toggle flagging on a range and by dragging. 980125: (bug fix) Now only prints a warning (to stderr) when trying to modify a locked variable. 980125: (documentation) Added some documentation on rimap. 980125: (feature) Now sorts imported imap folders alphabetically 980124: (feature) Now grays out open folders in the open folder menus. 980124: (feature) It is now possible to have multiple folder windows. 980123: (feature) Added checkpoint function which checkpoints the flags in the current folder periodically. 980118: (merge) merged changes from branch: * (bug fix) TkRat did not esacpe lines containing a single dot when sending to a prgram (but did for SMTP). Now it does. * (bug fix) You got a tcl error when you closed the folder key definition window. * ***** released version 1.1 ***** * (bug fix) Modified configuration so it works on RedHat 5.0 systems. * (bug fix) The counter in the send deferred window could get out of sync. We now also close this window when the sending process dies. * (bug fix) The sender process dumped core when a send failed. * (bug fix) Made the BalloonShow code more robust. * (bug fix) The texts for the sending program input data width were restored (in the preferences window). * (bug fix) Made RatAddressIsMe more robust against strange (but legal) address fields. * (feature) Now allows the user to insert spaces in the address fields. * (bug fix) In the compsoe window backspace and delete only removes the selection if the cursor is placed in it. 980118: (bug fix) Did not realise the SMTP serrver supported DSN if it was the last capability reported after EHLO. 980117: (bug fix) The Probs DSN button did not show in the preferences window. 980110: (feature) Removed all references to tk.h from the library files. 980110: (feature) Now tkrat does not die when a mailbox stream dies. It just closes the folder in question. 980109: (feature) Changed from building a special wish to creating a dynamically loadable package of all the c-parts. 980106: Branched sources to start working on 2.0. 980105: ***** released version 1.1a ***** 980104: (feature) Now also checks the from address you have specified in the options dialog when checking oif a message is from yourself. 971227: (bug fix) You could get an error message about variable mh(save_to) from the compose window 971226: (feature) Now removes any prepended or appended whitespace from vfolder names. 971226: (feature) You can now turn off the auto-expunge which happens when folders are closed. 971226: (feature) Now check for tk version and warns about bug in tk8.0. 971222: (feature) Changed copyright statement. 971221: (bug fix) Redid the timezone references (the new version lent from tcl 8.0p2). This should fix some compilation problems. 971219: (bug fix) It is now possible to insert a return character in the content and comment fields in the alias window. 971214: (bug fix) The "Folder flagged" command could return invalid indexes. Thus group operations could fail under some circumstances. 971210: (bug fix) Deleting aliases did not update the saved aliases. Thanks goes to Bernard PERROT for the fix. 971207: (bug fix) Keep the 'N' flag when moving unread messages from IMAP mailboxes. 971207: (bug fix) An extra newline got added when copying messages to IMAP folders 971207: (bug fix) When you move a flagged message to another folder the flagging is cleared instead of kept as it used to be. 971207: (bug fix) It is no longer possible to open multiple edit windows for one alias. 971202: (bug fix) Address lists generated inside tkrat did not work well at all. 971130: (bug fix) Save all aliases when new aliases has been extracted 971127: (feature) Changed the default tk binding for entries and text so that the selection is not cleared as soon as you move the cursor. 971127: (feature) Added documentation in CONFIGURATION-file of how to set the system address book. 971127: (bug fix) It was impossible to actually delete IMAP folders. 971126: (bug fix) Sometimes the wrong message was selected after an expunge. 971126: (feature) Total rewrite of the help system. Theer are now balloon help entries for most buttons and fields and the help window is much more intuitive (as well as redone). 971125: (bug fix) Deferred sending did not work if you tried to send to many messages in one batch. 971125: (bug fix) Now RatDaysSinceExpire can not return a negative value. 971109: (bug fix) The url matching sometimes matched to much. 971109: (bug fix) TkRat dumped on reading certain (invalid) messages. Thanks goes to Andrew.Greer@vuw.ac.nz for the fix. 971105: (bug fix) Should not cache any connection or password if the login failed. 971022: (bug fix) The dismiss button in the preferences window did not work. 971022: (bug fix) Code which interpreted host:port syntax for SMTP host was broken. 971022: (bug fix) Removed all references to CFLAGS in the Makefile since it did not work. 971021: (bug fix) Moved bad references to sigbut in folder window code. This bug made tkrat crash on start if pgp support was not enabled. 971021: (bug fix) Generated error when opening the create group window. 971021: (bug fix) Fixed build problem on sun machines. 971019: ***** released version 1.0.5 ***** 971019: (bug fix) Both the advanced and the basic expression window are now managed by the grid manager. This hopefully fixes the problems some people have with it turning into no size. 971019: (bug fix) Can now read the bad vfolderdef files which one version generated. 971019: (bug fix) You could initiate a drag from an empty subfolder in the New/Edit vfolder window. This gave tcl errors. 971019: (bug fix) Should now never record a position such that no part of a window is visible. Also removed the annoying small window on the first startup. 971019: (bug fix) Could dump core under some circumstances when syncing an IMAP folder. 971011: (bug fix) You got an tcl error if you entered text in the command text widget while no command was selected. 971011: (bug fix) Could dump core when saving copies of outgoing messages to IMAP folders. 971009: (bug fix) An error of type "Cut & Paste" has occurred. The code which did PGP checking was checking the wrong option (lookup_name) to see if pgp was enabled or not. 971008: (bug fix) Now detects if a save copy of outgoing message fails and warns the user. It could also crash when this happened. 971007: (bug fix) You could get a core-dump when you saved a copy of an outgoing message to a file. 971005: (feature) The Makefiles now honors the environment variable CFLAGS. Another new feature is that the c-client libraries are not rebuilt unless it is needed. 971004: (bug fix) Removed definition of global variable timezone. 971004: (bug fix) Fixed bad username in imap login which occured when moving to an IMAP folder. 971004: (bug fix) Fixed case for some coredumps which occured if you did cache connections but not passwords. Thanks goes to Marc Mengel . 971004: (feature) Can now import mh-folder structures. Thanks goes to Marc Mengel . 971004: (bug fix) Newly created vfolderlist files got the wrong version number. 971004: (bug fix) Fixed waitpid() includes. 971004: (bug fix) Removed a big memory leak. 971001: (bug fix) Now checks that the signature file not is a directory before trying to read it. 971001: (bug fix) Saving the aliases found when scanning the old files did not work. 971001: (bug fix) TkRat got into an incosistent internal state if one deleted a compose window with the window manager. 971001: (bug fix) Copying aliases from one address book to another did not make sense (the names must be unique anyway). 971001: (bug fix) The reverse natural sort order was not reversed. 971001: (bug fix) Misc code cleanups which should reduce number of warnings and errors on 64bit systems. 970921: (bug fix) The apdding macro in ratAddress had a bug so it did not work to read aliases on ultric machines. 970921: (bug fix) The new FormatDate function added an extra space to the date. 970918: (bug fix) Removed an old reference to option(aliases_file) in convert. 970918: (feature) Added doc/userproc.example 970917: (bug fix) TkRat dumped when you tried to save to a dynamic folder. 970916: (bug fix) Crashed when trying to save outgoing messages to an IMAP folder. 970916: (bug fix) The conversion of aliases failed (to update the list of aliases shown). 970916: (bug fix) You could get an error when closing the alias window. 970912: ***** released version 1.0.4 ***** 970907: (bug fix) The "move to inbox" expiration type did not work. 970904: (bug fix) Ignore set flag requests for read-only mailboxes. 970902: (bug fix) Made the calling for userprocs from C robus against errors in the routines. 970901: (bug fix) Now doesn't set the answered flag when you forward a message. 970901: (bug fix) Messages from yourself got named To: when saved to a dynamic folder. 970831: (bug fix) Fixed coredump which occured when one entered an rfc822 group addresse. 970831: (enhancement) Did some speed optimizations. 970827: (enhancement) Redid the caching controls. All the different caching is now controlled by identical set of preferences and you can also set infinite caching (by setting the timeout to zero). 970824: (bug fix) At last! An old bug which made saving copies of outgoing messages via imap to picky (well, not extremely forgiving) IMAP servers fail is at last fixed. Big thanks goes to Greg Owen for help with debuuging. 970824: (feature) Implemented a browse mode. In this mode the actual message bodies are not shown by default. This mode is selecatble via a menu and via a folder default. 970824: (bug fix) TkRat tried to decrypt old style encrypted PGP messages even if you had pgp support turned off. This is now fixed. 970824: (feature) It is now impossible to change the color scheme if your tk is older than 8.0. These versions had a bug which made this not work anyway. You get a warning instead. 970824: (feature) Added a find window which can search the message body or the list of messages. Had to convert the list of messages to a text widget to be able to acomplish this. 970823: (feature) The URL parsding is now done on demand instead of all at once. Also made a small modification to the search expression. 970806: (feature) Many internal changes in the preferences window. Partly a new look as well. 970805: (bug fix) The state of the Watcher button in the TkRat menu was not always saved correctly. 970804: (bug fix) The signature was wrongly calculated when you signed a message which was constructed by forward as attachment. 970804: (bug fix) TkRat dumped when you tried to repy to an encrypted message. 970804: (bug fix) group move operations were not aborted when an error occurred. 970804: (bug fix) The ChooseMessage dialog asked which message you wanted to reply to when you were forwarding inline. Message fixed. 970803: (feature) Added private version of bgerror with "Send bug report" button to make it easier to report tcl bugs. 970803: (feature) Rewrote the alis window from scratch. Many enhancements to the alias system in general as well. 970728: (feature) Aliases can now nest any level (but not loop). 970721: (bug fix) Fixed problem with stealing mail from netscape. 970719: (bug fix) The menu shortcuts should no longer also invoke any other bindings. 970706: (feature) TkRat now send multiple messages through one SMTP-channel when sending deferred. The send deferred window now behaves better when you send more messages while already sending. 970706: (bug fix) TkRat now survies if $HOME ends with a /. 970706: (bug fix) TkRat is now more resistent to bad images in attachments. 970704: (bug fix) Improved appending to imap folders code. 970701: (bug fix) TkRat dumped core if the users gecos field contained any non us-ascii characters. 970701: (feature) The show URL feature is now bound to the ButtonRelease event (and you can cancel by moving the pointer before releasing. 970629: ***** released version 1.0.3 ***** 970629: (bug fix) TkRat should not show the pgp output window if there was no output. 970629: (bug fix) Now shows application/pgp messages. 970628: (bug fix) TkRat included the wrong part of some multipart- messages when you were replying to messages from the Database or contained messages. 970628: (bug fix) Fixed dependecies in lib/Makefile.in. 970627: (bug fix) Fixed a stupid bug in the database code. Sometimes the database did not see the last messages in the folder. The index information for these messages could get lost (but the messages are still in the database and willshow up as LostMessages when you check the database). 970627: (bug fix) You got an error from the compose window if you had deleted the default save folder. 970627: (bug fix) Garbage was attached to message when they were saved to IMAP folders while sending. 970625: (bug fix) The SendBugReport menu entry did now work. 970625: (bug fix) TkRat could still crash when you got a new message and deleted some other messages before you synchronized. 970625: ***** released version 1.0.2 ***** 970625: (bug fix) The sync command did not work! 970624: (bug fix) Some dates in the changes files were wrong. 970624: (bug fix) Under some circumstances could tkrat and the imap toolkit get out of sync and you got the message "bad msgno". 970623: (bug fix) Recoded RatMangleNumber in C since tcl8 has new number representations the breaks the tcl version. 970623: (bug fix) Importing IMAP folder could give strange results when the first folder in a subtree was selectable. 970622: (bug fix) Attached PGP keys are now sent as multipart with only one key per part. 970620: (bug fix) Signatures did not match if the message contained any non-text part. 970620: (bug fix) Some picky IMAP servers refused to save outgoing messages with the message "Message contains bare newlines". 970620: (bug fix) The define keys window did not behave well when resized. 970620: (bug fix) Message list scrolling did not always work correctly under tcl/tk 8.0. 970620: (feature) URL now flash when ypu press them. 970620: (feature) The color of URLs is now an option. 970620: (bug fix) TkRat got into an incosistent internal state if you did not restart after enabling PGP. 970619: (bug fix) Fixed a couple of potential coredumps that could happen when one aborted decoding of an old style PGP message. 970617: (feature) Now does not show output from PGP if it is less than two characters. 970617: (bug fix) Replies to PGP/MIME messages got part of the headers included. 970617: (bug fix) Improved the regexp used to scan for embedded URLs. 970616: (bug fix) RatInitCurrent got called before all options were set. Fixed by changing it into an trace function and trace the relevant options. 970616: (bug fix) Fixed a couple of beatuy errors in ratDbase.c (which caused warnings (and errors) on some systems. 970616: (feature) Now builds somewhat better on HPUX-10 systems. 970616: (cleanup) Improved calculation of default font width. 970616: (bug fix) Keyboard shortcuts in menus could be indicated wrongly if they containd a Shift-. 970612: (bug fix) Pressing right mousebutton over an URL might give an error message since we did not clean up after the previous run. 970612: (feature) The bug report now includes information about which versions of tcl/tk you have used. 970612: (bug fix) Added wait call when signing and encrypting messages to eliminate PGP zombies. 970611: (feature) Changed order of the changes file so that the most recent changes are on top. Thanks goes to Matt Shibla who did the reordering. 970611: (bug fix) Changed order of compilation flags so the c-client directory is included before the system directories. 970610: (bug fix) Scan for PGP messages continued long after the current message so this test returned to many trues (which gave strange results). Also tried to display PGP output even if the user aborted the operation and thus no output was generated. 970607: ***** released version 1.0.1 ***** 970607: (feature) Added support for the frink tcl minimizer. 970605: (bug fix) You could get errors when forwarding messages (inline) and you had a header that tkrat doesn't know about in the selected headers list. 970604: (bug fix) Fixed focus problem for people who uses click to focus mode. 970604: (bug fix) Fixed unaligned errors on alphas (I hope). 970603: (feature) You can now use space and BackSpace to scroll in the help window. 970603: (feature) Recognises embedded URLs and highlights them. Also adds bindings to the so the user can start an browser by just clicking. 970602: (feature) Now has keybindings for scrolling messages line by line and also a keyboard shortcut for moving to the end of the message. Thanks goes to Richard Meitzler . 970601: (feature) Will now build with tcl/tk 8.0 if available 970601: (feature) Added units to some of the fileds in the preferences window. Also changed the unit of log_timoeut to seconds. 970601: (bug fix) Now builds with tcl/tk 8.0b1 970531: (bug fix) Added a section about the permissions of /var/spool/mail to the README file. 970530: (bug fix) Added mopre paranoid checks of addresses to prevent cores. 970529: (feature) Added PGP support. 970529: (bug fix) Made minor fixes to the helptexts. 970529: (bug fix) Moved the color initialization to after the font initialization in order to make it usable on systems that do not have the default tk fonts available. 970529: (feature) Changed default interactive command to 'xterm -e sh -c' 970519: (bug fix) The address entry windows assumed they were always 73 characters wide. 970518: (feature) The text widget in the compose window now removes the indention from empty lines. 970518: (bug fix) You could get an error from the fileselector if you entered a directory that did not exist 970517: (feature) Move the folder sort options to their own submenu. 970503: (feature) You can now set the default action of the copy attached files entry in the compose window. 970503: (bug fix) The New/Edit aliases window does now resize properly. 970502: (bug fix) Fixed bugs in line wrapping code. 970502: (feature) Added "Send bugreport..." menu entry. 970502: (bug fix) lib/Makefile.in should now care about the LIBS variable from configure 970430: (bug fix) It is no longer possible to move a vfolder struct into itself or any descendant of itself. 970421: (bug fix) Now doesn't include the sender field in replies to messages. 970416: (bug fix) Added vertical scrollbar on key definition window. 970416: (bug fix) TkRat can now import IMAP folder which are selectable and have children. 970416: (bug fix) Ultrix fixes. Also changed all calls to strdup to cpystr. 970414: (bug fix) Now the child process clears the cache passwords at start. 970408: (bug fix) While sending messages via SMTP some lines of the messages got delimited by \r\r\n. 970404: (bug fix) Fixed a bug where the sending process could crash (under some very rare circumstances). 970403: (bug fix) TkRat was VERY picky about the names of the header lines in the Headers entry in the compose preferences window. 970326: (bug fix) Fixed the general font selection so that entries and texts have the correct default font. 970326: (feature) Added sorting on subject and on sender. Also speeded up sorting on subject by date. 970326: (feature) Replaced call to strstr in RatTclPutsSMTP with a custom loop. The strstr implementation on SunOS 4 was awfully slow. 970325: (bug fix) Fixed reading uniniztialized memory in RatType. 970323: (feature) There are now keyboard shortcuts for every menu. 970323: (feature) Added option to include or not include signature of letters we are replying to in the reply. 970323: (feature) Added linking of the build-in imap routine which supports autentification without sending passordws inb the clear. 970322: (feature) Now checks that we have a valid hostname before we try to send any messages. 970322: (bug fix) Now uses local hostname instead of ".MISSING.HOST.NAME" in addresses that are missing the domain part. 970322: (bug fix) Plugged memeory leaks. 970321: (feature) Redone the way the program is started. Now a /bin/sh-script gets installed in the bin directory. This script sets some environment variables before the actual program is started. This means that we no longer compile any paths into the tkrat program. 970321: (feature) Added internal version of file command (gets used if we link with tcl < 8) which contains limited copy and delete functionality. 970320: (feature) Added RatUP_Bell userproc. 970320: (bug fix) The check database function now starts by checking if the database even exists. 970320: (bug fix) Fixed scrolling problem under tk8.0a2 (thanks to phillf@fridu.com) 970320: (feature) Added preferences for the SMTP timeout. 970320: (bug fix) Added test for empty search expressions. 970320: (bug fix) The caching of passwords and conenctions didn't work under some conditions. 970320: (bug fix) Now the buttons and menus change fontsize just as the rest of the widgets. 970316: (bug fix) You could get the expression window to crash if you selected advanced mode while you still were in advanced mode. You could also cause tkrat to dump a core if you gave an empty expression. 970316: (bug fix) The elm alias importion routine did not work. 970315: (bug fix) Fixed bad trace deletion in compose.tcl 970313: (bug fix) Fixed stupid bug in compose.tcl which made it crash when you tried to use the alias list (thanks to eubell@itwm.uni-kl.de). 970312: ***** released version 1.0 ***** 970311: (bug fix) Some of the dit operations in the compose window didn't check that we were in the rigth state (like having selected text before tanking it). 970311: (bug fix) Fixed stupid bug which made it fail while autoloading AddImapPorts if you upgraded from version 0.74 to 1.0. 970310: (bug fix) Made sure that lines in the database index never may contain an newline. 970310: (bug fix) Made several fixes to encoding and decoding of header- lines. 970309: (bug fix) The view DSN window (where one sees a full DSN) failed to show any extra information when the apropriate button was pressed. 970309: (bug fix) Garbage could be shown at the end of some messages. 970308: (bug fix) The blank line separating header and body could dissapear when you moved from dbase to dbase. 970306: (bug fix) Improved configure script. Now looks for include files in more places and also checks for the 's' library (used on AIX). 970306: (feature) Changed the texts of the buttons in the alias edit window. 970306: (bug fix) TkRat failed to send messages if you had a hostname which included space. 970305: (bug fix) You couldn't save outgoing messages to IMAP folders. 970305: (bug fix) There was garbage added to the end of messages read from the database. 970305: (bug fix) Remade the way unkown text messages and messages with unknown encodings are shown. The old behaviour didn't mix well with the text widget. 970305: (bug fix) Found yet a couple of bugs in the date parsing code which made messages sort in the wrong order. 970304: (bug fix) The internal copy of the mailcap got corrupted when it contained an test-clause which used parameters. 970304: (bug fix) The configure files didn't relly care about the --with-tk-config argument. Also added test for crypt library. 970304: (bug fix) The import IMAP folders code required that a port number was specified. 970302: (bug fix) Fixed multiple bugs in ComposeForwardInline and related functions. They didn't always find the rigth bodypart to inline. They could fail when you aborted in some cases and the also failed to clean up after themselves. 970302: (feature) There is now a key to group messages (default 'g'). 970302: (bug fix) Improved code that read elm-aliases so that it now understands multi-line aliases. Thanks goes to Jonathan Cook . 970302: (bug fix) Since you can not move messages to POP-folders then it is pretty meaningless to include thos folders in the Move menus. 970301: (feature) The "See old messages" window did not export the selection. 970301: (bug fix) "Subject by date" wasn't always correctly sorted. 970301: (bug fix) Fixed bug which made it crash sometimes when moving between network folders and local folders. 970227: (bug fix) Removed text about "Save outgoing" in the help window. It was an experimental feature that dissapeared many versions ago. 970226: ***** released version 0.75 ***** 970225: (bug fix) You can now have other characters than a-z in alias names. 970225: (bug fix) Extract addresses didn't work very well on addresses that were MIME-encoded. 970224: (bug fix) Close the login-window directly when done so that we do not accidentally lock the X-display if something bad happens. 970223: (bug fix) If you did a reply to an message which contained an embedded message which contained further messages you didn't get to choose among them. 970223: (upgrade) Upgraded to imap-4.1 toolkit (from 4.0). 970221: (bug fix) The "Select the message before the first unread on open" mode should select the last message if there are no new messages. 970220: (bug fix) The imap code could crash if it tried to read an message with an embedded message from an 4.1Rev1 server. 970220: (bug fix) The AliasExtract function should not extract the full name into the alias content field. 970220: (bug fix) The address entry field (in the compose window) failed to calculate the correct with of an address when placing it in columns. It also always deleted trailing commas when the focus left it. 970220: (bug fix) Increased SMTP timeout time to 120 seconds. 970218: (bug fix) There were a couple of problems with the insert alias window (when composing messages). You couldn't scroll with the mouse and you couldn't insert any addresses after the first one. 970217: (feature) You can now speficy the imap port number when importing imap folders. 970217: (bug fix) A number of tests in ratDbase were too forgiving so the program could crash. 970214: (bug fix) Probing for DSN support always reported yes if you had verbose mode on. 970213: (bug fix) Disabled caching of pop-connections. They are not usable and it turns out that at least some pop servers only expunge messages when they receive a quit command (and this didn't always happen because we kept the link open). 970209: (feature) Changed config-scripts to include tests for tcl(tk8.0. But I commented the tests out since I have since found problems with tcl/tk8.0a2. 970209: (bug fix) Made sure that saved positions are within the visible area of the screen. 970209: (bug fix) Text parts in unkown charsets were alwas shown in a 12pt font. 970209: (feature) Now defaults to specify the imap port. If this is not done then the imap toolkit tries to do an rsh to the remove host when opening an imap folder, first when this has failed did it try over the network. 970209: (bug fix) Tkrat could dump if you tried to move a message to an imap folder. 970209: (feature) You can now autosave outgoing messages by marking one of your vfolders. 970208: (bug fix) Now expands ~ in print command. 970208: (bug fix) The alias extract window didn't care if you pressed ok or cancel. It added the aliases anyway, and it failed if all suggested aliases wree deselected. 970206: (bug fix) Changes to the watcher time interval only took effect when opening a new folder. 970205: (bug fix) Do not move slection (of message) when doing a group move. 970205: (bug fix) Stupid bug made the program crash if there were more than 64 entries in the mailcap files. 970204: (bug fix) The alias chooser window didn't change its view when the selection moved outside the current view. 970203: (bug fix) You could get an error if you clicked on an empty subtopic in the help window and the Detach button shouldn't be enabled if you click in an empty attachment list in the compose window. 970203: (bug fix) Remember the scrollbar position in the alias list when updating it. 970203: (bug fix) You could get an error if you selected cancel when you were asked to choose message to forward/reply to. 970202: (feature) The prefereces window is now scrollable. 970202: (bug fix) A stupid bug in the address splitting code sometimes dropped the final character from addresses. 970201: (bug fix) TkRat only found a subset of the embedded messages when replying to a message which contains embedded messages. It also had problems if it found to many messages (the window grew to large). 970130: (bug fix) The configure file didn't really check the --with-tk-config argument. Also now runs wish to get the lib directory that way (only if $DISPLAY is defined). 970129: (bug fix) Importing aliases called the wrong function when it wanted to update eventual alias windows. 970128: ***** released version 0.74 ***** 970128: (bug fix) Made the configure script more robust. Also replaced the TCL_CONFIG_FILE and TK_CONFIG_FILE environment variables with real arguments to configure. 970127: (bug fix) You could crash tkrat if you continued to try to define a file folder of dynamic folder after the first attempt failed. 970123: (bug fix) The first entry in the Send&Save menu was never deleted. Thanks goes to P.H.A.Venemans@research.kpn.com. 970119: (feature) The fileselector now sorts directories first. 970119: (bug fix) TkRat didn't care if mail_ping reported that the stream was dead. 970119: (bug fix) Sometimes TkRat crashed when done sending deferred messages. This was due to a bug in tcl. 970116: (bug fix) Do not add the fullname to alias expansions if the alias in question doesn't have a fullname. 970113: (bug fix) The code (undocumented) which were supposed to split the SMTP-hostname and an eventual portnumber didn't work. Thanks goes to Pieter H.A. Venemans 970112: (feature) It is now possible to extract aliases from messages. 970112: (feature) Remodeled the alias window. Now it is much more compact. 970112: (feature) (happy birthday HAL) Added the version date to the version window. 970111: (feature) Added a Print entry to the group menu. 970111: (bug fix) RatBgExec failed when you had more than one call outstanding. 970110: (feature) Added menu entries for folder sort order. 970110: (bug fix) Tearoffed move-menus moved the message that was current when they were tearoffed. Now they take the current message at the time of selection. 970109: (feature) Moved some of the menu entries. 970108: (bug fix) You could not import empty directories in the vfolderdef window. 970108: (feature) Can now create/delete the actual IMAP folders. 970106: (feature) Now shows the recipient instead of the sender in the message list if the sender is oneself (and vice-versa for recipient). 970105: (feature) Added support for mailcap files. While I were at it I changed they way unkown things are displayed in the show window (cosmetic changes only). 961228: (bug fix) Increased the size of one internal buffer in the pop-code. 961228: (bug fix) Fixed some memory leaks. 961228: (bug fix) Sometimes one submenu in the vfolderlist could obtain an inbox mark. 961227: (feature) Now asks for confirmation before it overwrites files when saving bodyparts. 961227: (feature) Added ability to specify ports for IMAP and POP3 folders. 961226: (feature) Added command to check database (and fix it as well). 961219: (feature) Rewrote the address split function (used when entering addresses) to handle commas correctly. 961217: (feature) You can now change the heigth of the list of messages. 961217: (bug fix) The dates shown were the time of arrival instead of the date header. 961216: (bug fix) The program crashed when it encountered a character set name which contained whitespace. 961215: (bug fix) You couldn't delete aliases with spaces in the name. Also did enhancements to the alias window(s), now updates show in all of them. 961215: (bug fix) TkRat failed to monitor the inbox if this didn't exist when the program was started. 961214: (bug fix) The folder menu didn't work if you had an dynamic folder with no entries in it. 961214: (bug fix) You couldn't forward a message (inline) unless its top level content-type was text/plain or multipart/mixed. 961214: (bug fix) Sometimes an extra "Status: RO" was added to the last header row of messages. 961214: (bug fix) Tell the user that he must restart for a change to masquerade_as to take effect. 961103: ***** released version 0.73 ***** 961102: (feature) The Makefiles now support the --program-prefix and --program-suffix configure arguments. 961102: (bug fix) It was possible to get the fileselector to accept a directory by selecting it and then to press OK. 961102: (bug fix) The watcher didn't show the message if the mailbox was empty when a new message arrived. 961102: (bug fix) The compose window was destroyed even if the send failed. 961102: (bug fix) The ^S key combination did not work when the focus was in the text part of the compose window. 961031: (bug fix) The local-part of an address may contain dots '.' without beeing quoted. 961030: (bug fix) Addresses and their fullnames were converted to lowercase when expanding them. 961030: (bug fix) FolderGetNextUnread failed if the folder only contained one message (which was read) and the direction was reverse. 961029: (bug fix) it was not possible to bind Alt-key cominations since alt also wanted to traverse menus. 961029: (feature) Now tries to use tcl7.6/tk4.2 first. 961029: (bug fix) Now global_config_path really does depend on the prefix argument to configure. 961029: (bug fix) clarified the labels of option(from) and option(masquerade_as) in the preferences window. 961029: (bug fix) unqualified addresses were wrongly expanded with the local hostname even if masquerade_as was defined. 961028: (bug fix) there were some problems with header-names that contained '-' and their names. 961028: (bug fix) Initialize the font list to null in RemoveFont. 961028: (bug fix) The cleanup of old DSN-files failed if there were no old files. 961028: (bug fix) The quit key wasn't included in the folder window keyboard definition dialog. 961028: (bug fix) A couple of labels in the edit alias window were wrong. 961028: (bug fix) Expression list failed if there were no saved expressions. 961027: ***** released version 0.72 ***** 961027: (feature) Removed the patch for sendmail, use sendmail 8.8 instead. Did misc cleanup in the documentation. 961027: (feature) Replaced imap-4.BETA with imap-4. 961027: (bug fix) Added configuration code which checks for WNOHANG. 961027: (feature) Added redo functionality. 961024: (bug fix) Fixed hostname given during EHLO in SMTP phase. Sometimes the domain name appeared twice. 961020: (feature) Now checks for active compose sessions when quitting and gives you a chance to abort the quit. 961020: (bug fix) Folder menus higher than the screen are now handled gracefully. 961020: (feature) Added userproc for signature 961020: (bug fix) Improved the focus handling in the compose window. 961020: (bug fix) The DSN coded did not remove the files associated with each DSN when it expired. This is now fixed and the directory is also cleaned when starting this version the first time. 961019: (feature) Added keyboard shortcuts to the compose window. 961019: (feature) Now prints the capital letter when the Shift modifier is prsent in the accelerator fields. 961018: (feature) can now build with tcl7.6 and tk4.2. 961017: (bug fix) Some parts of some windows could be obscured when the window was shrunk. 961016: (feature) made alias names case insensitive. 961016: (bug fix) The Reply-To and Content-Description fields did not work in the compose window. 961016: (bug fix) TkRat refused to start if the inbox folder got deleted. Fixed by first making sure there always is an inbox. 961016: (feature) added option to enable/disable the watcher window 961015: (bug fix) There was internal data corruption if an embedded message contained more than one multipart. 961010: (feature) added keyboard shortcut to change number of shown headers. 961010: (feature) replaced bounce and forward with "Forward inline" and "Forward attachment" 961006: (bug fix) the dbase is now more robust and can handle messages without From: and Date: fields. 961006: (bug fix) changed the text strings in the "Create alias" window. 961006: (bug fix) messages were sometimes left on the status bar when the sending of a mail failed. 961006: (bug fix) Messages of type message/delivery-status were always marked is read (before the user actually read them). 961006: (feature) Made the encoding button in the Attach window state more clearly that it represents the current encoding. Also made if more "fool-proof" by disabling the other entries if the encoding is 8bit or binary. 961005: (bug fix) TkRat sometimes failed to note that you made changes to an header entry in the compose window. 961003: (bug fix) TkRat failed to handle addresses like "foo:bar"@fubar.com This was due to code which didn't handle rfc822 well. Fixed. 960917: (bug fix) some realy weird addresses like "@lucent.lucent.com" could get the information routines to dump. 960915: (bug fix) modified RatDecodeHeader so that it can take a NULL pointer or an empty string and not crash. 960912: (feature) may now suggest a name when saving a bodypart. 960911: (bug fix) the A flag was set even if you held or aborted the composition of the reply. 960911: (feature) now ignore addresses matching "*-owner" or "owner-*" in the sender field when doing a reply to all. 960911: (feature) added dynamic folders. 960911: (bug fix) improved the fileselector so it doesn't quits when you press return if you should give a directory. 960911: (bug fix) sufficently advanced expressions got corrupted when saved. 960910: (feature) added option to start iconified. 960909: (bug fix) outgoing messages were labeled with the charset of the default interface language instead of the charset they were written in. 960908: (bug fix) Db_FetchFirstTextProc didn't find the start of the first text part if it was a multipart message. 960908: (feature) can now convert to local newline conventions when saving bodyparts. 960908: (feature) now handles text messages in unkown character sets much better. 960906: (feature) added terse mode to smtp_verbose. 960906: (bug fix) didn't recognize DSN's. 960906: (bug fix) you couldn't find any messages in a database folder if the folder was defined with no expiration time. 960905: (bug fix) better handling of multiparts inside multipart/alternatives. 960902: ***** released version 0.71 ***** 960903: (bug fix) check if there are any saved expressions before showing the menu 960903: (bug fix) the Create in window window did not work correctly. 960831: (bug fix) Problems with the keydef window: you couldn't resue a deleted key without reopening the window. Fields didn't grow when keys were added. 960831: (bug fix) Added a version-date so that tkrat needen't be confused when the last_version isn't in the list of known versions. 960831: (bug fix) the welcome window should block the application. 960831: (bug fix) tkrat didn't implement the xtext part of rfc1891. 960829: (bug fix) added charcter set specification to the default fonts. 960829: (bug fix) somehow big chunks of code must have dissappeared from the code that send via program. The sending program didn't get any recipients as arguments at all. This is now fixed. 960828: (feature) there is now a default user name when creating imap folders. 960828: (bug fix) there were bugs in the calculation of the domain name. 960827: (feature) added attribution of messages 960827: (bug fix) the password caching was less than perfect. The passwd was cached even if it was wrong. 960826: (bug fix) you could not move messages between IMAP folders on the same host. 960825: (feature) added a group menu and code to support it (much code). 960821: (feature) added list command to message entities and modified the folder list command. 960820: (bug fix) there were multiple bugs in the dbase searching code that showed up whenever you tried to search for something with more than one keyword. 960820: (bug fix) the "select message before the first new" did select the message after instead. 960820: (bug fix) fixed a problem in imap which made c-client crash under some instances while opening mh-folders. 960820: (bug fix) the sending process would sometimes hang. This was solved by using blocking mode on the SMTP channel all the time unless we are waiting on a response (we need to timeout this). 960818: (bug fix) fixed stupid bug in ReadElmAliases. 960807: (bug fix) the fileselector looped when you selected '../' while standing in '~'. 960807: (bug fix) FolderSelectNextUnread could loop if the folder only had one message in it. 960806: (bug fix) the standard folder did become confused about how many messages it had when expunging messages. This lead to sequence number errors when accessing POP-folders. 960806: (bug fix) increased height of preferences window. 960806: (bug fix) changed destoy to destroy in info.tcl 960805: (bug fix) the fileselector need to know if the selected file should already exist or not. It did also hang if you tried to access .. when standing in ~. 960805: (feature) added a handler for the delete protocol for '.'. This handler just calls Quit. 960805: (bug fix) do not set any colors when running on a B&W display. 960804: (bug fix) the size calculation was wrong (should obviously not include the selectborder in the calculations). 960804: (bug fix) fixed focus resetting in ComposeHold. 960804: (bug fix) fixed problem in dsn.tcl where date would be called with numers that had leading zeroes. 960803: ***** released version 0.70 ***** 960802: (feature) you can now mark any of your vfolders as the inbox. 960802: (feature) there are now two different icon bitmaps that the program can use. 960731: (bug fix) you could not change the name of a vfolder once it was defined. 960731: (feature) now the first message choice makes more sense when one of the reveres folder sortings is selected. 960731: (bug fix) rewrote RatTclPutsSMTP to escape dots at the beginning of lines. 960731: (bug fix) changed man uses of tkwait to callbacks instead. Also modified OkButtons last argument. 960730: (bug fix) the alias edit/create windows now no longer blocks the entire application. 960730: (bug fix) now handles empty gecos fields. Thanks to Lloyd Parkes. 960729: (bug fix) added env to the set of global variables when reading the configuration file. 960729: (bug fix) changed deiconfiy to deiconify in preferences.tcl 960720: Feature added automatic indention to the text editor in the compose window. 960720: (feature) now updates the folder window immediately if any of the associated options are changed. 960719: (bug fix) now remembers the position of the sendDeferred window. 960719: (bug fix) now only sets the LC_CTYPE locale. 960719: (bug fix) now ignores aliases with empty names. 960719: (bug fix) fixed problem were included images were cropped (it works better if you set the canvas to the correct size). 960718: (bug fix) fixed bug where the vfolder entries could be overwritten. 960718: (feature) added cache for passwords supplied via mm_login. 960716: (bug fix) should now ignore multiple adjacent whitespace characters in mail alias files. 960715: (bug fix) VFolderEdit didn't call FileSelectorDone before using the value (when editing file folders). 960715: (feature) Fixed code which checked c-client folders when copying messages to see if c-client really could copy between the folders. 960714: (bug fix) Fixed info, version and vfolderdef windows so that if you try to create a second instance it just deiconfiys and raises the existing one. 960712: (feature) now lets you specify wildcard expressions used to determine which files/directories to import. Imported files/dirs are now also sorted alphabetically. 960712: (feature) made so that Tkrat just beeps if you try to press space in an address entry field. You should be able to use shift-space though. 960712: (bug fix) made RatAlias expand? also handle syntax errors gracefully. 960712: (bug fix) fixed so that saved copies of outgoing messages are marked as old and read. Also fixed a small bug which appended garbage to the letters if inserted into the database. 960712: (feature) added option to set color scheme. 960711: (feature) You can now remove whole trees of vfolders. You can now also walk around in the directory tree when importing. There was also a bug which made the convas not to change size when the vfolderdef window was resized. 960711: (feature) The preferences window now remembers which screen you last used (only within sessions). 960711: (bug fix) Fixed so that the insertion cursor is visible after wrapping in the compose window. 960711: (feature) Attached files are now copied at attach time instead of at sending time. 960711: (bug fix) fixed problems introduced by %i conversion in folder list. 960710: (feature) Added support for mh folders. 960704: ***** released version 0.69 ***** 960702: (feature) added code which tries to put the send cache on /tmp if ~/.ratatoskrc doesn't exist. 960702: (feature) keep connections for network folders open for a little while ater the user has closed them. If possible we reuse them. 960702: (bug fix) you will now see the end of the notification list when the notification window pops up. 960701: (feature) some minor changes in the menu structure. 960701: (bug fix) fixed error in folder example in CONFIGURATION. Also fixed some minor spelling errors. 960701: (bug fix) fixed so that the watcher window is created on demand and that TkRat notes if the wac=tcher window is destroyed. 960701: (bug fix) Send & Save menu does nothing if no valid folder is selected. 960628: (feature) Added possibility to disable the copying of attachmens. 960628: (bug fix) Added code to watcher which rebuilds the window if the user destroys it. 960627: (feature) added request for bug reports etc to version window. 960627: (bug fix) there is no need to call FolderSelect when resyncing the folder, unless the previously active message is gone. 960627: (feature) Added 'i' conversion character for folder list command (the messages current index). 960627: (feature) Changes bindings of 'r' and 'R'. 960627: (bug fix) Don't write the last-used-version number if the current version is earlier than one we have used before. 960627: (bug fix) removed the b-binding on previous page. 960626: (bug fix) Addded test on new file folders so that the file specified really is good for this purpose. 960626: (bug fix) Changed method used to calculate the length of a line to one that is not dependant on the actual window size. 960626: (feature) Sending is now done in a separate subprocess. There are now two modes for sending "direct" and "deferred". This required massive rewrites of the send and hold code. 960622: (bug fix) Fixed misc small oddities which gcc -Wall complained about. 960621: (feature) Made it possible to define languages which uses non iso-8859-1 compatible character sets as the user interface language. character set associated with each language 960621: (bug fix) Changed default mailbox path to /var/spool/mail/$env(USER) 960619: (bug fix) Fixed faulty dependecies for the install target in tkrat/Makefile. This fixes the bug where tkrat complains that it can't find the InitLanguages procedure. 960619: (bug fix) Added checking of to argument the "to" in RatDbInsert, this avoids a possible core-dump which occurred when you tried to insert a message with no To: header-row. 960618: (bug fix) Changed the code so that fh(folder_handle) only exists when there is a folder. It will never be the empty string. 960618: (bug fix) Added a missing break in Std_CloseProc() (Thanks goes to Joel Crisp) 960618: (bug fix) Fixed a syntax error in the sequence number generated in Std_ExpungeProc(). 960618: Upgrade: Changed to newer version of imap-4 (dated Jun 6 1996) 960618: (bug fix) It was impossible to create POP3 folders. 960618: (bug fix) Added a SHELL definition to the Makefiles. This is for the stupid SGI's which uses csh for Makefiles (Thanks goes to Joel Crisp). 960618: (feature) Added comment in ratSMTP.c which warns about using prereleases of tcl and tk. 960617: ***** released version 0.68 ***** 960617: (feature) added support for RATLIBDIR environment variable and some checking for correct installation. 960617: (feature) added menu entry to rescan aliases 960610: (feature) now reads alias lists by pine 969609: (feature) created a new widget for entry of addresses. 960608: (bug fix) Old files were left in the hold. 960608: (feature) You can now see what is happening while sending a message via SMTP. 960608: (bug fix) fixed bug which caused the watcher to pop up even if there where no messages to show (when a DSN had arrived and been snarfed). 960608: (bug fix) improved fileselector handling of nonexisting directories and unreadable files. 960607: (feature) Completely rewrote the line-wrap code in the composition window. Changed the justify text meny entry to a checkbutton which controls the automatic wrapping. 960607: (feature) Now also deleting bindings in the keydef window gets applied when you press "Ok". 960607: (feature) Added validity checking when editing vfolders. 960606: (bug fix) Clear the display when we try to open a new folder but fails (the old one is still closed). 960606: (bug fix) Updated all commands operating on the current message to check that there actually is a selection. 960606: (bug fix) Fixed the code that should have been able to open file- folders which doesn't exist but should be in directories that does exist. 960606: (feature) It is now possible to set a default value for the From: header-line. It is also possible to prevent this via the use_from option. 960606: (bug fix) Shift wasn't considered a modifier when defining keys. 960606: (bug fix) Corrected the helptext which said that database expiration is not implemente yet. It is. 960606: (bug fix) The sending code did a check of the validity of the sending program, but the result was never communicated to the user. Also added a check in the preferences window which checks if the first word of option(sendprog) is an executable. 960606 (bug fix) Make sure that env(USER) and env(HOME) have values when starting. If not then assign them good values. 960606 (feature) Reply-To added to the default value of option(show_header_selection) 960606 (bug fix) Changed the size of the "-Adobe-Helvetica-Bold-R-Normal" font in the version window from 20 to 24 (a more common size). 960605 (bug fix) Added code to remove all newlines from the subject of DSN messages when inserting them into the index. 960604 *** v0.67 The first public beta tkrat_2.2cvs20100105-dfsg.orig/doc/curkit000066400000000000000000000000601137544547100177710ustar00rootroot00000000000000screen window region (scrollable) Menu bindings tkrat_2.2cvs20100105-dfsg.orig/doc/features000066400000000000000000000156451137544547100203250ustar00rootroot00000000000000CURRENT gmail imap.gmail.com BUGS Move two (flagged) messages from spam to inbox causes it to download all messages again MUST TODO Color icon? Hotkeys for moving email to folders Create groups on dates Search LDAP when entering address New imap toolkit Better keydef window Dialogs, Status, Reason, Action (Verb) Fix bcc problem when doing pgp Icons in vfolderdef window (instead of Monitored,INBOX et al) Do not open pop-folders in offline mode Accept both dir and command as pgp_path Print multiple messages to file Warn before sending an empty message More detailed date info specifier Create generic paning-widget Performance Userproc for alias expansion (gives list of possible expansions) Alias-expansion should expand on given part of address... Keyboard shortcut to show body Accept rfc2368 mailto-URLs LockGUI during group-operations & sync etc Keyboard focus when alias-window comes up Button to close alias window? Go offline when all connections fails Rename RatAddressSize to RatAddressMaxSize (and change implementation) Move more stuff to preferences window (everything which uses rat_list) Escape '^From ' in attachments and text Ignore trailing dot in urls Do not always update the master folder when running in online-mode? Include From in reply-to all Implement save to dynamic folder Use mime.types-file and possibly other file command Reimplement PGP (sign & encrypt immediately) Partial quotations Network sync command which includes ';' Import & generate vcard Show attachment name in menu Decode attachment name Confirm on send & dismiss Kill sender when quitting PGP Pipe messages to command compressed folders Sort according to message size Be able to show changes messages Option to make the folder menu popup new folders More intelligent handling of images to big for the paper Eliminate calls to Tcl_Eval in c-code Determine type of attachment from filename for octet-stream attachments (option) Scrollbars for images Detailed reply dialog Deescape >From Subscriptions Improve pgp, specify addresses etc Check for proper values of preferences fields Command line options Send handling Backward searching Print in compose window. Browse mode should also need prompting for downloading of attachments Move from pine address book Replace regexp search in RatDecodeHeader Show number of flagged and deleted messages in mailbox Associate detached menus with main window Keyboard bindings in watcher window N flag in browse mode Classes to make colorization easier Append outgoing to POP3 and dynamic Content-disposition Filter actions save to folder should have subargs: save without attachments let user decide about attachements save unencrypted always save encrypted with X save encrypted with X if already encrypted same with signed? Possiblility to keep attached messages in replies Add posibility to decode in the show window Should use $env(HOME) for all paths under $env(HOME) Subscribe and unsubscribe to IMAP folders Change icon when new mail arrives Show all To-lines when multiple are present Move to nonexisting IMAP-folder should create it See which other aliases an alias is included in Sender adding if from not oneself should perhaps be an option? Turn off inline showing of gifs Read next flagged message hide deleted messages Userproc to suggest folder to save message in New sorting options Specify folder to open on command line Do not print attachments Better colormaps You should not be able to delete the active folder Insert message into compose window (with quoting and attribution etc... Posibility to redecode a message Add more keys that the message gets encrypted to (both preference and on the spot) Default to not delete the imap folder when deleting vfolders Saving outgoing encrypted messages Changes to watcher preferences should take directly. Command line arg: -iconGeomtry Diferent handlers for different URL protocols Specify fonts Automatically (after user confirmation) restart tkrat if needed from options. scrollbar on extract addresses window. uudecoding decode not all headers Replies to your own messages Tmpdir Remove unneeded checks of input values Only one outstanding external edit Editor for deferred messages Support content-disposition Autosave backup copies of compose windows. Ability to specify filters for bodyparts (of unkown types primarily) True & False as option texts in the preferences window Check performance Tear-off menues are indistinguisable Option to not include text in responses Default name of new filefolders to the filename Hold outstanding compositions on SIGINT? When imporintg from an IMAP server which already have a submenu this should be used again Option to do automatic expunge Option to not automatically show any messages Sort imported imap folders Preference for automatic indention in compose Search current message Indicator that there are held messages The fileselector should remember different dirs for save and read etc Automove read messages? Address entry in dbase serach expression Split digests Search in folder (a'la emacs ^s) Construct dynamic folders on demand (when opening that submenu) Advanced reply (select which addresses to include and which bodyparts). Search helptexts See year of message Address fields columnizing (should be neater) Userproc to look up addresses (gets one string in and returns a list of possible results). Group on size and date intervals Remember tornoff menus Tab expansion of addresses (among aliases) Select startup folder on command line Forward group Edit saved expressions Busy cursor Mark line that was last on previous page when using space to scroll? Remember placement of icon. manpage Match everything in dbase Don't rebuild everything every time you write make Improve support of mh folders Max 120 recipients (or something like that) Attach arbitrary message to the message being composed Edit big aliases Store bcc in local messages (but filter upon sending) Change order of header-fields Modify the buttonbar Select folder from vfolderdef window Remail a message to a different address (Remail)? Option to always start external editor Handle application/tcl See sender (instead of from) Global address books More emacs-bindings in window M-space M-w och M-y Test handling of expanded DSN's Test operation when the filesystem is full. Firm up aliases (illegal characters (rfc822)) More than one folder window Localize internal messages Handle message/external Display how different types are handled (depends on mailcap) Move contents of one folder to another when the folder definition is edited.? Find reports in dbase too. UP that is run when messages arrive. Keep fullname in alias expand2 (option?) Thread messages. Wrap lines in message beeing replied to. See all used keywords (in dbase) Multiple alias lists Use imap4 better Take aliases from netscape Resync on error? LONG RANGE Support for compressed folders? Handle message/partial Form replies Remote controlling (through send) Ability to view a message in a separate window. User defined new header lines? tkrat_2.2cvs20100105-dfsg.orig/doc/interface000066400000000000000000000611221137544547100204360ustar00rootroot00000000000000-*- mode: Text; fill-column: 78; -*- INTERFACE between ratatosk library and the interface Martin Forssn TkRat software and its included text is Copyright 1996-2004 by Martin Forssn. The full text of the legal notice is contained in the file called COPYRIGHT, included with this distribution. This file documents the tcl entities which the ratatosk C-library defines. It also defines which tcl-functions the C-library expects the interface to provide. COMMANDS AND ENTITIES PROVIDED BY THE LIBRARY Folder entities RatOpenFolder def Opens a folder. The argument should be the definition of the folder to open. RatGetOpenHandler def Gets the folder handler of the folder if it is already open. Otherwise it returns an empty string. $folder update mode Updates the mailbox. Mode can be either 'update', 'checkpoint' or 'sync'. If it is 'update' then the function only check for new mail. If it is 'checkpoint' then it writes any unsaved flag-changes etc to disk and if it is 'sync' then the folder is expunged. Returns the number of new messages. $folder close [force] Closes the folder. After this the folder handler is deleted. If the force argument is present and true then the folder is closed no matter how many times it has been opened. $folder setName name Sets the folder name to "name". $folder info Returns a list with the following elements: Folder name (readable text string) Number of messages in folder (int) Size of folder (int) Some folder types may not support the size value. In this case -1 is returned. $folder list format Returns a list with one entry per message in the folder The format of the entry is specified in the "format" argument. The "format" argument looks like a string to printf except the only thing that may follow the '%' (except another '%') is an optional integer (may be negative) and one of the following characters: s - Subject of message n - Name of sender or mailaddress if the name is not available. If the sender matches myself then use 'To: recpient' instead. N - Like 'n' but withot the special handling of myself. m - Mail address of sender r - Name of recipient R - Mail address of recipient b - Approximate size of message in bytes B - Approximate size of message in bytes (expressed as a mangled number string) d - Message date (formatted) D - Message date (in seconds since the epoch) S - Message status (string of maximum 5 characters) i - Current message index in folder t - Threading string M - Message ID u - Message UID c - Canonalized subject In some cases the message size may not be available. In that case it is replaced by a '?'. $folder get index Returns a message handler for the message specified by "index". This is an index into the list of messages as it is returned by the list command. $folder setFlag {indexes} flag value Sets the specified flag of the messages specified by the list of indexes to a value (a boolean value). The flag is one of 'seen', 'deleted', 'flagged' or 'answered'. $folder getFlag index flag Gets the specified flag of the message specified by "index". The flag is one of 'seen', 'deleted','flagged' or 'answered'. $folder flagged flag value Returns a list of messages (thir indexes) that have the given flag set to the given value. Flag can be one of 'seen', 'deleted','flagged' or 'answered'. $folder insert message [message ...] Inserts the messages whose handlers are passed in the message arguments into the folder. $folder type Returns the type of the folder (dbase, std or dis). $folder find message Returns the index in the folder for the specified message, or -1 if the message is not associated with this folder. $folder match expId Returns a list of indexes of messages that matches the given expression (you get the id from RatParseExp). $folder getSortOrder Returns the current sort order of a folder $folder setSortOrder order Sets a new sort order of a folder $folder netsync Synchronizes the folder over the network if possible (only disconnected folders support this). $folder refcount Returns how many current references there are to this folder. $folder role Returns the role to use for this folder. $folder dbinfo_get This is only valid for database folders. It returns the default values for new messages in this folder. The return value is a list consisting of: {list_of_keywords expiration_time expiration_event} $folder dbinfo_set indexes keywords exdate extype This is only valid for database folders. It update the keywords, expiration time and action for the messages whose indexes are included in the indexes list. Message entities Each message has an associated variable named msgInfo_$message. When the message is deleted this variable is unset. $message headers Returns a list which contains all header lines from the message. Each element of the list is a list of two elements, the first is the header name and the second is the value. $message body Returns the name of the bodypart entity contained in this message $message rawText Returns the complete message as text. $message get field... Returns a list of address entities found in the specified fields. The following are the valid fields: return_path, from, sender, reply_to, to, cc and bcc. $message reply to role Creates a new message handler which is fit to use for RatSend which is a reply to this message. The to field is either sender or all and denotes to whom the reply should go. There will be a member named data in the handler array which contains the body text of the reply. The new handler is returned. $message copy vfolder_def Inserts a copy of the message in the vfolder defined by vfolder_def. $message list format Returns information about the message. This command works like the folder list command but operates on only one message. See the documentation on folder list for description of the format argument. $message pgp sign encrypt role signer enc_rcpts Do pgp operation on message. 'sign' and 'encrypt' are both booleans and inidcates which operation(s) to perform. 'role' is the role we are operating in. 'signer' is the use which should sign the message (ignores if 'sign' is false. 'enc_rcpts' is the recipients to which we should encrypt it (ignored if 'encrypt' is false. This modifies the message. $message remove_internal Removes TkRat internal header fields from the message. This only has an effect on RatFrMessages. $message duplicate Create a copy of this message $message delete_attachments attachments dest_folder Delete the specified attachemnts and put the resulting message in the specified folder. Attachments are specified as a list of integers which give the "path" to the attachment. $message rerunPGP Rerun any PGP operation on the body. $message dbinfo_get This is only valid for database messages. It returns the current database information for this message. The return value is a list consisting of: {list_of_keywords expiration_time expiration_event} Bodypart entities $body children Returns a list of bodyparts contained in this bodypart. This only makes sense for multipart entities. $body message Extracts a message from this bodypart and returns the message handler. This can only be done on message entities. If the message is empty an empty string is returned. $body dsn Extracts the DSN data from a message/delivery-status body part. The data is returned as: {msg-fields} {{recipient1-fields} {recipient2-fields}} Where there can be an arbitrary number of recipient fields. Each item is actually a list of lists. Each sublist contains a key and a value. $body type Returns a list containing the type and subtype of the bodypart. $body params Returns a list of the parameters set for this bodypart. The list is a list of lists where each sublist contains two elements. The parameter name and its value. $body parameter name Returns the value of the parameter specified by "name". If the parameter is unavailable an empty string is returned. $body disp_type Returns the disposition type as set in the Content-Disposition header for this bodypart. If the bodypart does not have a Content-Disposition header the function returns an empty string. $body disp_parm Returns a list of the parameters set in the Content-Disposition header for this bodypart. The list is a list of lists where each sublist contains two elements. The parameter name and its value. $body id Returns the bodypart ID if available. Otherwise an empty string is returned. $body description Returns the bodypart description if available. Otherwise an empty string is returned. $body size Returns the size of the bodypart in bytes. $body lines Returns the size of the bodypart in lines. $body encoding Returns the encoding the body is in. $body isGoodCharset Returns true if this body is encoded in a charset we know how to handle. $body data encoded charset Returns a string which contains the body content. If encoded is false any contet-transfer-encoding will be undone. The optional charset parameter tells which charset we should assume the body is encoded in, if no charset is given the body parameters are used. $body saveData fileId encoded convertNL Saves the body data in the already opened file. The file must be opened for writing. If encoded is true the data is saved in the form it is in the mailbox. If encoded is false any transport encoding is undone. The convertNL argument controls if the lineends are converted to simple '\n'. $body findShowCommand This checks if there is an entry in the mailcaps that matches this bodypart. The return value is a list. If there was no matching element then every entry in the list is empty. The list is {cmd term copious desc bitmap}. And the entries are: cmd - The command to use to show this body (possibly with '%s') term - A '1' if this program needs a terminal copious - A '1' if this program gives lots of output desc - A description of this type bitmap - The name of an xbm-file which contains an icon for this type $body filename [gen_if_empty] Asks the body if it has a good candidate for a filename for the user who wishes to save this bodypart. The return can be empty unless the optionional gen_if_empty flag is given in which case a random name will be returned. $body encoded Returns 1 if the bodypart has been decoded. Zero otherwise. $body sigstatus Gets the signature status for this bodypart. Possible return values are "pgp_none", "pgp_unchecked", "pgp_good", "pgp_bad", "pgp_err" or "pgp_abort" $body checksig Causes this bodypart to check the signature. The return value is the output from pgp. $body getPGPOutput Returns the output of the PGP operation performed on this bodypart. Address entities $address isMe useUP Returns 1 if this address points to me, otherwise 0 is returned. If useUP is present and false then any eventual userproc is not called. $address compare address Compares two addresses and returns 0 if they point to the same user. Otherwise 1 is returned. $address set personal name host Sets the address. $address get form Returns the address in a textual form. The form argument can have the following values: rfc822 - returns the entire address in rfc822 format. mail - returns the name of the mailbox name - returns the personal name RatCreateAddress ?-nodomain? address ?role? Returns an address command containing the given address. Exactly one of -nodomain or role must be specified. -nodomain tells it to not associate a domain with the address while the role argument tells which domain to use if needed. OLD Message hold RatHold insert handler description Insert the message described by handler into the hold with the given description text as description. For a description on handler see the RatSend command. RatHold list Returns a list of descriptions of the messages in the hold. RatHold extract index Extracts the intex'th message from the hold. A handler which has the same members as the one inserted is returned. /OLD Aliases RatAlias add book name fullname content comment pgp_key options Adds the given alias to the alias list. RatAlias delete name ?...? Deletes the named aliases from the alias list. RatAlias get alias Returns the definition of an alias. The return value is {book fullname content comment pgp_key options}. RatAlias list var ?nocase? Returns the alias list. The alias list is inserted into an array named "var". The index is the name of the alias and the content is a list of elements {book fullname content comment pgp_key options}. If nocase is specified the all the keys in the array will be converted to lower case. RatAlias read book filename Reads aliases from filename and adds them to the internal list as members of the given book. RatAlias save book filename Saves an address book to the specified file. RatAlias expand level adrlist role Takes one address line as argument and tries to do address expansion on it. How much the addresses are expanded is controlled by the level argument. The following values are legal: display - Resolve for display. Entries which resolves to multiple addresses are shown instead of expanded. sending - Resolve for sending. All addresses are fully expanded. pgp - Get pgp keys. Returns a tcl-list of pgp key tuples {id name} or email addresses to encrypt the message to. pgpactions - Get the pgp actions requested by the addresses. The return is a tuple of two booleans {sign encrypt}. PGP commands RatPGP listkeys [keyring] Lists the keys on a keyring. Returns a tuple of two elements where the first is a suitable title to show above the descriptions. The second element is a list of keys where each key is represented by a tuple of the following elements: {key_id email_addresses description subjects sign encrypt} The keyring names 'PubRing' and 'SecRing' are reserved and refers to the users default keyring. RatPGP extract id [keyring] Returns the ascii version of the wanted key. RatPGP add keys [keyring] Adds the given keys to the specified keyring. Misc commands RatGenId Generate a unique ID (ie unique for this machine) which is a maximum of 14 characters long RatWrapCited Wraps a text which is expected to be a cited message. RatBgExec exitStatus args Works like the tcl exec command except that the commands is always placed in the background and the call to RatBgExec returns immediately. When the executed commands are done the variable exitStatus will be set to the exit value of the last process that exited. RatNudgeSender Causes the sender process, if idle, to rescan the outbox and send any messages (if online). RatGetEncoding filename Examines a file and returns a guess of which MIME-encoding it is in. RatCleanup This should always be called before the program exits. RatTildeSubst Do tilde substitution on a filename and return the new filename. RatTime [+days] Returns the time now, or in +days days as an integer (unix time). RatInsert msgId keywords exDate exType Insert the given message into the dbase. Keywords are the keywords the message should get, exDate is expiration date in number of days from now. exType should be 'none', 'remove', 'incoming', 'backup' or 'custom '. RatLock variable ... Locks the varibles agains changes. Locked variables can't be changed or unset. RatIsLocked variable Returns 1 if the variable is locked, else 0 is returned. RatType filename Tries to determine what MIME-type the given file has. The result is a list of two elements; {type/subtype encoding}. RatDaysSinceExpire Returns the number of days that have passed since the last time the database was expired. RatExpire inbox backupDir Expires the database. Inbox should be the name of the inbox folder (which must be open). Returns the following list: {num_scanned num_deleted num_backup num_inbox num_custom}. RatDSNList List the messages in the list. Return a list of message entries. Each message entry consists of the following list: {id date subject {{recipient1} {recipient2} ...}} The id identifies this message. The date is the number of seconds since the epoch when the message was sent. There may be one or more recipient blocks. The syntax is: {status recipient id} Recipient is the recipient address. Id contains an identification which can be used in other calls to obtain more information about this delivery. Status is the current status and can have the following values: none, failed, delayed, delivered, relayed or expanded RatDSNGet what id ?recipient? Returns data about the referenced DSN. The what argument can have one of the following values: msg - A message handle which handles the DSN message. report - A list of DSN report fields. Each item consists of a list with two values, the key and the value. The recipient argument determines which of the recipient fields to return. RatSMTPSupportDSN host Returns true if the given host supports DSN. RatImport id Imports mail folders. The id argument points to a vfolderdef. This command modifies the folder definitions under id. RatTestImport wildcard def Does atest import. Uses the definition passed in def and the given wildcard. The list of found items is returned. RatLL line Returns the length of the given line. This function counts tab stops to the next even eight characters. RatGen count Generates (and returns) a string of spaces which has the given length. RatDbaseCheck fix Checks the dbase and if fix is true then it tries to fix the dbase as well. The result is a list with the following values: Total number of messages in index Number of malformed entries Number of entries without messages Number of unlinked messages Total size in bytes of all messages A list of diagnostic messages RatDbaseInfo Returns some information about the database. The command returns a list with the following elements: * approximate number of messages in database * timstamp of earliest message * timestamp of last message * approximate size of database RatDbasekeywords List the keywords usied in the database along whith a count of how many messages are using each keyword. Returns a list of tuples where each tuple is made up of a keyword and usage count. RatParseExp expression Parses the given expression and returns an expression identifier. RatGetExp id Returns the requested expression as a tcl list. RatFreeExp id Removes the identified expression from all internal tables. RatSplitAdr address Expects a string with addresses as input and splits it into a list of addresses. RatMailcapReload Reloads the mailcap data RatCreateFolder def Creates a new folder RatCheckFolder def Checks if the specified folder exists. Returns true if it does. RatDeleteFolder def Deletes a folder RatMangleNumber number This command returns a short string represenatation of the number. RatCheckEncodings variable charsets Checks if any of the encodings can encode the data contained in the variable whose name is passed in the variable argument. This function will return the name of the matching characters set or an empty string if none matches. RatPurgePwChache Purge that password cache, both in memory and on disk RatEncodeMutf7 string Encodes the given string in the modified version of UTF-7 described in section 5.1.3 of rfc2060. RatEncodeQP charset string Expresses the given string in 'charset' and encodes that in QP. RatDecodeQP charset string Deocdes the given string which is encoded in charset and then QP-encoded. RatLibSetOnlineMode online Transistions into online or offline mode. The online argument should be a boolean indicating if the new mode is online or not. RatBusy cmd Execute the command while "busying" the interface by changing the cursor and to block all user input. RatGetCurrent what role Get current data. The 'what' parameter can have the following values: 'host', 'mailbox', 'personal'. The results depends on the role definition. RatGenerateAddresses handler Generates headers which will be used when sending the message identified by handler. Returns the following list: {from sender} RatGetCTE datatype data Returns which CTE the data will fit into. The 'data' argument depends on the 'datatype' Valid datatypes are 'file', in which case data is a filename, and 'blob', in which case the data is the actual data to examine. The return value is one of 'binary', '8bit' or '7bit'. RatCreateMessage role {envelope body} Creates a new message and returns the handler. The envelope is a list of tuples where each tuple contains header-name and value. The body is described by the following informal grammar: body - {type subtype {*param} encoding disp {*param} {*header} bodydata} type - A MIME type subtype - A MIME subtype param - {name value} bodydata - {datablob / {*body}} datablob - {utfblob/file DATA} RatExtractAddresses role adrlist... Parses the given address lists and returns the user@domain parts of them. RatGenerateDate Generates a date-string formatted according to rfc822. RatGenerateStipple height Generates a bitmap suitable as stipple for a text which is 'height' pixels high. RatGetMatchingAddrsImpl addrlist match max Look for addresses in addrlist which starts with the match charaters (case insensitive). Return no more than "max" addresses. RatGenerateMsgId role Return a suitable message id for the given role RatCheckListFormat list_format Checks if the argument is a valid message list format. Returns ok if it is valid, otherwise an error message is returned. RatDecodeUrlc string addr Decodes any %HH encoded characters in the given string. Addr should be true if this is an address field. Returns the string with the charcters decoded. TCL COMMANDS THE LIBRARY EXPECTS THE INTERFACE TO PROVIDE RatLog level message duration Delivers a message from the library to the interface. The message is a string and level is a number with the following meaning: 0 - babble messages from library, doesn't have to be shown 1 - parse error, should probably be shown in some situations 2 - informative message, should be displayed 3 - warning messages, should be displayed 4 - error, should be acknowledged by the user 5 - fatal, the application is about to die The duration is an optional argument and can have a value of "time" or "explicit". If the value is explicit the a handle will be returned, and this handle can then be used in a call to RatClearLog RatWantSave This command should ask the user if he wants to save an outgoing message. If the answer is no nothing is returned. If the answer is yes a the answer must look like one of these: {file } or {dbase }. RatLogin host trial user prot port The host mentioned in the arguments requires an username and password for this user. The trial argument says how many tries we already have done. The user is the user we should try to log in as and prot is the protocol we are going to use. Finally port is the portnumber we will contact the remote host at. The routine should return a list of three elements {user passwd store} when store is a boolean indicating if we should store the password on disk or not. RatDSNRecieve subject action recipient id A DSN has arrived for the mentioned message and recipient. Please notify the user. UpdateDSN Gets called from the library when new DSN(s) arrive. RatSendFailed name reason Called when TkRat fails to send a message. The "name" parameter contains a handler which identifies the message and "reason" is a text string describing what happened. This routine must take care of the passed message since it will otherwise be lost. RatSaveOutgoing msg folder Try to save the give message to the given folder. tkrat_2.2cvs20100105-dfsg.orig/doc/tkrat.1000066400000000000000000000112131137544547100177560ustar00rootroot00000000000000.TH TkRat 1 "July 9 2006" "TkRat 2.2 distribution" .SH NAME tkrat \- a graphical Mail User Agent .SH SYNOPSIS .B tkrat .RB [ "\-confdir \fIdirectory\fP" ] .RB [ "\-appname \fIname\fP" ] .RB [ \-open " [" \fIspec\fP ]] .RB [ \-opennew " [" \fIspec\fP ]] .RB [ \-compose " [" \fIpredef\fP ]] .RB [ \-netsync " [" \fIset\fP ]] .RB [ \-blank ] .RB [ mailto:\fImailto_link\fP ] .SH DESCRIPTION TkRat is a graphical Mail User Agent (MUA) which is based on the c-client and tcl/tk toolkits. Its primary purpose is to allow the user to read and send internet mail as defined in the relevant rfcs. The current goals with the TkRat project is to create a MUA which is easy to use but still powerful and fast. .PP The only documentation of how TkRat should be used is contained in the program. Both as a number of help-pages and as balloon help over almost every feature in the program. .PP Some of the actions of TkRat can be controlled by the command line options. The program also checks for an already running instances of itself and if it finds one it reuses that one to carry out the command line arguments. That means that you can remote control an already running instance via the command line. .SH OPTIONS If no arguments are given on the command line TkRat defaults to open your incoming mailbox. TkRat accepts the following command line options: .TP 0.5i .BI \-confdir " directory" Use this to specify a different default configuration directory than \fI~/.ratatosk\fR. .TP 0.5i .BI \-appname " name" Use the given \fIname\fP as name of the application. This name is used to locate already running instances of TkRat. That means that if you want to run two different instances you must change the name of one of them via this option. The default value is \fBtkrat\fP. .TP .B \-open \fR[\fIspec\fR] Opens a folder in an existing folder window (the currently open folder is closed). If no window exists the a new one is created. Which folder to open is specified in the optional \fIspec\fP argument. If it is not present then the incoming mailbox is assumed. Otherwise \fIspec\fP may be the name of one of the users defined folders (case is significant) or a full folder specification (see the file CONFIGURATION in the source release for details). .TP .B \-opennew \fR[\fIspec\fR] Works as \fB\-open\fP but always opens the folder in a new window. .TP .B \-compose \fR[\fIpredef\fR] Opens the compose window so that the user may compose a new message. Default for some header-fields can be given in the \fIpredef\fP argument. This should be a list with and even number of elements separated by space (remember to put quotes around the entire list). The first element is the name of a header-field (case does not matter) and the second is the value of that field. The third value is the name of another header field and the fourth is that value etc. The following header-fields can be given values: .BR to ", " cc ", " bcc ", " .BR from ", " subject ", " date ", " .BR reply_to ", " in_reply_to " and " message_id .TP .B \-netsync \fR[\fIset\fR] Performs network synchronization. This may mean that deferred messages are sent, disconnected mailboxes updated or arbitrary commands are run. Exactly which action will be performed is controlled by the optional \fIset\fP argument. If it is not present then the actions which the user has defined in the "Setup Network sync" window are performed. If it is specified it should be on the form of a list with the elements separated by spaces. Those actions mentioned in the list will be performed. The possible actions are: .RS .TP .B send Sends any deferred messages. .TP .B fetch Updates all disconnected folders .TP .B cmd Runs the command specified by the user .RE .TP .B \-blank Opens a blank folder window. This is useful if you want to start without opening any folders. .TP .B [ mailto:\fImailto_link\fP ] Opens a compose window with the relevant parts filled in from the mailto link. The mailto link should follow RFC2368, which is what most browsers produce. .SH EXAMPLES Three different ways of opening the users incoming mailbox: .RS .B tkrat .PP .B tkrat -open .PP .B tkrat -open INBOX .RE .PP This assumes that the inbox is named \fBINBOX\fP in the users list of folders .RE .PP To start composing a message to \fBfoo@bar.com\fP with the subject \fBGurka i lungan\fP use: .RS \fBtkrat -compose "to foo@bar.com subject 'Gurka i lungan'"\fP .RE or .RS \fBtkrat mailto:foo@bar.com?subject=Gurka%20i%20lungan\fP .RE .SH FILES .I $HOME/.ratatosk .SH WWW Information on the latest version as well as known bugs and some more is available on the homepage of TkRat on the WWW. This page can be found at: .B http://www.tkrat.org/ .SH AUTHOR TkRat is written Martin Forssen tkrat_2.2cvs20100105-dfsg.orig/doc/userproc.example000066400000000000000000000105171137544547100217740ustar00rootroot00000000000000# # This file contains examples of a couple of userprocs. These are ways you # can extend the functionality of TkRat in a couple of areas. You usersprocs # should be placed in ~/.ratatosk/userproc # # RatUP_IsMe -- # # Checks if a mail address really points at myself. # # Arguments # mailbox Mailbox name # domain The domain part of the mail address # personal The personal name phrase (if any) # adl At-domain-list source route (probably empty) # Results # Should return true or false (1 or 0) depending on if the indicated # address points at the user or not. proc RatUP_IsMe {mailbox domain personal adl} { # Here we do it easy for ourselves. Since regexp already returns a # boolean value we can use that as the return value directly. return [regexp {(maf|ratatosk.+)@.+.chalmers.se} $mailbox@$domain] # This expression matches everything that is sent to maf or # ratatosk(plus something) at a domain under chalmers.se. # That means that it will match maf@dtek.chalmers.se, # maf@math.chalmers.se, raratosk-request@dtek.chalmers.se # and even maf@no_such_domain.chalmers.se. The latter is an # unfortunate side effect of the expression but I do not care. # Note that the expression will not match ratatosk@dtek.chalmers.se # since there must be something between ratatosk and the '@'. } # RatUP_Translate -- # # Translate outgoing addresses # # Arguments # mailbox Mailbox name # domain The domain part of the mail address # personal The personal name phrase (if any) # adl At-domain-list source route (probably empty) # Results # Should return a list with four elemnts {mailbox domain personal adl} proc RatUP_Translate {mailbox domain personal adl} { # Set up a list of addresses we consider local set isLocal {root foo driftavd} # Here we do the test, check if the mailbox is one of the local ones # and if the domain is under chalmers.se. If so is the case then we # skip the domain part. This is really just another way of doing a # similar test as we did in RatUP_IsMe above. if {-1 != [lsearch $isLocal $mailbox] && [regexp {[^.]+.chalmers.se} $domain]} { return [list $mailbox {} $personal $adl] } # If the above cause did not match we should return the address # unharmed. return [list $mailbox $domain $personal $adl] } # RatUP_Citation -- # # Figures out a good citation # # Arguments # message Handler to message we should figure out citation for # reply to # Results # Should return the desired citation proc RatUP_Citation {message} { # Ignore any addresses but the first in from set from [lindex [$message get from] 0] # Check that we really have an address if [string length $from] { # See if we have a full name set fn [$from get name] if [string length $fn] { # Use the initals as citation set initials "" foreach n $fn { set initials $initials[string $n 0] } return "$initals> " } # No initials were available so use the first part of the mail address return "[lindex [split [$from get mail] {._@}] 0]> " } # Default value which is used if no from address was found return "> " } # RatUP_NetsyncFolder -- # # Determines if a folder should be synchronized at this moment or not # # Arguments # spec A folde specification {host:port}mailbox # user User to connect as # prot Protocol to use # # Results # Should return a boolean value proc RatUP_NetsyncFolder {spec} { # This example checks if the host is reachable first # Extract host and port from folder specification regexp {\{([^:/\}]+)(:([0-9]+))?\}} $spec unused host unused port # Try to open a socket (do it asynchronously so we do not have # to wait a long time for failure if [catch {socket -async $host $port} s] { return 0 } fconfigure $s -blocking no # Wait half a second, we assume that the host always replies within this # time when it is up after 500 # Try to read one character. If this fails the connection failed if [catch {gets $s 1}] { return 0 } # If we are blocked here we assume that the host is unreachanble (it # has not replied within the allowed time) and the connection attempt # still hangs. if [fblocked $s] { catch {close $s} return 0 } else { catch {close $s} return 1 } } tkrat_2.2cvs20100105-dfsg.orig/doc/userprocs000066400000000000000000000062521137544547100205260ustar00rootroot00000000000000 User defined procedures TkRat software and its included text is Copyright 1996-2004 by Martin Forssn. The full text of the legal notices is contained in the file called COPYRIGHT, included with this distribution. User defined procedures is a way for the user to have more control over certain features. The procedures are ordinary tcl procedures (i.e. they are written in tcl7.5). None of the procedures must exist, they will be used only if they exists. The user may define procedures in the ~/.ratatosk/userproc file (or whatever the userproc option is set to). If the procedures need any global varaibles these must start with "ratUP_". The following procedures may be defined: RatUP_IsMe mailbox domain personal adl mailbox - Mailbox name domain - The domain part personal - The personal name phrase (if any) adl - At-domain-list source route This procedure should determine if the address is pointing to me or not. It should return a boolean value, which should be true if the address is pointing at me. RatUP_Translate mailbox domain personal adl mailbox - Mailbox name domain - The domain part personal - The personal name phrase (if any) adl - At-domain-list source route (probably empty) When we are replying to a message all addresses we are sending it to are run through this procedure. It is expected to return a list with four elements {mailbox domain personal adl}. RatUP_Signature message message - The name of a global array which may contain information about the message the signature will be appended to This function should return a text string which will later be appended to the first text part of the message. The message argument is the name of a global array which MAY contain information about the message. See the section on RatSend in the interface-file. Note that this routine gets called before the user has had any chance to edit the message so there will not be any interesting information available at all for new messages. It only contains interesting things for replies and forwards. RatUP_Bell This function should notify the user that new mail has arrived. If this function does not exist then the terminal bell is rung. RatUP_ShowURL url url - An URL to show This function may get called when the user presses the left mousebutton over an URL in a message. It is expected to invoke a browser (in the background). RatUP_Citation message message - The handler for the message which is being cited This function should return a string which will be prepended to all lines in the body of the cited message. It wil be called when you reply to a message. RatUP_NetsyncFolder spec spec - A network folder specification {host:port}mailbox This function is should return a boolean value which indicates if the disconnected folder given as arguments should be synchronized at this moment or not. That means that this function will be called once for each disconnected mailbox when you select "Network Sync" from the menu. It will NOT be called when you select "Network sync. folder" from the admin menu to synchronize the current folder. tkrat_2.2cvs20100105-dfsg.orig/imap/000077500000000000000000000000001137544547100167325ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/.cvsignore000066400000000000000000000001061137544547100207270ustar00rootroot00000000000000c-client mtest ipopd imapd OSTYPE SPECIALS dmail mailutil mlock tmail tkrat_2.2cvs20100105-dfsg.orig/imap/CONTENTS000066400000000000000000000053211137544547100201130ustar00rootroot00000000000000 TOOLKIT DIRECTORY CONTENTS Documentation: . CONTENTS this file . CPYRIGHT copyright notice and export warning . README read this file first . SUPPORT where to go to ask questions and/or report bugs . docs/BUILD build and installation instructions . docs/CONFIG detailed configuration notes . docs/FAQ.html frequently asked questions and answers . docs/FAQ.txt text version of FAQ.HTML . docs/RELNOTES release notes . docs/SSLBUILD build and installation instructions using SSL . docs/Y2K information relating to Y2K issues . docs/bugs.txt known bugs and deficiencies in this software . docs/calendar.txt information relating to the calendar . docs/drivers.txt how various mailbox format drivers interact, and comparable features and performance . docs/formats.txt mailbox formats . docs/internal.txt programming interfaces . docs/locking.txt how file locking works . docs/md5.txt CRAM-MD5 authentication setup instructions . docs/naming.txt mailbox naming conventions . docs/rfc Internet protocol documentation Sources: . Makefile master makefile for UNIX . makefile.nt master makefile for NT/Win32 . makefile.ntk master makefile for NT/Win32 using Kerberos V . makefile.os2 master makefile for OS/2 . makefile.w2k master makefile for Windows 2000 . makefile.wce master makefile for Windows CE . src/ansilib pre-processed ANSI library routines . src/c-client pre-processed c-client sources . src/charset pre-processed character set conversion tables . src/dmail pre-processed user mail delivery sources . src/ipopd pre-processed POP2/POP3 daemon sources . src/imapd pre-processed IMAP4rev1 daemon sources . src/mailutil pre-processed mailbox utility sources . src/mlock pre-processed mailbox locking sources . src/mtest pre-processed c-client testbed sources . src/osdep/amiga pre-processed Amiga-specific sources . src/osdep/dos pre-processed DOS-specific sources . src/osdep/mac pre-processed Mac-specific sources . src/osdep/nt pre-processed NT-specific sources . src/osdep/os2 pre-processed OS/2-specific sources (incomplete) . src/osdep/tops-20 pre-processed TOPS-20 specific sources . src/osdep/unix pre-processed UNIX-specific sources . src/osdep/vms pre-processed VAX/VMS specific sources . src/osdep/wce pre-processed Windows CE-specific sources (incomplete) . src/tmail pre-processed system mail delivery sources . tools internal tools needed as part of the build process Directories created at build time on UNIX and NT/Win32: . c-client post-processed c-client sources and binary . ipopd post-processed POP2/POP3 daemon sources and binaries . imapd post-processed IMAP4rev1 daemon sources and binaries . mtest post-processed c-client testbed sources and binaries tkrat_2.2cvs20100105-dfsg.orig/imap/CPYRIGHT000066400000000000000000000077501137544547100201170ustar00rootroot00000000000000University of Washington's Free-Fork License University of Washington IMAP toolkit Version 2005 of IMAP toolkit Copyright 1988-2005 University of Washington This University of Washington Distribution (IMAP Toolkit code and documentation) is made available to the open source community as a public service by the University of Washington. Contact the University of Washington at imap-license@cac.washington.edu for information on placing or integrating modifications to the Distribution into proprietary systems. Unmodified distribution is governed by the terms identified below. Under this license, this Distribution may be modified and the original version and modified versions may be copied, distributed, publicly displayed and performed provided that the following conditions are met: (1) modified versions are distributed with source code and documentation and with permission for others to use any code and documentation (whether in original or modified versions) as granted under this license; (2) if modified, the source code, documentation, and user run-time elements should be clearly labeled by placing an identifier of origin (such as a name, initial, or other tag) after the version number; (3) users, modifiers, distributors, and others coming into possession or using the Distribution in original or modified form accept the entire risk as to the possession, use, and performance of the Distribution; (4) this copyright management information (software identifier and version number, copyright notice and license) shall be retained in all versions of the Distribution; (5) the University of Washington may make modifications to the Distribution that are substantially similar to modified versions of the Distribution, and may make, use, sell, copy, distribute, publicly display, and perform such modifications, including making such modifications available under this or other licenses, without obligation or restriction; (6) modifications incorporating code, libraries, and/or documentation subject to any other open source license may be made, and the resulting work may be distributed under the terms of such open source license if required by that open source license, but doing so will not affect this Distribution, other modifications made under this license or modifications made under other University of Washington licensing arrangements; (7) no permission is granted to distribute, publicly display, or publicly perform modifications to the Distribution made using proprietary materials that cannot be released in source format under conditions of this license; (8) the name of the University of Washington may not be used in advertising or publicity pertaining to Distribution of the software without specific, prior written permission; (9) pursuant to U.S. laws, Distribution may not be downloaded, acquired or otherwise exported or re-exported (i) into, or to a national or resident of any country to which the U.S. has embargoed goods; or (ii) to anyone on the U.S. Treasury Department's list of Specially Designated Nations or the U.S. Commerce Department's Table of Denial Orders. By downloading the Distribution, you represent that: 1) you are not located in or under the control of a national or resident of any such country or on any such list; and 2) you will not export or re-export the Distribution to any prohibited country, or to any prohibited person, entity, or end-user as specified by U.S. export controls. This software is made available "as is", and THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF WASHINGTON 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, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. tkrat_2.2cvs20100105-dfsg.orig/imap/Makefile000066400000000000000000000427151137544547100204030ustar00rootroot00000000000000# Program: IMAP Toolkit Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 7 December 1989 # Last Edited: 30 April 2005 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2005 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Normal command to build IMAP toolkit: # make [EXTRAAUTHENTICATORS=xxx] [EXTRADRIVERS=xxx] [EXTRACFLAGS=xxx] # [PASSWDTYPE=xxx] [SSLTYPE=xxx] [IP=n] # Port name. These refer to the *standard* compiler on the given system. # This means, for example, that the hpx port is for HP's compiler and not for # a non-standard compiler such as gcc. # # If you are using gcc and it is not the standard compiler on your system, try # using an ANSI port that is close to what you have. For example, if your # system is SVR4ish, try a32 or lnx; if it's more BSDish, try nxt, mct, or bsi. # # The following ports are bundled: # a32 AIX 3.2 for RS/6000 # a41 AIX 4.1 for RS/6000 # aix AIX/370 (not RS/6000!!) # ami AmigaDOS # am2 AmigaDOS with a 68020+ # ama AmigaDOS using AS225R2 # amn AmigaDOS with a 680x0 using "new" socket library # aos AOS for RT # art AIX 2.2.1 for RT # asv Altos SVR4 # aux A/UX # bs3 BSD/i386 3.0 and higher # bsd generic BSD 4.3 (as in ancient 1980s version) # bsf FreeBSD # bsi BSD/i386 # bso OpenBSD (yes, yet another one...) # cvx Convex # cyg Cygwin # d-g Data General DG/UX prior to 5.4 (d41 port no longer exists) # d54 Data General DG/UX 5.4 # do4 Apollo Domain/OS sr10.4 # dpx Bull DPX/2 B.O.S. # drs ICL DRS/NX # dyn Dynix # epx EP/IX # ga4 GCC AIX 4.x for RS/6000 # gas GCC Altos SVR4 # gcs GCC Solaris with Blastwave Community Open Source Software # gh9 GCC HP-UX 9.x # ghp GCC HP-UX 10.x # ghs GCC HP-UX 10.x with Trusted Computer Base # go5 GCC 2.7.1 (95q4 from Skunkware _not_ 98q2!) SCO Open Server 5.0.x # gsc GCC Santa Cruz Operation # gsg GCC SGI # gso GCC Solaris # gsu GCC SUN-OS # gul GCC RISC Ultrix (DEC-5000) # hpp HP-UX 9.x (see gh9) # hpx HP-UX 10.x (see ghp, ghs, hxd, and shp) # hxd HP-UX 10.x with DCE security (see shp) # isc Interactive Systems # ldb Debian Linux # lnx Linux with traditional passwords and crypt() in the C library # (see lnp, sl4, sl5, and slx) # lnp Linux with Pluggable Authentication Modules (PAM) # lmd Mandrake Linux # lrh RedHat Linux 7.2 and later # lsu SuSE Linux # lyn LynxOS # mct MachTen # mnt Atari ST Mint (not MacMint) # neb NetBSD/FreeBSD # nec NEC UX # nto QNX Neutrine RTP # nxt NEXTSTEP # nx3 NEXTSTEP 3.x # osf OSF/1 (see sos, os4) # os4 OSF/1 (Digital UNIX) 4 # osx Mac OS X # oxp Mac OS X with Pluggable Authentication Modules (PAM) # fnk Mac OS X + openssl from Fink # ptx PTX # pyr Pyramid # qnx QNX 4 # s40 SUN-OS 4.0 (*not* Solaris) # sc5 SCO Open Server 5.0.x (see go5) # sco Santa Cruz Operation (see sc5, go5) # shp HP-UX with Trusted Computer Base # sgi Silicon Graphics IRIX # sg6 Silicon Graphics IRIX 6.5 # sl4 Linux using -lshadow to get the crypt() function # sl5 Linux with shadow passwords, no extra libraries # slx Linux using -lcrypt to get the crypt() function # snx Siemens Nixdorf SININX or Reliant UNIX # soc Solaris with /opt/SUNWspro/bin/cc # sol Solaris (won't work unless "ucbcc" works -- use gso instead) # sos OSF/1 with SecureWare # ssn SUN-OS with shadow password security # sun SUN-OS 4.1 or better (*not* Solaris) (see ssn) # sv2 SVR2 on AT&T PC-7300 (incomplete port) # sv4 generic SVR4 # ult RISC Ultrix (DEC-5000) # uw2 UnixWare SVR4.2 # vul VAX Ultrix # vu2 VAX Ultrix 2.3 (e.g. for VAXstation-2000 or similar old version) # Extra authenticators (e.g. OTP, Kerberos, etc.). Adds linkage for # auth_xxx.c and executes Makefile.xxx, where xxx is the name of the # authenticator. Some authenticators are only available from third parties. # # The following extra authenticators are bundled: # gss Kerberos V EXTRAAUTHENTICATORS= # Additional mailbox drivers. Add linkage for xxxdriver. Some drivers are # only available from third parties. # # The following extra drivers are bundled: # mbox if file "mbox" exists on the home directory, automatically moves mail # from the spool directory to "mbox" and uses "mbox" as INBOX. EXTRADRIVERS=mbox # Plaintext password type. Defines how plaintext password authentication is # done on this system. # # The following plaintext login types are bundled: # afs AFS authentication database # dce DCE authentication database # gss Kerberos V # nul plaintext authentication never permitted # pam PAM authentication (note: for Linux, you should use the "lnp" port # instead of setting this...also, you may have to modify PAMLDFLAGS # in the imap-[]/src/osdep/unix/Makefile # pmb PAM authentication for broken implementations such as Solaris. # you may have to modify PAMLDFLAGS # std system standard (typically passwd file), determined by port # two try alternative (defined by CHECKPWALT), then std PASSWDTYPE=std # SSL type. Defines whether or not SSL support is on this system # # The following SSL types are bundled: # none no SSL support # unix SSL support using OpenSSL # nopwd SSL support using OpenSSL, and plaintext authentication permitted only # in SSL/TLS sessions # sco link SSL before other libraries (for SCO systems) # unix.nopwd same as nopwd # sco.nopwd same as nopwd, plaintext authentication in SSL/TLS only # # SSLTYPE=nopwd is now the default as required by RFC 3501 SSLTYPE=nopwd # IP protocol version # # The following IP protocol versions are defined: # o IPv4 support, no DNS (truly ancient systems) # 4 (default) IPv4 support only # 6 IPv6 and IPv4 support IP=4 # The following extra compilation flags are defined. None of these flags are # recommended. If you use these, include them in the EXTRACFLAGS. # # -DDISABLE_POP_PROXY # By default, the ipop[23]d servers offer POP->IMAP proxy access, # which allow a POP client to access mail on an IMAP server by using the # POP server as a go-between. Setting this option disables this # facility. # # -DOLDFILESUFFIX=\"xxx\" # Change the default suffix appended to the backup .newsrc file from # "old". # # -DSTRICT_RFC822_TIMEZONES # Disable recognition of the non-standard UTC (0000), MET (+0100), # EET (+0200), JST (+0900), ADT (-0300), AST (-0400), YDT (-0800), # YST (-0900), and HST (-1000) symbolic timezones. # # -DBRITISH_SUMMER_TIME # Enables recognition of non-standard symbolic timezone BST as +0100. # # -DBERING_STANDARD_TIME # Enables recognition of non-standard symbolic timezone BST as -1100. # # -DNEWFOUNDLAND_STANDARD_TIME # Enables recognition of non-standard symbolic timezone NST as -0330. # # -DNOME_STANDARD_TIME # Enables recognition of non-standard symbolic timezone NST as -1100. # # -DSAMOA_STANDARD_TIME # Enables recognition of non-standard symbolic timezone SST as -1100. # # -DY4KBUGFIX # Turn on the Y4K bugfix (yes, that's year 4000). It isn't well-known, # but century years evenly divisible by 4000 are *not* leap years in the # Gregorian calendar. A lot of "Y2K compilant" software does not know # about this rule. Remember to turn this on sometime in the next 2000 # years. # # -DUSEORTHODOXCALENDAR # Use the more accurate Eastern Orthodox calendar instead of the # Gregorian calendar. The century years which are leap years happen # at alternating 400 and 500 year intervals without shifts every 4000 # years. The Orthodox and Gregorian calendars diverge by 1 day for # gradually-increasing intervals, starting at 2800-2900, and becoming # permanent at 48,300. # # -DUSEJULIANCALENDAR # Use the less accurate Julian calendar instead of the Gregorian # calendar. Leap years are every 4 years, including century years. # My apologies to those in the English-speaking world who object to # the reform of September 2, 1752 -> September 14, 1752, since this # code still uses January 1 (which Julius Ceasar decreed as the start # of the year, which since 153 BCE was the day that Roman consuls # took office), rather than the traditional March 25 used by the # British. As of 2005, the Julian calendar and the Gregorian calendar # diverge by 15 days. EXTRACFLAGS= # Extra linker flags (additional/alternative libraries, etc.) EXTRALDFLAGS= # Special make flags (e.g. to override make environment variables) EXTRASPECIALS= SPECIALS= # Normal commands CAT=cat CD=cd LN=ln -s MAKE=make MKDIR=mkdir BUILDTYPE=rebuild RM=rm -rf SH=sh SYSTEM=unix TOOLS=tools TOUCH=touch # Primary build command BUILD=$(MAKE) build EXTRACFLAGS='$(EXTRACFLAGS)'\ EXTRALDFLAGS='$(EXTRALDFLAGS)'\ EXTRADRIVERS='$(EXTRADRIVERS)'\ EXTRAAUTHENTICATORS='$(EXTRAAUTHENTICATORS)'\ PASSWDTYPE=$(PASSWDTYPE) SSLTYPE=$(SSLTYPE) IP=$(IP)\ EXTRASPECIALS='$(EXTRASPECIALS)' # Make the IMAP Toolkit all: SPECIALS c-client rebuild c-client: @echo Not processed yet. In a first-time build, you must specify @echo the system type so that the sources are properly processed. @false SPECIALS: echo $(SPECIALS) > SPECIALS # Note on SCO you may have to set LN to "ln". a32 a41 aix bs3 bsi d-g d54 do4 drs epx ga4 gas gh9 ghp ghs go5 gsc gsg gso gul hpp hpx lnp lyn mct mnt neb nec nto nxt nx3 osf os4 ptx qnx sc5 sco sgi sg6 shp sl4 sl5 slx snx soc sol sos uw2: an $(BUILD) BUILDTYPE=$@ # If you use sv4, you may find that it works to move it to use the an process. # If so, you probably will want to delete the "-Dconst=" from the sv4 CFLAGS in # the c-client Makefile. aos art asv aux bsd cvx dpx dyn isc pyr sv4 ult vul vu2: ua $(BUILD) BUILDTYPE=$@ # Knotheads moved Kerberos and SSL locations on these platforms bsf bso: an $(BUILD) BUILDTYPE=$@ \ SPECIALS="GSSDIR=/usr SSLDIR=/usr SSLINCLUDE=/usr/include/openssl SSLCERTS=/etc/ssl/certs SSLKEYS=/etc/ssl/private LOCKPGM=/usr/sbin/mlock" cyg: an $(BUILD) BUILDTYPE=cyg \ SPECIALS="SSLDIR=/usr/ssl SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib" gcs: an $(BUILD) BUILDTYPE=gso \ SPECIALS="SSLDIR=/opt/csw/ssl SSLINCLUDE=/opt/csw/include/openssl SSLLIB=/opt/csw/lib" ldb: an $(BUILD) BUILDTYPE=lnp \ SPECIALS="GSSDIR=/usr SSLDIR=/usr SSLINCLUDE=/usr/include/openssl SSLCERTS=/etc/ssl/certs SSLKEYS=/etc/ssl/private LOCKPGM=/usr/sbin/mlock" lmd: an $(BUILD) BUILDTYPE=lnp \ SPECIALS="SSLDIR=/usr/lib/ssl SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib LOCKPGM=/usr/sbin/mlock" lrh: an $(BUILD) BUILDTYPE=lnp \ SPECIALS="GSSDIR=/usr/kerberos SSLDIR=/usr/share/ssl SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib LOCKPGM=/usr/sbin/mlock" \ EXTRACFLAGS="$(EXTRACFLAGS) -I/usr/kerberos/include" lsu: an $(BUILD) BUILDTYPE=lnp \ SPECIALS="SSLDIR=/usr/share/ssl SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib" osx: an $(BUILD) BUILDTYPE=osx \ IP=6 EXTRAAUTHENTICATORS="$(EXTRAAUTHENTICATORS) gss" \ SPECIALS="SSLDIR=/System/Library/OpenSSL SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib" oxp: an $(BUILD) BUILDTYPE=osx \ IP=6 EXTRAAUTHENTICATORS="$(EXTRAAUTHENTICATORS) gss" \ SPECIALS="SSLDIR=/System/Library/OpenSSL SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib PAMLDFLAGS=-lpam" \ PASSWDTYPE=pam \ EXTRACFLAGS="$(EXTRACFLAGS) -DMAC_OSX_KLUDGE=1" fnk: an $(BUILD) BUILDTYPE=osx \ SPECIALS="SSLDIR=/sw/etc/ssl SSLINCLUDE=/sw/include/openssl SSLLIB=/sw/lib" # Linux shadow password support doesn't build on traditional systems, but most # Linux systems are shadow these days. lnx: lnxnul an $(BUILD) BUILDTYPE=$@ lnxnul: @$(SH) -c '(test $(PASSWDTYPE) = nul) || make lnxok' lnxok: @echo You are building for traditional Linux. Most modern Linux @echo systems require that you build using make slx. Do you want @echo to continue this build? Type y or n please: @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac' @echo OK, I will remember that you really want to build for @echo traditional Linux. You will not see this message again. @echo If you discover that you can not log in to the POP and IMAP @echo servers, then do the following commands: @echo % rm lnxok @echo % make clean @echo % make slx @echo If slx does not work, try sl4 or sl5. Be sure to do a @echo make clean between each try! @$(TOUCH) lnxok # SUN-OS C compiler makes you load libdl by hand... ssn sun: sunok suntools ua $(BUILD) BUILDTYPE=$@ suntools: $(CD) tools;$(MAKE) LDFLAGS=-ldl gsu: sunok an $(BUILD) BUILDTYPE=$@ s40: sunok ua $(BUILD) BUILDTYPE=$@ sunok: @echo You are building for the old BSD-based SUN-OS. This is NOT @echo the modern SVR4-based Solaris. If you want to build for @echo Solaris, you should use make gso or make sol or make soc. Do @echo you want to continue this build? Type y or n please: @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac' @echo OK, I will remember that you really want to build for the old @echo BSD-based SUN-OS. You will not see this message again. @echo If the build fails and you realize that you really wanted to @echo build for Solaris, then do the following commands: @echo % rm sunok @echo % make clean @echo % make gso @echo If gso does not work, try sol. Be sure to do a make clean @echo between each try! @$(TOUCH) sunok # SVR2 doesn't have symbolic links (at least my SVR2 system doesn't) sv2: $(MAKE) ua LN=ln $(BUILD) BUILDTYPE=$@ LN=ln # Pine port names, not distinguished in c-client bs2: an $(BUILD) BUILDTYPE=bsi pt1: an $(BUILD) BUILDTYPE=ptx # Compatibility hxd: $(BUILD) BUILDTYPE=hpx PASSWDTYPE=dce # Amiga ami am2 ama amn: $(MAKE) an LN=cp SYSTEM=amiga $(BUILD) BUILDTYPE=$@ LN=cp # Courtesy entries for Microsoft systems nt: nmake /nologo /f makefile.nt ntk: nmake /nologo /f makefile.ntk w2k: nmake /nologo /f makefile.w2k wce: nmake /nologo /f makefile.wce # SSL build choices sslnopwd sslunix.nopwd sslsco.nopwd: @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @echo + Building in full compliance with RFC 3501 security @echo + requirements: @echo ++ TLS/SSL encryption is supported @echo ++ Unencrypted plaintext passwords are prohibited @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sslunix sslsco: @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @echo + Building in PARTIAL compliance with RFC 3501 security @echo + requirements: @echo + Compliant: @echo ++ TLS/SSL encryption is supported @echo + Non-compliant: @echo ++ Unencrypted plaintext passwords are permitted @echo + @echo + In order to rectify this problem, you MUST build with: @echo ++ SSLTYPE=$(SSLTYPE).nopwd @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @echo @echo Do you want to continue this build anyway? Type y or n please: @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac' sslnone: @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @echo + Building in NON-COMPLIANCE with RFC 3501 security requirements: @echo + Non-compliant: @echo ++ TLS/SSL encryption is NOT supported @echo ++ Unencrypted plaintext passwords are permitted @echo + @echo + In order to rectify this problem, you MUST build with: @echo ++ SSLTYPE=nopwd @echo + You must also have OpenSSL or equivalent installed. @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @echo @echo Do you want to continue this build anyway? Type y or n please: @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac' # C compiler types an ua: #$(MAKE) ssl$(SSLTYPE) @echo Applying $@ process to sources... $(TOOLS)/$@ "$(LN)" src/c-client c-client $(TOOLS)/$@ "$(LN)" src/ansilib c-client $(TOOLS)/$@ "$(LN)" src/charset c-client $(TOOLS)/$@ "$(LN)" src/osdep/$(SYSTEM) c-client $(TOOLS)/$@ "$(LN)" src/mtest mtest $(TOOLS)/$@ "$(LN)" src/ipopd ipopd $(TOOLS)/$@ "$(LN)" src/imapd imapd $(TOOLS)/$@ "$(LN)" src/mailutil mailutil $(TOOLS)/$@ "$(LN)" src/mlock mlock $(TOOLS)/$@ "$(LN)" src/dmail dmail $(TOOLS)/$@ "$(LN)" src/tmail tmail $(LN) $(TOOLS)/$@ . build: OSTYPE rebuild rebuildclean OSTYPE: @echo Building c-client for $(BUILDTYPE)... @$(TOUCH) SPECIALS echo `$(CAT) SPECIALS` $(EXTRASPECIALS) > c-client/SPECIALS $(CD) c-client;$(MAKE) $(BUILDTYPE) EXTRACFLAGS='$(EXTRACFLAGS)'\ EXTRALDFLAGS='$(EXTRALDFLAGS)'\ EXTRADRIVERS='$(EXTRADRIVERS)'\ EXTRAAUTHENTICATORS='$(EXTRAAUTHENTICATORS)'\ PASSWDTYPE=$(PASSWDTYPE) SSLTYPE=$(SSLTYPE) IP=$(IP)\ $(SPECIALS) $(EXTRASPECIALS) echo $(BUILDTYPE) > OSTYPE $(TOUCH) rebuild rebuild: @$(SH) -c '(test $(BUILDTYPE) = rebuild -o $(BUILDTYPE) = `$(CAT) OSTYPE`) || (echo Already built for `$(CAT) OSTYPE` -- you must do \"make clean\" first && exit 1)' @echo Rebuilding c-client for `$(CAT) OSTYPE`... @$(TOUCH) SPECIALS $(CD) c-client;$(MAKE) all CC=`$(CAT) CCTYPE` \ CFLAGS="`$(CAT) CFLAGS`" `$(CAT) SPECIALS` rebuildclean: $(SH) -c '$(RM) rebuild || true' bundled: @echo Building bundled tools... $(CD) mtest;$(MAKE) $(CD) ipopd;$(MAKE) $(CD) imapd;$(MAKE) $(CD) mailutil;$(MAKE) @$(SH) -c '(test -f /usr/include/sysexits.h ) || make sysexitwarn' $(CD) mlock;$(MAKE) || true $(CD) dmail;$(MAKE) || true $(CD) tmail;$(MAKE) || true sysexitwarn: @echo Hmm...it does not look like /usr/include/sysexits.h exists. @echo Either your system is too ancient to have the sysexits.h @echo include, or your C compiler gets it from some other location @echo than /usr/include. If your system is too old to have the @echo sysexits.h include, you will not be able to build the @echo following programs. clean: @echo Removing old processed sources and binaries... $(SH) -c '$(RM) an ua OSTYPE SPECIALS c-client mtest imapd ipopd mailutil mlock dmail tmail || true' $(CD) tools;$(MAKE) clean # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/README000066400000000000000000000043241137544547100176150ustar00rootroot00000000000000 IMAP Toolkit Environment 14 September 2004 Mark Crispin UNIX QUICK BUILD NOTES These quick build notes assume that you have installed OpenSSL before attempting to build this software, and that you do not have any non-default configuration parameters. If you need additional information in building this software with OpenSSL, please refer to the docs/SSLBUILD file for more information. If you intend to build this software with a non-default configuration (including building a non-compliant server without SSL support), please refer to the docs/BUILD file for more information. 1) Look in imap-2004/Makefile and find your system type code. For example, modern versions of Linux will use either "slx", "lnp", or one of the lnp-variants (such as "lrh"). 2) Type "make" followed by the system type, e.g. "make slx". 3) Install the POP2 daemon (ipopd/ipop2d), the POP3 daemon (ipopd/ipop3d), and the IMAP daemon (imapd/imapd) on a system directory of your choosing. 4) Update /etc/services to register the pop2 service on TCP port 109, the pop3 service on TCP port 110, and the imap service on TCP port 143. Also update Yellow Pages/NIS/NetInfo/etc. if appropriate on your system. 5) Update /etc/inetd.conf to invoke the POP2, POP3, and IMAP daemons on their associated services. 6) That's all! Read the file docs/BUILD if you need more detailed information and/or you don't understand these quick build instructions. MISCELLANEOUS NOTES mtest has been run under UNIX, DOS, Windows, NT, Macintosh, TOPS-20, and VMS. It is a very primitive interface, however, and is suited mainly as a model of how to write a main program for c-client. You should take a look at the source to figure out how to use it. Briefly, it first asks for a mailbox name (either a local file path or an IMAP mailbox in the form "{hostname}mailbox") and then puts you in a command mode where "?" will give you a list of commands. Pine is available separately on the FTP.CAC.Washington.EDU archives. The focus of development and support is for UNIX and Win32 (including Windows 95/98/Millenium, Windows NT, and Windows 2000). The other ports are not frequently used or tested, and may be incomplete. tkrat_2.2cvs20100105-dfsg.orig/imap/SUPPORT000066400000000000000000000005231137544547100200310ustar00rootroot00000000000000 BUG REPORTING ADDRESS Bug reports, comments, or questions regarding this software may be reported to the c-client@u.washington.edu mailing list. You can subscribe to this list by sending a message to: listproc@u.washington.edu with contents: subscribe c-client An alternative is to use the comp.mail.imap newsgroup. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/000077500000000000000000000000001137544547100176625ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/docs/BUILD000066400000000000000000000452221137544547100204510ustar00rootroot00000000000000 BUILD AND INSTALLATION NOTES Last Updated: 5 January 2004 Table of Contents: 1. UNIX Build Notes 2. UNIX Installation Notes 3. Win32 Build Notes 4. Win32 Installation Notes 5. Inactive Ports (TOPS-20, VMS) 6. Other ports (Macintosh, DOS/Win16, Windows CE, Amiga, OS/2) UNIX BUILD NOTES The default build is with IPv4 only. To build with IPv6, add "IP=6" to the make command line, e.g. make lrh IP=6 The default build is to build with SSL and disabling plaintext passwords unless SSL/TLS encryption is in effect (SSLTYPE=nopwd). This means that OpenSSL MUST be installed before building the IMAP toolkit. Please refer to the SSLBUILD file for more information. To build without SSL, add "SSLTYPE=none" to the make command line. Note that doing so will produce an IMAP server which is NON-COMPLIANT with current IESG security requirements. You must build through the top-level imap-2004/Makefile, which will run a "process" step the first time and create the imap-2004/c-client, imap-2004/ipopd, and imap-2004/imapd directories in which building actually takes place. Before doing a make on UNIX, you should read imap-2004/Makefile and see if your system type is known. The various system types are three-letter codes. If your system type is known, then use this as the make option. After the first time you do a make, this option is remembered in a file called OSTYPE, so just typing "make" suffices. For example, if you are using a more or less modern Linux system, your system type is either slx (shadow passwords only) or lnp (PAM), and the appropriate command is: make slx (or make lnp) There are other make options, described in imap-2004/src/osdep/Makefile. It's probably best to see if an existing port will work on your system before inventing a new port. Try: sv4 generic SVR4, non-ANSI compiler a32 modern SVR4 bsd basic 4.3 BSD, non-ANSI compiler nxt, bsf modern BSD If you must invent a new port, you need to create an entry in imap-2004/Makefile and imap-2004/src/osdep/Makefile for your new port, as well as osdep/os_???.h and osdep/os_???.c files with the appropriate OS-dependent support for that system. You also need to determine which setup process to use. You should use the ua process unless you are sure that your compiler supports *ALL* aspects of ANSI C prototyping. Note that some compilers, such as Ultrix, support some aspects of ANSI C but not others; c-client really beats on the full prototyping capability of ANSI C so you have to use the non-ANSI source tree for such systems. If you send a new port back to us, we will make it available for others who use your particular system type. The mbox driver is now enabled by default. If the file "mbox" exists on the user's home directory and is in UNIX mailbox format, then when INBOX is opened this file will be selected as INBOX instead of the mail spool file. Messages will be automatically transferred from the mail spool file into the mbox file. To disable this behavior, delete "mbox" from the EXTRADRIVERS list in the top-level Makefile and rebuild. WARNING: The SVR2 (sv2) port is *incomplete*. SVR2 does not appear to have any way to do ftruncate(), which is needed by the mbox, mbx, mmdf, mtx, tenex, and unix drivers. UNIX INSTALLATION NOTES Binaries from the build are: imap-2004/mtest/mtest c-client testbed program imap-2004/ipopd/ipop2d POP2 daemon imap-2004/ipopd/ipop3d POP3 daemon imap-2004/imapd/imapd IMAP4rev1 daemon mtest is normally not used except by c-client developers. STEP 1: inetd setup The ipop2d, ipop3d, and imapd daemons should be installed in a system daemon directory (in the following examples, /usr/local/etc is used), and invoked by your /etc/inetd.conf file with lines such as: pop stream tcp nowait root /usr/local/etc/ipop2d ipop2d pop3 stream tcp nowait root /usr/local/etc/ipop3d ipop3d imap stream tcp nowait root /usr/local/etc/imapd imapd Note that different variants of UNIX have different versions of inetd, so you should verify the precise form of these commands (for example, some versions of inetd do not require the "nowait"). IMPORTANT NOTE: inetd has a limit of how many new connections it will allow in a certain interval, and when this limit is exceeded, it shuts down the server. If you have anything beyond a small-scale server, you are probably going to run up against this limit. You'll know when it happens; your syslog will give the misleading message "imap/tcp server failing (looping), service terminated" and users will complain that IMAP service is unavailable for the next 10 minutes. Similarly with "pop3/tcp server failing"... It must be emphasized that this is *NOT* a bug in the IMAP or POP servers, nor is it anything that I can "fix". It is an inetd problem, and the only way to fix it is to change inetd's behavior. By default, the parameters of this limit are (from inetd.c source code): #define TOOMANY 40 /* don't start more than TOOMANY */ #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ #define RETRYTIME (60*10) /* retry after bind or server fail */ That is, no more than 40 connections (TOOMANY) in 60 seconds (CNT_INTL), and if exceeded, shut down the server for 10 minutes (RETRYTIME). This was a good setting in the 1980s ARPAnet, but is much too small today. Some versions of inetd allow you to see a higher maximum in the /etc/inetd.conf file. Read "man inetd" and see if you see something like this in the text: The wait/nowait entry is applicable to datagram sockets only [...] [...] The optional ``max'' suffix (separated from ``wait'' or ``nowait'' by a dot) specifies the maximum number of server instances that may be spawned from inetd within an interval of 60 sec- onds. When omitted, ``max'' defaults to 40. If you see this, then edit the /etc/inetd.conf entry for imapd to be something like: imap stream tcp nowait.100 root /usr/local/etc/imapd imapd (or, if you use TCP wrappers) imap stream tcp nowait.100 root /usr/local/etc/tcpd imapd Otherwise, you'll need to edit the inetd source code to set TOOMANY to a higher value, then rebuild inetd. STEP 2: services setup You may also have to edit your /etc/services (or Yellow Pages, NetInfo, etc. equivalent) to register these services, such as: pop 109/tcp pop3 110/tcp imap 143/tcp STEP 3: optional rimap setup If you want to enable the rimap capability, which allows users with a suitable client and .rhosts file on the server to access IMAP services without transmitting her password in the clear over the network, you need to have /etc/rimapd as a link to the real copy of imapd. Assuming you have imapd installed on /usr/local/etc as above: % ln -s /usr/local/etc/imapd /etc/rimapd Technical note: rimap works by having the client routine tcp_aopen() invoke `rsh _host_ exec /etc/rimapd' in an child process, and then returning pipes to that process' standard I/O instead of a TCP socket. You can set up `e-mail only accounts' by making the shell be something which accepts only that string and not ordinary UNIX shell commands. STEP 4: notes on privileges Neither user "root", not any other UID 0 account, can log in via IMAP or POP. "That's not a bug, it's a feature!" This software is designed to run without privileges. The mail spool directory must be protected 1777; that is, with world write and the sticky bit. Of course, mail *files* should be protected 600! An alternative to having the mail spool directory protected 1777, at the cost of some performance, is to use the external "mlock" program, available as part of the imap-utils package. With mlock installed as /etc/mlock and setgid mail, the spool directory can be protected 775 with group mail. Please disregard this paragraph if you don't understand it COMPLETELY, and know EXACTLY what to do without question. STEP 5: SVR4 specific setup There is one "gotcha" on System V Release 4 based systems such as Solaris. These systems do not use the standard UNIX mail format, but rather a variant of that format that depends upon a bogus "Content-Length:" message header. This is widely recognized to have been a terrible mistake. One symptom of the problem is that under certain circumstances, a message may get broken up into several messages. I'm also aware of security bugs caused by programs that foolishly trust "Content-Length:" headers with evil values. To fix your system, edit your sendmail.cf to change the Mlocal line to have the -E flag. A typical entry will lool like: Mlocal, P=/usr/lib/mail.local, F=flsSDFMmnPE, S=10, R=20, A=mail.local -d $u WIN32 BUILD NOTES Visual C++ 6.0 along with the current Microsoft Platform SDK (specifically the CORE SDK and the Internet Development SDK) is required to build on Windows 9x/Me/NT/2K/XP. If you do not have the Platform SDK installed or in your path properly, you'll get errors when building os_nt.c, typically in env_nt.c, ssl_nt.c, ssl_w2k.c, or gss_shim.c. You can download the Microsoft Platform SDK from Microsoft's web site. There is also considerable debate about how new mail is to be snarfed. I am currently using something that seems to work with WinSMTP. Look at the definition of MAILFILE in imap-2004/src/osdep/nt/mailfile.h and at the sysinbox() function in imap-2004/src/osdep/nt/env_nt.c to see what's there now, so you have a clue about how to hack it. To build under Windows 95/98/NT, connect to the imap-2004 directory and do: nmake -f makefile.nt The resulting binaries will support SSL if either schannel.dll or security.dll is installed in Windows, using the old, undocumented, SSL interfaces. You can also use this to build under Me/2000/XP, but it is not the preferred build on this platform. To build with MIT Kerberos support, connect to the imap-2004 directory and do: nmake -f makefile.ntk The resulting binaries will support SSL if either schannel.dll or security.dll is installed in Windows, using the old, undocumented SSL interfaces. They will also support MIT Kerberos. Note, however, that these binaries will only run on systems which have the MIT Kerberos DLLs installed, and will not run otherwise. To build under Windows Me/2000/XP, connect to the imap-2004 directory and do: nmake -f makefile.w2k The resulting binaries will support SSL and Microsoft Kerberos, using the official, documented, Microsoft interfaces. Note, however, that these binaries will not run under Windows 95, Windows 98, or Windows NT4. WIN32 INSTALLATION NOTES The resulting binaries will be: imap-2004\mtest\mtest.exe (testbed client) imap-2004\ipopd\ipop2d.exe POP2 server imap-2004\ipopd\ipop3d.exe POP3 server imap-2004\imapd\imapd.exe IMAP4rev1 server These servers are stdio servers. I wrote a simple network listener for NT called inetlisn; currently it is available as: ftp://ftp.cac.washington.edu/mail/nt/inetlisn.tar To build this, use "nmake" after connecting to the inetlisn directory. inetlisn takes two arguments, the first being the port number and the second being the binary to run to serve a connection on that port, e.g. c:\bin\inetlisn 143 c:\mail_daemons\imapd Note that NT imapd must be started as SYSTEM in order to be recognized as being "not logged in"; otherwise it will preauth as whatever user it is running as which is probably not what you want. One way to have it run as system is to have inetlisn run by an AT command, e.g. if the time now is 2:05PM, try something like: AT 14:06 "c:\bin\inetlisn 143 c:\mail_daemons\imapd" A more advanced network lister called wsinetd is available on: http://wsinetd.sourceforge.net It is based on inetlisn, and essentially is a "completed" version of inetlisn. Bottom line: this is not plug-and-play. If you're not a hacker and/or are unwilling to invest the time to do some programming, you probably want to buy a commercial server product. INACTIVE PORTS The TOPS-20 and VMS ports were developed at one time or another, but are no longer actively developed. However, from time to time I test build both of these to make sure that they compile without errors and that mtest runs, and will continue doing so as long as I have access to systems running these operating systems. TOPS-20 BUILD NOTES I have provided a c-client port for TOPS-20 systems, but you're on your own in terms of a nice TOPS-20 like main program. Maybe someday some nice person will try porting Pine to TOPS-20. This assumes the use of KCC 6, and probably will not build with other compilers or older versions of KCC. You do not use imap-2004/Makefile under TOPS-20, nor do you build any components other than c-client and mtest. Merge the contents of imap-2004/src/c-client, imap-2004/src/charset, imap-2004/src/mtest, and imap-2004/src/osdep/tops-20 onto a single directory on TOPS-20 and build from that. The command: DO BUILD.CTL will build the sources. If you don't have MIC, then SUBMIT BUILD.CTL and let BATCON execute it. VMS BUILD NOTES You do not use imap-2004/Makefile under VMS, nor do you build any components other than c-client and mtest. Merge the contents of imap-2004/src/c-client, imap-2004/src/charset, imap-2004/src/mtest, and imap-2004/src/osdep/vms onto a single directory on VMS and build from that. The command to build it is: @BUILD MULTINET or @BUILD NETLIB If you just do @BUILD it will build with dummy TCP code, and since only TCP based drivers are provided here this isn't too useful. If you aren't on the Pacific coast of the US or Canada, you probably will need to change the wired-in timezone in the BUILD.COM file. Apparently, the wonderful VMS system that DEC loves so much doesn't maintain any concept of time zone; the VMS C compiler returns a null pointer from gmtime()! Otherwise you're pretty much on your own here. OTHER PORTS The following ports were developed at one time or another, but are no longer actively developed or tested. It is not known if they still work or not. Port Status ---- ------ Macintosh Obsolete; Mac OS X uses UNIX port DOS/Win16 Obsolete; modern PCs use Win32 port Windows CE Never completed Amiga Unknown OS/2 Unknown MACINTOSH BUILD NOTES If you are building a Macintosh client, you will need MacTCP installed on your system as well as the MacTCP C includes and libraries. You do not use imap-2004/Makefile on the Mac, nor do you build any components other than c-client and mtest. Merge the contents of imap-2004/src/c-client, imap-2004/src/charset, imap-2004/src/mtest, and imap-2004/src/osdep/mac onto a single directory on the Mac and build from that. mtext.sit.hqx is a THINK C project file and cute icon for building mtest, encoded with Binhex and StuffIt. THINK C is a truly wretched product which help make me understand why Macintosh has lost most of its market share. Not only does it do cretinous things such as barf about a cast in front of an lvalue, it also limits the size of code *or* data in a single file to 32K! So much for having large character set tables. Symantec says that "MacOS requires it, break up your files into smaller pieces" yet somehow gcc under MachTen contrives to compile C programs without subjecting the programmer to this idiocy. As a result of this, I found myself obliged to comment out the #includes of the East Asian character sets in utf8.c in order to get it to build. It's also necessary to break up some of the files, at least mail.c and imap4r1.c. Maybe you don't have to do this in CodeWarrior or whatever the new compiler is called, but I've pretty much given up on Macintosh. If you use precompiled headers, you may get some compilation errors since some Apple symbols need to be redefined in order to get it to build under all versions of MacOS. Try turning off the precompiled headers (so it will re-read the .h files) and see if it builds any better. If you use a Mac C compiler with 2-byte ints (such as THINK C's normal mode) you will need to fix some bugs in the MacTCP C includes and libraries to prevent it from generating bad code, since those MacTCP files violate Apple's standards of always using explicit shorts or longs, never ints. You could avoid this if you set 4-byte ints in THINK C; however, the ANSI and UNIX libraries in THINK C use 2-byte ints so you will also need to build 4-byte int versions of these. c-client itself is 2-byte int or 4-byte int clean; it can be used in either mode. The most important bug in the MacTCP files that you need to fix is in the file AddressXlation.h, you need to change the definition of the rtnCode member of the hostInfo structure to be long instead of int. There are several other changes you need to make if you decide to compile dnr.c under THINK C instead of using the Apple-supplied object file; see me for details if you decide to undertake such an effort. This is fixed in newer versions from Apple. DOS/WIN16 BUILD NOTES If you are building a DOS client, you will need a TCP/IP stack installed on your DOS system along with its development environment. The currently supported stacks are Beame & Whiteside, PC-NFS, Novell, PC/IP, Waterloo, and Winsock. mtest and a version of Pine called PC Pine run under DOS. You do not use imap-2004/Makefile under DOS, nor do you build any components other than c-client and mtest. Merge the contents of imap-2004/src/c-client, imap-2004/src/charset, imap-2004/src/mtest, and imap-2004/src/osdep/dos onto a single directory on DOS and build from that. The MAKE command on DOS takes an argument identifying the TCP/IP stack in use. For example, do: MAKE MAKEFILE OS=WSK (or MAKE -F MAKEFILE OS=WSK) to build for Winsock. If you write a program for DOS/Win16, you will probably have to write a replacement cache manager (look at mm_cache()) and otherwise disable most of c-client's caching. Even so, memory limitations will be an ongoing problem, particularly with DOS, and you will have some severe performance problems. It's a bit better on Win16, but in my opinion you are better off writing a 32-bit program and telling your Win16 customers to upgrade to Windows 95 or at least install Win32s. WINDOWS CE BUILD NOTES I build using Visual C++ 6.0 with the WCE extensions. The current code has SH3 wired in for the compiler building. To build under NT, connect to the imap-2004 directory and do: nmake -f makefile.wce The only binary produced is a cclient.lib file. I haven't gotten as far as building mtest on WCE, mainly because I don't have a stdlib library. AMIGA BUILD AND INSTALLATION NOTES The Amiga port was contributed. Maybe the UNIX notes will help. OS2 BUILD NOTES The OS2 port was contributed. Maybe the Win32 Build Notes will help. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/CONFIG000066400000000000000000000174051137544547100205610ustar00rootroot00000000000000 UNIX Configuration Notes The IMAP and POP3 servers are plug-and-play on standard UNIX systems. There is no special configuration needed. Please ignore all rumors to the effect that you need to create an IMAP configuration file. If your system is non-standard, virtually everything that you are likely to want to modify can be found in the source file .../src/osdep/unix/env_unix.c In particular, special attention should be given to the routines: env_init() initialize c-client environment variables, especially the user name and home directory sysinbox() return the UNIX path of the INBOX in which mail delivery will place mail mailboxdir() translate a mailbox name into the associated UNIX directory for listing mailboxfile() translate a mailbox name into the associated UNIX file for opening There are also build options in the top-level makefile which you can give on the command line when building the software. The most common build options are "SSLTYPE=unix", to build the software with SSL, and "SSLTYPE=nopwd", to build the software with SSL and disable plaintext authentication unless the session is encrypted. You should modify these routines as necessary for local policy. The most common modifications are to env_init(), to modify the software's idea of the home directory (which is used everywhere as the default directory), and to sysinbox(), to modify where the software looks for newly-delivered mail. Example 1: suppose your mailer delivers mail to file ".mailbox" in the user's home directory instead of the default UNIX mail spool directory. You will want to change routine sysinbox(), changing the line that reads: sprintf (tmp,"%s/%s",MAILSPOOL,myusername ()); to be: sprintf (tmp,"%s/.mailbox",myhomedir ()); Example 2: suppose you want to change c-client's idea of the user's mailbox directory to be the "mail" subdirectory of the user's home directory instead of the user's home directory. You will want to change variable mailsubdir, changing the line that reads: static char *mailsubdir = NIL; /* mail subdirectory name */ to be: static char *mailsubdir = "mail";/* mail subdirectory name */ Example 3: suppose you want to disable plaintext authentication in the IMAP and POP servers. If you want to disable plaintext authentication in unencrypted sessions but permit it in encrypted sessions, you should use "SSLTYPE=nopwd" in the make command line when building the software. For example, to do this on a Linux system with PAM authentication, do: make lnp SSLTYPE=nopwd If you want to disable plaintext authentication under all circumstances (including SSL or TLS encrypted sessions), use "PASSWDTYPE=nul", e.g.: make lnx EXTRAAUTHENTICATORS=gss PASSWDTYPE=nul which will make it impossible to log in except via Kerberos. Example 4: suppose you want the IMAP and POP servers to do a chroot() to the user's home directory. This is not recommended; there are known ways of attacking chroot() based security mechanisms. Furthermore, if you do this you can not use a traditional UNIX format INBOX in the mail spool directory, since chroot() will prevent access to that directory. If you really want to do this, you need to change variable closedBox, changing the line which reads: static short closedBox = NIL; /* is a closed box */ to be: static short closedBox = T; /* is a closed box */ Example 5: suppose you want to disable non-namespace access to the filesystem root and other users' names, but do not want to go to the extreme of chroot() and you want to allow access to a traditional UNIX format INBOX in the mail spool directory. You need to change variable restrictBox, changing the line which reads: static short restrictBox = NIL; /* is a restricted box */ to be: static short restrictBox = -1; /* is a restricted box */ Other values to set in restrictBox can be found in env_unix.h. Ignore all references in env_unix.c to a configuration file; that code is for UW-internal use only. It is extremely unlikely that that facility will work usefully for you; it is extremely likely that you will shoot yourself in the foot by using; and it frequently changes in an incompatible manner. There are two other build-time configuration issues which you may need to consider: drivers and authenticators. Both of these are set up in the top-level Makefile -- in particular, by the EXTRADRIVERS and EXTRAAUTHENTICATORS variables. Drivers are code modules that support different mailbox storage technologies. By default, all drivers are enabled. There is little benefit to be gained by disabling a driver, with one exception. The mbox driver implements the behavior of automatically moving new mail from the spool directory to the "mbox" file on the user's home directory, if and *only* if the "mbox" exists and is in mailbox format. The mbox driver is listed under EXTRADRIVERS; if you wish to disable it just remove it from that list and rebuild. Authenticators are code modules that support authentication technology for the server (password file lookup, Kerberos, S/Key, etc.). EXTRAAUTHENTICATORS is used to add an authenticator. This subject can be complex; find a wizard if you can't figure it out. It is also possible to add your own drivers and authenticators. This is a topic for wizards, and is beyond the scope of this text. NT Configuration Notes This software is not plug-and-play on NT. If you're not a hacker and/or are unwilling to invest the time to do some programming, you probably want to buy a commercial server for NT. The primary issue that you need to deal with is the format of mail, where the INBOX is located, and where secondary folders are located. As distributed, the software supports mail in the default format used on UNIX (unix format) as well as mbx, mtx, and tenex formats. mbx format is encouraged if at all possible; mtx and tenex format are for compatibility with the past. However, it all depends upon how and where your SMTP server delivers mail. To change the default mailbox format, edit the symbol DEFAULTDRIVER in: ../src/osdep/nt/makefile.nt or ../src/osdep/nt/makefile.ntk To change the default location of INBOX, edit the file: ../src/osdep/nt/mailfile.h Virtually everything else having to do with environment that you are likely to want to modify can be found in the source file: .../src/osdep/nt/env_nt.c In particular, special attention should be given to the routines: env_init() initialize c-client environment variables, especially the user name and home directory sysinbox() return the NT path of the INBOX in which mail delivery will place mail mailboxdir() translate a mailbox name into the associated NT directory for listing mailboxfile() translate a mailbox name into the associated NT file for opening You should modify these routines as necessary. The most common modifications are to env_init(), to modify the software's idea of the home directory (which is used everywhere as the default directory), and to sysinbox(), to modify where the software looks for newly-delivered mail. There are two other build-time configuration issues which you may need to consider: drivers and authenticators. Both of these are set up in the top-level Makefile -- in particular, by the EXTRADRIVERS and EXTRAAUTHENTICATORS variables. Drivers are code modules that support different mailbox storage technologies. By default, all drivers are enabled. There is little benefit to be gained by disabling a driver. Authenticators are code modules that support authentication technology for the server (password file lookup, Kerberos, S/Key, etc.). EXTRAAUTHENTICATORS is used to add an authenticator. This subject can be complex; find a wizard if you can't figure it out. It is also possible to add your own drivers and authenticators. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/FAQ.html000066400000000000000000004106721137544547100211710ustar00rootroot00000000000000

IMAP Toolkit Frequently Asked Questions

Table of Contents


1. General/Software Feature Questions


1.1 Can I set up a POP or IMAP server on UNIX/Linux/OSF/etc.?
Yes. Refer to the UNIX specific notes in files CONFIG and BUILD.
Back to top
1.2 I am currently using qpopper as my POP3 server on UNIX. Do I need to replace it with ipop3d in order to run imapd?
Not necessarily.

Although ipop3d interoperates with imapd better than qpopper, imapd and qpopper will work together. The few qpopper/imapd interoperability issues mostly affect users who use both IMAP and POP3 clients; those users would probably be better served if their POP3 server is ipop3d.

If you are happy with qpopper and just want to add imapd, you should do that, and defer a decision on changing qpopper to ipop3d. That way, you can get comfortable with imapd's performance, without changing anything for your qpopper users.

Many sites have subsequently decided to change from qpopper to ipop3d in order to get better POP3/IMAP interoperability. If you need to do this, you'll know. There also seems to be a way to make qpopper work better with imapd; see the answer to the My qpopper users keep on getting the DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or IMAP. How can I fix this? question.

Back to top
1.3 Can I set up a POP or IMAP server on Windows XP, 2000, NT, Me, 98, or 95?
Yes. Refer to the NT specific notes in files CONFIG and BUILD. Also, you *must* set up CRAM-MD5 authentication, as described in md5.txt.

There is no file access control on Windows 9x or Me, so you probably will have to do modifications to env_unix.c to prevent people from hacking others' mail.

Note, however, that the server is not plug and play the way it is for UNIX.

Back to top
1.4 Can I set up a POP or IMAP server on Windows 3.1 or DOS?
1.5 Can I set up a POP or IMAP server on Macintosh?
1.6 Can I set up a POP or IMAP server on VAX/VMS?
Yes, it's just a small matter of programming.
Back to top
1.7 Can I set up a POP or IMAP server on TOPS-20?
You have a TOPS-20 system? Cool.

If IMAP2 (RFC 1176) is good enough for you, you can use MAPSER which is about the ultimate gonzo pure TOPS-20 extended addressing assembly language program. Unfortunately, IMAP2 is barely good enough for Pine these days, and most other IMAP clients won't work with IMAP2 at all. Maybe someone will hack MAPSER to do IMAP4rev1 some day.

We don't know if anyone wrote a POP3 server for TOPS-20. There definitely was a POP2 server once upon a time.

Or you can port the POP and IMAP server from this IMAP toolkit to it. All that you need for a first stab is to port the MTX driver. That'll probably be just a couple of hours of hacking.

Back to top
1.8 Are hierarchical mailboxes supported?
1.9 Are "dual-use" mailboxes supported?
1.10 Can I have a mailbox that has both messages and sub-mailboxes?
Yes. However, there is one important caveat.

Some mailbox formats, including the default which is the traditional UNIX mailbox format, are stored as a single file containing all the messages. UNIX does not permit a name in the filesystem to be both a file and a directory; consequently you can not have a sub-mailbox within a mailbox that is in one of these formats.

This is not a limitation of the software; this is a limitation of UNIX. For example, there are mailbox formats in which the name is a directory and each message is a file within that directory; these formats support sub-mailboxes within such mailboxes. However, for technical reasons, the "flat file" formats are generally preferred since they perform better. Read imap-2004/docs/formats.txt for more information on this topic.

It is always permissible to create a directory that is not a mailbox, and have sub-mailboxes under it. The easiest way to create a directory is to create a new mailbox inside a directory that doesn't already exist. For example, if you create "Mail/testbox" on UNIX, the directory "Mail/" will automatically be created and then the mailbox "testbox" will be created as a sub-mailbox of "Mail/".

It is also possible to create the name "Mail/" directly. Check the documentation for your client software to see how to do this with that software.

Of course, on Windows systems you would use "\" instead of "/".

Back to top
1.11 What is the difference between "mailbox" and "folder"?
The term "mailbox" is IMAP-speak for what a lot of software calls a "folder" or a "mail folder". However, "folder" is often used in other contexts to refer to a directory, for example, in the graphic user interface on both Windows and Macintosh.

A "mailbox" is specifically defined as a named object that contains messages. It is not required to be capable of containing other types of objects including other mailboxes; although some mailbox formats will permit this.

In IMAP-speak, a mailbox which can not contain other mailboxes is called a "no-inferiors mailbox". Similarly, a directory which can not contain messages is not a mailbox and is called a "no-select name".

Back to top
1.12 What is the status of internationalization?
The IMAP toolkit is partially internationalized and multilingualized.

Searching is supported in the following charsets: US-ASCII, UTF-8, ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6, ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-10, ISO-8859-11, ISO-8859-13, ISO-8859-14, ISO-8859-15, ISO-8859-16, KOI8-R, KOI8-U (alias KOI8-RU), TIS-620, VISCII, ISO-2022-JP, ISO-2022-KR, ISO-2022-CN, ISO-2022-JP-1, ISO-2022-JP-2, GB2312 (alias CN-GB), CN-GB-12345, BIG5 (alias CN-BIG5), EUC-JP, EUC-KR, Shift_JIS, Shift-JIS, KS_C_5601-1987, KS_C_5601-1992, WINDOWS_874, WINDOWS-1250, WINDOWS-1251, WINDOWS-1252, WINDOWS-1253, WINDOWS-1254, WINDOWS-1255, WINDOWS-1256, WINDOWS-1257, WINDOWS-1258.

All ISO-2022-?? charsets are treated identically, and support ASCII, JIS Roman, hankaku katakana, ISO-8859-[1 - 10], TIS, GB 2312, JIS X 0208, JIS X 0212, KSC 5601, and planes 1 and 2 of CNS 11643.

EUC-JP includes support for JIS X 0212 and hankaku katakana.

c-client library support also exists to convert text in any of the above charsets into Unicode, including headers with MIME encoded-words.

There is no support for localization (e.g. non-English error messages) at the present time, but such support is planned.

Back to top
1.13 Can I use SSL?
Yes. See the answer to the How do I configure SSL? question.
Back to top
1.14 Can I use TLS and the STARTTLS facility?
Yes. See the answer to the How do I configure TLS and the STARTTLS facility? question.
Back to top
1.15 Can I use CRAM-MD5 authentication?
Yes. See the answer to the How do I configure CRAM-MD5 authentication? question.
Back to top
1.16 Can I use APOP authentication?
Yes. See the How do I configure APOP authentication? question.

Note that there is no client support for APOP authentication.

Back to top
1.17 Can I use Kerberos V5?
Yes. See the answer to the How do I configure Kerberos V5? question.
Back to top
1.18 Can I use PAM for plaintext passwords?
Yes. See the answer to the How do I configure PAM for plaintext passwords? question.
Back to top
1.19 Can I use Kerberos 5 for plaintext passwords?
Yes. See the answer to the How do I configure Kerberos 5 for plaintext passwords? question.
Back to top
1.20 Can I use AFS for plaintext passwords?
Yes. See the answer to the How do I configure AFS for plaintext passwords? question.
Back to top
1.21 Can I use DCE for plaintext passwords?
Yes. See the answer to the How do I configure DCE for plaintext passwords? question.
Back to top
1.22 Can I use the CRAM-MD5 database for plaintext passwords?
Yes. See the answer to the How do I configure the CRAM-MD5 database for plaintext passwords? question.
Back to top
1.23 Can I disable plaintext passwords?
Yes. See the answer to the How do I disable plaintext passwords? question.
Back to top
1.24 Can I disable plaintext passwords on unencrypted sessions, but allow them on encrypted sessions?
Yes. See the answer to the How do I disable plaintext passwords on unencrypted sessions, but allow them in SSL or TLS sessions? question.
Back to top
1.25 Can I use virtual hosts?
Yes. See the answer to the How do I configure virtual hosts? question.
Back to top
1.26 Can I use RPOP authentication?
There is no support for RPOP authentication.
Back to top
1.27 Can I use Kerberos V4?
Kerberos V4 is not supported. Kerberos V4 client-only contributed code is available in
ftp://ftp.cac.washington.edu/mail/kerberos4-patches.tar.Z

This is a patchkit which must be applied to the IMAP toolkit according to the instructions in the patchkit's README. We can not promise that this code works.
Back to top
1.28 Is there support for S/Key or OTP?
There is currently no support for S/Key or OTP. There may be an OTP SASL authenticator available from third parties.
Back to top
1.29 Is there support for NTLM or SPA?
There is currently no support for NTLM or SPA, nor are there any plans to add such support. In general, I avoid vendor-specific mechanisms. I also believe that these mechanisms are being deprecated by their vendor.

There may be an NTLM SASL authenticator available from third parties.

Back to top
1.30 Is there support for mh?
Yes, but only as a legacy format. Your mh format INBOX is accessed by the name "#mhinbox", and all other mh format mailboxes are accessed by prefixing "#mh/" to the name, e.g. "#mh/foo". The mh support uses the "Path:" entry in your .mh_profile file to identify the root directory of your mh format mailboxes.

Non-legacy use of mh format is not encouraged. There is no support for permanent flags or unique identifiers; furthermore there are known severe performance problems with the mh format.

Back to top
1.31 Is there support for qmail and the maildir format?
There is no support for qmail or the maildir format in our distribution, nor are there any plans to add such support. Maildir support may be available from third parties.
Back to top
1.32 Is there support for the Cyrus mailbox format?
No.
Back to top
1.33 Is this software Y2K compliant?
Please read the files Y2K and calendar.txt.
Back to top



2. What Do I Need to Build This Software?


2.1 What do I need to build this software with SSL on UNIX?
You need to build and install OpenSSL first.
Back to top
2.2 What do I need to build this software with Kerberos V on UNIX?
You need to build and install MIT Kerberos first.
Back to top
2.3 What do I need to use a C++ compiler with this software to build my own application?
If you are building an application using the c-client library, use the new c-client.h file instead of including the other include files. It seems that c-client.h should define away all the troublesome names that conflict with C++.

If you use gcc, you may need to use -fno-operator-names as well.

Back to top
2.4 What do I need to build this software on Windows?
You need Microsoft Visual C++ 6.0, Visual C++ .NET, or Visual C# .NET (which you can buy from any computer store), along with the Microsoft Platform SDK (which you can download from Microsoft's web site).

You do not need to install the entire Platform SDK; it suffices to install just the Core SDK and the Internet Development SDK.

Back to top
2.5 What do I need to build this software on DOS?
It's been several years since we last attempted to do this. At the time, we used Microsoft C.
Back to top
2.6 Can't I use Borland C to build this software on the PC?
Probably not. If you know otherwise, please let us know.
Back to top
2.7 What do I need to build this software on the Mac?
It has been several years since we last attempted to do this. At the time, we used Symantec THINK C; but today you'll need a C compiler which allows segments to be more than 32K.
Back to top
2.8 What do I need to build this software on VMS?
You need the VMS C compiler, and either the Multinet or Netlib TCP.
Back to top
2.9 What do I need to build this software on TOPS-20?
You need the TOPS-20 KCC compiler.
Back to top
2.10 What do I need to build this software on Amiga or OS/2?
We don't know.
Back to top
2.11 What do I need to build this software on Windows CE?
This port is incomplete. Someone needs to finish it.
Back to top



3. Build and Configuration Questions


3.1 How do I configure the IMAP and POP servers on UNIX?
3.2 I built and installed the servers according to the BUILD instructions. It can't be that easy. Don't I need to write a config file?
For ordinary "vanilla" UNIX systems, this software is plug and play; just build it, install it, and you're done. If you have a modified system, then you may want to do additional work; most of this is to a single source code file (env_unix.c on UNIX systems). Read the file CONFIG for more details.

Yes, it's that easy. There are some additional options, such as SSL or Kerberos, which require additional steps to build. See the relevant questions below.

Back to top
3.3 How do I make the IMAP and POP servers look for INBOX at some place other than the mail spool directory?
3.4 How do I make the IMAP server look for secondary folders at some place other than the user's home directory?
Please read the file CONFIG for discussion of this and other issues.
Back to top
3.5 How do I configure SSL?
3.6 How do I configure TLS and the STARTTLS facility?
imap-2004 supports SSL and TLS client functionality on UNIX and 32-bit Windows for IMAP, POP3, SMTP, and NNTP; and SSL and TLS server functionality on UNIX for IMAP and POP3.

UNIX SSL build requires that a third-party software package, OpenSSL, be installed on the system first. Read imap-2004/docs/SSLBUILD for more information.

SSL is supported via undocumented Microsoft interfaces in Windows 9x and NT4; and via standard interfaces in Windows 2000, Windows Millenium, and Windows XP.

Back to top
3.7 How do I build/install OpenSSL and obtain/create certificates for use with SSL?
If you need help in doing this, try the contacts mentioned in the OpenSSL README. We do not offer support for OpenSSL or certificates.
Back to top
3.8 How do I configure CRAM-MD5 authentication?
3.9 How do I configure APOP authentication?
CRAM-MD5 authentication is enabled in the IMAP and POP3 client code on all platforms. Read md5.txt to learn how to set up CRAM-MD5 and APOP authentication on UNIX and NT servers.

There is no support for APOP client authentication.

Back to top
3.10 How do I configure Kerberos V5?
imap-2004 supports client and server functionality on UNIX and 32-bit Windows.

Kerberos V5 is supported by default in Windows 2000 builds:

 nmake -f makefile.w2k

Other builds require that a third-party Kerberos package, e.g. MIT Kerberos, be installed on the system first.

To build with Kerberos V5 on UNIX, include EXTRAAUTHENTICATORS=gss in the make command line, e.g.

 make lnp EXTRAAUTHENTICATORS=gss

To build with Kerberos V5 on Windows 9x, Windows Millenium, and NT4, use the "makefile.ntk" file instead of "makefile.nt":

 nmake -f makefile.ntk
Back to top
3.11 How do I configure PAM for plaintext passwords?
On Linux systems, use the lnp port, e.g.
 make lnp
On Solaris systems and other systems with defective PAM implementations, build with PASSWDTYPE=pmb, e.g.
 make sol PASSWDTYPE=pmb
On all other systems, build with PASSWDTYPE=pam, e.g
 make foo PASSWDTYPE=pam
If you build with PASSWDTYPE=pam and authentication does not work, try rebuilding (after a "make clean") with PASSWDTYPE=pmb.
Back to top
3.12 It looks like all I have to do to make the server use Kerberos is to build with PAM on my Linux system, and set it up in PAM for Kerberos passwords. Right?
Yes and no.

Doing this will make plaintext password authentication use the Kerberos password instead of the /etc/passwd password.

However, this will NOT give you Kerberos-secure authentication. See the answer to the How do I configure Kerberos V5? question for how to build with Kerberos-secure authentication.

Back to top
3.13 How do I configure Kerberos 5 for plaintext passwords?
Build with PASSWDTYPE=gss, e.g.
 make sol PASSWDTYPE=gss
However, this will NOT give you Kerberos-secure authentication. See the answer to the How do I configure Kerberos V5? question for how to build with Kerberos-secure authentication.
Back to top
3.14 How do I configure AFS for plaintext passwords?
Build with PASSWDTYPE=afs, e.g
 make sol PASSWDTYPE=afs
Back to top
3.15 How do I configure DCE for plaintext passwords?
Build with PASSWDTYPE=dce, e.g
 make sol PASSWDTYPE=dce
Back to top
3.16 How do I configure the CRAM-MD5 database for plaintext passwords?
The CRAM-MD5 password database is automatically used for plaintext password if it exists.

Note that this is NOT CRAM-MD5-secure authentication. You probably want to consider disabling plaintext passwords for non-SSL/TLS sessions. See the next two questions.

Back to top
3.17 How do I disable plaintext passwords?
Server-level plaintext passwords can be disabled by setting PASSWDTYPE=nul, e.g.
 make lnx EXTRAAUTHENTICATORS=gss PASSWDTYPE=nul
Note that you must have a CRAM-MD5 database installed or specify at least one EXTRAAUTHENTICATOR, otherwise it will not be possible to log in to the server.

When plaintext passwords are disabled, the IMAP server will advertise the LOGINDISABLED capability and the POP3 server will not advertise the USER capability.

Back to top
3.18 How do I disable plaintext passwords on unencrypted sessions, but allow them in SSL or TLS sessions?

Do not set PASSWDTYPE=nul or SSLTYPE=unix. Set SSLTYPE=nopwd instead, e.g.

 make lnx SSLTYPE=nopwd

When plaintext passwords are disabled, the IMAP server will advertise the LOGINDISABLED capability and the POP3 server will not advertise the USER capability.

Plaintext passwords will always be enabled in SSL sessions; the IMAP server will not advertise the LOGINDISABLED capability and the POP3 server will advertise the USER capability.

If the client does a successful start-TLS in a non-SSL session, plaintext passwords will be enabled, and a new CAPABILITY or CAPA command (which is required after start-TLS) will show the effect as in SSL sessions.

Back to top
3.19 How do I configure virtual hosts?
This is automatic, but with certain restrictions.

The most important one is that each virtual host must have its own IP address; otherwise the server has no way of knowing which virtual host is desired.

As distributed, the software uses a global password file; hence user "fred" on one virtual host is "fred" on all virtual hosts. You may want to modify the checkpw() routine to implement some other policy (e.g. separate password files).

Note that the security model assumes that all users have their own unique UNIX UID number. So if you use separate password files you should make certain that the UID numbers do not overlap between different files.

More advanced virtual host support may be available as patches from third parties.

Back to top
3.20 Why do I get compiler warning messages such as:
 passing arg 3 of `scandir' from incompatible pointer type
 Pointers are not assignment-compatible.
 Argument #4 is not the correct type.
during the build?
You can safely ignore these messages.

Over the years, the prototype for scandir() has changed, and thus is variant across different UNIX platforms. In particular, the definitions of the third argument (type select_t) and fourth argument (type compar_t) have changed over the years, the issue being whether or not the arguments to the functions pointed to by these function pointers are of type const or not.

The way that c-client calls scandir() will tend to generate these compiler warnings on newer systems such as Linux; however, it will still build. The problem with fixing the call is that then it won't build on older systems.

Back to top
3.21 Why do I get compiler warning messages such as
 Operation between types "void(*)(int)" and "void*" is not allowed.
 Function argument assignment between types "void*" and "void(*)(int)" is not allowed.
 Pointers are not assignment-compatible.
 Argument #5 is not the correct type.
during the build?
You can safely ignore these messages.

All known systems have no problem with casting a function pointer to/from a void* pointer, certain C compilers issue a compiler diagnostic because this facility is listed as a "Common extension" by the C standard:

 K.5.7  Function pointer casts
  [#1] A pointer to an object or to void may be cast to a pointer
       to a function, allowing data to be invoked as a function (6.3.4).
  [#2] A pointer to a function may be cast to a pointer to an
       object or to void, allowing a function to be inspected or
       modified (for example, by a debugger) (6.3.4).
It may be just a "common extension", but this facility is relied upon heavily by c-client.
Back to top
3.22 Why do I get linker warning messages such as:
mtest.c:515: the `gets' function is dangerous and should not be used.
during the build? Isn't this a security bug?
You can safely ignore this message.

Certain linkers, most notably on Linux, give this warning message. It is indeed true that the traditional gets() function is not a safe one.

However, the mtest program is only a demonstration program, a model of a very basic application program using c-client. It is not something that you would install, much less run in any security-sensitive context.

mtest has numerous other shortcuts that you wouldn't want to do in a real application program.

The only "security bug" with mtest would be if it was run by some script in a security-sensitive context, but mtest isn't particularly useful for such purposes. If you wanted to write a script to automate some email task using c-client, you'd be better off using imapd instead of mtest.

mtest only has two legitimate uses. It's a useful testbed for me when debugging new versions of c-client, and it's useful as a model for someone writing a simple c-client application to see how the various calls work.

By the way, if you need a more advanced example of c-client programming than mtest (and you probably will), I recommend that you look at the source code for imapd and Pine.

Back to top
3.23 Why do I get linker warning messages such as:
 auth_ssl.c:92: the `tmpnam' function is dangerous and should not be used.
during the build? Isn't this a security bug?
You can safely ignore this message.

Certain linkers, most notably on Linux, give this warning message, based upon two known issues with tmpnam():

there can be a buffer overflow if an inadequate buffer is allocated.
there can be a timing race caused by certain incautious usage of the return value.

Neither of these issues applies in the particular use that is made of tmpnam(). More importantly, the tmpnam() call is never executed on Linux systems.

Back to top
3.24 OK, suppose I see a warning message about a function being "dangerous and should not be used" for something other than this gets() or tmpnam() call?
Please forward the details for investigation.
Back to top



4. Operational Questions


4.1 How can I enable anonymous IMAP logins?
Create the file /etc/anonymous.newsgroups. At the present time, this file should be empty. This will permit IMAP logins as anonymous as well as the ANONYMOUS SASL authenticator. Anonymous users have access to mailboxes in the #news., #ftp/, and #public/ namespaces only.
Back to top
4.2 How do I set up an alert message that each IMAP user will see?
Create the file /etc/imapd.alert with the text of the message. This text should be kept to one line if possible. Note that this will cause an alert to every IMAP user every time they initiate an IMAP session, so it should only be used for critical messages.
Back to top
4.3 How does the c-client library choose which of its several mechanisms to use to establish an IMAP connection to the server? I noticed that it can connect on port 143, port 993, via rsh, and via ssh.
c-client chooses how to establish an IMAP connection via the following rules:
  • If /ssl is specified, use an SSL connection. Fail otherwise.
  • Else if client is a UNIX system and "ssh server exec /etc/rimapd" works, use that
  • Else if /tryssl is specified and an SSL connection works, use that.
  • Else if client is a UNIX system and "rsh server exec /etc/rimapd" works, use that.
  • Else use a non-SSL connection.
Back to top
4.4 I am using a TLS-capable IMAP server, so I don't need to use /ssl to get encryption. However, I want to be certain that my session is TLS encrypted before I send my password. How to I do this?
Use the /tls option in the mailbox name. This will cause an error message and the connection to fail if the server does not negotiate STARTTLS.
Back to top
4.5 How do I use one of the alternative formats described in the formats.txt document? In particular, I hear that mbx format will give me better performance and allow shared access.
The rumors about mbx format being preferred are true. It is faster than the traditional UNIX mailbox format and permits shared access.

However, and this is very important, note that using an alternative mailbox format is an advanced facility, and only expert users should undertake it. If you don't understand any of the following notes, you may not be enough of an expert yet, and are probably better off not going this route until you are more comfortable with your understanding.

Some of the formats, including mbx, are only supported by the software based on the c-client library, and are not recognized by other mailbox programs. The "vi" editor will corrupt any mbx format mailbox that it encounters.

Another problem is that the certain formats, including mbx, use advanced file access and locking techniques that do not work reliably with NFS. NFS is not a real filesystem. Use IMAP instead of NFS for distributed access.

Each of the following steps are in escalating order of involvement. The further you go down this list, the more deeply committed you become:

  • The simplest way to create a mbx-format mailbox is to prefer the name with "#driver.mbx/" when creating a mailbox through c-client. For example, if you create "#driver.mbx/foo", the mailbox "foo" will be created in mbx format. Only use "#driver.mbx/" when creating the mailbox. At all other times, just use the name ("foo" in this example); the software will automatically select the driver for mbx whenever that mailbox is accessed without you doing anything else.
  • You can use the "mailutil copy" command to copy an existing mailbox to a new mailbox in mbx format. Read the man page provided with the mailutil program for details.
  • If you create an mbx-format INBOX, by creating "#driver.mbx/INBOX" (note that "INBOX" must be all uppercase), then subsequent access to INBOX by any c-client based application will use the mbx-format INBOX. Any mail delivered to the traditional format mailbox in the spool directory (e.g. /var/spool/mail/$USER) will automatically be copied into the mbx-format INBOX and the spool directory copy removed.
  • You can cause any newly-created mailboxes to be in mbx-format by default by changing the definition of CREATEPROTO=unixproto to be CREATEPROTO=mbxproto in src/osdep/unix/Makefile, then rebuilding the IMAP toolkit (do a "make clean" first). Do not change EMPTYPROTO, since mbx format mailboxes are never a zero-byte file. If you use Pine or the imap-utils, you should probably also rebuild them with the new IMAP toolkit too.
  • You can deliver directly to the mbx-format INBOX by use of the tmail or dmail programs. tmail is for direct invocation from sendmail (or whatever MTA program you use); dmail is for calls from procmail. Both of these programs have man pages which must be read carefully before making this change.

Most other servers (e.g. Cyrus) require use of a non-standard format. A full-fledged format conversion is not significantly different from what you have to do with other servers. The difference, which makes format conversion procedures somewhat more complicated with this server, is that there is no "all or nothing" requirement with this server. There are many points in between. A format conversion can be anything from a single mailbox or single user, to systemwide.

This is good in that you can decide how far to go, or do the steps incrementally as you become more comfortable with the result. On the other hand, there's no "One True Way" which can be boiled down to a simple set of pedagogical instructions.

A number of sites have done full-fledged format conversions, and are reportedly quite happy with the results. Feel free to ask in the comp.mail.imap newsgroup or the c-client mailing list for advice or help.

Back to top
4.6 How do I set up shared mailboxes?
At the simplest level, a shared mailbox is one which has UNIX file and directory protections which permit multiple users to access it. What this means is that your existing skills and tools to create and manage shared files on your UNIX system apply to shared mailboxes; e.g.
 chmod 666 mailbox

You may want to consider the use of a mailbox format which permits multiple simultaneous read/write sessions, such as the mbx format. The traditional UNIX format only allows only read/write session to a mailbox at a time.

An additional convenience item are three system directories, which can be set up for shared namespaces. These are: #ftp, #shared, and #public, and are defined by creating the associated UNIX users and home directories as described below.

#ftp/ refers to the anonymous ftp filesystem exported by the ftp server, and is equivalent to the home directory for UNIX user "ftp". For example, #ftp/foo/bar refers to the file /foo/bar in the anonymous FTP filesystem, or ~ftp/foo/bar for normal users. Anonymous FTP files are available to anonymous IMAP logins. By default, newly-created files in #ftp/ are protected 644.

#public/ refers to an IMAP toolkit convention called "public" files, and is equivalent to the home directory for UNIX user "imappublic". For example, #public/foo/bar refers to the file ~imappublic/foo/bar. Public files are available to anonymous IMAP logins. By default, newly-created files in #public are created with protection 0666.

#shared/ refers to an IMAP toolkit convention called "shared" files, and is equivalent to the home directory for UNIX user "imapshared". For example, #shared/foo/bar refers to the file ~imapshared/foo/bar. Shared files are not available to anonymous IMAP logins. By default, newly-created files in #shared are created with protection 0660.

Back to top
4.7 How can I make the server syslogs go to someplace other than the mail syslog?
The openlog() call that sets the syslog facility is in src/osdep/unix/env_unix.c in routine server_init(). You need to edit this file to change the syslog facility from LOG_MAIL to the facility you want, then rebuild. You also need to set up your /etc/syslog.conf properly.

Refer to the man pages for syslog and syslogd for more information on what the available syslog facilities are and how to configure syslogs. If you still don't understand what to do, find a UNIX system expert.

Back to top



5. Security Questions


5.1 I see that the IMAP server allows access to arbitary files on the system, including /etc/passwd! How do I disable this?
You should not worry about this if your IMAP users are allowed shell access. The IMAP server does not permit any access that the user can not have via the shell.

If, and only if, you deny your IMAP users shell access, you may want to consider one of three choices. Note that these choices reduce IMAP functionality, and may have undesirable side effects. Each of these choices involves an edit to file src/osdep/unix/env_unix.c

The first (and recommended) choice is to set restrictBox as described in file CONFIG. This will disable access to the filesystem root, to other users' home directory, and to superior directory.

The second (and strongly NOT recommended) choice is to set closedBox as described in file CONFIG. This puts each IMAP session into a so-called "chroot jail", and thus setting this option is extremely dangerous; it can make your system much less secure and open to root compromise attacks. So do not use this option unless you are absolutely certain that you understand all the issues of a "chroot jail."

The third choice is to rewrite routine mailboxfile() to implement whatever mapping from mailbox name to filesystem name (and restrictions) that you wish. This is the most general choice. As a guide, you can see at the start of routine mailboxfile() what the restrictBox choice does.

Back to top
5.2 I've heard that IMAP servers are insecure. Is this true?
There are no known security problems in this version of the IMAP toolkit, including the IMAP and POP servers. The IMAP and POP servers limit what can be done while not logged in, and as part of the login process discard all privileges except those of the user.

As with other software packages, there have been buffer overflow vulnerabilities in past versions. All known problems of this nature are fixed in this version.

There is every reason to believe that the bad guys are engaged in an ongoing effort to find vulnerabilities in the IMAP toolkit. We look for such problems, and when one is found we fix it.

It's unfortunate that any vulnerabilities existed in past versions, and we're doing my best to keep the IMAP toolkit free of vulnerabilities. No new vulnerabilities have been discovered in quite a while, but efforts will not be relaxed.

Beware of vendors who claim that their implementations can not have vulnerabilities.

Back to top
5.3 How do I know that I have the most secure version of the server?
The best way is to keep your server software up to date. The bad guys are always looking for ways to crack software, and when they find one, let all their friends know.

Oldtimers used to refer to a concept of software rot: if your software hasn't been updated in a while, it would "rot" -- tend to acquire problems that it didn't have when it was new.

The latest release version of the IMAP toolkit is always available at ftp://ftp.cac.washington.edu/mail/imap.tar.Z

Back to top
5.4 I see all these strcpy() and sprintf() calls, those are unsafe, aren't they?
Yes and no.

It can be unsafe to do these calls if you do not know that the string being written will fit in the buffer. However, they are perfectly safe if you do know that.

Beware of programmers who advocate doing a brute-force change of all instances of

 strcpy (s,t);
to
 strncpy (s,t,n)[n] = '\0';
and similar measures in the name of "fixing all possible buffer overflows."

There are examples in which a security bug was introduced because of this type of "fix", due to the programmer using the wrong value for n. In one case, the programmer thought that n was larger than it actually was, causing a NUL to be written out of the buffer; in another, n was too small, and a security credential was truncated.

What is particularly ironic was that in both cases, the original strcpy() was safe, because the size of the source string was known to be safe.

With all this in mind, the software has been inspected, and it is believed that all places where buffer overflows can happen have been fixed. The strcpy()s that are still are in the code occur after a size check was done in some other way.

Note that the common C idiom of

 *s++ = c;
is just as vulnerable to buffer overflows. You can't cure buffer overflows by outlawing certain functions, nor is it desirable to do so; sometimes operations like strcpy() translate into fast machine instructions for better performance.

Nothing replaces careful study of code. That's how the bad guys find bugs. Security is not accomplished by means of brute-force shortcuts.

Back to top
5.5 Those /tmp lock files are protected 666, is that really right?
Yes. Shared mailboxes won't work otherwise. Also, you get into accidental denial of service problems with old lock files left lying around; this happens fairly frequently.

The deliberate mischief that can be caused by fiddling with the lock files is small-scale; harassment level at most. There are many -- and much more effective -- other ways of harassing another user on UNIX. It's usually not difficult to determine the culprit.

Before worrying about deliberate mischief, worry first about things happening by accident!

Back to top



6. Why Did You Do This Strange Thing? Questions


6.1 Why don't you use GNU autoconfig / automake / autoblurdybloop?
Autoconfig et al are not available on all the platforms where the IMAP toolkit is supported; and do not work correctly on some of the platforms where they do exist. Furthermore, these programs add another layer of complexity to an already complex process.

Coaxing software that uses autoconfig to build properly on platforms which were not specifically considered by that software wastes an inordinate amount of time. When (not if) autoconfig fails to do the right thing, the result is an inpenetrable morass to untangle in order to find the problem and fix it.

The concept behind autoconfig is good, but the execution is flawed. It rarely does the right thing on a platform that wasn't specifically considered. Human life is too short to debug autoconfig problems, especially since the current mechanism is so much easier.

If an autoconfig mechanism were used, it wouldn't be the GNU one, due to the viral nature of the GNU license.

Back to top
6.2 Why do you insist upon a build with -g? Doesn't it waste disk and memory space?
From time to time a submitted port has snuck in without -g. This has always ended up causing problems. There are only two valid excuses for not using -g in a port:
  • The compiler does not support -g
  • An alternate form of -g is needed with optimization, e.g. -g3.

There will be no new ports added without -g (or a suitable alternative) being set.

-g has not been arbitrarily added to the ports which do not currently have it because we don't know if doing so would break the build. However, any support issues with one of those port will lead to the correct -g setting being determined and permanently added.

Processors are fast enough (and disk space is cheap enough) that -g should be automatic in all compilers with no way of turning it off, and /bin/strip should be a symlink to /bin/true. Human life is too short to deal with binaries built without -g. Such binaries should be a bad memory of the days of KIPS processors and disks that costs several dollars per kilobyte.

Back to top
6.3 Why don't you make c-client a shared library?
All too often, shared libraries create far more problems than they solve.

Remember that you only gain the benefit of a shared library when there are multiple applications which use that shared library. Even without shared libraries, on most modern operating systems (and many ancient ones too!) applications will share their text segments between across multiple processes running the same application. This means that if your system only runs one application (e.g. imapd) that uses the c-client library, then you gain no benefit from making c-client a shared library even if it has 100 imapd processes. You will, however suffer added complexity.

If you have a server system that just runs imapd and ipop3d, then making c-client a shared library will save just one copy of c-client no matter how many IMAP/POP3 processes are running.

The problem with shared libraries is that you have to keep around a copy of the library every time something changes in the library that would affect the interface the library presents to the application. So, you end up having many copies of the same shared library.

If you don't keep multiple copies of the shared library, then one of two things happens. If there was proper versioning, then you'll get a message such as "cannot open shared object file" or "minor versions don't match" and the application won't run. Otherwise, the application will run, but will fail in mysterious ways.

Several sites and third-party distributors have modified the c-client makefile in order to make c-client be a shared library. When (not if) a c-client based application fails in mysterious ways because of a library compatibility problem, the result is a bug report. A lot of time and effort ends up getting wasted investigating such bug reports.

Memory is so cheap these days that it's not worth it. Human life is too short to deal with shared library compatibility problems.

Back to top
6.4 Why don't you use iconv() for internationalization support?
iconv() is not ubiquitous enough.
Back to top
6.5 Why is the IMAP server connected to the home directory by default?
The IMAP server has no way of knowing what you might call "mail" as opposed to "some other file"; in fact, you can use IMAP to access any file.

The IMAP server also doesn't know whether your preferred subdirectory for mailbox files is "mail/", ".mail/", "Mail/", "Mailboxes/", or any of a zillion other possibilities. If one such name were chosen, it would undoubtably anger the partisans of all the other names.

It is possible to modify the software so that the default connected directory is someplace else. Please read the file CONFIG for discussion of this and other issues.

Back to top
6.6 I have a Windows system. Why isn't the server plug and play for me?
There is no standard for how mail is stored on Windows; nor a single standard SMTP server. The closest to either would be the SMTP server in Microsoft's IIS.

So there's no default by which to make assumptions. As the software is set up, it assumes that the each user has an Windows login account and private home directory, and that mail is stored on that home directory as files in one of the popular UNIX formats. It also assumes that there is some tool equivalent to inetd on UNIX that does the TCP/IP listening and server startup.

Basically, unless you're an email software hacker, you probably want to look elsewhere if you want IMAP/POP servers for Windows.

Back to top
6.7 I looked at the UNIX SSL code and saw that you have the SSL data payload size set to 8192 bytes. SSL allows 16K; why aren't you using the full size?
This is to avoid an interoperability problem with:
  • PC IMAP clients that use Microsoft's SChannel.DLL (SSPI) for SSL support
  • Microsoft Exchange server (which also uses SChannel).

SChannel has a bug that makes it think that the maximum SSL data payload size is 16379 bytes -- 5 bytes too small. Thus, c-client has to make sure that it never transmits full sized SSL packets.

The reason for using 8K (as opposed to, say, 16379 bytes, or 15K, or...) is that it corresponds with the TCP buffer size that the software uses elsewhere for input; there's a slight performance benefit to having the two sizes correspond or at least be a multiple of each other. Also, it keeps the size as a power of two, which might be significant on some platforms.

There wasn't a significant difference that we could measure between 8K and 15K.

Microsoft has developed a hotfix for this bug. Look up MSKB article number 300562. Contrary to the article text which implies that this is a Pine issue, this bug also affects Microsoft Exchange server with any client that transmits full-sized SSL payloads.

Back to top
6.8 Why is an mh format INBOX called #mhinbox instead of just INBOX?
It's a long story. In brief, the mh format driver is less functional than any of the other drivers. It turned out that there were some users (including high-level administrators) who tried mh years ago and no longer use it, but still had an mh profile left behind.

When the mh driver used INBOX, it would see the mh profile, and proceed to move the user's INBOX into the mh format INBOX. This caused considerable confusion as some things stopped working.

Back to top
6.9 Why don't you support the maildir format?
It is technically difficult to support maildir in IMAP while maintaining acceptable performance, robustness, following the requirements of the IMAP protocol specification, and following the requirements of maildir.

No one has succeeded in accomplishing all four together. The various maildir drivers offered as patches all have these problems. The problem is exacerbated because this implementation supports multiple formats; consequently this implementation can't make any performance shortcuts by assuming that all the world is maildir.

We can't do a better job than the maildir fan community has done with their maildir drivers. Similarly, if the maildir fan community provides the maildir driver, they take on the responsibility for answering maildir-specific support questions. This is as it should be, and that is why maildir support is left to the maildir fan community.

Back to top
6.10 Why don't you support the Cyrus format?
There's no point to doing so. An implementation which supports multiple formats will never do as well as one which is optimized to support one single format.

If you want to use Cyrus mailbox format, you should use the Cyrus server, which is the native implementation of that format and is specifically optimized for that format. That's also why Cyrus doesn't implement any other format.

Back to top
6.11 Why is it creating extra forks on my SVR4 system?
This is because your system only has fcntl() style locking and not flock() style locking. fcntl() locking has a design flaw that causes a close() to release any locks made by that process on the file opened on that file descriptor, even if the lock was made on a different file descriptor.

This design flaw causes unexpected loss of lock, and consequent mailbox corruption. The workaround is to do certain "dangerous operations" in another fork, thus avoiding doing a close() in the vulnerable fork.

The best way to solve this problem is to upgrade your SVR4 (Solaris, AIX, HP-UX, SGI) or OSF/1 system to a more advanced operating system, such as Linux or BSD. These more advanced operating systems have fcntl() locking for compatibility with SVR4, but also have flock() locking.

Beware of certain SVR4 systems, such as AIX, which have an "flock()" function in their C library that is just a jacket that does an fcntl() lock. This is not a true flock(), and has the same design flaw as fcntl().

Back to top
6.12 Why are you so fussy about the date/time format in the internal "From " line in traditional UNIX mailbox files? My other mail program just considers every line that starts with "From " to be the start of the message.
You just answered your own question. If any line that starts with "From " is treated as the start of a message, then every message text line which starts with "From " has to be quoted (typically by prefixing a ">" character). People complain about this -- "why did a > get stuck in my message?"

So, good mail reading software only considers a line to be a "From " line if it follows the actual specification for a "From " line. This means, among other things, that the day of week is fixed-format: "May 14", but "May  7" (note the extra space) as opposed to "May 7". ctime() format for the date is the most common, although POSIX also allows a numeric timezone after the year. For compatibility with ancient software, the seconds are optional, the timezone may appear before the year, the old 3-letter timezones are also permitted, and "remote from xxx" may appear after the whole thing.

Unfortunately, some software written by novices use other formats. The most common error is to have a variable-width day of month, perhaps in the erroneous belief that RFC 2822 (or RFC 822) defines the format of the date/time in the "From " line (it doesn't; no RFC describes internal formats). I've seen a few other goofs, such as a single-digit second, but these are less common.

If you are writing your own software that writes mailbox files, and you really aren't all that savvy with all the ins and outs and ancient history, you should seriously consider using the c-client library (e.g. routine mail_append()) instead of doing the file writes yourself. If you must do it yourself, use ctime(), as in:

 fprintf (mbx,"From %s@%h %s",user,host,ctime (time (0)));
rather than try to figure out a good format yourself. ctime() is the most traditional format and nobody will flame you for using it.
Back to top
6.13 Why is traditional UNIX format the default format?
Compatibility with the past 30 or so years of UNIX history. This server is the only one that completely interoperates with legacy UNIX mail tools.
Back to top
6.14 Why do you write this "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA" message at the start of traditional UNIX and MMDF format mailboxes?
This pseudo-message serves two purposes.

First, it establishes the mailbox format even when the mailbox has no messages. Otherwise, a mailbox with no messages is a zero-byte file, which could be one of several formats.

Second, it holds mailbox metadata used by IMAP: the UID validity, the last assigned UID, and mailbox keywords. Without this metadata, which must be preserved even when the mailbox has no messages, the traditional UNIX format wouldn't be able to support the full capabilities of IMAP.

Back to top
6.15 Why don't you stash the mailbox metadata in the first real message of the mailbox instead of writing this fake FOLDER INTERNAL DATA message?
In fact, that is what is done if the mailbox is non-empty and does not already have a FOLDER INTERNAL DATA message.

One problem with doing that is that if some external program removes the first message, the metadata is lost and must be recreated, thus losing any prior UID or keyword list status that IMAP clients may depend upon.

Another problem is that this doesn't help if the last message is deleted. This will result in an empty mailbox, and the necessity to create a FOLDER INTERNAL DATA message.

Back to top
6.16 Why aren't "dual-use" mailboxes the default?
Compatibility with the past 30 or so years of UNIX history, not to mention compatibility with user expectations when using shell tools.
Back to top
6.17 Why do you use ucbcc to build on Solaris?
It is a long, long story about why cc is set to ucbcc. You need to invoke the C compiler so that it links with the SVR4 libraries and not the BSD libraries, otherwise readdir() will return the wrong information.

Of all the names in the most common path, ucbcc is the only name to be found (on /usr/ccs/bin) that points to a suitable compiler. cc is likely to be /usr/ucb/cc which is absolutely not the compiler that you want. The real SVR4 cc is probably something like /opt/SUNWspro/bin/cc which is rarely in anyone's path by default.

ucbcc is probably a link to acc, e.g. /opt/SUNWspro/SC4.0/bin/acc, and is the UCB C compiler using the SVR4 libraries.

If ucbcc isn't on your system, then punt on the SUN C compiler and use gcc instead (the gso port instead of the sol port).

If, in spite of all the above warnings, you choose to change "ucbcc" to "cc", you will probably find that the -O2 needs to be changed to -O. If you don't get any error messages with -O2, that's a pretty good indicator that you goofed and are running the compiler that will link with the BSD libraries.

To recap:

  • The sol port is designed to be built using the UCB compiler using the SVR4 libraries. This compiler is "ucbcc", which is lunk to acc. You use -O2 as one of the CFLAGS.
  • If you build the sol port with the UCB compiler using the BSD libraries, you will get no error messages but you will get bad binaries (the most obvious symptom is dropping the first two characters return filenames from the imapd LIST command. This compiler also uses -O2, and is very often what the user gets from "cc". BEWARE
  • If you build the sol port with the real SVR4 compiler, which is often hidden away or unavailable on many systems, then you will get errors from -O2 and you need to change that to -O. But you will get a good binary. However, you should try it with -O2 first, to make sure that you got this compiler and not the UCB compiler using BSD libraries.
Back to top
6.18 Why should I care about some old system with BSD libraries? cc is the right thing on my Solaris system!
Because there still are sites that use such systems. On those systems, the assumption that "cc" does the right thing will lead to corrupt binaries with no error message or other warning that anything is amiss.

Too many sites have fallen victim to this problem.

Back to top
6.19 Why do you insist upon writing .lock files in the spool directory?
Compatibility with the past 30 years of UNIX software which deals with the spool directory, especially software which delivers mail. Otherwise, it is possible to lose mail.
Back to top
6.20 Why should I care about compatibility with the past?
This is one of those questions in which the answer never convinces those who ask it. Somehow, everybody who ever asks this question ends up answering it for themselves as they get older, with the very answer that they rejected years earlier.
Back to top



7. Problems and Annoyances


7.1 Help! My INBOX is empty! What happened to my messages?
If you are seeing "0 messages" when you open INBOX and you know you have messages there (and perhaps have looked at your mail spool file and see that messages are there), then probably there is something wrong with the very first line of your mail spool file. Make sure that the first five bytes of the file are "From ", followed by an email address and a date/time in ctime() format, e.g.:
 From fred@foo.bar Mon May  7 20:54:30 2001
Back to top
7.2 Help! All my messages in a non-INBOX mailbox have been concatenated into one message which claims to be from me and has a subject of the file name of the mailbox! What's going on?
Something wrong with the very first line of the mailbox. Make sure that the first five bytes of the file are "From ", followed by an email address and a date/time in ctime() format, e.g.:
 From fred@foo.bar Mon May  7 20:54:30 2001
Back to top
7.3 Why do I get the message: CREATE failed: Can't create mailbox node xxxxxxxxx: File exists and how do I fix it?
See the answer to the Are hierarchical mailboxes supported? question.
Back to top
7.4 Why can't I log in to the server? The user name and password are right!
There are a myriad number of possible answers to this question. The only way to say for sure what is wrong is run the server under a debugger such as gdb while root (yes, you must be root) with a breakpoint at routines checkpw() and loginpw(), then single-step until you see which test rejected you. The server isn't going to give any error messages other than "login failed" in the name of not giving out any unnecessary information to unauthorized individuals.

Here are some of the more common reasons why login may fail:

  • You didn't really give the correct user name and/or password.
  • Your client doesn't send the LOGIN command correctly; for example, IMAP2 clients won't send a password containing a "*" correctly to an IMAP4 server.
  • If you have set up a CRAM-MD5 database, remember that the password used is the one in the CRAM-MD5 database, and furthermore that there must also be an entry in /etc/passwd (but the /etc/passwd password is not used).
  • If you are using PAM, have you created a service file for the server in /etc/pam.d?
  • If you are using shadow passwords, have you used an appropriate port when building? In particular, note that "lnx" is for Linux systems without shadow passwords; you probably want "slx" or "lnp" instead.
  • If your system has account or password expirations, check to see that the expiration date hasn't passed.
  • You can't log in as root or any other UID 0 user. This is for your own safety, not to mention the fact that the servers use UID 0 as meaning "not logged in".
Back to top
7.5 Help! My load average is soaring and I see hundreds of POP and IMAP servers, many logged in as the same user!
Certain inferior losing GUI mail reading programs have a "synchronize all mailboxes at startup" (IMAP) or "check for new mail every second" (POP) feature which causes a rapid and unchecked spawning of servers.

This is not a problem in the server; the client is really asking for all those server sessions. Unfortunately, there isn't much that the POP and IMAP servers can do about it; they don't spawned themselves.

Some sites have added code to record the number of server sessions spawned per user per hour, and disable login for a user who has exceeded a predetermined rate. This doesn't stop the servers from being spawned; it just means that a server session will commit suicide a bit faster.

Another possibility is to detect excessive server spawning activity at the level where the server is spawned, which would be inetd or possibly tcpd. The problem here is that this is a hard time to quantify. 50 sessions in a minute from a multi-user timesharing system may be perfectly alright, whereas 10 sessions a minute from a PC may be too much.

The real solution is to fix the client configuration, by disabling those evil features. Also tell the vendors of those clients how you feel about distributing denial-of-service attack tools in the guise of mail reading programs.

Back to top
7.6 Why does mail disappear even though I set "keep mail on server"?
7.7 Why do I get the message Moved ##### bytes of new mail to /home/user/mbox from /var/spool/mail/user and why did this happen?
This is probably caused by the mbox driver. If the file "mbox" exists on the user's home directory and is in UNIX mailbox format, then when INBOX is opened this file will be selected as INBOX instead of the mail spool file. Messages will be automatically transferred from the mail spool file into the mbox file.

To disable this behavior, delete "mbox" from the EXTRADRIVERS list in the top-level Makefile and rebuild. Note that if you do this, users won't be able to access the messages that have already been moved to mbox unless they open mbox instead of INBOX.

Back to top
7.8 Why isn't it showing the local host name as a fully-qualified domain name?
7.9 Why is the local host name in the From/Sender/Message-ID headers of outgoing mail not coming out as a fully-qualified domain name?
Your UNIX system is misconfigured. The entry for your system in /etc/hosts must have the fully-qualified domain name first, e.g.
 105.69.1.234	bombastic.blurdybloop.com bombastic

A common mistake of novice system administrators is to have the short name first, e.g.

 105.69.1.234	bombastic bombastic.blurdybloop.com

or to omit the fully qualified domain name entirely, e.g.

 105.69.1.234	bombastic

The result of this is that when the IMAP toolkit does a gethostbyname() call to get the fully-qualified domain name, it would get "bombastic" instead of "bombastic.blurdybloop.com".

On some systems, a configuration file (typically named /etc/svc.conf, /etc/netsvc.conf, or /etc/nsswitch.conf) can be used to configure the system to use the domain name system (DNS) instead of /etc/hosts, so it doesn't matter if /etc/hosts is misconfigured.

Check the man pages for gethostbyname, hosts, svc, and/or netsvc for more information.

Unfortunately, certain vendors, most notably SUN, have failed to make this clear in their documentation. Most of SUN's documentation assumes a corporate network that is not connected to the Internet.

net.folklore once (late 1980s) held that the proper procedure was to append the results of getdomainname() to the name returned by gethostname(), and some versions of sendmail configuration files were distributed that did this. This was incorrect; the string returned from getdomainname() is the Yellow Pages (a.k.a NIS) domain name, which is a completely different (albeit unfortunately named) entity from an Internet domain. These were often fortuitously the same string, except when they weren't. Frequently, this would result in host names with spuriously doubled domain names, e.g.

 bombastic.blurdybloop.com.blurdybloop.com

This practice has been thoroughly discredited for many years, but folklore dies hard.

Back to top
7.10 What does the message: Mailbox vulnerable - directory /var/spool/mail must have 1777 protection mean? How can I fix this?
In order to update a mailbox in the default UNIX format, it is necessary to create a lock file to prevent the mailer from delivering mail while an update is in progress. Some systems use a directory protection of 775, requiring that all mail handling programs be setgid mail; or of 755, requiring that all mail handling programs be setuid root.

The IMAP toolkit does not run with any special privileges, and I plan to keep it that way. It is antithetical to the concept of a toolkit if users can't write their own programs to use it. Also, I've had enough bad experiences with security bugs while running privileged; the IMAP and POP servers have to be root when not logged in, in order to be able to log themselves in. I don't want to go any deeper down that slippery slope.

Directory protection 1777 is secure enough on most well-managed systems. If you can't trust your users with a 1777 mail spool (petty harassment is about the limit of the abuse exposure), then you have much worse problems then that.

If you absolutely insist upon requiring privileges to create a lock file, external file locking can be done via a setgid mail program named /etc/mlock (this is defined by LOCKPGM in the c-client Makefile). If the toolkit is unable to create a <...mailbox...>.lock file in the directory by itself, it will try to call mlock to do it. I do not recommend doing this for performance reasons.

A sample mlock program is included as part of imap-2004. We have tried to make this sample program secure, but it has not been thoroughly audited.

Back to top
7.11 What does the message: Mailbox is open by another process, access is readonly mean? How do I fix this?
A problem occurred in applying a lock to a /tmp lock file. Either some other program has the mailbox open and won't relenquish it, or something is wrong with the protection of /tmp or the lock.

Make sure that the /tmp directory is protected 1777. Some security scripts incorrectly set the protection of the /tmp directory to 775, which disables /tmp for all non-privileged programs.

Back to top
7.12 What does the message: Can't get write access to mailbox, access is readonly mean?
The mailbox file is write-protected against you.
Back to top
7.13 I set my POP3 client to "delete messages from server" but they never get deleted. What is wrong?
Make sure that your mailbox is not read-only: that the mailbox is owned by you and write enabled (protection 0600), and that the /tmp directory is longer world-writeable. /tmp must be world-writeable because lots of applications use it for scratch space. To fix this, do
 chmod 1777 /tmp
as root.

Make sure that your POP3 client issues a QUIT command when it finishes. The POP3 protocol specifies that deletions are discarded unless a proper QUIT is done.

Make sure that you are not opening multiple POP3 sessions to the same mailbox. It is a requirement of the POP3 protocol than only one POP3 session be in effect to a mailbox at a time, however some, poorly-written POP3 clients violate this. Also, some background "check for new mail" tasks also cause a violation. See the answer to the What does the syslog message: Killed (lost mailbox lock) user=... host=... mean? question for more details.

Back to top
7.14 What do messages such as:
 Message ... UID ... already has UID ...
 Message ... UID ... less than ...
 Message ... UID ... greater than last ...
 Invalid UID ... in message ..., rebuilding UIDs
mean?
Something happened to corrupt the unique identifier regime in the mailbox. In traditional UNIX-format mailboxes, this can happen if the user deleted the "DO NOT DELETE" internal message.

This problem is relatively harmless; a new valid unique identifier regime will be created. The main effect is that any references to the old UIDs will no longer be useful.

So, unless it is a chronic problem or you feel like debugging, you can safely ignore these messages.

Back to top
7.15 What do the error messages:
 Unable to read internal header at ...
 Unable to find CRLF at ...
 Unable to parse internal header at ...
 Unable to parse message date at ...
 Unable to parse message flags at ...
 Unable to parse message UID at ...
 Unable to parse message size at ...
 Last message (at ... ) runs past end of file ...
mean? I am using mbx format.
The mbx-format mailbox is corrupted and needs to be repaired.

You should make an effort to find out why the corruption happened. Was there an obvious system problem (crash or disk failure)? Did the user accidentally access the file via NFS? Mailboxes don't get corrupted by themselves; something caused the problem.

Some people have developed automated scripts, but if you're comfortable using emacs it's pretty easy to fix it manually. Do not use vi or any other editor unless you are certain that editor can handle binary!!!

If you are not comfortable with emacs, or if the file is too large to read with emacs, see the "step-by-step" technique later on for another way of doing it.

After the word "at" in the error message is the byte position it got to when it got unhappy with the file, e.g. if you see:

 Unable to parse internal header at 43921: ne bombastic blurdybloop
The problem occurs at the 43,931 byte in the file. That's the point you need to fix. c-client is expecting an internal header at that byte number, looking something like:
 6-Jan-1998 17:42:24 -0800,1045;000000100001-00000001
The format of this internal line is:
 dd-mmm-yyyy hh:mm:ss +zzzz,ssss;ffffffffFFFF-UUUUUUUU
The only thing that is variable is the "ssss" field, it can be as many digits as needed. All other fields (inluding the "dd") are fixed width. So, the easiest thing to do is to look forward in the file for the next internal header, and delete everything from the error point to that internal header.

Here's what to do if you want to be smarter and do a little bit more work. Generally, you're in the middle of a message, and there's nothing wrong with that message. The problem happened in the *previous* message. So, search back to the previous internal header. Now, remember that "ssss" field? That's the size of that message.

Mark where you are in the file, move the cursor to the line after the internal header, and skip that many bytes ("ssss") forward. If you're at the point of the error in the file, then that message is corrupt. If you're at a different point, then perhaps the previous message is corrupt and has a too long size count that "ate" into this message.

Basically, what you need to do is make sure that all those size counts are right, and that moving "ssss" bytes from the line after the internal header will land you at another internal header.

Usually, once you know what you're looking at, it's pretty easy to work out the corruption, and the best remedial action. Repair scripts will make the problem go away but may not always do the smartest/best salvage of the user's data. Manual repair is more flexible and usually preferable.

Here is a step-by-step technique for fixing corrupt mbx files that's a bit cruder than the procedure outlined above, but works for any size file.

In this example, suppose that the corrupt file is INBOX, the error message is

 Unable to find CRLF at 132551754
and the size of the INBOX file is 132867870 bytes.

The first step is to split the mailbox file at the point of the error:

  • Rename the INBOX file to some other name, such as INBOX.bad.
  • Copy the first 132,551,754 bytes of INBOX.bad to another file, such as INBOX.new.
  • Extract the trailing 316,116 bytes (132867870-132551754) of INBOX.bad into another file, such as INBOX.tail.
  • You no longer need INBOX.bad. Delete it.
In other words, use the number from the "Unable to find CRLF at" as the point to split INBOX into two new files, INBOX.new and INBOX.tail.

Now, remove the erroneous data:

  • Verify that you can open INBOX.new in IMAP or Pine.
  • The last message of INBOX.new is probably corrupted. Copy it to another file, such as badmsg.1, then delete and expunge that last message from INBOX.new
  • Locate the first occurance of text in INBOX.tail which looks like an internal header, as described above.
  • Remove all the text which occurs prior to that point, and place it into another file, such as badmsg.2. Note that in the case of a single digit date, there is a leading space which must not be removed (e.g. " 6-Nov-2001" not "6-Nov-2001").

Reassemble the mailbox:

  • Append INBOX.tail to INBOX.new.
  • You no longer need INBOX.tail. Delete it.
  • Verify that you can open INBOX.new in IMAP or Pine.

Reinstall INBOX.new as INBOX:

  • Check to see if you have received any new messages while repairing INBOX.
  • If you haven't received any new messages while repairing INBOX, just rename INBOX.new to INBOX.
  • If you have received new messages, be sure to copy the new messages from INBOX to INBOX.new before doing the rename.

You now have a working INBOX, as well as two files with corrupted data (badmsg.1 and badmsg.2). There may be some useful data in the two badmsg files that you might want to try salvaging; otherwise you can delete the two badmsg files.

Back to top
7.16 What do the syslog messages:
 imap/tcp server failing (looping)
 pop3/tcp server failing (looping)
mean? When it happens, the listed service shuts down. How can I fix this?
The error message "server failing (looping), service terminated" is not from either the IMAP or POP servers. Instead, it comes from inetd, the daemon which listens for TCP connections to a number of servers, including the IMAP and POP servers.

inetd has a limit of 40 new server sessions per minute for any particular service. If more than 40 sessions are initiated in a minute, inetd will issue the "failing (looping), service terminated" message and shut down the service for 10 minutes. inetd does this to prevent system resource consumption by a client which is spawning infinite numbers of servers. It should be noted that this is a denial of service; however for some systems the alternative is a crash which would be a worse denial of service!

For larger server systems, the limit of 40 is much too low. The limit was established many years ago when a system typically only ran a few dozen servers.

On some versions of inetd, such as the one distributed with most versions of Linux, you can modify the /etc/inetd.conf file to have a larger number of servers by appending a period followed by a number after the nowait word for the server entry. For example, if your existing /etc/inetd.conf line reads:

 imap    stream  tcp     nowait  root    /usr/etc/imapd imapd
try changing it to be:
 imap    stream  tcp     nowait.100  root    /usr/etc/imapd imapd
Another example (using TCP wrappers):
 imap    stream  tcp     nowait  root    /usr/sbin/tcpd  imapd
try changing it to be:
 imap    stream  tcp     nowait.100  root    /usr/sbin/tcpd  imapd
to increase the limit to 100 sessions/minute.

Before making this change, please read the information in "man inetd" to determine whether or not your inetd has this feature. If it does not, and you make this change, the likely outcome is that you will disable IMAP service entirely.

Another way to fix this problem is to edit the inetd.c source code (provided by your UNIX system vendor) to set higher limits, rebuild inetd, install the new binary, and reboot your system. This should only be done by a UNIX system expert. In the inetd.c source code, the limits TOOMANY (normally 40) is the maximum number of new server sessions permitted per minute, and RETRYTIME (normally 600) is the number of seconds inetd will shut down the server after it exceeds TOOMANY.

Back to top
7.17 What does the syslog message: Mailbox lock file /tmp/.600.1df3 open failure: Permission denied mean?
This usually means that some "helpful" security script person has protected /tmp so that it is no longer world-writeable. /tmp must be world-writeable because lots of applications use it for scratch space. To fix this, do
 chmod 1777 /tmp
as root.

If that isn't the answer, check the protection of the named file. If it is something other than 666, then either someone is hacking or some "helpful" person modified the code to have a different default lock file protection.

Back to top
7.18 What do the syslog messages:
 Command stream end of file, while reading line user=... host=...
 Command stream end of file, while reading char user=... host=...
 Command stream end of file, while writing text user=... host=...
mean?
This message occurs when the session is disconnected without a proper LOGOUT (IMAP) or QUIT (POP) command being received by the server first.

In many cases, this is perfectly normal; many client implementations are impolite and do this. Some programmers think this sort of rudeness is "more efficient".

The condition could, however, indicate a client or network connectivity problem. The server has no way of knowing whether there's a problem or just a rude client, so it issues this message instead of a Logout.

Certain inferior losing clients disconnect abruptly after a failed login, and instead of saying that the login failed, just say that they can't access the mailbox. They then complain to the system manager, who looks in the syslog and finds this message. Not very helpful, eh? See the answer to the Why can't I log in to the server? The user name and password are right! question.

If the user isn't reporting a problem, you can probably ignore this message.

Back to top
7.19 Why did my POP or IMAP session suddenly disconnect? The syslog has the message: Killed (lost mailbox lock) user=... host=...
This message only happens when either the traditional UNIX mailbox format or MMDF format is in use. This format only allows one session to have the mailbox open read/write at a time.

The servers assume that if a second session attempts to open the mailbox, that means that the first session is probably owned by an abandoned client. The common scenario here is a user who leaves his client running at the office, and then tries to read his mail from home. Through an internal mechanism called kiss of death, the second session requests the first session to kill itself. When the first session receives the "kiss of death", it issues the "Killed (lost mailbox lock)" syslog message and terminates. The second session then seizes read/write access, and becomes the new "first" session.

Certain poorly-designed clients routinely open multiple sessions to the same mailbox; the users of those clients tend to get this message a lot.

Another cause of this message is a background "check for new mail" task which does its work by opening a POP session to server every few seconds. They do this because POP doesn't have a way to announce new mail.

The solution to both situations is to replace the client with a good online IMAP client such as Pine. Life is too short to waste on POP clients and poorly-designed IMAP clients.

Back to top
7.20 Why does my IMAP client show all the files on the system, recursively from the UNIX root directory?
7.21 Why does my IMAP client show all of my files, recursively from my UNIX home directory?
A well-written client should only show one level of hierarchy and then stop, awaiting explicit user action before going lower. However, some poorly-designed clients will recursively list all files, which may be a very long list (especially if you have symbolic links to directories that create a loop in the filesystem graph!).

This behavior has also been observed in some third-party c-client drivers, including maildir drivers. Consequently, this problem has even been observed in Pine. It is important to understand that this is not a problem in Pine or c-client; it is a problem in the third-party driver. A Pine built without that third-party driver will not have this problem.

See also the answer to Why does my IMAP client show all my files in my home directory?

Back to top
7.22 Why does my IMAP client show that I have mailboxes named "#mhinbox", "#mh", "#shared", "#ftp", "#news", and "#public"?
These are IMAP namespace names. They represent other hierarchies in which messages may exist. These hierarchies may not necessarily exist on a server, but the namespace name is still in the namespace list in order to mark it as reserved.

A few poorly-designed clients display all namespace names as if they were top-level mailboxes in a user's list of mailboxes, whether or not they actually exist. This is a flaw in those clients.

Back to top
7.23 Why does my IMAP client show all my files in my home directory?
As distributed, the IMAP server is connected to your home directory by default. It has no way of knowing what you might call "mail" as opposed to "some other file"; in fact, you can use IMAP to access any file.

Most clients have an option to configure your connected directory on the IMAP server. For example, in Pine you can specify this as the "Path" in your folder-collection, e.g.

 Nickname  : Secondary Folders
 Server    : imap.blurdybloop.com
 Path      : mail/
 View      : 
In this example, the user is connected to the "mail" subdirectory of his home directory.

Other servers call this the "folder prefix" or similar term.

It is possible to modify the IMAP server so that all users are automatically connected to some other directory, e.g. a subdirectory of the user's home directory. Read the file CONFIG for more details.

Back to top
7.24 Why is there a long delay before I get connected to the IMAP or POP server, no matter what client I use?
There are two common occurances of this problem:
  • You are running a system (e.g. certain versions of Linux) which by default attempts to connect to an "IDENT" protocol (port 113) server on your client. However, a firewall or NAT box is blocking connections to that port, so the connection attempt times out.

    The IDENT protocol is a well-known bad idea that does not deliver any real security but causes incredible problems. The idea is that this will give the server a record of the user name, or at least what some program listening on port 113 says is the user name. So, if somebody coming from port nnnnn on a system does something bad, IDENT may give you the userid of the bad guy.

    The problem is, IDENT is only meaningful on a timesharing system which has an administrator who is privileged and users who are not. It is of no value on a personal system which has no separate concept of "system administrator" vs. "unprivileged user".

    On either type of system, security-minded people either turn IDENT off or replace it with an IDENT server that lies. Among other things, IDENT gives spammers the ability to harvest email addresses from anyone who connects to a web page.

    This problem has been showing up quite frequently on systems which use xinetd instead of inetd. Look for files named /etc/xinetd.conf, /etc/xinetd.d/imapd, /etc/inetd.d/ipop2d, and /etc/xinetd.d/ipop3d. In those files, look for lines containing "USERID", e.g.

     log_on_success	+= USERID
    
    Hunt down such lines, and delete them ruthlessly from all files in which they occur. Don't be shy about it.

  • The DNS is taking a long time to do a reverse DNS (PTR record) lookup of the IP address of your client. This is a problem in your DNS, which either you or you ISP need to resolve. Ideally, the DNS should return the client's name; but if it can't it should at least return an error quickly.

As you may have noticed, neither of these are actual problems in the IMAP or POP servers; they are configuration issues with either your system or your network infrastructure. If this is all new to you, run (don't walk) to the nearest technical bookstore and get yourself a good pedagogical text on system administration for the type of system you are running.

Back to top
7.25 Why is there a long delay in Pine or any other c-client based application call before I get connected to the IMAP server? The hang seems to be in the c-client mail_open() call. I don't have this problem with any other IMAP client. There is no delay connecting to a POP3 or NNTP server with mail_open().
By default, the c-client library attempts to make a connection through rsh (and ssh, if you enable that). If the command:
 rsh imapserver exec /etc/rimapd
(or ssh if that is enabled) returns with a "* PREAUTH" response, it will use the resulting rsh session as the IMAP session and not require an authentication step on the server.

Unfortunately, rsh has a design error that treats "TCP connection refused" as "temporary failure, try again"; it expects the "rsh not allowed" case to be implemented as a successful connection followed by an error message and close the connection.

It must be emphasized that this is a bug in rsh. It is not a bug in the IMAP toolkit.

The use of rsh can be disabled in any the following ways:

  • You can disable it for this particular session by either:
    • setting an explicit port number in the mailbox name, e.g.
       {imapserver.foo.com:143}INBOX
      
    • using SSL (the /ssl switch)

  • You can disable rsh globally by setting the rsh timeout value to 0 with the call:
     mail_parameters (NIL,SET_RSHTIMEOUT,0);
    
Back to top
7.26 Why does a message sometimes get split into two or more messages on my SUN system?
This is caused by an interaction of two independent design problems in SUN mail software. The first problem is that the "forward message" option in SUN's mail tool program includes the internal "From " header line in the text that it forwarded. This internal header line is specific to traditional UNIX mailbox files and is not suitable for use in forwarded messages.

The second problem is that the mail delivery agent assumes that mail reading programs will not use the traditional UNIX mailbox format but instead an incompatible variant that depends upon a Content-Length: message header. Content-Length is widely recognized to have been a terrible mistake, and is no longer recommended for use in mail (it is used in other facilities that use MIME).

One symptom of the problem is that under certain circumstances, a message may get broken up into several messages. I'm also aware of security bugs caused by programs that foolishly trust "Content-Length:" headers with evil values.

To fix the mailer on your system, edit your sendmail.cf to change the Mlocal line to have the -E flag. A typical entry will lool like:

 Mlocal,	P=/usr/lib/mail.local, F=flsSDFMmnPE, S=10, R=20,
		A=mail.local -d $u
This fix will also work around the problem with mail tool, because it will insert a ">" before the internal header line to prevent it from being interpreted by mail reading software as an internal header line.
Back to top
7.27 Why did my POP or IMAP session suddenly disconnect? The syslog has the message:
 Autologout user=<...my user name...> host=<...my client system...>
This is a problem in your client.

In the case of IMAP, it failed to communicate with the IMAP server for over 30 minutes; in the case of POP, it failed to communicate with the POP server for over 10 minutes.

Back to top
7.28 What does the UNIX error message: TLS/SSL failure: myserver: SSL negotiation failed mean?
7.29 What does the PC error message: TLS/SSL failure: myserver: Unexpected TCP input disconnect mean?
This usually means that an attempt to negotiate TLS encryption via the STARTTLS command failed, because the server advertises STARTTLS functionality, but doesn't actually have it (e.g. because no certificates are installed).

Use the /notls option in the mailbox name to disable TLS negotiation.

Back to top
7.30 What does the error message: TLS/SSL failure: myserver: Server name does not match certificate mean?
An SSL or TLS session encryption failed because the server name in the server's certificate does not match the name that you gave it. This could indicate that the server is not really the system you think that it is, but can be also be called if you gave a nickname for the server or name that was not fully-qualified. You must use the fully-qualified domain name for the server in order to validate its certificate

Use the /novalidate-cert option in the mailbox name to disable validation of the certificate.

Back to top
7.31 What does the UNIX error message: TLS/SSL failure: myserver: self-signed certificate mean?
7.32 What does the PC error message: TLS/SSL failure: myserver: Self-signed certificate or untrusted authority mean?
An SSL or TLS session encryption failed because your server's certificate is "self-signed"; that is, it is not signed by any Certificate Authority (CA) and thus can not be validated. A CA-signed certificate costs money, and some smaller sites either don't want to pay for it or haven't gotten one yet. The bad part about this is that this means there is no guarantee that the server is really the system you think that it is.

Use the /novalidate-cert option in the mailbox name to disable validation of the certificate.

Back to top
7.33 What does the UNIX error message: TLS/SSL failure: myserver: unable to get local issuer certificate mean?
An SSL or TLS session encryption failed because your system does not have the Certificate Authority (CA) certificates installed on OpenSSL's certificates directory. On most systems, this directory is /usr/local/ssl/certs). As a result, it is not possible to validate the server's certificate.

If CA certificates are properly installed, you should see factory.pem and about a dozen other .pem names such as thawteCb.pem.

As a workaround, you can use the /novalidate-cert option in the mailbox name to disable validation of the certificate; however, note that you are then vulnerable to various security attacks by bad guys.

The correct fix is to copy all the files from the certs/ directory in the OpenSSL distribution to the /usr/local/ssl/certs (or whatever) directory. Note that you need to do this after building OpenSSL, because the OpenSSL build creates a number of needed symbolic links. For some bizarre reason, the OpenSSL "make install" doesn't do this for you, so you must do it manually.

Back to top
7.34 Why does reading certain messages hang when using Netscape? It works fine with Pine!
There are two possible causes.

Check the mail syslog. If you see the message "Killed (lost mailbox lock)" for the impacted user(s), read the FAQ entry regarding that message.

Check the affected mailbox to see if there are embedded NUL characters in the message. NULs in message texts are a technical violation of both the message format and IMAP specifications. Most clients don't care, but apparently Netscape does.

You can work around this by rebuilding imapd with the NETSCAPE_BRAIN_DAMAGE option set (see src/imapd/Makefile); this will cause imapd to convert all NULs to 0x80 characters. A better solution is to enable the feature in your MTA to MIME-convert messages with binary content. See the documentation for your MTA for how to do this.

Back to top
7.35 Why does Netscape say that there's a problem with the IMAP server and that I should "Contact your mail server administrator."?
Certain versions of Netscape do this when you click the Manage Mail button, which uses an undocumented feature of Netscape's proprietary IMAP server.

You can work around this by rebuilding imapd with the NETSCAPE_BRAIN_DAMAGE option set (see src/imapd/Makefile) to a URL that points either to an alternative IMAP client (e.g. Pine) or perhaps to a homebrew mail account management page.

Back to top
7.36 Why is one user creating huge numbers of IMAP or POP server sessions?
The user is probably using Outlook Express, Eudora, or a similar program. See the answer to the Help! My load average is soaring and I see hundreds of POP and IMAP servers, many logged in as the same user! question.
Back to top
7.37 Why don't I get any new mail notifications from Outlook Express or Outlook after a while?
This is a known bug in Outlook Express. Microsoft is aware of the problem and its cause. They have informed us that they do not have any plans to fix it at the present time.

The problem is also reported in Outlook 2000, but not verified.

Outlook Express uses the IMAP IDLE command to avoid having to "ping" the server every few minutes for new mail. Unfortunately, Outlook Express overlooks the part in the IDLE specification which requires that a client terminate and restart the IDLE before the IMAP 30 minute inactivity autologout timer triggers.

When this happens, Outlook Express displays "Not connected" at the bottom of the window. Since it's no longer connected to the IMAP server, it isn't going to notice any new mail.

As soon as the user does anything that would cause an IMAP operation, Outlook Express will reconnect and new mail will flow again. If the user does something that causes an IMAP operation at least every 29 minutes, the problem won't happen.

Modern versions of imapd attempt to work around the problem by automatically reporting fake new mail after 29 minutes. This causes Outlook Express to exit the IDLE state; as soon as this happens imapd revokes the fake new mail. As long as this behavior isn't known to cause problems with other clients, this workaround will remain in imapd.

Back to top
7.38 Why don't I get any new mail notifications from Entourage?
This is a known bug in Entourage.

You built an older version of imapd with the MICROSOFT_BRAIN_DAMAGE option set, in order to disable support for the IDLE command. However, Entourage won't get new mail unless IDLE command support exists.

Note: the MICROSOFT_BRAIN_DAMAGE option no longer exists in modern versions, as the Outlook Express problem which it attempted to solve has been worked around in another way.

Back to top
7.39 Why doesn't Entourage work at all?
It's hard to know. Entourage breaks almost every rule in the book for IMAP. It is highly instructive to do a packet trace on Entourage, as an example of how not to use IMAP. It does things like STATUS (MESSAGES) on the currently selected mailbox and re-fetching the same static data over and over again.

It seems that every time we understand what it is doing wrong in Entourage and come up with a workaround, we learn about something else that's broken.

Try building imapd with the ENTOURAGE_BRAIN_DAMAGE option set, in order to disable the diagnostic that occurs when doing STATUS on the currently selected mailbox.

Back to top
7.40 Why doesn't Netscape Notify (NSNOTIFY.EXE) work at all?
This is a bug in NSNOTIFY; it doesn't handle unsolicited data from the server correctly.

Fortunately, there is no reason to use this program with IMAP; NSNOTIFY is a polling program to let you know when new mail has appeared in your maildrop. This is necessary with POP; but since IMAP dynamically announces new mail in the session you're better off (and will actually cause less load on the server!) keeping your mail reading program's IMAP session open and let IMAP do the notifying for you.

Consequently, the recommended fix for the NSNOTIFY problem is to delete the NSNOTIFY binary.

Back to top
7.41 Why can't I connect via SSL to Eudora? It says the connection has been broken, and in the server syslogs I see "Command stream end of file".
There is a report that you can fix the problem by going into Eudora's advanced network configuration menu and increasing the network buffer size to 8192.
Back to top
7.42 Sheesh. Aren't there any good IMAP clients out there?
Yes!

Pine is a wonderful client. It's fast, it uses IMAP well, and it generates text mail (life is too short to waste on HTML mail). Also, there are some really wonderful things in progress in the Pine world.

There are some good GUI clients out there, mostly from smaller vendors. Without naming names, look for the vendors who are active in the IMAP protocol development community, and their products.

Netscape, Eudora, and Outlook can be configured with enough effort to be good citizens and work well for users, but they can also be badly misconfigured, and often the misconfiguration is the default.

Back to top
7.43 But wait! PC Pine (or other PC program build with c-client) crashes with the message incomplete SecBuffer exceeds maximum buffer size when I use SSL connections. This is a bug in c-client, right?
It's a bug in the Microsoft SChannel.DLL, which implements SSL. Microsoft admits it (albeit with an unstatement: "it's not fully RFC compliant"). The problem is that SChannel indicates that the maximum SSL packet data size is 5 bytes smaller than the actual maximum. Thus, any IMAP server which transmits a maximum sized SSL packet will not work with PC Pine or any other program which uses SChannel.

It can take a while for the problem to show up. The client has to do something that causes at least 16K of contiguous data. Many clients do partial fetching, which tends to reduce the number of cases where this can happen. However, all software which uses SChannel to support SSL is affected by this bug.

This problem does not affect UNIX code, since OpenSSL is used on UNIX.

This problem most recently showed up with the CommunigatePro IMAP server. They have an update which trims down their maximum contiguous data to less than 16K, in order to work around the problem.

This problem has also shown up with the Exchange IMAP server with UNIX clients (including Pine built with an older version of c-client) which sends full-sized 16K SSL packets. Modern c-client works around the problem by trimming down its maximum outgoing SSL packet size to 8K.

Microsoft has developed a hotfix for this bug. Look up MSKB article number 300562. Contrary to the article text which implies that this is a Pine issue, this bug also affect Microsoft Exchange server with *any* UNIX based client that transmits full-sized SSL payloads.

Back to top
7.44 My qpopper users keep on getting the DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or IMAP. How can I fix this?
This is an incompatibility between qpopper and the c-client library used by Pine, imapd, and ipop[23]d.

Assuming that you want to continue using qpopper, look into qpopper's --enable-uw-kludge-flag configuration flag, which is documented as "check for and hide UW 'Folder Internal Data' messages".

The other alternative is to switch from qpopper to ipop3d.

Back to top
7.45 Help! I installed the servers but I can't connect to them from my client!
Review the installation instructions carefully. Make sure that you have not skipped any of the steps. Make sure that you have made the correct entries in the configuration files; pay careful attention to the exact spelling of the service names and the path names. Make sure as well that you have properly restarted inetd.

If you have a system with Yellow Pages/NIS such as Solaris, have you updated the service names there as well as in /etc/services?

If you have a system with TCP wrappers, have you properly updated the TCP wrapper files (e.g. /etc/hosts.allow and /etc/hosts.deny) for the servers?

If you have a system which uses xinetd instead of inetd, have you made sure that you have made the correct corresponding xinetd changes for those services?

Try telneting to the server port (143 for IMAP, 110 for POP3). If you get a "refused" error, that probably means that you don't have the service set up in inetd.conf. If the connection opens and then closes with no message, the service is set up, but either the path name of the server binary in inetd.conf is wrong or your TCP wrappers are configured to deny access.

If you don't know how to make the corresponding changes to these files, seek the help of a local expert for your system.

Back to top
7.46 Why do I get the message Can not authenticate to SMTP server: 421 SMTP connection went away! and why did this happen? There was also something about SECURITY PROBLEM: insecure server advertised AUTH=PLAIN
Some versions of qmail, including that running on mail.smtp.yahoo.com, disconnect the SMTP session if you fail to authenticate prior to attempting to transmit mail. An attempt to authenticate was made, but it failed because the server had already disconnected.

To work around this, you need to specify /user=... in the host name specification.

The SECURITY PROBLEM came about because the server advertised the AUTH=PLAIN SASL authentication mechanism outside of a TLS-encrypted session, in violation of RFC 2595. This message is just a warning, and in fact occurred after the server had disconnected.

Back to top
7.47 Why do I get the message SMTP Authentication cancelled and why did this happen? There was also something about SECURITY PROBLEM: insecure server advertised AUTH=PLAIN
This is a bug in the SMTP server.

Some versions of qmail, including that running on mail.smtp.yahoo.com, have a bug in their implementation of SASL in their SMTP server, which renders it non-compliant with the standard.

If the client does not provide an initial response in the command line for an authentication mechanism whose profile does not have an initial challenge, qmail issues a bogus response:

 334 ok, go on
The problem is the "ok, go on". This violates RFC 2554's requirement that the text part in a 334 response be a BASE64 encoded string; in other words, it is a protocol syntax error.

In the case of AUTH=PLAIN, RFC 2222 (pp 4-5) requires that the encoded string have no data. In other words, the appropropiate standards-compliant server response is "334" followed by a SPACE and a CRLF.

The SECURITY PROBLEM came about because the server advertised the AUTH=PLAIN SASL authentication mechanism outside of a TLS-encrypted session, in violation of RFC 2595. This message is just a warning, and is not related the "Authentication cancelled" problem.

Back to top
7.48 Why do I get the message Invalid base64 string when I try to authenticate to a Cyrus server?
This slightly misleading message is the way that a Cyrus server indicates that an authentication exchange was cancelled. It is not indicative of a bug or protocol violation.

The most common reason that this happens is if the Cyrus server offers Kerberos authentication, c-client is built with Kerberos support, but your client system is not within the Kerberos realm. In this case, the client code will try to authenticate via Kerberos, fail to get the Kerberos credentials, cancel the authentication attempt, and try the next available authentication technology.

Back to top



8. Licensing Questions


8.1 Why does the UW choose to make the IMAP toolkit available under this Free-Fork license?
Unlike many other open source licenses, the Free-Fork license is non-viral.

The Free-Fork license allows developers a broad range of rights to the IMAP toolkit software, and more options to combine the IMAP toolkit software with other open source or private-use software, without requiring that the terms of the Free-Fork license be enforced on that other software.

The Free-Fork license also allows the IMAP toolkit software to be licensed under other conditions (for example, distribution with proprietary software where the IMAP code remains under the Free-Fork) if a need arises.

Back to top
8.2 I want to redistribute a modified version of the IMAP toolkit. How can I do this?
The Free-Fork license permits redistributions as long as the IMAP toolkit and any modifications to the toolkit remains under the Free-Fork license as open source.

Send a message to imap-license@cac.washington.edu if your application will be proprietary and you need alternative licensing arrangements , or if you are unsure whether this blanket permission applies in your case.

Back to top
8.3 Can I redistribute an unmodified version of the IMAP toolkit in a proprietary application?
Yes. The terms of the Free-Fork license apply to all unmodified distributions.
Back to top
8.4 Is the Free-Fork license compatible with GPL?
Yes. A Free-Fork licensee has all the rights of a GPL licensee, plus additional rights provided under the Free-Fork.

The Free-Fork does not require that software not specifically licensed under the Free-Fork use the same terms as the Free-Fork. For example, if you incorporate Free-Fork software in an application that you write, and put the application under GPL, that does not cause the Free-Fork software to become GPL. Someone else can copy your application, strip out the GPL code so that only the Free-Fork code remains, and by doing so have Free-Fork rights.

Additionally, a Free-Fork licensee has the right to negotiate other license terms with the UW.

Back to top



9. Where to Go For Additional Information


9.1 Where can I go to ask questions?
9.2 I have some ideas for enhancements to IMAP. Where should I go?
If you have questions about the IMAP protocol, or want to participate in discussions of future directions of the IMAP protocol, the appropriate mailing list is imap@u.washington.edu. You must be a subscriber to post to the list; to subscribe, use imap-request@u.washington.edu

If you have questions about this software, you can send me email directly or use the c-client@u.washington.edu mailing list. You can subscribe to this list via c-client-request@u.washington.edu

As an alternative to either of these, you can use the comp.mail.imap newsgroup.

Back to top
9.3 Where can I read more about IMAP and other email protocols?
We recommend Internet Email Protocols: A Developer's Guide, by Kevin Johnson, published by Addison Wesley, ISBN 0-201-43288-9.
Back to top
9.4 Where can I find out more about setting up and administering an IMAP server?
We recommend Managing IMAP, by Dianna Mullet & Kevin Mullet, published by O'Reilly, ISBN 0-596-00012-X.

This book also has an excellent comparison of the UW and Cyrus IMAP servers.

Back to top

Last Updated: 15 June 2004

tkrat_2.2cvs20100105-dfsg.orig/imap/docs/FAQ.txt000066400000000000000000004462301137544547100210430ustar00rootroot00000000000000 IMAP Toolkit Frequently Asked Questions Table of Contents * 1. General/Software Feature Questions + 1.1 Can I set up a POP or IMAP server on UNIX/Linux/OSF/etc.? + 1.2 I am currently using qpopper as my POP3 server on UNIX. Do I need to replace it with ipop3d in order to run imapd? + 1.3 Can I set up a POP or IMAP server on Windows XP, 2000, NT, Me, 98, or 95? + 1.4 Can I set up a POP or IMAP server on Windows 3.1 or DOS? + 1.5 Can I set up a POP or IMAP server on Macintosh? + 1.6 Can I set up a POP or IMAP server on VAX/VMS? + 1.7 Can I set up a POP or IMAP server on TOPS-20? + 1.8 Are hierarchical mailboxes supported? + 1.9 Are "dual-use" mailboxes supported? + 1.10 Can I have a mailbox that has both messages and sub-mailboxes? + 1.11 What is the difference between "mailbox" and "folder"? + 1.12 What is the status of internationalization? + 1.13 Can I use SSL? + 1.14 Can I use TLS and the STARTTLS facility? + 1.15 Can I use CRAM-MD5 authentication? + 1.16 Can I use APOP authentication? + 1.17 Can I use Kerberos V5? + 1.18 Can I use PAM for plaintext passwords? + 1.19 Can I use Kerberos 5 for plaintext passwords? + 1.20 Can I use AFS for plaintext passwords? + 1.21 Can I use DCE for plaintext passwords? + 1.22 Can I use the CRAM-MD5 database for plaintext passwords? + 1.23 Can I disable plaintext passwords? + 1.24 Can I disable plaintext passwords on unencrypted sessions, but allow them on encrypted sessions? + 1.25 Can I use virtual hosts? + 1.26 Can I use RPOP authentication? + 1.27 Can I use Kerberos V4? + 1.28 Is there support for S/Key or OTP? + 1.29 Is there support for NTLM or SPA? + 1.30 Is there support for mh? + 1.31 Is there support for qmail and the maildir format? + 1.32 Is there support for the Cyrus mailbox format? + 1.33 Is this software Y2K compliant? * 2. What Do I Need to Build This Software? + 2.1 What do I need to build this software with SSL on UNIX? + 2.2 What do I need to build this software with Kerberos V on UNIX? + 2.3 What do I need to use a C++ compiler with this software to build my own application? + 2.4 What do I need to build this software on Windows? + 2.5 What do I need to build this software on DOS? + 2.6 Can't I use Borland C to build this software on the PC? + 2.7 What do I need to build this software on the Mac? + 2.8 What do I need to build this software on VMS? + 2.9 What do I need to build this software on TOPS-20? + 2.10 What do I need to build this software on Amiga or OS/2? + 2.11 What do I need to build this software on Windows CE? * 3. Build and Configuration Questions + 3.1 How do I configure the IMAP and POP servers on UNIX? + 3.2 I built and installed the servers according to the BUILD instructions. It can't be that easy. Don't I need to write a config file? + 3.3 How do I make the IMAP and POP servers look for INBOX at some place other than the mail spool directory? + 3.4 How do I make the IMAP server look for secondary folders at some place other than the user's home directory? + 3.5 How do I configure SSL? + 3.6 How do I configure TLS and the STARTTLS facility? + 3.7 How do I build/install OpenSSL and obtain/create certificates for use with SSL? + 3.8 How do I configure CRAM-MD5 authentication? + 3.9 How do I configure APOP authentication? + 3.10 How do I configure Kerberos V5? + 3.11 How do I configure PAM for plaintext passwords? + 3.12 It looks like all I have to do to make the server use Kerberos is to build with PAM on my Linux system, and set it up in PAM for Kerberos passwords. Right? + 3.13 How do I configure Kerberos 5 for plaintext passwords? + 3.14 How do I configure AFS for plaintext passwords? + 3.15 How do I configure DCE for plaintext passwords? + 3.16 How do I configure the CRAM-MD5 database for plaintext passwords? + 3.17 How do I disable plaintext passwords? + 3.18 How do I disable plaintext passwords on unencrypted sessions, but allow them in SSL or TLS sessions? + 3.19 How do I configure virtual hosts? + 3.20 Why do I get compiler warning messages such as: o passing arg 3 of `scandir' from incompatible pointer type o Pointers are not assignment-compatible. o Argument #4 is not the correct type. during the build? + 3.21 Why do I get compiler warning messages such as o Operation between types "void(*)(int)" and "void*" is not allowed. o Function argument assignment between types "void*" and "void(*)(int)" is not allowed. o Pointers are not assignment-compatible. o Argument #5 is not the correct type. during the build? + 3.22 Why do I get linker warning messages such as: o mtest.c:515: the `gets' function is dangerous and should not be used. during the build? Isn't this a security bug? + 3.23 Why do I get linker warning messages such as: o auth_ssl.c:92: the `tmpnam' function is dangerous and should not be used. during the build? Isn't this a security bug? + 3.24 OK, suppose I see a warning message about a function being "dangerous and should not be used" for something other than this gets() or tmpnam() call? * 4. Operational Questions + 4.1 How can I enable anonymous IMAP logins? + 4.2 How do I set up an alert message that each IMAP user will see? + 4.3 How does the c-client library choose which of its several mechanisms to use to establish an IMAP connection to the server? I noticed that it can connect on port 143, port 993, via rsh, and via ssh. + 4.4 I am using a TLS-capable IMAP server, so I don't need to use /ssl to get encryption. However, I want to be certain that my session is TLS encrypted before I send my password. How to I do this? + 4.5 How do I use one of the alternative formats described in the formats.txt document? In particular, I hear that mbx format will give me better performance and allow shared access. + 4.6 How do I set up shared mailboxes? + 4.7 How can I make the server syslogs go to someplace other than the mail syslog? * 5. Security Questions + 5.1 I see that the IMAP server allows access to arbitary files on the system, including /etc/passwd! How do I disable this? + 5.2 I've heard that IMAP servers are insecure. Is this true? + 5.3 How do I know that I have the most secure version of the server? + 5.4 I see all these strcpy() and sprintf() calls, those are unsafe, aren't they? + 5.5 Those /tmp lock files are protected 666, is that really right? * 6. Why Did You Do This Strange Thing? Questions + 6.1 Why don't you use GNU autoconfig / automake / autoblurdybloop? + 6.2 Why do you insist upon a build with -g? Doesn't it waste disk and memory space? + 6.3 Why don't you make c-client a shared library? + 6.4 Why don't you use iconv() for internationalization support? + 6.5 Why is the IMAP server connected to the home directory by default? + 6.6 I have a Windows system. Why isn't the server plug and play for me? + 6.7 I looked at the UNIX SSL code and saw that you have the SSL data payload size set to 8192 bytes. SSL allows 16K; why aren't you using the full size? + 6.8 Why is an mh format INBOX called #mhinbox instead of just INBOX? + 6.9 Why don't you support the maildir format? + 6.10 Why don't you support the Cyrus format? + 6.11 Why is it creating extra forks on my SVR4 system? + 6.12 Why are you so fussy about the date/time format in the internal "From " line in traditional UNIX mailbox files? My other mail program just considers every line that starts with "From " to be the start of the message. + 6.13 Why is traditional UNIX format the default format? + 6.14 Why do you write this "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA" message at the start of traditional UNIX and MMDF format mailboxes? + 6.15 Why don't you stash the mailbox metadata in the first real message of the mailbox instead of writing this fake FOLDER INTERNAL DATA message? + 6.16 Why aren't "dual-use" mailboxes the default? + 6.17 Why do you use ucbcc to build on Solaris? + 6.18 Why should I care about some old system with BSD libraries? cc is the right thing on my Solaris system! + 6.19 Why do you insist upon writing .lock files in the spool directory? + 6.20 Why should I care about compatibility with the past? * 7. Problems and Annoyances + 7.1 Help! My INBOX is empty! What happened to my messages? + 7.2 Help! All my messages in a non-INBOX mailbox have been concatenated into one message which claims to be from me and has a subject of the file name of the mailbox! What's going on? + 7.3 Why do I get the message: o CREATE failed: Can't create mailbox node xxxxxxxxx: File exists and how do I fix it? + 7.4 Why can't I log in to the server? The user name and password are right! + 7.5 Help! My load average is soaring and I see hundreds of POP and IMAP servers, many logged in as the same user! + 7.6 Why does mail disappear even though I set "keep mail on server"? + 7.7 Why do I get the message o Moved ##### bytes of new mail to /home/user/mbox from /var/spool/mail/user and why did this happen? + 7.8 Why isn't it showing the local host name as a fully-qualified domain name? + 7.9 Why is the local host name in the From/Sender/Message-ID headers of outgoing mail not coming out as a fully-qualified domain name? + 7.10 What does the message: o Mailbox vulnerable - directory /var/spool/mail must have 1777 protection mean? How can I fix this? + 7.11 What does the message: o Mailbox is open by another process, access is readonly mean? How do I fix this? + 7.12 What does the message: o Can't get write access to mailbox, access is readonly mean? + 7.13 I set my POP3 client to "delete messages from server" but they never get deleted. What is wrong? + 7.14 What do messages such as: o Message ... UID ... already has UID ... o Message ... UID ... less than ... o Message ... UID ... greater than last ... o Invalid UID ... in message ..., rebuilding UIDs mean? + 7.15 What do the error messages: o Unable to read internal header at ... o Unable to find CRLF at ... o Unable to parse internal header at ... o Unable to parse message date at ... o Unable to parse message flags at ... o Unable to parse message UID at ... o Unable to parse message size at ... o Last message (at ... ) runs past end of file ... mean? I am using mbx format. + 7.16 What do the syslog messages: o imap/tcp server failing (looping) o pop3/tcp server failing (looping) mean? When it happens, the listed service shuts down. How can I fix this? + 7.17 What does the syslog message: o Mailbox lock file /tmp/.600.1df3 open failure: Permission denied mean? + 7.18 What do the syslog messages: o Command stream end of file, while reading line user=... host=... o Command stream end of file, while reading char user=... host=... o Command stream end of file, while writing text user=... host=... mean? + 7.19 Why did my POP or IMAP session suddenly disconnect? The syslog has the message: o Killed (lost mailbox lock) user=... host=... + 7.20 Why does my IMAP client show all the files on the system, recursively from the UNIX root directory? + 7.21 Why does my IMAP client show all of my files, recursively from my UNIX home directory? + 7.22 Why does my IMAP client show that I have mailboxes named "#mhinbox", "#mh", "#shared", "#ftp", "#news", and "#public"? + 7.23 Why does my IMAP client show all my files in my home directory? + 7.24 Why is there a long delay before I get connected to the IMAP or POP server, no matter what client I use? + 7.25 Why is there a long delay in Pine or any other c-client based application call before I get connected to the IMAP server? The hang seems to be in the c-client mail_open() call. I don't have this problem with any other IMAP client. There is no delay connecting to a POP3 or NNTP server with mail_open(). + 7.26 Why does a message sometimes get split into two or more messages on my SUN system? + 7.27 Why did my POP or IMAP session suddenly disconnect? The syslog has the message: o Autologout user=<...my user name...> host=<...my imap server...> + 7.28 What does the UNIX error message: o TLS/SSL failure: myserver: SSL negotiation failed mean? + 7.29 What does the PC error message: o TLS/SSL failure: myserver: Unexpected TCP input disconnect mean? + 7.30 What does the error message: o TLS/SSL failure: myserver: Server name does not match certificate mean? + 7.31 What does the UNIX error message: o TLS/SSL failure: myserver: self-signed certificate mean? + 7.32 What does the PC error message o TLS/SSL failure: myserver: Self-signed certificate or untrusted authority mean? + 7.33 What does the UNIX error message: o TLS/SSL failure: myserver: unable to get local issuer certificate mean? + 7.34 Why does reading certain messages hang when using Netscape? It works fine with Pine! + 7.35 Why does Netscape say that there's a problem with the IMAP server and that I should "Contact your mail server administrator."? + 7.36 Why is one user creating huge numbers of IMAP or POP server sessions? + 7.37 Why don't I get any new mail notifications from Outlook Express or Outlook after a while? + 7.38 Why don't I get any new mail notifications from Entourage? + 7.39 Why doesn't Entourage work at all? + 7.40 Why doesn't Netscape Notify (NSNOTIFY.EXE) work at all? + 7.41 Why can't I connect via SSL to Eudora? It says the connection has been broken, and in the server syslogs I see "Command stream end of file". + 7.42 Sheesh. Aren't there any good IMAP clients out there? + 7.43 But wait! PC Pine (or other PC program build with c-client) crashes with the message o incomplete SecBuffer exceeds maximum buffer size when I use SSL connections. This is a bug in c-client, right? + 7.44 My qpopper users keep on getting the DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or IMAP. How can I fix this? + 7.45 Help! I installed the servers but I can't connect to them from my client! + 7.46 Why do I get the message o Can not authenticate to SMTP server: 421 SMTP connection went away! and why did this happen? There was also something about o SECURITY PROBLEM: insecure server advertised AUTH=PLAIN + 7.47 Why do I get the message o SMTP Authentication cancelled and why did this happen? There was also something about o SECURITY PROBLEM: insecure server advertised AUTH=PLAIN + 7.48 Why do I get the message o Invalid base64 string when I try to authenticate to a Cyrus server? * 8. Licensing Questions + 8.1 Why does the UW choose to make the IMAP toolkit available under this Free-Fork license? + 8.2 I want to redistribute a modified version of the IMAP toolkit. How can I do this? + 8.3 Can I redistribute an unmodified version of the IMAP toolkit in a proprietary application? + 8.4 Is the Free-Fork license compatible with GPL? * 9. Where to Go For Additional Information + 9.1 Where can I go to ask questions? + 9.2 I have some ideas for enhancements to IMAP. Where should I go? + 9.3 Where can I read more about IMAP and other email protocols? + 9.4 Where can I find out more about setting up and administering an IMAP server? _________________________________________________________________ 1. General/Software Feature Questions _________________________________________________________________ 1.1 Can I set up a POP or IMAP server on UNIX/Linux/OSF/etc.? Yes. Refer to the UNIX specific notes in files CONFIG and BUILD. _________________________________________________________________ 1.2 I am currently using qpopper as my POP3 server on UNIX. Do I need to replace it with ipop3d in order to run imapd? Not necessarily. Although ipop3d interoperates with imapd better than qpopper, imapd and qpopper will work together. The few qpopper/imapd interoperability issues mostly affect users who use both IMAP and POP3 clients; those users would probably be better served if their POP3 server is ipop3d. If you are happy with qpopper and just want to add imapd, you should do that, and defer a decision on changing qpopper to ipop3d. That way, you can get comfortable with imapd's performance, without changing anything for your qpopper users. Many sites have subsequently decided to change from qpopper to ipop3d in order to get better POP3/IMAP interoperability. If you need to do this, you'll know. There also seems to be a way to make qpopper work better with imapd; see the answer to the My qpopper users keep on getting the DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or IMAP. How can I fix this? question. _________________________________________________________________ 1.3 Can I set up a POP or IMAP server on Windows XP, 2000, NT, Me, 98, or 95? Yes. Refer to the NT specific notes in files CONFIG and BUILD. Also, you *must* set up CRAM-MD5 authentication, as described in md5.txt. There is no file access control on Windows 9x or Me, so you probably will have to do modifications to env_unix.c to prevent people from hacking others' mail. Note, however, that the server is not plug and play the way it is for UNIX. _________________________________________________________________ 1.4 Can I set up a POP or IMAP server on Windows 3.1 or DOS? 1.5 Can I set up a POP or IMAP server on Macintosh? 1.6 Can I set up a POP or IMAP server on VAX/VMS? Yes, it's just a small matter of programming. _________________________________________________________________ 1.7 Can I set up a POP or IMAP server on TOPS-20? You have a TOPS-20 system? Cool. If IMAP2 (RFC 1176) is good enough for you, you can use MAPSER which is about the ultimate gonzo pure TOPS-20 extended addressing assembly language program. Unfortunately, IMAP2 is barely good enough for Pine these days, and most other IMAP clients won't work with IMAP2 at all. Maybe someone will hack MAPSER to do IMAP4rev1 some day. We don't know if anyone wrote a POP3 server for TOPS-20. There definitely was a POP2 server once upon a time. Or you can port the POP and IMAP server from this IMAP toolkit to it. All that you need for a first stab is to port the MTX driver. That'll probably be just a couple of hours of hacking. _________________________________________________________________ 1.8 Are hierarchical mailboxes supported? 1.9 Are "dual-use" mailboxes supported? 1.10 Can I have a mailbox that has both messages and sub-mailboxes? Yes. However, there is one important caveat. Some mailbox formats, including the default which is the traditional UNIX mailbox format, are stored as a single file containing all the messages. UNIX does not permit a name in the filesystem to be both a file and a directory; consequently you can not have a sub-mailbox within a mailbox that is in one of these formats. This is not a limitation of the software; this is a limitation of UNIX. For example, there are mailbox formats in which the name is a directory and each message is a file within that directory; these formats support sub-mailboxes within such mailboxes. However, for technical reasons, the "flat file" formats are generally preferred since they perform better. Read imap-2004/docs/formats.txt for more information on this topic. It is always permissible to create a directory that is not a mailbox, and have sub-mailboxes under it. The easiest way to create a directory is to create a new mailbox inside a directory that doesn't already exist. For example, if you create "Mail/testbox" on UNIX, the directory "Mail/" will automatically be created and then the mailbox "testbox" will be created as a sub-mailbox of "Mail/". It is also possible to create the name "Mail/" directly. Check the documentation for your client software to see how to do this with that software. Of course, on Windows systems you would use "\" instead of "/". _________________________________________________________________ 1.11 What is the difference between "mailbox" and "folder"? The term "mailbox" is IMAP-speak for what a lot of software calls a "folder" or a "mail folder". However, "folder" is often used in other contexts to refer to a directory, for example, in the graphic user interface on both Windows and Macintosh. A "mailbox" is specifically defined as a named object that contains messages. It is not required to be capable of containing other types of objects including other mailboxes; although some mailbox formats will permit this. In IMAP-speak, a mailbox which can not contain other mailboxes is called a "no-inferiors mailbox". Similarly, a directory which can not contain messages is not a mailbox and is called a "no-select name". _________________________________________________________________ 1.12 What is the status of internationalization? The IMAP toolkit is partially internationalized and multilingualized. Searching is supported in the following charsets: US-ASCII, UTF-8, ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6, ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-10, ISO-8859-11, ISO-8859-13, ISO-8859-14, ISO-8859-15, ISO-8859-16, KOI8-R, KOI8-U (alias KOI8-RU), TIS-620, VISCII, ISO-2022-JP, ISO-2022-KR, ISO-2022-CN, ISO-2022-JP-1, ISO-2022-JP-2, GB2312 (alias CN-GB), CN-GB-12345, BIG5 (alias CN-BIG5), EUC-JP, EUC-KR, Shift_JIS, Shift-JIS, KS_C_5601-1987, KS_C_5601-1992, WINDOWS_874, WINDOWS-1250, WINDOWS-1251, WINDOWS-1252, WINDOWS-1253, WINDOWS-1254, WINDOWS-1255, WINDOWS-1256, WINDOWS-1257, WINDOWS-1258. All ISO-2022-?? charsets are treated identically, and support ASCII, JIS Roman, hankaku katakana, ISO-8859-[1 - 10], TIS, GB 2312, JIS X 0208, JIS X 0212, KSC 5601, and planes 1 and 2 of CNS 11643. EUC-JP includes support for JIS X 0212 and hankaku katakana. c-client library support also exists to convert text in any of the above charsets into Unicode, including headers with MIME encoded-words. There is no support for localization (e.g. non-English error messages) at the present time, but such support is planned. _________________________________________________________________ 1.13 Can I use SSL? Yes. See the answer to the How do I configure SSL? question. _________________________________________________________________ 1.14 Can I use TLS and the STARTTLS facility? Yes. See the answer to the How do I configure TLS and the STARTTLS facility? question. _________________________________________________________________ 1.15 Can I use CRAM-MD5 authentication? Yes. See the answer to the How do I configure CRAM-MD5 authentication? question. _________________________________________________________________ 1.16 Can I use APOP authentication? Yes. See the How do I configure APOP authentication? question. Note that there is no client support for APOP authentication. _________________________________________________________________ 1.17 Can I use Kerberos V5? Yes. See the answer to the How do I configure Kerberos V5? question. _________________________________________________________________ 1.18 Can I use PAM for plaintext passwords? Yes. See the answer to the How do I configure PAM for plaintext passwords? question. _________________________________________________________________ 1.19 Can I use Kerberos 5 for plaintext passwords? Yes. See the answer to the How do I configure Kerberos 5 for plaintext passwords? question. _________________________________________________________________ 1.20 Can I use AFS for plaintext passwords? Yes. See the answer to the How do I configure AFS for plaintext passwords? question. _________________________________________________________________ 1.21 Can I use DCE for plaintext passwords? Yes. See the answer to the How do I configure DCE for plaintext passwords? question. _________________________________________________________________ 1.22 Can I use the CRAM-MD5 database for plaintext passwords? Yes. See the answer to the How do I configure the CRAM-MD5 database for plaintext passwords? question. _________________________________________________________________ 1.23 Can I disable plaintext passwords? Yes. See the answer to the How do I disable plaintext passwords? question. _________________________________________________________________ 1.24 Can I disable plaintext passwords on unencrypted sessions, but allow them on encrypted sessions? Yes. See the answer to the How do I disable plaintext passwords on unencrypted sessions, but allow them in SSL or TLS sessions? question. _________________________________________________________________ 1.25 Can I use virtual hosts? Yes. See the answer to the How do I configure virtual hosts? question. _________________________________________________________________ 1.26 Can I use RPOP authentication? There is no support for RPOP authentication. _________________________________________________________________ 1.27 Can I use Kerberos V4? Kerberos V4 is not supported. Kerberos V4 client-only contributed code is available in ftp://ftp.cac.washington.edu/mail/kerberos4-patches.tar.Z This is a patchkit which must be applied to the IMAP toolkit according to the instructions in the patchkit's README. We can not promise that this code works. _________________________________________________________________ 1.28 Is there support for S/Key or OTP? There is currently no support for S/Key or OTP. There may be an OTP SASL authenticator available from third parties. _________________________________________________________________ 1.29 Is there support for NTLM or SPA? There is currently no support for NTLM or SPA, nor are there any plans to add such support. In general, I avoid vendor-specific mechanisms. I also believe that these mechanisms are being deprecated by their vendor. There may be an NTLM SASL authenticator available from third parties. _________________________________________________________________ 1.30 Is there support for mh? Yes, but only as a legacy format. Your mh format INBOX is accessed by the name "#mhinbox", and all other mh format mailboxes are accessed by prefixing "#mh/" to the name, e.g. "#mh/foo". The mh support uses the "Path:" entry in your .mh_profile file to identify the root directory of your mh format mailboxes. Non-legacy use of mh format is not encouraged. There is no support for permanent flags or unique identifiers; furthermore there are known severe performance problems with the mh format. _________________________________________________________________ 1.31 Is there support for qmail and the maildir format? There is no support for qmail or the maildir format in our distribution, nor are there any plans to add such support. Maildir support may be available from third parties. _________________________________________________________________ 1.32 Is there support for the Cyrus mailbox format? No. _________________________________________________________________ 1.33 Is this software Y2K compliant? Please read the files Y2K and calendar.txt. _________________________________________________________________ 2. What Do I Need to Build This Software? _________________________________________________________________ 2.1 What do I need to build this software with SSL on UNIX? You need to build and install OpenSSL first. _________________________________________________________________ 2.2 What do I need to build this software with Kerberos V on UNIX? You need to build and install MIT Kerberos first. _________________________________________________________________ 2.3 What do I need to use a C++ compiler with this software to build my own application? If you are building an application using the c-client library, use the new c-client.h file instead of including the other include files. It seems that c-client.h should define away all the troublesome names that conflict with C++. If you use gcc, you may need to use -fno-operator-names as well. _________________________________________________________________ 2.4 What do I need to build this software on Windows? You need Microsoft Visual C++ 6.0, Visual C++ .NET, or Visual C# .NET (which you can buy from any computer store), along with the Microsoft Platform SDK (which you can download from Microsoft's web site). You do not need to install the entire Platform SDK; it suffices to install just the Core SDK and the Internet Development SDK. _________________________________________________________________ 2.5 What do I need to build this software on DOS? It's been several years since we last attempted to do this. At the time, we used Microsoft C. _________________________________________________________________ 2.6 Can't I use Borland C to build this software on the PC? Probably not. If you know otherwise, please let us know. _________________________________________________________________ 2.7 What do I need to build this software on the Mac? It has been several years since we last attempted to do this. At the time, we used Symantec THINK C; but today you'll need a C compiler which allows segments to be more than 32K. _________________________________________________________________ 2.8 What do I need to build this software on VMS? You need the VMS C compiler, and either the Multinet or Netlib TCP. _________________________________________________________________ 2.9 What do I need to build this software on TOPS-20? You need the TOPS-20 KCC compiler. _________________________________________________________________ 2.10 What do I need to build this software on Amiga or OS/2? We don't know. _________________________________________________________________ 2.11 What do I need to build this software on Windows CE? This port is incomplete. Someone needs to finish it. _________________________________________________________________ 3. Build and Configuration Questions _________________________________________________________________ 3.1 How do I configure the IMAP and POP servers on UNIX? 3.2 I built and installed the servers according to the BUILD instructions. It can't be that easy. Don't I need to write a config file? For ordinary "vanilla" UNIX systems, this software is plug and play; just build it, install it, and you're done. If you have a modified system, then you may want to do additional work; most of this is to a single source code file (env_unix.c on UNIX systems). Read the file CONFIG for more details. Yes, it's that easy. There are some additional options, such as SSL or Kerberos, which require additional steps to build. See the relevant questions below. _________________________________________________________________ 3.3 How do I make the IMAP and POP servers look for INBOX at some place other than the mail spool directory? 3.4 How do I make the IMAP server look for secondary folders at some place other than the user's home directory? Please read the file CONFIG for discussion of this and other issues. _________________________________________________________________ 3.5 How do I configure SSL? 3.6 How do I configure TLS and the STARTTLS facility? imap-2004 supports SSL and TLS client functionality on UNIX and 32-bit Windows for IMAP, POP3, SMTP, and NNTP; and SSL and TLS server functionality on UNIX for IMAP and POP3. UNIX SSL build requires that a third-party software package, OpenSSL, be installed on the system first. Read imap-2004/docs/SSLBUILD for more information. SSL is supported via undocumented Microsoft interfaces in Windows 9x and NT4; and via standard interfaces in Windows 2000, Windows Millenium, and Windows XP. _________________________________________________________________ 3.7 How do I build/install OpenSSL and obtain/create certificates for use with SSL? If you need help in doing this, try the contacts mentioned in the OpenSSL README. We do not offer support for OpenSSL or certificates. _________________________________________________________________ 3.8 How do I configure CRAM-MD5 authentication? 3.9 How do I configure APOP authentication? CRAM-MD5 authentication is enabled in the IMAP and POP3 client code on all platforms. Read md5.txt to learn how to set up CRAM-MD5 and APOP authentication on UNIX and NT servers. There is no support for APOP client authentication. _________________________________________________________________ 3.10 How do I configure Kerberos V5? imap-2004 supports client and server functionality on UNIX and 32-bit Windows. Kerberos V5 is supported by default in Windows 2000 builds: nmake -f makefile.w2k Other builds require that a third-party Kerberos package, e.g. MIT Kerberos, be installed on the system first. To build with Kerberos V5 on UNIX, include EXTRAAUTHENTICATORS=gss in the make command line, e.g. make lnp EXTRAAUTHENTICATORS=gss To build with Kerberos V5 on Windows 9x, Windows Millenium, and NT4, use the "makefile.ntk" file instead of "makefile.nt": nmake -f makefile.ntk Back to top _________________________________________________________________ 3.11 How do I configure PAM for plaintext passwords? On Linux systems, use the lnp port, e.g. make lnp On Solaris systems and other systems with defective PAM implementations, build with PASSWDTYPE=pmb, e.g. make sol PASSWDTYPE=pmb On all other systems, build with PASSWDTYPE=pam, e.g make foo PASSWDTYPE=pam If you build with PASSWDTYPE=pam and authentication does not work, try rebuilding (after a "make clean") with PASSWDTYPE=pmb. _________________________________________________________________ 3.12 It looks like all I have to do to make the server use Kerberos is to build with PAM on my Linux system, and set it up in PAM for Kerberos passwords. Right? Yes and no. Doing this will make plaintext password authentication use the Kerberos password instead of the /etc/passwd password. However, this will NOT give you Kerberos-secure authentication. See the answer to the How do I configure Kerberos V5? question for how to build with Kerberos-secure authentication. _________________________________________________________________ 3.13 How do I configure Kerberos 5 for plaintext passwords? Build with PASSWDTYPE=gss, e.g. make sol PASSWDTYPE=gss However, this will NOT give you Kerberos-secure authentication. See the answer to the How do I configure Kerberos V5? question for how to build with Kerberos-secure authentication. _________________________________________________________________ 3.14 How do I configure AFS for plaintext passwords? Build with PASSWDTYPE=afs, e.g make sol PASSWDTYPE=afs Back to top _________________________________________________________________ 3.15 How do I configure DCE for plaintext passwords? Build with PASSWDTYPE=dce, e.g make sol PASSWDTYPE=dce Back to top _________________________________________________________________ 3.16 How do I configure the CRAM-MD5 database for plaintext passwords? The CRAM-MD5 password database is automatically used for plaintext password if it exists. Note that this is NOT CRAM-MD5-secure authentication. You probably want to consider disabling plaintext passwords for non-SSL/TLS sessions. See the next two questions. _________________________________________________________________ 3.17 How do I disable plaintext passwords? Server-level plaintext passwords can be disabled by setting PASSWDTYPE=nul, e.g. make lnx EXTRAAUTHENTICATORS=gss PASSWDTYPE=nul Note that you must have a CRAM-MD5 database installed or specify at least one EXTRAAUTHENTICATOR, otherwise it will not be possible to log in to the server. When plaintext passwords are disabled, the IMAP server will advertise the LOGINDISABLED capability and the POP3 server will not advertise the USER capability. _________________________________________________________________ 3.18 How do I disable plaintext passwords on unencrypted sessions, but allow them in SSL or TLS sessions? Do not set PASSWDTYPE=nul or SSLTYPE=unix. Set SSLTYPE=nopwd instead, e.g. make lnx SSLTYPE=nopwd When plaintext passwords are disabled, the IMAP server will advertise the LOGINDISABLED capability and the POP3 server will not advertise the USER capability. Plaintext passwords will always be enabled in SSL sessions; the IMAP server will not advertise the LOGINDISABLED capability and the POP3 server will advertise the USER capability. If the client does a successful start-TLS in a non-SSL session, plaintext passwords will be enabled, and a new CAPABILITY or CAPA command (which is required after start-TLS) will show the effect as in SSL sessions. _________________________________________________________________ 3.19 How do I configure virtual hosts? This is automatic, but with certain restrictions. The most important one is that each virtual host must have its own IP address; otherwise the server has no way of knowing which virtual host is desired. As distributed, the software uses a global password file; hence user "fred" on one virtual host is "fred" on all virtual hosts. You may want to modify the checkpw() routine to implement some other policy (e.g. separate password files). Note that the security model assumes that all users have their own unique UNIX UID number. So if you use separate password files you should make certain that the UID numbers do not overlap between different files. More advanced virtual host support may be available as patches from third parties. _________________________________________________________________ 3.20 Why do I get compiler warning messages such as: passing arg 3 of `scandir' from incompatible pointer type Pointers are not assignment-compatible. Argument #4 is not the correct type. during the build? You can safely ignore these messages. Over the years, the prototype for scandir() has changed, and thus is variant across different UNIX platforms. In particular, the definitions of the third argument (type select_t) and fourth argument (type compar_t) have changed over the years, the issue being whether or not the arguments to the functions pointed to by these function pointers are of type const or not. The way that c-client calls scandir() will tend to generate these compiler warnings on newer systems such as Linux; however, it will still build. The problem with fixing the call is that then it won't build on older systems. _________________________________________________________________ 3.21 Why do I get compiler warning messages such as Operation between types "void(*)(int)" and "void*" is not allowed. Function argument assignment between types "void*" and "void(*)(int)" is not a llowed. Pointers are not assignment-compatible. Argument #5 is not the correct type. during the build? You can safely ignore these messages. All known systems have no problem with casting a function pointer to/from a void* pointer, certain C compilers issue a compiler diagnostic because this facility is listed as a "Common extension" by the C standard: K.5.7 Function pointer casts [#1] A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.3.4). [#2] A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.3.4). It may be just a "common extension", but this facility is relied upon heavily by c-client. _________________________________________________________________ 3.22 Why do I get linker warning messages such as: mtest.c:515: the `gets' function is dangerous and should not be used. during the build? Isn't this a security bug? You can safely ignore this message. Certain linkers, most notably on Linux, give this warning message. It is indeed true that the traditional gets() function is not a safe one. However, the mtest program is only a demonstration program, a model of a very basic application program using c-client. It is not something that you would install, much less run in any security-sensitive context. mtest has numerous other shortcuts that you wouldn't want to do in a real application program. The only "security bug" with mtest would be if it was run by some script in a security-sensitive context, but mtest isn't particularly useful for such purposes. If you wanted to write a script to automate some email task using c-client, you'd be better off using imapd instead of mtest. mtest only has two legitimate uses. It's a useful testbed for me when debugging new versions of c-client, and it's useful as a model for someone writing a simple c-client application to see how the various calls work. By the way, if you need a more advanced example of c-client programming than mtest (and you probably will), I recommend that you look at the source code for imapd and Pine. _________________________________________________________________ 3.23 Why do I get linker warning messages such as: auth_ssl.c:92: the `tmpnam' function is dangerous and should not be used. during the build? Isn't this a security bug? You can safely ignore this message. Certain linkers, most notably on Linux, give this warning message, based upon two known issues with tmpnam(): there can be a buffer overflow if an inadequate buffer is allocated. there can be a timing race caused by certain incautious usage of the return value. Neither of these issues applies in the particular use that is made of tmpnam(). More importantly, the tmpnam() call is never executed on Linux systems. _________________________________________________________________ 3.24 OK, suppose I see a warning message about a function being "dangerous and should not be used" for something other than this gets() or tmpnam() call? Please forward the details for investigation. _________________________________________________________________ 4. Operational Questions _________________________________________________________________ 4.1 How can I enable anonymous IMAP logins? Create the file /etc/anonymous.newsgroups. At the present time, this file should be empty. This will permit IMAP logins as anonymous as well as the ANONYMOUS SASL authenticator. Anonymous users have access to mailboxes in the #news., #ftp/, and #public/ namespaces only. _________________________________________________________________ 4.2 How do I set up an alert message that each IMAP user will see? Create the file /etc/imapd.alert with the text of the message. This text should be kept to one line if possible. Note that this will cause an alert to every IMAP user every time they initiate an IMAP session, so it should only be used for critical messages. _________________________________________________________________ 4.3 How does the c-client library choose which of its several mechanisms to use to establish an IMAP connection to the server? I noticed that it can connect on port 143, port 993, via rsh, and via ssh. c-client chooses how to establish an IMAP connection via the following rules: + If /ssl is specified, use an SSL connection. Fail otherwise. + Else if client is a UNIX system and "ssh server exec /etc/rimapd" works, use that + Else if /tryssl is specified and an SSL connection works, use that. + Else if client is a UNIX system and "rsh server exec /etc/rimapd" works, use that. + Else use a non-SSL connection. _________________________________________________________________ 4.4 I am using a TLS-capable IMAP server, so I don't need to use /ssl to get encryption. However, I want to be certain that my session is TLS encrypted before I send my password. How to I do this? Use the /tls option in the mailbox name. This will cause an error message and the connection to fail if the server does not negotiate STARTTLS. _________________________________________________________________ 4.5 How do I use one of the alternative formats described in the formats.txt document? In particular, I hear that mbx format will give me better performance and allow shared access. The rumors about mbx format being preferred are true. It is faster than the traditional UNIX mailbox format and permits shared access. However, and this is very important, note that using an alternative mailbox format is an advanced facility, and only expert users should undertake it. If you don't understand any of the following notes, you may not be enough of an expert yet, and are probably better off not going this route until you are more comfortable with your understanding. Some of the formats, including mbx, are only supported by the software based on the c-client library, and are not recognized by other mailbox programs. The "vi" editor will corrupt any mbx format mailbox that it encounters. Another problem is that the certain formats, including mbx, use advanced file access and locking techniques that do not work reliably with NFS. NFS is not a real filesystem. Use IMAP instead of NFS for distributed access. Each of the following steps are in escalating order of involvement. The further you go down this list, the more deeply committed you become: + The simplest way to create a mbx-format mailbox is to prefer the name with "#driver.mbx/" when creating a mailbox through c-client. For example, if you create "#driver.mbx/foo", the mailbox "foo" will be created in mbx format. Only use "#driver.mbx/" when creating the mailbox. At all other times, just use the name ("foo" in this example); the software will automatically select the driver for mbx whenever that mailbox is accessed without you doing anything else. + You can use the "mailutil copy" command to copy an existing mailbox to a new mailbox in mbx format. Read the man page provided with the mailutil program for details. + If you create an mbx-format INBOX, by creating "#driver.mbx/INBOX" (note that "INBOX" must be all uppercase), then subsequent access to INBOX by any c-client based application will use the mbx-format INBOX. Any mail delivered to the traditional format mailbox in the spool directory (e.g. /var/spool/mail/$USER) will automatically be copied into the mbx-format INBOX and the spool directory copy removed. + You can cause any newly-created mailboxes to be in mbx-format by default by changing the definition of CREATEPROTO=unixproto to be CREATEPROTO=mbxproto in src/osdep/unix/Makefile, then rebuilding the IMAP toolkit (do a "make clean" first). Do not change EMPTYPROTO, since mbx format mailboxes are never a zero-byte file. If you use Pine or the imap-utils, you should probably also rebuild them with the new IMAP toolkit too. + You can deliver directly to the mbx-format INBOX by use of the tmail or dmail programs. tmail is for direct invocation from sendmail (or whatever MTA program you use); dmail is for calls from procmail. Both of these programs have man pages which must be read carefully before making this change. Most other servers (e.g. Cyrus) require use of a non-standard format. A full-fledged format conversion is not significantly different from what you have to do with other servers. The difference, which makes format conversion procedures somewhat more complicated with this server, is that there is no "all or nothing" requirement with this server. There are many points in between. A format conversion can be anything from a single mailbox or single user, to systemwide. This is good in that you can decide how far to go, or do the steps incrementally as you become more comfortable with the result. On the other hand, there's no "One True Way" which can be boiled down to a simple set of pedagogical instructions. A number of sites have done full-fledged format conversions, and are reportedly quite happy with the results. Feel free to ask in the comp.mail.imap newsgroup or the c-client mailing list for advice or help. _________________________________________________________________ 4.6 How do I set up shared mailboxes? At the simplest level, a shared mailbox is one which has UNIX file and directory protections which permit multiple users to access it. What this means is that your existing skills and tools to create and manage shared files on your UNIX system apply to shared mailboxes; e.g. chmod 666 mailbox You may want to consider the use of a mailbox format which permits multiple simultaneous read/write sessions, such as the mbx format. The traditional UNIX format only allows only read/write session to a mailbox at a time. An additional convenience item are three system directories, which can be set up for shared namespaces. These are: #ftp, #shared, and #public, and are defined by creating the associated UNIX users and home directories as described below. #ftp/ refers to the anonymous ftp filesystem exported by the ftp server, and is equivalent to the home directory for UNIX user "ftp". For example, #ftp/foo/bar refers to the file /foo/bar in the anonymous FTP filesystem, or ~ftp/foo/bar for normal users. Anonymous FTP files are available to anonymous IMAP logins. By default, newly-created files in #ftp/ are protected 644. #public/ refers to an IMAP toolkit convention called "public" files, and is equivalent to the home directory for UNIX user "imappublic". For example, #public/foo/bar refers to the file ~imappublic/foo/bar. Public files are available to anonymous IMAP logins. By default, newly-created files in #public are created with protection 0666. #shared/ refers to an IMAP toolkit convention called "shared" files, and is equivalent to the home directory for UNIX user "imapshared". For example, #shared/foo/bar refers to the file ~imapshared/foo/bar. Shared files are not available to anonymous IMAP logins. By default, newly-created files in #shared are created with protection 0660. _________________________________________________________________ 4.7 How can I make the server syslogs go to someplace other than the mail syslog? The openlog() call that sets the syslog facility is in src/osdep/unix/env_unix.c in routine server_init(). You need to edit this file to change the syslog facility from LOG_MAIL to the facility you want, then rebuild. You also need to set up your /etc/syslog.conf properly. Refer to the man pages for syslog and syslogd for more information on what the available syslog facilities are and how to configure syslogs. If you still don't understand what to do, find a UNIX system expert. _________________________________________________________________ 5. Security Questions _________________________________________________________________ 5.1 I see that the IMAP server allows access to arbitary files on the system, including /etc/passwd! How do I disable this? You should not worry about this if your IMAP users are allowed shell access. The IMAP server does not permit any access that the user can not have via the shell. If, and only if, you deny your IMAP users shell access, you may want to consider one of three choices. Note that these choices reduce IMAP functionality, and may have undesirable side effects. Each of these choices involves an edit to file src/osdep/unix/env_unix.c The first (and recommended) choice is to set restrictBox as described in file CONFIG. This will disable access to the filesystem root, to other users' home directory, and to superior directory. The second (and strongly NOT recommended) choice is to set closedBox as described in file CONFIG. This puts each IMAP session into a so-called "chroot jail", and thus setting this option is extremely dangerous; it can make your system much less secure and open to root compromise attacks. So do not use this option unless you are absolutely certain that you understand all the issues of a "chroot jail." The third choice is to rewrite routine mailboxfile() to implement whatever mapping from mailbox name to filesystem name (and restrictions) that you wish. This is the most general choice. As a guide, you can see at the start of routine mailboxfile() what the restrictBox choice does. _________________________________________________________________ 5.2 I've heard that IMAP servers are insecure. Is this true? There are no known security problems in this version of the IMAP toolkit, including the IMAP and POP servers. The IMAP and POP servers limit what can be done while not logged in, and as part of the login process discard all privileges except those of the user. As with other software packages, there have been buffer overflow vulnerabilities in past versions. All known problems of this nature are fixed in this version. There is every reason to believe that the bad guys are engaged in an ongoing effort to find vulnerabilities in the IMAP toolkit. We look for such problems, and when one is found we fix it. It's unfortunate that any vulnerabilities existed in past versions, and we're doing my best to keep the IMAP toolkit free of vulnerabilities. No new vulnerabilities have been discovered in quite a while, but efforts will not be relaxed. Beware of vendors who claim that their implementations can not have vulnerabilities. _________________________________________________________________ 5.3 How do I know that I have the most secure version of the server? The best way is to keep your server software up to date. The bad guys are always looking for ways to crack software, and when they find one, let all their friends know. Oldtimers used to refer to a concept of software rot: if your software hasn't been updated in a while, it would "rot" -- tend to acquire problems that it didn't have when it was new. The latest release version of the IMAP toolkit is always available at ftp://ftp.cac.washington.edu/mail/imap.tar.Z _________________________________________________________________ 5.4 I see all these strcpy() and sprintf() calls, those are unsafe, aren't they? Yes and no. It can be unsafe to do these calls if you do not know that the string being written will fit in the buffer. However, they are perfectly safe if you do know that. Beware of programmers who advocate doing a brute-force change of all instances of strcpy (s,t); to strncpy (s,t,n)[n] = '\0'; and similar measures in the name of "fixing all possible buffer overflows." There are examples in which a security bug was introduced because of this type of "fix", due to the programmer using the wrong value for n. In one case, the programmer thought that n was larger than it actually was, causing a NUL to be written out of the buffer; in another, n was too small, and a security credential was truncated. What is particularly ironic was that in both cases, the original strcpy() was safe, because the size of the source string was known to be safe. With all this in mind, the software has been inspected, and it is believed that all places where buffer overflows can happen have been fixed. The strcpy()s that are still are in the code occur after a size check was done in some other way. Note that the common C idiom of *s++ = c; is just as vulnerable to buffer overflows. You can't cure buffer overflows by outlawing certain functions, nor is it desirable to do so; sometimes operations like strcpy() translate into fast machine instructions for better performance. Nothing replaces careful study of code. That's how the bad guys find bugs. Security is not accomplished by means of brute-force shortcuts. _________________________________________________________________ 5.5 Those /tmp lock files are protected 666, is that really right? Yes. Shared mailboxes won't work otherwise. Also, you get into accidental denial of service problems with old lock files left lying around; this happens fairly frequently. The deliberate mischief that can be caused by fiddling with the lock files is small-scale; harassment level at most. There are many -- and much more effective -- other ways of harassing another user on UNIX. It's usually not difficult to determine the culprit. Before worrying about deliberate mischief, worry first about things happening by accident! _________________________________________________________________ 6. Why Did You Do This Strange Thing? Questions _________________________________________________________________ 6.1 Why don't you use GNU autoconfig / automake / autoblurdybloop? Autoconfig et al are not available on all the platforms where the IMAP toolkit is supported; and do not work correctly on some of the platforms where they do exist. Furthermore, these programs add another layer of complexity to an already complex process. Coaxing software that uses autoconfig to build properly on platforms which were not specifically considered by that software wastes an inordinate amount of time. When (not if) autoconfig fails to do the right thing, the result is an inpenetrable morass to untangle in order to find the problem and fix it. The concept behind autoconfig is good, but the execution is flawed. It rarely does the right thing on a platform that wasn't specifically considered. Human life is too short to debug autoconfig problems, especially since the current mechanism is so much easier. If an autoconfig mechanism were used, it wouldn't be the GNU one, due to the viral nature of the GNU license. _________________________________________________________________ 6.2 Why do you insist upon a build with -g? Doesn't it waste disk and memory space? From time to time a submitted port has snuck in without -g. This has always ended up causing problems. There are only two valid excuses for not using -g in a port: + The compiler does not support -g + An alternate form of -g is needed with optimization, e.g. -g3. There will be no new ports added without -g (or a suitable alternative) being set. -g has not been arbitrarily added to the ports which do not currently have it because we don't know if doing so would break the build. However, any support issues with one of those port will lead to the correct -g setting being determined and permanently added. Processors are fast enough (and disk space is cheap enough) that -g should be automatic in all compilers with no way of turning it off, and /bin/strip should be a symlink to /bin/true. Human life is too short to deal with binaries built without -g. Such binaries should be a bad memory of the days of KIPS processors and disks that costs several dollars per kilobyte. _________________________________________________________________ 6.3 Why don't you make c-client a shared library? All too often, shared libraries create far more problems than they solve. Remember that you only gain the benefit of a shared library when there are multiple applications which use that shared library. Even without shared libraries, on most modern operating systems (and many ancient ones too!) applications will share their text segments between across multiple processes running the same application. This means that if your system only runs one application (e.g. imapd) that uses the c-client library, then you gain no benefit from making c-client a shared library even if it has 100 imapd processes. You will, however suffer added complexity. If you have a server system that just runs imapd and ipop3d, then making c-client a shared library will save just one copy of c-client no matter how many IMAP/POP3 processes are running. The problem with shared libraries is that you have to keep around a copy of the library every time something changes in the library that would affect the interface the library presents to the application. So, you end up having many copies of the same shared library. If you don't keep multiple copies of the shared library, then one of two things happens. If there was proper versioning, then you'll get a message such as "cannot open shared object file" or "minor versions don't match" and the application won't run. Otherwise, the application will run, but will fail in mysterious ways. Several sites and third-party distributors have modified the c-client makefile in order to make c-client be a shared library. When (not if) a c-client based application fails in mysterious ways because of a library compatibility problem, the result is a bug report. A lot of time and effort ends up getting wasted investigating such bug reports. Memory is so cheap these days that it's not worth it. Human life is too short to deal with shared library compatibility problems. _________________________________________________________________ 6.4 Why don't you use iconv() for internationalization support? iconv() is not ubiquitous enough. _________________________________________________________________ 6.5 Why is the IMAP server connected to the home directory by default? The IMAP server has no way of knowing what you might call "mail" as opposed to "some other file"; in fact, you can use IMAP to access any file. The IMAP server also doesn't know whether your preferred subdirectory for mailbox files is "mail/", ".mail/", "Mail/", "Mailboxes/", or any of a zillion other possibilities. If one such name were chosen, it would undoubtably anger the partisans of all the other names. It is possible to modify the software so that the default connected directory is someplace else. Please read the file CONFIG for discussion of this and other issues. _________________________________________________________________ 6.6 I have a Windows system. Why isn't the server plug and play for me? There is no standard for how mail is stored on Windows; nor a single standard SMTP server. The closest to either would be the SMTP server in Microsoft's IIS. So there's no default by which to make assumptions. As the software is set up, it assumes that the each user has an Windows login account and private home directory, and that mail is stored on that home directory as files in one of the popular UNIX formats. It also assumes that there is some tool equivalent to inetd on UNIX that does the TCP/IP listening and server startup. Basically, unless you're an email software hacker, you probably want to look elsewhere if you want IMAP/POP servers for Windows. _________________________________________________________________ 6.7 I looked at the UNIX SSL code and saw that you have the SSL data payload size set to 8192 bytes. SSL allows 16K; why aren't you using the full size? This is to avoid an interoperability problem with: + PC IMAP clients that use Microsoft's SChannel.DLL (SSPI) for SSL support + Microsoft Exchange server (which also uses SChannel). SChannel has a bug that makes it think that the maximum SSL data payload size is 16379 bytes -- 5 bytes too small. Thus, c-client has to make sure that it never transmits full sized SSL packets. The reason for using 8K (as opposed to, say, 16379 bytes, or 15K, or...) is that it corresponds with the TCP buffer size that the software uses elsewhere for input; there's a slight performance benefit to having the two sizes correspond or at least be a multiple of each other. Also, it keeps the size as a power of two, which might be significant on some platforms. There wasn't a significant difference that we could measure between 8K and 15K. Microsoft has developed a hotfix for this bug. Look up MSKB article number 300562. Contrary to the article text which implies that this is a Pine issue, this bug also affects Microsoft Exchange server with any client that transmits full-sized SSL payloads. _________________________________________________________________ 6.8 Why is an mh format INBOX called #mhinbox instead of just INBOX? It's a long story. In brief, the mh format driver is less functional than any of the other drivers. It turned out that there were some users (including high-level administrators) who tried mh years ago and no longer use it, but still had an mh profile left behind. When the mh driver used INBOX, it would see the mh profile, and proceed to move the user's INBOX into the mh format INBOX. This caused considerable confusion as some things stopped working. _________________________________________________________________ 6.9 Why don't you support the maildir format? It is technically difficult to support maildir in IMAP while maintaining acceptable performance, robustness, following the requirements of the IMAP protocol specification, and following the requirements of maildir. No one has succeeded in accomplishing all four together. The various maildir drivers offered as patches all have these problems. The problem is exacerbated because this implementation supports multiple formats; consequently this implementation can't make any performance shortcuts by assuming that all the world is maildir. We can't do a better job than the maildir fan community has done with their maildir drivers. Similarly, if the maildir fan community provides the maildir driver, they take on the responsibility for answering maildir-specific support questions. This is as it should be, and that is why maildir support is left to the maildir fan community. _________________________________________________________________ 6.10 Why don't you support the Cyrus format? There's no point to doing so. An implementation which supports multiple formats will never do as well as one which is optimized to support one single format. If you want to use Cyrus mailbox format, you should use the Cyrus server, which is the native implementation of that format and is specifically optimized for that format. That's also why Cyrus doesn't implement any other format. _________________________________________________________________ 6.11 Why is it creating extra forks on my SVR4 system? This is because your system only has fcntl() style locking and not flock() style locking. fcntl() locking has a design flaw that causes a close() to release any locks made by that process on the file opened on that file descriptor, even if the lock was made on a different file descriptor. This design flaw causes unexpected loss of lock, and consequent mailbox corruption. The workaround is to do certain "dangerous operations" in another fork, thus avoiding doing a close() in the vulnerable fork. The best way to solve this problem is to upgrade your SVR4 (Solaris, AIX, HP-UX, SGI) or OSF/1 system to a more advanced operating system, such as Linux or BSD. These more advanced operating systems have fcntl() locking for compatibility with SVR4, but also have flock() locking. Beware of certain SVR4 systems, such as AIX, which have an "flock()" function in their C library that is just a jacket that does an fcntl() lock. This is not a true flock(), and has the same design flaw as fcntl(). _________________________________________________________________ 6.12 Why are you so fussy about the date/time format in the internal "From " line in traditional UNIX mailbox files? My other mail program just considers every line that starts with "From " to be the start of the message. You just answered your own question. If any line that starts with "From " is treated as the start of a message, then every message text line which starts with "From " has to be quoted (typically by prefixing a ">" character). People complain about this -- "why did a > get stuck in my message?" So, good mail reading software only considers a line to be a "From " line if it follows the actual specification for a "From " line. This means, among other things, that the day of week is fixed-format: "May 14", but "May 7" (note the extra space) as opposed to "May 7". ctime() format for the date is the most common, although POSIX also allows a numeric timezone after the year. For compatibility with ancient software, the seconds are optional, the timezone may appear before the year, the old 3-letter timezones are also permitted, and "remote from xxx" may appear after the whole thing. Unfortunately, some software written by novices use other formats. The most common error is to have a variable-width day of month, perhaps in the erroneous belief that RFC 2822 (or RFC 822) defines the format of the date/time in the "From " line (it doesn't; no RFC describes internal formats). I've seen a few other goofs, such as a single-digit second, but these are less common. If you are writing your own software that writes mailbox files, and you really aren't all that savvy with all the ins and outs and ancient history, you should seriously consider using the c-client library (e.g. routine mail_append()) instead of doing the file writes yourself. If you must do it yourself, use ctime(), as in: fprintf (mbx,"From %s@%h %s",user,host,ctime (time (0))); rather than try to figure out a good format yourself. ctime() is the most traditional format and nobody will flame you for using it. _________________________________________________________________ 6.13 Why is traditional UNIX format the default format? Compatibility with the past 30 or so years of UNIX history. This server is the only one that completely interoperates with legacy UNIX mail tools. _________________________________________________________________ 6.14 Why do you write this "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA" message at the start of traditional UNIX and MMDF format mailboxes? This pseudo-message serves two purposes. First, it establishes the mailbox format even when the mailbox has no messages. Otherwise, a mailbox with no messages is a zero-byte file, which could be one of several formats. Second, it holds mailbox metadata used by IMAP: the UID validity, the last assigned UID, and mailbox keywords. Without this metadata, which must be preserved even when the mailbox has no messages, the traditional UNIX format wouldn't be able to support the full capabilities of IMAP. _________________________________________________________________ 6.15 Why don't you stash the mailbox metadata in the first real message of the mailbox instead of writing this fake FOLDER INTERNAL DATA message? In fact, that is what is done if the mailbox is non-empty and does not already have a FOLDER INTERNAL DATA message. One problem with doing that is that if some external program removes the first message, the metadata is lost and must be recreated, thus losing any prior UID or keyword list status that IMAP clients may depend upon. Another problem is that this doesn't help if the last message is deleted. This will result in an empty mailbox, and the necessity to create a FOLDER INTERNAL DATA message. _________________________________________________________________ 6.16 Why aren't "dual-use" mailboxes the default? Compatibility with the past 30 or so years of UNIX history, not to mention compatibility with user expectations when using shell tools. _________________________________________________________________ 6.17 Why do you use ucbcc to build on Solaris? It is a long, long story about why cc is set to ucbcc. You need to invoke the C compiler so that it links with the SVR4 libraries and not the BSD libraries, otherwise readdir() will return the wrong information. Of all the names in the most common path, ucbcc is the only name to be found (on /usr/ccs/bin) that points to a suitable compiler. cc is likely to be /usr/ucb/cc which is absolutely not the compiler that you want. The real SVR4 cc is probably something like /opt/SUNWspro/bin/cc which is rarely in anyone's path by default. ucbcc is probably a link to acc, e.g. /opt/SUNWspro/SC4.0/bin/acc, and is the UCB C compiler using the SVR4 libraries. If ucbcc isn't on your system, then punt on the SUN C compiler and use gcc instead (the gso port instead of the sol port). If, in spite of all the above warnings, you choose to change "ucbcc" to "cc", you will probably find that the -O2 needs to be changed to -O. If you don't get any error messages with -O2, that's a pretty good indicator that you goofed and are running the compiler that will link with the BSD libraries. To recap: + The sol port is designed to be built using the UCB compiler using the SVR4 libraries. This compiler is "ucbcc", which is lunk to acc. You use -O2 as one of the CFLAGS. + If you build the sol port with the UCB compiler using the BSD libraries, you will get no error messages but you will get bad binaries (the most obvious symptom is dropping the first two characters return filenames from the imapd LIST command. This compiler also uses -O2, and is very often what the user gets from "cc". BEWARE + If you build the sol port with the real SVR4 compiler, which is often hidden away or unavailable on many systems, then you will get errors from -O2 and you need to change that to -O. But you will get a good binary. However, you should try it with -O2 first, to make sure that you got this compiler and not the UCB compiler using BSD libraries. _________________________________________________________________ 6.18 Why should I care about some old system with BSD libraries? cc is the right thing on my Solaris system! Because there still are sites that use such systems. On those systems, the assumption that "cc" does the right thing will lead to corrupt binaries with no error message or other warning that anything is amiss. Too many sites have fallen victim to this problem. _________________________________________________________________ 6.19 Why do you insist upon writing .lock files in the spool directory? Compatibility with the past 30 years of UNIX software which deals with the spool directory, especially software which delivers mail. Otherwise, it is possible to lose mail. _________________________________________________________________ 6.20 Why should I care about compatibility with the past? This is one of those questions in which the answer never convinces those who ask it. Somehow, everybody who ever asks this question ends up answering it for themselves as they get older, with the very answer that they rejected years earlier. _________________________________________________________________ 7. Problems and Annoyances _________________________________________________________________ 7.1 Help! My INBOX is empty! What happened to my messages? If you are seeing "0 messages" when you open INBOX and you know you have messages there (and perhaps have looked at your mail spool file and see that messages are there), then probably there is something wrong with the very first line of your mail spool file. Make sure that the first five bytes of the file are "From ", followed by an email address and a date/time in ctime() format, e.g.: From fred@foo.bar Mon May 7 20:54:30 2001 Back to top _________________________________________________________________ 7.2 Help! All my messages in a non-INBOX mailbox have been concatenated into one message which claims to be from me and has a subject of the file name of the mailbox! What's going on? Something wrong with the very first line of the mailbox. Make sure that the first five bytes of the file are "From ", followed by an email address and a date/time in ctime() format, e.g.: From fred@foo.bar Mon May 7 20:54:30 2001 Back to top _________________________________________________________________ 7.3 Why do I get the message: CREATE failed: Can't create mailbox node xxxxxxxxx: File exists and how do I fix it? See the answer to the Are hierarchical mailboxes supported? question. _________________________________________________________________ 7.4 Why can't I log in to the server? The user name and password are right! There are a myriad number of possible answers to this question. The only way to say for sure what is wrong is run the server under a debugger such as gdb while root (yes, you must be root) with a breakpoint at routines checkpw() and loginpw(), then single-step until you see which test rejected you. The server isn't going to give any error messages other than "login failed" in the name of not giving out any unnecessary information to unauthorized individuals. Here are some of the more common reasons why login may fail: + You didn't really give the correct user name and/or password. + Your client doesn't send the LOGIN command correctly; for example, IMAP2 clients won't send a password containing a "*" correctly to an IMAP4 server. + If you have set up a CRAM-MD5 database, remember that the password used is the one in the CRAM-MD5 database, and furthermore that there must also be an entry in /etc/passwd (but the /etc/passwd password is not used). + If you are using PAM, have you created a service file for the server in /etc/pam.d? + If you are using shadow passwords, have you used an appropriate port when building? In particular, note that "lnx" is for Linux systems without shadow passwords; you probably want "slx" or "lnp" instead. + If your system has account or password expirations, check to see that the expiration date hasn't passed. + You can't log in as root or any other UID 0 user. This is for your own safety, not to mention the fact that the servers use UID 0 as meaning "not logged in". _________________________________________________________________ 7.5 Help! My load average is soaring and I see hundreds of POP and IMAP servers, many logged in as the same user! Certain inferior losing GUI mail reading programs have a "synchronize all mailboxes at startup" (IMAP) or "check for new mail every second" (POP) feature which causes a rapid and unchecked spawning of servers. This is not a problem in the server; the client is really asking for all those server sessions. Unfortunately, there isn't much that the POP and IMAP servers can do about it; they don't spawned themselves. Some sites have added code to record the number of server sessions spawned per user per hour, and disable login for a user who has exceeded a predetermined rate. This doesn't stop the servers from being spawned; it just means that a server session will commit suicide a bit faster. Another possibility is to detect excessive server spawning activity at the level where the server is spawned, which would be inetd or possibly tcpd. The problem here is that this is a hard time to quantify. 50 sessions in a minute from a multi-user timesharing system may be perfectly alright, whereas 10 sessions a minute from a PC may be too much. The real solution is to fix the client configuration, by disabling those evil features. Also tell the vendors of those clients how you feel about distributing denial-of-service attack tools in the guise of mail reading programs. _________________________________________________________________ 7.6 Why does mail disappear even though I set "keep mail on server"? 7.7 Why do I get the message Moved ##### bytes of new mail to /home/user/mbox from /var/spool/mail/user and why did this happen? This is probably caused by the mbox driver. If the file "mbox" exists on the user's home directory and is in UNIX mailbox format, then when INBOX is opened this file will be selected as INBOX instead of the mail spool file. Messages will be automatically transferred from the mail spool file into the mbox file. To disable this behavior, delete "mbox" from the EXTRADRIVERS list in the top-level Makefile and rebuild. Note that if you do this, users won't be able to access the messages that have already been moved to mbox unless they open mbox instead of INBOX. _________________________________________________________________ 7.8 Why isn't it showing the local host name as a fully-qualified domain name? 7.9 Why is the local host name in the From/Sender/Message-ID headers of outgoing mail not coming out as a fully-qualified domain name? Your UNIX system is misconfigured. The entry for your system in /etc/hosts must have the fully-qualified domain name first, e.g. 105.69.1.234 bombastic.blurdybloop.com bombastic A common mistake of novice system administrators is to have the short name first, e.g. 105.69.1.234 bombastic bombastic.blurdybloop.com or to omit the fully qualified domain name entirely, e.g. 105.69.1.234 bombastic The result of this is that when the IMAP toolkit does a gethostbyname() call to get the fully-qualified domain name, it would get "bombastic" instead of "bombastic.blurdybloop.com". On some systems, a configuration file (typically named /etc/svc.conf, /etc/netsvc.conf, or /etc/nsswitch.conf) can be used to configure the system to use the domain name system (DNS) instead of /etc/hosts, so it doesn't matter if /etc/hosts is misconfigured. Check the man pages for gethostbyname, hosts, svc, and/or netsvc for more information. Unfortunately, certain vendors, most notably SUN, have failed to make this clear in their documentation. Most of SUN's documentation assumes a corporate network that is not connected to the Internet. net.folklore once (late 1980s) held that the proper procedure was to append the results of getdomainname() to the name returned by gethostname(), and some versions of sendmail configuration files were distributed that did this. This was incorrect; the string returned from getdomainname() is the Yellow Pages (a.k.a NIS) domain name, which is a completely different (albeit unfortunately named) entity from an Internet domain. These were often fortuitously the same string, except when they weren't. Frequently, this would result in host names with spuriously doubled domain names, e.g. bombastic.blurdybloop.com.blurdybloop.com This practice has been thoroughly discredited for many years, but folklore dies hard. _________________________________________________________________ 7.10 What does the message: Mailbox vulnerable - directory /var/spool/mail must have 1777 protection mean? How can I fix this? In order to update a mailbox in the default UNIX format, it is necessary to create a lock file to prevent the mailer from delivering mail while an update is in progress. Some systems use a directory protection of 775, requiring that all mail handling programs be setgid mail; or of 755, requiring that all mail handling programs be setuid root. The IMAP toolkit does not run with any special privileges, and I plan to keep it that way. It is antithetical to the concept of a toolkit if users can't write their own programs to use it. Also, I've had enough bad experiences with security bugs while running privileged; the IMAP and POP servers have to be root when not logged in, in order to be able to log themselves in. I don't want to go any deeper down that slippery slope. Directory protection 1777 is secure enough on most well-managed systems. If you can't trust your users with a 1777 mail spool (petty harassment is about the limit of the abuse exposure), then you have much worse problems then that. If you absolutely insist upon requiring privileges to create a lock file, external file locking can be done via a setgid mail program named /etc/mlock (this is defined by LOCKPGM in the c-client Makefile). If the toolkit is unable to create a <...mailbox...>.lock file in the directory by itself, it will try to call mlock to do it. I do not recommend doing this for performance reasons. A sample mlock program is included as part of imap-2004. We have tried to make this sample program secure, but it has not been thoroughly audited. _________________________________________________________________ 7.11 What does the message: Mailbox is open by another process, access is readonly mean? How do I fix this? A problem occurred in applying a lock to a /tmp lock file. Either some other program has the mailbox open and won't relenquish it, or something is wrong with the protection of /tmp or the lock. Make sure that the /tmp directory is protected 1777. Some security scripts incorrectly set the protection of the /tmp directory to 775, which disables /tmp for all non-privileged programs. _________________________________________________________________ 7.12 What does the message: Can't get write access to mailbox, access is readonly mean? The mailbox file is write-protected against you. _________________________________________________________________ 7.13 I set my POP3 client to "delete messages from server" but they never get deleted. What is wrong? Make sure that your mailbox is not read-only: that the mailbox is owned by you and write enabled (protection 0600), and that the /tmp directory is longer world-writeable. /tmp must be world-writeable because lots of applications use it for scratch space. To fix this, do chmod 1777 /tmp as root. Make sure that your POP3 client issues a QUIT command when it finishes. The POP3 protocol specifies that deletions are discarded unless a proper QUIT is done. Make sure that you are not opening multiple POP3 sessions to the same mailbox. It is a requirement of the POP3 protocol than only one POP3 session be in effect to a mailbox at a time, however some, poorly-written POP3 clients violate this. Also, some background "check for new mail" tasks also cause a violation. See the answer to the What does the syslog message: Killed (lost mailbox lock) user=... host=... mean? question for more details. _________________________________________________________________ 7.14 What do messages such as: Message ... UID ... already has UID ... Message ... UID ... less than ... Message ... UID ... greater than last ... Invalid UID ... in message ..., rebuilding UIDs mean? Something happened to corrupt the unique identifier regime in the mailbox. In traditional UNIX-format mailboxes, this can happen if the user deleted the "DO NOT DELETE" internal message. This problem is relatively harmless; a new valid unique identifier regime will be created. The main effect is that any references to the old UIDs will no longer be useful. So, unless it is a chronic problem or you feel like debugging, you can safely ignore these messages. _________________________________________________________________ 7.15 What do the error messages: Unable to read internal header at ... Unable to find CRLF at ... Unable to parse internal header at ... Unable to parse message date at ... Unable to parse message flags at ... Unable to parse message UID at ... Unable to parse message size at ... Last message (at ... ) runs past end of file ... mean? I am using mbx format. The mbx-format mailbox is corrupted and needs to be repaired. You should make an effort to find out why the corruption happened. Was there an obvious system problem (crash or disk failure)? Did the user accidentally access the file via NFS? Mailboxes don't get corrupted by themselves; something caused the problem. Some people have developed automated scripts, but if you're comfortable using emacs it's pretty easy to fix it manually. Do not use vi or any other editor unless you are certain that editor can handle binary!!! If you are not comfortable with emacs, or if the file is too large to read with emacs, see the "step-by-step" technique later on for another way of doing it. After the word "at" in the error message is the byte position it got to when it got unhappy with the file, e.g. if you see: Unable to parse internal header at 43921: ne bombastic blurdybloop The problem occurs at the 43,931 byte in the file. That's the point you need to fix. c-client is expecting an internal header at that byte number, looking something like: 6-Jan-1998 17:42:24 -0800,1045;000000100001-00000001 The format of this internal line is: dd-mmm-yyyy hh:mm:ss +zzzz,ssss;ffffffffFFFF-UUUUUUUU The only thing that is variable is the "ssss" field, it can be as many digits as needed. All other fields (inluding the "dd") are fixed width. So, the easiest thing to do is to look forward in the file for the next internal header, and delete everything from the error point to that internal header. Here's what to do if you want to be smarter and do a little bit more work. Generally, you're in the middle of a message, and there's nothing wrong with that message. The problem happened in the *previous* message. So, search back to the previous internal header. Now, remember that "ssss" field? That's the size of that message. Mark where you are in the file, move the cursor to the line after the internal header, and skip that many bytes ("ssss") forward. If you're at the point of the error in the file, then that message is corrupt. If you're at a different point, then perhaps the previous message is corrupt and has a too long size count that "ate" into this message. Basically, what you need to do is make sure that all those size counts are right, and that moving "ssss" bytes from the line after the internal header will land you at another internal header. Usually, once you know what you're looking at, it's pretty easy to work out the corruption, and the best remedial action. Repair scripts will make the problem go away but may not always do the smartest/best salvage of the user's data. Manual repair is more flexible and usually preferable. Here is a step-by-step technique for fixing corrupt mbx files that's a bit cruder than the procedure outlined above, but works for any size file. In this example, suppose that the corrupt file is INBOX, the error message is Unable to find CRLF at 132551754 and the size of the INBOX file is 132867870 bytes. The first step is to split the mailbox file at the point of the error: + Rename the INBOX file to some other name, such as INBOX.bad. + Copy the first 132,551,754 bytes of INBOX.bad to another file, such as INBOX.new. + Extract the trailing 316,116 bytes (132867870-132551754) of INBOX.bad into another file, such as INBOX.tail. + You no longer need INBOX.bad. Delete it. In other words, use the number from the "Unable to find CRLF at" as the point to split INBOX into two new files, INBOX.new and INBOX.tail. Now, remove the erroneous data: + Verify that you can open INBOX.new in IMAP or Pine. + The last message of INBOX.new is probably corrupted. Copy it to another file, such as badmsg.1, then delete and expunge that last message from INBOX.new + Locate the first occurance of text in INBOX.tail which looks like an internal header, as described above. + Remove all the text which occurs prior to that point, and place it into another file, such as badmsg.2. Note that in the case of a single digit date, there is a leading space which must not be removed (e.g. " 6-Nov-2001" not "6-Nov-2001"). Reassemble the mailbox: + Append INBOX.tail to INBOX.new. + You no longer need INBOX.tail. Delete it. + Verify that you can open INBOX.new in IMAP or Pine. Reinstall INBOX.new as INBOX: + Check to see if you have received any new messages while repairing INBOX. + If you haven't received any new messages while repairing INBOX, just rename INBOX.new to INBOX. + If you have received new messages, be sure to copy the new messages from INBOX to INBOX.new before doing the rename. You now have a working INBOX, as well as two files with corrupted data (badmsg.1 and badmsg.2). There may be some useful data in the two badmsg files that you might want to try salvaging; otherwise you can delete the two badmsg files. _________________________________________________________________ 7.16 What do the syslog messages: imap/tcp server failing (looping) pop3/tcp server failing (looping) mean? When it happens, the listed service shuts down. How can I fix this? The error message "server failing (looping), service terminated" is not from either the IMAP or POP servers. Instead, it comes from inetd, the daemon which listens for TCP connections to a number of servers, including the IMAP and POP servers. inetd has a limit of 40 new server sessions per minute for any particular service. If more than 40 sessions are initiated in a minute, inetd will issue the "failing (looping), service terminated" message and shut down the service for 10 minutes. inetd does this to prevent system resource consumption by a client which is spawning infinite numbers of servers. It should be noted that this is a denial of service; however for some systems the alternative is a crash which would be a worse denial of service! For larger server systems, the limit of 40 is much too low. The limit was established many years ago when a system typically only ran a few dozen servers. On some versions of inetd, such as the one distributed with most versions of Linux, you can modify the /etc/inetd.conf file to have a larger number of servers by appending a period followed by a number after the nowait word for the server entry. For example, if your existing /etc/inetd.conf line reads: imap stream tcp nowait root /usr/etc/imapd imapd try changing it to be: imap stream tcp nowait.100 root /usr/etc/imapd imapd Another example (using TCP wrappers): imap stream tcp nowait root /usr/sbin/tcpd imapd try changing it to be: imap stream tcp nowait.100 root /usr/sbin/tcpd imapd to increase the limit to 100 sessions/minute. Before making this change, please read the information in "man inetd" to determine whether or not your inetd has this feature. If it does not, and you make this change, the likely outcome is that you will disable IMAP service entirely. Another way to fix this problem is to edit the inetd.c source code (provided by your UNIX system vendor) to set higher limits, rebuild inetd, install the new binary, and reboot your system. This should only be done by a UNIX system expert. In the inetd.c source code, the limits TOOMANY (normally 40) is the maximum number of new server sessions permitted per minute, and RETRYTIME (normally 600) is the number of seconds inetd will shut down the server after it exceeds TOOMANY. _________________________________________________________________ 7.17 What does the syslog message: Mailbox lock file /tmp/.600.1df3 open failure: Permission denied mean? This usually means that some "helpful" security script person has protected /tmp so that it is no longer world-writeable. /tmp must be world-writeable because lots of applications use it for scratch space. To fix this, do chmod 1777 /tmp as root. If that isn't the answer, check the protection of the named file. If it is something other than 666, then either someone is hacking or some "helpful" person modified the code to have a different default lock file protection. _________________________________________________________________ 7.18 What do the syslog messages: Command stream end of file, while reading line user=... host=... Command stream end of file, while reading char user=... host=... Command stream end of file, while writing text user=... host=... mean? This message occurs when the session is disconnected without a proper LOGOUT (IMAP) or QUIT (POP) command being received by the server first. In many cases, this is perfectly normal; many client implementations are impolite and do this. Some programmers think this sort of rudeness is "more efficient". The condition could, however, indicate a client or network connectivity problem. The server has no way of knowing whether there's a problem or just a rude client, so it issues this message instead of a Logout. Certain inferior losing clients disconnect abruptly after a failed login, and instead of saying that the login failed, just say that they can't access the mailbox. They then complain to the system manager, who looks in the syslog and finds this message. Not very helpful, eh? See the answer to the Why can't I log in to the server? The user name and password are right! question. If the user isn't reporting a problem, you can probably ignore this message. _________________________________________________________________ 7.19 Why did my POP or IMAP session suddenly disconnect? The syslog has the message: Killed (lost mailbox lock) user=... host=... This message only happens when either the traditional UNIX mailbox format or MMDF format is in use. This format only allows one session to have the mailbox open read/write at a time. The servers assume that if a second session attempts to open the mailbox, that means that the first session is probably owned by an abandoned client. The common scenario here is a user who leaves his client running at the office, and then tries to read his mail from home. Through an internal mechanism called kiss of death, the second session requests the first session to kill itself. When the first session receives the "kiss of death", it issues the "Killed (lost mailbox lock)" syslog message and terminates. The second session then seizes read/write access, and becomes the new "first" session. Certain poorly-designed clients routinely open multiple sessions to the same mailbox; the users of those clients tend to get this message a lot. Another cause of this message is a background "check for new mail" task which does its work by opening a POP session to server every few seconds. They do this because POP doesn't have a way to announce new mail. The solution to both situations is to replace the client with a good online IMAP client such as Pine. Life is too short to waste on POP clients and poorly-designed IMAP clients. _________________________________________________________________ 7.20 Why does my IMAP client show all the files on the system, recursively from the UNIX root directory? 7.21 Why does my IMAP client show all of my files, recursively from my UNIX home directory? A well-written client should only show one level of hierarchy and then stop, awaiting explicit user action before going lower. However, some poorly-designed clients will recursively list all files, which may be a very long list (especially if you have symbolic links to directories that create a loop in the filesystem graph!). This behavior has also been observed in some third-party c-client drivers, including maildir drivers. Consequently, this problem has even been observed in Pine. It is important to understand that this is not a problem in Pine or c-client; it is a problem in the third-party driver. A Pine built without that third-party driver will not have this problem. See also the answer to Why does my IMAP client show all my files in my home directory? _________________________________________________________________ 7.22 Why does my IMAP client show that I have mailboxes named "#mhinbox", "#mh", "#shared", "#ftp", "#news", and "#public"? These are IMAP namespace names. They represent other hierarchies in which messages may exist. These hierarchies may not necessarily exist on a server, but the namespace name is still in the namespace list in order to mark it as reserved. A few poorly-designed clients display all namespace names as if they were top-level mailboxes in a user's list of mailboxes, whether or not they actually exist. This is a flaw in those clients. _________________________________________________________________ 7.23 Why does my IMAP client show all my files in my home directory? As distributed, the IMAP server is connected to your home directory by default. It has no way of knowing what you might call "mail" as opposed to "some other file"; in fact, you can use IMAP to access any file. Most clients have an option to configure your connected directory on the IMAP server. For example, in Pine you can specify this as the "Path" in your folder-collection, e.g. Nickname : Secondary Folders Server : imap.blurdybloop.com Path : mail/ View : In this example, the user is connected to the "mail" subdirectory of his home directory. Other servers call this the "folder prefix" or similar term. It is possible to modify the IMAP server so that all users are automatically connected to some other directory, e.g. a subdirectory of the user's home directory. Read the file CONFIG for more details. _________________________________________________________________ 7.24 Why is there a long delay before I get connected to the IMAP or POP server, no matter what client I use? There are two common occurances of this problem: + You are running a system (e.g. certain versions of Linux) which by default attempts to connect to an "IDENT" protocol (port 113) server on your client. However, a firewall or NAT box is blocking connections to that port, so the connection attempt times out. The IDENT protocol is a well-known bad idea that does not deliver any real security but causes incredible problems. The idea is that this will give the server a record of the user name, or at least what some program listening on port 113 says is the user name. So, if somebody coming from port nnnnn on a system does something bad, IDENT may give you the userid of the bad guy. The problem is, IDENT is only meaningful on a timesharing system which has an administrator who is privileged and users who are not. It is of no value on a personal system which has no separate concept of "system administrator" vs. "unprivileged user". On either type of system, security-minded people either turn IDENT off or replace it with an IDENT server that lies. Among other things, IDENT gives spammers the ability to harvest email addresses from anyone who connects to a web page. This problem has been showing up quite frequently on systems which use xinetd instead of inetd. Look for files named /etc/xinetd.conf, /etc/xinetd.d/imapd, /etc/inetd.d/ipop2d, and /etc/xinetd.d/ipop3d. In those files, look for lines containing "USERID", e.g. log_on_success += USERID Hunt down such lines, and delete them ruthlessly from all files in which they occur. Don't be shy about it. + The DNS is taking a long time to do a reverse DNS (PTR record) lookup of the IP address of your client. This is a problem in your DNS, which either you or you ISP need to resolve. Ideally, the DNS should return the client's name; but if it can't it should at least return an error quickly. As you may have noticed, neither of these are actual problems in the IMAP or POP servers; they are configuration issues with either your system or your network infrastructure. If this is all new to you, run (don't walk) to the nearest technical bookstore and get yourself a good pedagogical text on system administration for the type of system you are running. _________________________________________________________________ 7.25 Why is there a long delay in Pine or any other c-client based application call before I get connected to the IMAP server? The hang seems to be in the c-client mail_open() call. I don't have this problem with any other IMAP client. There is no delay connecting to a POP3 or NNTP server with mail_open(). By default, the c-client library attempts to make a connection through rsh (and ssh, if you enable that). If the command: rsh imapserver exec /etc/rimapd (or ssh if that is enabled) returns with a "* PREAUTH" response, it will use the resulting rsh session as the IMAP session and not require an authentication step on the server. Unfortunately, rsh has a design error that treats "TCP connection refused" as "temporary failure, try again"; it expects the "rsh not allowed" case to be implemented as a successful connection followed by an error message and close the connection. It must be emphasized that this is a bug in rsh. It is not a bug in the IMAP toolkit. The use of rsh can be disabled in any the following ways: + You can disable it for this particular session by either: o setting an explicit port number in the mailbox name, e.g. {imapserver.foo.com:143}INBOX o using SSL (the /ssl switch) + You can disable rsh globally by setting the rsh timeout value to 0 with the call: mail_parameters (NIL,SET_RSHTIMEOUT,0); _________________________________________________________________ 7.26 Why does a message sometimes get split into two or more messages on my SUN system? This is caused by an interaction of two independent design problems in SUN mail software. The first problem is that the "forward message" option in SUN's mail tool program includes the internal "From " header line in the text that it forwarded. This internal header line is specific to traditional UNIX mailbox files and is not suitable for use in forwarded messages. The second problem is that the mail delivery agent assumes that mail reading programs will not use the traditional UNIX mailbox format but instead an incompatible variant that depends upon a Content-Length: message header. Content-Length is widely recognized to have been a terrible mistake, and is no longer recommended for use in mail (it is used in other facilities that use MIME). One symptom of the problem is that under certain circumstances, a message may get broken up into several messages. I'm also aware of security bugs caused by programs that foolishly trust "Content-Length:" headers with evil values. To fix the mailer on your system, edit your sendmail.cf to change the Mlocal line to have the -E flag. A typical entry will lool like: Mlocal, P=/usr/lib/mail.local, F=flsSDFMmnPE, S=10, R=20, A=mail.local -d $u This fix will also work around the problem with mail tool, because it will insert a ">" before the internal header line to prevent it from being interpreted by mail reading software as an internal header line. _________________________________________________________________ 7.27 Why did my POP or IMAP session suddenly disconnect? The syslog has the message: Autologout user=<...my user name...> host=<...my client system...> This is a problem in your client. In the case of IMAP, it failed to communicate with the IMAP server for over 30 minutes; in the case of POP, it failed to communicate with the POP server for over 10 minutes. _________________________________________________________________ 7.28 What does the UNIX error message: TLS/SSL failure: myserver: SSL negotiation failed mean? 7.29 What does the PC error message: TLS/SSL failure: myserver: Unexpected TCP input disconnect mean? This usually means that an attempt to negotiate TLS encryption via the STARTTLS command failed, because the server advertises STARTTLS functionality, but doesn't actually have it (e.g. because no certificates are installed). Use the /notls option in the mailbox name to disable TLS negotiation. _________________________________________________________________ 7.30 What does the error message: TLS/SSL failure: myserver: Server name does not match certificate mean? An SSL or TLS session encryption failed because the server name in the server's certificate does not match the name that you gave it. This could indicate that the server is not really the system you think that it is, but can be also be called if you gave a nickname for the server or name that was not fully-qualified. You must use the fully-qualified domain name for the server in order to validate its certificate Use the /novalidate-cert option in the mailbox name to disable validation of the certificate. _________________________________________________________________ 7.31 What does the UNIX error message: TLS/SSL failure: myserver: self-signed certificate mean? 7.32 What does the PC error message: TLS/SSL failure: myserver: Self-signed certificate or untrusted authority mean? An SSL or TLS session encryption failed because your server's certificate is "self-signed"; that is, it is not signed by any Certificate Authority (CA) and thus can not be validated. A CA-signed certificate costs money, and some smaller sites either don't want to pay for it or haven't gotten one yet. The bad part about this is that this means there is no guarantee that the server is really the system you think that it is. Use the /novalidate-cert option in the mailbox name to disable validation of the certificate. _________________________________________________________________ 7.33 What does the UNIX error message: TLS/SSL failure: myserver: unable to get local issuer certificate mean? An SSL or TLS session encryption failed because your system does not have the Certificate Authority (CA) certificates installed on OpenSSL's certificates directory. On most systems, this directory is /usr/local/ssl/certs). As a result, it is not possible to validate the server's certificate. If CA certificates are properly installed, you should see factory.pem and about a dozen other .pem names such as thawteCb.pem. As a workaround, you can use the /novalidate-cert option in the mailbox name to disable validation of the certificate; however, note that you are then vulnerable to various security attacks by bad guys. The correct fix is to copy all the files from the certs/ directory in the OpenSSL distribution to the /usr/local/ssl/certs (or whatever) directory. Note that you need to do this after building OpenSSL, because the OpenSSL build creates a number of needed symbolic links. For some bizarre reason, the OpenSSL "make install" doesn't do this for you, so you must do it manually. _________________________________________________________________ 7.34 Why does reading certain messages hang when using Netscape? It works fine with Pine! There are two possible causes. Check the mail syslog. If you see the message "Killed (lost mailbox lock)" for the impacted user(s), read the FAQ entry regarding that message. Check the affected mailbox to see if there are embedded NUL characters in the message. NULs in message texts are a technical violation of both the message format and IMAP specifications. Most clients don't care, but apparently Netscape does. You can work around this by rebuilding imapd with the NETSCAPE_BRAIN_DAMAGE option set (see src/imapd/Makefile); this will cause imapd to convert all NULs to 0x80 characters. A better solution is to enable the feature in your MTA to MIME-convert messages with binary content. See the documentation for your MTA for how to do this. _________________________________________________________________ 7.35 Why does Netscape say that there's a problem with the IMAP server and that I should "Contact your mail server administrator."? Certain versions of Netscape do this when you click the Manage Mail button, which uses an undocumented feature of Netscape's proprietary IMAP server. You can work around this by rebuilding imapd with the NETSCAPE_BRAIN_DAMAGE option set (see src/imapd/Makefile) to a URL that points either to an alternative IMAP client (e.g. Pine) or perhaps to a homebrew mail account management page. _________________________________________________________________ 7.36 Why is one user creating huge numbers of IMAP or POP server sessions? The user is probably using Outlook Express, Eudora, or a similar program. See the answer to the Help! My load average is soaring and I see hundreds of POP and IMAP servers, many logged in as the same user! question. _________________________________________________________________ 7.37 Why don't I get any new mail notifications from Outlook Express or Outlook after a while? This is a known bug in Outlook Express. Microsoft is aware of the problem and its cause. They have informed us that they do not have any plans to fix it at the present time. The problem is also reported in Outlook 2000, but not verified. Outlook Express uses the IMAP IDLE command to avoid having to "ping" the server every few minutes for new mail. Unfortunately, Outlook Express overlooks the part in the IDLE specification which requires that a client terminate and restart the IDLE before the IMAP 30 minute inactivity autologout timer triggers. When this happens, Outlook Express displays "Not connected" at the bottom of the window. Since it's no longer connected to the IMAP server, it isn't going to notice any new mail. As soon as the user does anything that would cause an IMAP operation, Outlook Express will reconnect and new mail will flow again. If the user does something that causes an IMAP operation at least every 29 minutes, the problem won't happen. Modern versions of imapd attempt to work around the problem by automatically reporting fake new mail after 29 minutes. This causes Outlook Express to exit the IDLE state; as soon as this happens imapd revokes the fake new mail. As long as this behavior isn't known to cause problems with other clients, this workaround will remain in imapd. _________________________________________________________________ 7.38 Why don't I get any new mail notifications from Entourage? This is a known bug in Entourage. You built an older version of imapd with the MICROSOFT_BRAIN_DAMAGE option set, in order to disable support for the IDLE command. However, Entourage won't get new mail unless IDLE command support exists. Note: the MICROSOFT_BRAIN_DAMAGE option no longer exists in modern versions, as the Outlook Express problem which it attempted to solve has been worked around in another way. _________________________________________________________________ 7.39 Why doesn't Entourage work at all? It's hard to know. Entourage breaks almost every rule in the book for IMAP. It is highly instructive to do a packet trace on Entourage, as an example of how not to use IMAP. It does things like STATUS (MESSAGES) on the currently selected mailbox and re-fetching the same static data over and over again. It seems that every time we understand what it is doing wrong in Entourage and come up with a workaround, we learn about something else that's broken. Try building imapd with the ENTOURAGE_BRAIN_DAMAGE option set, in order to disable the diagnostic that occurs when doing STATUS on the currently selected mailbox. _________________________________________________________________ 7.40 Why doesn't Netscape Notify (NSNOTIFY.EXE) work at all? This is a bug in NSNOTIFY; it doesn't handle unsolicited data from the server correctly. Fortunately, there is no reason to use this program with IMAP; NSNOTIFY is a polling program to let you know when new mail has appeared in your maildrop. This is necessary with POP; but since IMAP dynamically announces new mail in the session you're better off (and will actually cause less load on the server!) keeping your mail reading program's IMAP session open and let IMAP do the notifying for you. Consequently, the recommended fix for the NSNOTIFY problem is to delete the NSNOTIFY binary. _________________________________________________________________ 7.41 Why can't I connect via SSL to Eudora? It says the connection has been broken, and in the server syslogs I see "Command stream end of file". There is a report that you can fix the problem by going into Eudora's advanced network configuration menu and increasing the network buffer size to 8192. _________________________________________________________________ 7.42 Sheesh. Aren't there any good IMAP clients out there? Yes! Pine is a wonderful client. It's fast, it uses IMAP well, and it generates text mail (life is too short to waste on HTML mail). Also, there are some really wonderful things in progress in the Pine world. There are some good GUI clients out there, mostly from smaller vendors. Without naming names, look for the vendors who are active in the IMAP protocol development community, and their products. Netscape, Eudora, and Outlook can be configured with enough effort to be good citizens and work well for users, but they can also be badly misconfigured, and often the misconfiguration is the default. _________________________________________________________________ 7.43 But wait! PC Pine (or other PC program build with c-client) crashes with the message incomplete SecBuffer exceeds maximum buffer size when I use SSL connections. This is a bug in c-client, right? It's a bug in the Microsoft SChannel.DLL, which implements SSL. Microsoft admits it (albeit with an unstatement: "it's not fully RFC compliant"). The problem is that SChannel indicates that the maximum SSL packet data size is 5 bytes smaller than the actual maximum. Thus, any IMAP server which transmits a maximum sized SSL packet will not work with PC Pine or any other program which uses SChannel. It can take a while for the problem to show up. The client has to do something that causes at least 16K of contiguous data. Many clients do partial fetching, which tends to reduce the number of cases where this can happen. However, all software which uses SChannel to support SSL is affected by this bug. This problem does not affect UNIX code, since OpenSSL is used on UNIX. This problem most recently showed up with the CommunigatePro IMAP server. They have an update which trims down their maximum contiguous data to less than 16K, in order to work around the problem. This problem has also shown up with the Exchange IMAP server with UNIX clients (including Pine built with an older version of c-client) which sends full-sized 16K SSL packets. Modern c-client works around the problem by trimming down its maximum outgoing SSL packet size to 8K. Microsoft has developed a hotfix for this bug. Look up MSKB article number 300562. Contrary to the article text which implies that this is a Pine issue, this bug also affect Microsoft Exchange server with *any* UNIX based client that transmits full-sized SSL payloads. _________________________________________________________________ 7.44 My qpopper users keep on getting the DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or IMAP. How can I fix this? This is an incompatibility between qpopper and the c-client library used by Pine, imapd, and ipop[23]d. Assuming that you want to continue using qpopper, look into qpopper's --enable-uw-kludge-flag configuration flag, which is documented as "check for and hide UW 'Folder Internal Data' messages". The other alternative is to switch from qpopper to ipop3d. _________________________________________________________________ 7.45 Help! I installed the servers but I can't connect to them from my client! Review the installation instructions carefully. Make sure that you have not skipped any of the steps. Make sure that you have made the correct entries in the configuration files; pay careful attention to the exact spelling of the service names and the path names. Make sure as well that you have properly restarted inetd. If you have a system with Yellow Pages/NIS such as Solaris, have you updated the service names there as well as in /etc/services? If you have a system with TCP wrappers, have you properly updated the TCP wrapper files (e.g. /etc/hosts.allow and /etc/hosts.deny) for the servers? If you have a system which uses xinetd instead of inetd, have you made sure that you have made the correct corresponding xinetd changes for those services? Try telneting to the server port (143 for IMAP, 110 for POP3). If you get a "refused" error, that probably means that you don't have the service set up in inetd.conf. If the connection opens and then closes with no message, the service is set up, but either the path name of the server binary in inetd.conf is wrong or your TCP wrappers are configured to deny access. If you don't know how to make the corresponding changes to these files, seek the help of a local expert for your system. _________________________________________________________________ 7.46 Why do I get the message Can not authenticate to SMTP server: 421 SMTP connection went away! and why did this happen? There was also something about SECURITY PROBLEM: insecure server advertised AUTH=PLAIN Some versions of qmail, including that running on mail.smtp.yahoo.com, disconnect the SMTP session if you fail to authenticate prior to attempting to transmit mail. An attempt to authenticate was made, but it failed because the server had already disconnected. To work around this, you need to specify /user=... in the host name specification. The SECURITY PROBLEM came about because the server advertised the AUTH=PLAIN SASL authentication mechanism outside of a TLS-encrypted session, in violation of RFC 2595. This message is just a warning, and in fact occurred after the server had disconnected. _________________________________________________________________ 7.47 Why do I get the message SMTP Authentication cancelled and why did this happen? There was also something about SECURITY PROBLEM: insecure server advertised AUTH=PLAIN This is a bug in the SMTP server. Some versions of qmail, including that running on mail.smtp.yahoo.com, have a bug in their implementation of SASL in their SMTP server, which renders it non-compliant with the standard. If the client does not provide an initial response in the command line for an authentication mechanism whose profile does not have an initial challenge, qmail issues a bogus response: 334 ok, go on The problem is the "ok, go on". This violates RFC 2554's requirement that the text part in a 334 response be a BASE64 encoded string; in other words, it is a protocol syntax error. In the case of AUTH=PLAIN, RFC 2222 (pp 4-5) requires that the encoded string have no data. In other words, the appropropiate standards-compliant server response is "334" followed by a SPACE and a CRLF. The SECURITY PROBLEM came about because the server advertised the AUTH=PLAIN SASL authentication mechanism outside of a TLS-encrypted session, in violation of RFC 2595. This message is just a warning, and is not related the "Authentication cancelled" problem. _________________________________________________________________ 7.48 Why do I get the message Invalid base64 string when I try to authenticate to a Cyrus server? This slightly misleading message is the way that a Cyrus server indicates that an authentication exchange was cancelled. It is not indicative of a bug or protocol violation. The most common reason that this happens is if the Cyrus server offers Kerberos authentication, c-client is built with Kerberos support, but your client system is not within the Kerberos realm. In this case, the client code will try to authenticate via Kerberos, fail to get the Kerberos credentials, cancel the authentication attempt, and try the next available authentication technology. _________________________________________________________________ 8. Licensing Questions _________________________________________________________________ 8.1 Why does the UW choose to make the IMAP toolkit available under this Free-Fork license? Unlike many other open source licenses, the Free-Fork license is non-viral. The Free-Fork license allows developers a broad range of rights to the IMAP toolkit software, and more options to combine the IMAP toolkit software with other open source or private-use software, without requiring that the terms of the Free-Fork license be enforced on that other software. The Free-Fork license also allows the IMAP toolkit software to be licensed under other conditions (for example, distribution with proprietary software where the IMAP code remains under the Free-Fork) if a need arises. _________________________________________________________________ 8.2 I want to redistribute a modified version of the IMAP toolkit. How can I do this? The Free-Fork license permits redistributions as long as the IMAP toolkit and any modifications to the toolkit remains under the Free-Fork license as open source. Send a message to imap-license@cac.washington.edu if your application will be proprietary and you need alternative licensing arrangements , or if you are unsure whether this blanket permission applies in your case. _________________________________________________________________ 8.3 Can I redistribute an unmodified version of the IMAP toolkit in a proprietary application? Yes. The terms of the Free-Fork license apply to all unmodified distributions. _________________________________________________________________ 8.4 Is the Free-Fork license compatible with GPL? Yes. A Free-Fork licensee has all the rights of a GPL licensee, plus additional rights provided under the Free-Fork. The Free-Fork does not require that software not specifically licensed under the Free-Fork use the same terms as the Free-Fork. For example, if you incorporate Free-Fork software in an application that you write, and put the application under GPL, that does not cause the Free-Fork software to become GPL. Someone else can copy your application, strip out the GPL code so that only the Free-Fork code remains, and by doing so have Free-Fork rights. Additionally, a Free-Fork licensee has the right to negotiate other license terms with the UW. _________________________________________________________________ 9. Where to Go For Additional Information _________________________________________________________________ 9.1 Where can I go to ask questions? 9.2 I have some ideas for enhancements to IMAP. Where should I go? If you have questions about the IMAP protocol, or want to participate in discussions of future directions of the IMAP protocol, the appropriate mailing list is imap@u.washington.edu. You must be a subscriber to post to the list; to subscribe, use imap-request@u.washington.edu If you have questions about this software, you can send me email directly or use the c-client@u.washington.edu mailing list. You can subscribe to this list via c-client-request@u.washington.edu As an alternative to either of these, you can use the comp.mail.imap newsgroup. _________________________________________________________________ 9.3 Where can I read more about IMAP and other email protocols? We recommend Internet Email Protocols: A Developer's Guide, by Kevin Johnson, published by Addison Wesley, ISBN 0-201-43288-9. _________________________________________________________________ 9.4 Where can I find out more about setting up and administering an IMAP server? We recommend Managing IMAP, by Dianna Mullet & Kevin Mullet, published by O'Reilly, ISBN 0-596-00012-X. This book also has an excellent comparison of the UW and Cyrus IMAP servers. Last Updated: 15 June 2004 tkrat_2.2cvs20100105-dfsg.orig/imap/docs/IPv6.txt000066400000000000000000000104221137544547100212060ustar00rootroot00000000000000The following information about configuring inetd and xinetd for IPv6 was contributed by a user. I have not checked it for accuracy or completeness, but have included it as-is in the hope that it may be useful: --------------------------------------------------------------------------- One thing you might consider adding to the docs are hints for setting up inetd or xinetd to simultaneously listen on BOTH IPv4 and IPv6 for different OS. Some OS want to see separate IPv4-only and IPv6-only listening sockets configured in inetd.conf or xinetd.conf. Others will accept IPv4 connections on lines configured for IPv6 and actually generate errors if you have both configured when inetd or xinetd start up. It's not clear to me whether this difference is due to how inetd or xinetd are built or whether it's due to the kernel's IP stack implementation. I suspect it's really the latter. Below are some examples: Here's a fragment of /usr/local/etc/xinetd.conf for a FreeBSD 4.9 server: service imap { socket_type = stream protocol = tcp wait = no user = root server = /usr/local/libexec/imapd } service imap { flags = IPv6 socket_type = stream protocol = tcp wait = no user = root server = /usr/local/libexec/imapd } service imaps { socket_type = stream protocol = tcp wait = no user = root server = /usr/local/libexec/imapd } service imaps { flags = IPv6 socket_type = stream protocol = tcp wait = no user = root server = /usr/local/libexec/imapd } Note that you have to specify a nearly identical paragraph for each service which differs only by the 'flags = IPv6'. An equivalent inetd.conf would look something like: imap stream tcp nowait root /usr/local/libexec/imapd imapd imap stream tcp6 nowait root /usr/local/libexec/imapd imapd imaps stream tcp nowait root /usr/local/libexec/imapd imapd imaps stream tcp6 nowait root /usr/local/libexec/imapd imapd The man pages for inetd suggest that if you use a single entry with 'tcp46' replacing either 'tcp' or 'tcp6' the system will listen on both types of addresses. At least for the case of FreeBSD this is actually incorrect. Now if you were to use the above xinetd.conf on Fedora Linux, it would complain. What does work under Linux is to create a single service paragraph for each service which will accept connections on both IPv4 and IPv6: In /etc/xinetd.d/imap: service imap { flags = IPv6 disable = no socket_type = stream wait = no user = root server = /usr/local/sbin/imapd } In /etc/xinetd.d/imaps: service imaps { flags = IPv6 disable = no socket_type = stream wait = no user = root server = /usr/local/sbin/imapd } The man page for xinetd says the IPv6 flag means xinetd will listen ONLY on IPv6. However the actual behaviour (for Fedora Linux) is to listen on both IPv4 and IPv6. All of the above also applies to ipop3d. Anyway, this might save some folks a little bit of head scratching time. --------------------------------------------------------------------------- Addendum from the original submitter: --------------------------------------------------------------------------- I've since learned that the discrepancy really is a function of the OS. It seems those systems that force you to create separate IPv4 and IPv6 listeners in inetd (or xinetd) are the same systems that also disable IPv4-mapped IPv6 addresses by default. This is a boot-time configuration option. If you enable IPv4-mapped IPv6 addresses, then the 'tcp46' option in inetd works and the simplified config would look like: imap4 stream tcp46 nowait root /usr/local/libexec/imapd imapd imaps stream tcp46 nowait root /usr/local/libexec/imapd imapd In FreeBSD, enabling IPv4-mapped addresses is done by adding ipv6_ipv4mapping="YES" to /etc/rc.conf (in addition to ipv6_enable="YES"). tkrat_2.2cvs20100105-dfsg.orig/imap/docs/RELNOTES000066400000000000000000000571111137544547100210450ustar00rootroot00000000000000Updated: 15 August 2005 imap-2004f is a maintenance release, and consists solely of a bugfix to the TCP code. Also included is a new version of the UNIX SSL/TLS routines that allows the SSL/TLS certificate validation client code to validate alternative names in server certificates. This code has not been thoroughly regression-tested but is believed to work. To use this new code instead of the old support: cd imap-2004f/src/osdep/unix mv ssl_unix.c ssl_unix.old mv ssl_unix.new ssl_unix.c Then rebuild. Updated: 21 June 2005 imap-2004e is a maintenance release, and consists solely of bugfixes. There are no user-visible functional enhancements in this version. Updated: 20 April 2005 imap-2004d is a maintenance release, released concurrently with Pine 4.63, and consists primarily of bugfixes There is now a workaround for RedHat breaking flock(). However, since RedHat has said that they don't support flock(), there is no guarantee that they won't break it in the future. So you may want to consider some other Linux distribution or BSD instead. See: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=123415 for the gruesome details. There are no user-visible functional enhancements in this version. Updated: 18 January 2005 imap-2004c is a maintenance release, released concurrently with Pine 4.62, including fixes to quoted-printable encoding and CRAM-MD5 authentication. NNTP proxy in imapd now supports the LIST and LSUB commands. There are no other user-visible functional enhancements in this version. Updated: 29 November 2004 imap-2004b is a maintenance release, consisting primarily of bugfixes. Programs written for imap-2004a will build with this version without modifification. There are new ports for Solaris with Blastwave Community Open Source Software (gcs) and Mandrake Linux (lmd). SET_SNARFINTERVAL now controls how frequently local drivers will move new mail from the mail spool as well as from a maildrop. Maildrops are still tied to a minimum interval of 1 minute, but there is now no minimum for the spool file. Character set conversions now map non-breaking space to space if the destination character set doesn't have nbsp. JIS Roman yen sign is now mapped to Unicode yen sign. There are no user-visible functional enhancements in this version. Updated: 8 July 2004 imap-2004a is a maintenance release, consisting primarily of critical bugfixes. Programs written for imap-2004 will build with this version without modification. imapd now has a supported NNTP proxy capability. If the file /etc/imapd.nntp exists, the contents of that file are used as the host name of an NNTP server which will be used whenever a #news. name is used. For example, if /etc/imapd.nntp contains nntp.example.com, and the IMAP client SELECTs or EXAMINEs the name #news.comp.mail.imap, what will actually be opened in imapd is {nntp.example.com/nntp}comp.mail.imap The OSF/1 port (Digital UNIX, Tru64) now uses flocksim instead of flcksafe. Some cretin decided to delete the winning flock() call and make flock() use the losing fcntl() call instead. The unix[nt] and mmdf drivers now prevent mail_append() from writing Status:, X-Status:, X-UID, X-IMAP[base]:, and X-Keywords: header lines to a traditional UNIX or MMDF format mailbox. If any such lines are in the text supplied to mail_append(), they will be quoted by prefixing with "X-Original-" (e.g. Status: will become X-Original-Status:). There are no user-visible functional enhancements in this version. Updated: 10 May 2004 imap-2004 is a major release. Programs written for imap-2002e should build with this version with minor modification. imap-2003 was not released except as development snapshots. mailutil has three new commands: delete, rename, and prune. IPv6 support now exists for UNIX and W2K. It is the default in W2K builds. On UNIX, add "IP=6" to the make command line. Windows IPv6 support is only for W2K builds. The NNTP driver now supports NNTP SASL and TLS. The ldb (Debian) and lrh (RedHat) ports now look for mlock on /usr/sbin/mlock instead of /etc/mlock. imapd now supports the LITERAL+ and SASL-IR initial-response extensions. The IMAP driver has some additional checks to reduce the amount of network traffic, including executing "silly searches" (searches of sequence numbers only) locally. The IMAP, POP, SMTP, and NNTP drivers now have diagnostic code to provide better information about servers which violate SASL's empty challenge requirements (e.g. with the PLAIN mechanism). There is a new mail_fetch_overview_sequence() function which is like mail_fetch_overview() but takes a sequence number string as an argument. There should have been a flags argument and FT_UID bit as in all the other mail_fetch_???() functions but compatibility with the past... :-( The overview_t callback (from mail_fetch_overview()) now has a fourth argument which contains the message sequence number (as opposed to the UID which is in the second argument). It turned out that some applications were calling mail_msgno() (which can be moderately expensive) to get the sequence number, and c-client already knew it. Many declarations which are completely internal to a driver have been removed from the driver .h file, and in those cases where there are no external declarations left the .h file has been eliminated entirely. As part of this, the mbox driver routines are now incorporated with the unix driver routines as opposed to being a separate file. The mbox driver still needs to be lunk in order to get the mbox functionality. Updated: 27 August 2003 imap-2002e is a minor release, released concurrently with Pine 4.58, and contains primarily bugfixes. Programs written for imap-2002d will build with this version without modification. The NNTP client code now tries to perform better with legacy NNTP servers which do not comply with the current NNTP protocol specification draft, most notably Netscape Collabra. Delivery notifications now work reliably with SMTP servers that support it. The following changes are primarily of concern to developers and power users: There is a "limited advertise" option in env_unix.c which, if set, will only advertise the user's own namespace and the #shared/ namespace. It is now possible to build the IMAP toolkit with a separate SSL KEY file from the certificate file (SSLKEYS vs. SSLCERTS). A new BODY structure element, sparep, is available for the main program to use as a pointer for its own purposes; as well as a SET_FREEBODYSPAREP function, similar to SET_FREEENVELOPESPAREP, SET_FREEELTSPAREP, etc. Updated: 28 May 2003 imap-2002d is a minor release, released concurrently with Pine 4.56, and contains primarily bugfixes. Programs written for imap-2002 should build with this version without modification, with one exception. That exception is the ngbogus envelope flag, which stopped being used in imap-2002c and is now gone for good. The NNTP newsgroup listing code now tries to use wildmats on the NNTP server, which should result in better performance especially on slow lines. It is also once again permitted to log in on NNTP servers when /loser is set. imapd now supports the UNSELECT command. A new envelope flag, imapenvonly, indicates that the envelope in a MESSAGE/RFC822 BODY structure only has the IMAP envelope components and not the additional components from c-client: Newsgroups, Followup-To, and References. Updated: 7 April 2003 imap-2002c is a minor release, released concurrently with Pine 4.55, and contains primarily bugfixes. Programs written for imap-2002 will build with this version without modification. The POP3 driver will, with new servers that support CAPA, use the LIST command to get the elt->rfc822_size and the TOP command to get the message header, instead of fetching the entire message. Note that it is a bad idea to do this with old servers, since they may misimplement LIST and TOP. The result is a substantial performance improvement. Subject extraction for comparisons in SORT and THREAD are now done in full compliance with the rules laid out in the specification. This only makes a difference if "re:" was part of a MIME quoted-word. The new experimental #move namespace allows download-and-delete from a source mailbox to a destination mailbox. Immediately following #move is a delimiter character which must not appear in the source mailbox name, then the source mailbox name, then the delimiter again, then the destination mailbox name. For example: #move+{pop3.foo.com/pop3}+INBOX will download messages from "pop3.foo.com" into your local INBOX. The NNTP driver now uses the LIST EXTENSIONS command as described in the current NNTP protocol specification draft, and will prefer to use OVER over XOVER, HDR over XHDR, etc. The SET_NNTPRANGE function of mail_parameters() can be used to limit the number of articles recognized by the NNTP driver, resulting in a substantial performance improvement with NNTP servers that may have hundreds of thousands of old articles in the spool. If set non-zero, then only the last n article numbers will be considered. If you are on a slow link, you may want to set this to 1000 or less. Besides the normally tested UNIX and 32-bit Microsoft platforms, this release has also been tested and will once build under TOPS-20 and VAX/VMS. I also fixed a bug which would keep it from building on 16-bit DOS, but I don't know if it will build on that platform or not since I no longer have a system with the old DOS C compiler. It has not been tested on Macintosh (note however that Mac OS X is a type of UNIX and should build), Amiga, or OS/2, and probably no longer builds on those platforms. Updated: 7 January 2003 imap-2002b is a maintenace release, released concurrently with Pine 4.52, and contains only bugfixes. Programs written for imap-2002 will build with this version without modification. Drivers which do not announce new mail are now indicated by the DR_NONEWMAIL driver flag. Driver which do not announce new mail when read-only are now indicated by the DR_NONEWMAILRONLY flag. There are no user-visible functional enhancements in this version. Updated: 10 December 2002 imap-2002a is a maintenance release, consisting entirely of critical bugfixes. Programs written for imap-2002 will build with this version without modification. There are no functional enhancements in this version. Updated: 28 October 2002 imap-2002 is a major release. Programs written for imap-2001 will probably build with this version without modification, with one exception. That exception is if the program uses [GS]ET_DISABLEAUTOMATICSHAREDNAMESPACES, which has been renamed to [GS]ET_DISABLEAUTOSHAREDNS in order to placate some compilers which don't like very long names. SSLTYPE=nopwd is now the default, in accordance with current IESG security requirements. In order to build the IMAP toolkit without SSL/TLS you must now use SSLTYPE=none. At initial build time, you will be told if the SSLTYPE setting is in compliance with IESG security requirements, and if it is not you will be asked to confirm to continue the build. ORDEREDSUBJECT threading has been changed in accordance with draft 12 of the IMAP threading specification. Previously, each non-root message in an ORDEREDSUBJECT thread has been a child of the message immediately preceeding it in the thread. Draft 12 changes this so that the second message in the thread is the child of the first (root) message, and all subsequent messages are siblings of the first message. This is significant in MUAs which display the thread structure graphically; the new definition is much saner than the old one since it does not nest endlessly due to parent/child relationships that may not exist. This also impacts imapd, since imapd's THREAD command will return a thread structure. RFC 1730 server support, which was disabled in imap-2001, is now fully removed from imapd. imapd still supports IMAP2bis, specifically the FIND command, since there are still a few IMAP2 clients out there. The IMAP client routines in the c-client library continue to support recognize RFC 1730 servers, but do not implement the deprecated features of RFC 1730. The Frequently Asked Questions file is now in HTML format, although a text version (generated from the HTML version with Lynx) is also provided. A new program, mailutil, is now bundled with the IMAP toolkit. mailutil replaces the old chkmail, imapcopy, imapmove, imapxfer, mbxcopy, mbxcreat, and mbxcvt programs that were distributed in the imap-utils. In addition, the tmail, dmail, and mlock programs from the imap-utils are now also bundled with the IMAP toolkit. In addition to the usual bugfixes, the following c-client functionalities are new in imap-2002: The SET_DISABLE822TZTEXT parameter allows a client to suppress generation of the "human friendly" time zone text in RFC822 dates. This placates netnews and some broken SMTP servers which think that long timezone names from Windows are an attempt at a buffer overflow attack. The restrictBox option in env_unix.c sets "restricted box" functionality, which disables access to the root (leading "/"), access to other user's directories (leading "~"), and access to superior directories via "..". Content-Location is now supported by the "location" member of the BODY structure. Note that there is a bug in the IMAP client code in older versions of the c-client library that causes it to handle BODYSTRUCTURE extension data improperly if that data is a literal. The new functionality for Content-Location may trigger this bug. The fix is either to upgrade the IMAP client program to the imap-2002 version of c-client or to remove the Content-Location support from imapd. There are now 8 spare bits for application use in both the elts and the mail streams. mail_search() now returns a value (previously it was void). If mail_search() returns NIL, then the supplied charset was invalid or the IMAP server returned NO (probably because the supplied charset was invalid). New utf8_charset() routine to look up a charset and return c-client's database about that charset if found. Among other things, this will give you the scripts supported by that charset and its Unicode conversion table. New FT_NOLOOKAHEAD flag for mail_fetch_structure() disables fetching of any envelopes other than the one specified. Otherwise, it will try to do anticipatory fetching (up to IMAPLOOKAHEAD). New GET_FETCHLOOKAHEAD allows better control of mail_fetch_structure() lookahead. Instead of looking IMAPLOOKAHEAD messages forward from the specified message, it will use a supplied SEARCHSET to generate message sequences and ranges. It will stop at IMAPLOOKAHEAD messages or at the completion of a range which exceeds IMAPLOOKAHEAD. The search set only applies to the next mail_fetch_structure() on that stream, and is cleared once it is used. Call with SEARCHSET **set = (SEARCHSET **) mail_parameters (stream,GET_FETCHLOOKAHEAD,(void *) stream); *set = pointer to desired search set New mail_shortdate() routine returns an date in the format expected by SEARCHPGMs. Updated: 2 November 2001 imap-2001a is a maintenance release, consisting primarily of bugfixes including some critical bugfixes to crash and denial of service problems. Programs written for imap-2001 will build with this version without modification. The following new facilities have also been added: The new /norsh switch in mailbox names provides a more intuitive way of disabling rsh-IMAP than the existing :143 or setting the rsh-timeout to 0. Passwords are no longer returned in mm_dlog() callbacks unless the application sets the SET_DEBUGSENSITIVE parameter. The SET_NETFSSTATBUG parameter allows an application to force the traditional UNIX mailbox driver to close and reopen the mailbox at ping time. This is EXTREMELY inefficient, and should only be used to access files stored on AFS and old NFS systems. The ISO 8859 and Windows conversion tables have been updated to comply with Unicode 3.1, and the KOI8-R table has been verified as compliant with Unicode 3.1. The SPECIALS mechanism for passing parameters to the lowest level Makefile has been updated to be more general. See the next item for why you might care. New lrh port to build on Red Hat Linux 7.2, with pre-set definitions for the places where Red Hat has placed Kerberos and SSL. It's actually just the lnp port with SPECIALS defined accordingly. You may want to use it as a model if your system needs such definitions. Note that SPECIALS is primarily for IMAP toolkit (and Pine) purposes, and that user settings should use EXTRASPECIALS instead. Updated: 22 June 2001 imap-2001 is a major release. Programs written for imap-2000 will probably build with this version without modification. The FAQ document has been significantly expanded. Be sure to read it for more information. In addition to the usual bugfixes, the following features are new in imap-2001: SSL is now fully integrated into the IMAP toolkit; the old "alt" kludges to be able to produce a "sanitized" version of the IMAP toolkit to comply with late unlamented US export regulations are now completely gone. Full client and server TLS support is also in this release. The server certificate must be signed by a trusted certificate authority and the name in the certificate match the user's entry for the server host name; this means that the user must enter a fully-qualified host name. To build with SSL/TLS on UNIX, you now use "SSLTYPE=unix" instead of the former "SPECIALAUTHENTICATORS=ssl". To build with SSL/TLS on UNIX and disable the use of plaintext passwords except when under SSL/TLS, use "SSLTYPE=nopwd" instead of "SSLTYPE=unix". RFC 1730 (IMAP4 as opposed to IMAP4rev1) support is turned off by default in imapd. No clients should still be using RFC 1730 protocol. Look at the imapd Makefile for how to re-enable RFC 1730 support. Note that this code may be removed in the future, so if you think you need it you had better let me know. There are some new options (turned off by default) which attempt to work around problems in certain clients. See the FAQ file for more details. Updated: 24 January 2001 imap-2000c is a maintenance release, consisting primarily of bugfixes. Updated: 9 January 2001 imap-2000b is a maintenance release, consisting primarily of bugfixes. Updated: 9 November 2000 imap-2000a is a maintenance release, consisting primarily of bugfixes. Updated: 19 September 2000 imap-2000 is a major release. There are major internal and external changes from earlier versions (imap-4.x and imap-3.x series). Programs written for imap-4.x will probably build with this version without modification. It is extremely unlikely that a program written for imap-3.x or earlier series will build with this version without modifications. Drivers written for earlier versions will definitely need to be rewritten. In addition to the usual bugfixes, the following features are new in imap-2000: SSL support is now available. For UNIX, it is necessary to install some version of OpenSSL; see imap-2000/docs/SSLBUILD for more information. SSL support is now automatic for the NT, NTK, and W2K ports. SSL use is indicated by the /ssl switch in the mailbox name. With SSL connections, the server certificate is validated by the client code on UNIX, and Windows 2000 unless /novalidate-cert is specified. Server certificates are currently is not validated on Windows 9x, Windows Millenium, or Windows NT 4; this is an artifact of the operating system and not the port (e.g. client code using the NT port will validate certificates if running on Windows 2000). On UNIX, the server certificate must be signed by a trusted certificate authority. On Windows 2000, the certificate must be signed by a trusted certificate authority and match the user's entry for the server host name; this means that the user must enter a fully-qualified host name. Calendar reclama for the benefit of old broken non-Y2K compliant software. Two digit years from 00 to 69 will be interpreted as 2000 through 2069. In addition, three digit years from 100 to 105 will be interpreted as 2000 through 2005. Support for REFERENCES threading (in addition to the previously-existing ORDEREDSUBJECT threading). Support for the IMAP MULTIAPPEND extension. This allows much faster uploading of multiple messages to an IMAP server. Support for the LOGINDISABLED IMAP capability. If the IMAP server sends LOGINDISABLED as a capability, the client code will never attempt to send an IMAP LOGIN command. Support for SASL authentication identity vs. authorization identity. If the authentication method does not support this concept (e.g. AUTH=CRAM-MD5, AUTH=LOGIN, LOGIN command), the "*" character in the user name may be used to indicate a separate authentication identity; for example, "fred*joe" indicates authorization identity "fred", authentication identity "joe". UNIX-specific Changes: Support for SASL authentication identity vs. authorization identity in the IMAP and POP3 servers. If the user indicated by the authentication identity is in the "mailadm" group, he may specify any authorization identity and get logged in as the authorization identity user. If the IMAP and POP3 servers are build with PASSWDTYPE=nul, it will send LOGINDISABLED as a capability and also disable the AUTH=LOGIN and AUTH=PLAIN SASL authenticators. New MAILSUBDIR build option to change the default mailbox directory from the user's home directory to a subdirectory of the user's home directory. See imap-2000/Makefile for more information. New CHROOT_SERVER build option for closed server systems only. If defined, a chroot() call to the user's home directory is done as part of the login process. See imap-2000/Makefile for more information. New ADVERTISE_THE_WORLD build option which will add an IMAP namespace that points to the root. Not for the faint of heart. UNIX format mailboxes no longer require the pseudo-message, nor will a pseudo-message be added to a mailbox that does not have one. A new X-IMAPbase: header will be written in the first message. This is rather less efficient and robust than the pseudo-message (which remains the encouraged mechanism; UNIX format mailboxes will always be created with it), but perhaps will pacify some people who get upset by the pseudo-message. When building with MIT Kerberos it will try to detect and use libk5crypto.a instead of libcrypto.a. The mbx driver is more aggressive about cleaning up expunged messages that couldn't be purged because of shared access to the mailbox at the time of expunge. Now, every checkpoint will try to purge such messages; and a checkpoint is attempted at close time. Windows-specific Changes: New W2K port for Windows 2000. In addition to supporting SSL using the official SSPI interface (the NT and NTK ports invoke SChannel.DLL directly), the W2K port also supports Microsoft Kerberos. Note that the NT and NTK ports will work on Windows 2000, but the W2K port will not work on NT4, Windows 9x, or Windows Millenium. There is now a #user namespace, equivalent to the "~" namespace on UNIX. Changes for Developers: New c-client.h file which acts as a master include. c-client based applications should now include c-client.h instead of the individual c-client files (mail.h, misc.h, etc.). It is believed that c-client.h will work in C++ applications. New GET_FREEENVELOPESPAREP/SET_FREEENVELOPESPAREP and GET_FREEELTSPAREP/SET_FREEELTSPAREP function callbacks to free the "sparep" member of the envelope and cache elements, respectively. New OP_MULNEWSRC flag to mail_open() to use multiple newsrc files, and new GET_NEWSRCQUERY/SET_NEWSRCQUERY function callbacks to get the name of the newsrc file for news access. New "secret" nntp_article() function to do the NNTP ARTICLE command; this is generally useful only when chasing news URLs. New GET_HIDEDOTFILES/SET_HIDEDOTFILES feature to suppress file names that start with "." in mail_list() results. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/SSLBUILD000066400000000000000000000220761137544547100210350ustar00rootroot00000000000000 SSL/TLS BUILD AND INSTALLATION NOTES FOR UNIX Last Updated: 5 January 2004 PREREQUISITES BEFORE STARTING: 1) Review the information in imap-2004/docs/BUILD. 2) Obtain a copy of OpenSSL. OpenSSL is available from third parties. We do not provide OpenSSL. 3) Make sure that you know how to build OpenSSL properly on the standard /usr/local/ssl directory. In particular, /usr/local/ssl/include (and /usr/local/ssl/include/openssl) and /usr/local/ssl/lib must be set up from the OpenSSL build. If you have a non-standard installation, then you must modify the imap-2004/src/osdep/unix/Makefile file to point to the appropriate locations. 4) Make sure that you know how to obtain appropriate certificates on your system. NOTE: We can NOT provide you with support in building/installing OpenSSL, or in obtaining certificates. If you need help in doing this, try the contacts mentioned in the OpenSSL README. SSL BUILD: By default, the IMAP toolkit builds with SSL and disabling plaintext passwords unless SSL/TLS encryption is in effect (SSLTYPE=nopwd). Note that doing so will produce an IMAP server which is NON-COMPLIANT with current IESG security requirements. To build with SSL but allow plaintext passwords in insecure sessions, add "SSLTYPE=unix" to the make command line. There are other make options relevant to SSL, described in imap-2004/src/osdep/unix/Makefile The most important of these are SSLDIR, SSLCRYPTO, and SSLRSA. SSLDIR is set to /usr/local/ssl by default. This is the normal installation directory for OpenSSL. If your system uses a different directory you will need to change this. SSLCRYPTO is set to -lcrypto by default. Older versions of MIT Kerberos also have a libcrypto and will cause a library name conflict. If you are using an older version of Kerberos, you may need to change SSLCRYPTO to $(SSLLIB)/libcrypto.a SSLRSA is set empty by default. It can be set to specify the RSAREF libraries, which you once had to use with OpenSSL to use RSA algorithms legally if you are in the USA, due to patent issues. Since RSA Security Inc. released the RSA algorithm into the public domain on September 6, 2000, there is no longer any reason to do this. SSL INSTALLATION: Binaries from the build are: imap-2004/mtest/mtest c-client testbed program imap-2004/ipopd/ipop2d POP2 daemon imap-2004/ipopd/ipop3d POP3 daemon imap-2004/imapd/imapd IMAP4rev1 daemon mtest is normally not used except by c-client developers. STEP 1: inetd setup The ipop2d, ipop3d, and imapd daemons should be installed in a system daemon directory (in the following examples, /usr/local/etc is used), and invoked by your /etc/inetd.conf file with lines such as: pop stream tcp nowait root /usr/local/etc/ipop2d ipop2d pop3 stream tcp nowait root /usr/local/etc/ipop3d ipop3d imap stream tcp nowait root /usr/local/etc/imapd imapd pop3s stream tcp nowait root /usr/local/etc/ipop3d ipop3d imaps stream tcp nowait root /usr/local/etc/imapd imapd Please refer to imap-2004/docs/BUILD for an important note about inetd's limit on the number of new connections. If that note applies to you, and you can configure the number of connection in /etc/inetd.conf as described in imap-2004/docs/build, here is the sample /etc/inetd.conf entry with SSL: pop3 stream tcp nowait.100 root /usr/local/etc/ipop3d ipop3d pop3s stream tcp nowait.100 root /usr/local/etc/ipop3d ipop3d imap stream tcp nowait.100 root /usr/local/etc/imapd imapd imaps stream tcp nowait.100 root /usr/local/etc/imapd imapd (or, if you use TCP wrappers) pop3 stream tcp nowait.100 root /usr/local/etc/tcpd ipop3d imap stream tcp nowait.100 root /usr/local/etc/tcpd imapd pop3s stream tcp nowait.100 root /usr/local/etc/ipop3d ipop3d imaps stream tcp nowait.100 root /usr/local/etc/imapd imapd NOTE: do *NOT* use TCP wrappers (tcpd) for the imaps and pop3s services! I don't know why, but it doesn't work with TCP wrappers. STEP 2: services setup You may also have to edit your /etc/services (or Yellow Pages, NetInfo, etc. equivalent) to register these services, such as: pop 109/tcp pop3 110/tcp imap 143/tcp imaps 993/tcp pop3s 995/tcp NOTE: The SSL IMAP service *MUST* be called "imaps", and the SSL POP3 service *MUST* be called "pop3s". STEP 3: certificates setup NOTE: We can NOT provide you with support in obtaining certificates. If you need help in doing this, try the contacts mentioned in the OpenSSL README. WARNING: Do NOT install servers built with SSL support unless you also plan to install proper certificates! It is NOT supported to run SSL-enabled servers on a system without the proper certificates. You must set up certificates on /usr/local/ssl/certs (this may be different if you have a non-standard installation of OpenSSL; for example, FreeBSD has modified OpenSSL to use /usr/local/certs). You should install both the certificate authority certificates from the SSL distribution after building OpenSSL, plus your own certificates. The latter should have been purchased from a certificate authority, although self-signed certificates are permissible. A sample certificate file is at the end of this document. Install the resulting certificate file on /usr/local/ssl/certs, with a file name consisting of the server name and a suffix of ".pem". For example, install the imapd certificate on /usr/local/ssl/certs/imapd.pem and the ipop3d certificate on /usr/local/ssl/certs/ipop3d.pem. These files should be protected against random people accessing them. It is permissible for imapd.pem and ipop3d.pem to be links to the same file. The imapd.pem and ipop3d.pem must contain a private key and a certificate. The private key must not be encrypted. The following command to openssl can be used to create a self-signed certificate with a 10-year expiration: req -new -x509 -nodes -out imapd.pem -keyout imapd.pem -days 3650 *** IMPORTANT *** We DO NOT recommend, encourage, or sanction the use of self-signed certificates. Nor will we be responsible for any problems (including security problems!) which result from your use of a self-signed certificate. Use of self-signed certificates should be limited to testing only. Buy a real certificate from a certificate authority! *** IMPORTANT *** If you have a multihomed system with multiple domain names (and hence separate certificates for each domain name), you can append the IP address to the service name. For example, the IMAP certificate for [12.34.56.78] would be /usr/local/ssl/certs/imapd-12.34.56.78.pem and so on. You only need to use this feature if you need to use multiple certificates (because different DNS names are used). SAMPLE CERTIFICATE FILE Here is a sample certificate file. Do *NOT* use this on your own machine; it is simply an example of what one would look like. -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDHkqs4YDbakYxRkYXIpY7xLXDQwULR5LW7xWVzuWmmZJOtzwlP 7mN87g+aaiQzwXUVndaCw3Zm6cOG4mytf20jPZq0tvWnjEB3763sorpfpOe/4Vsn VBFjyQY6YdqYXNmjmzff5gTAecEXOcJ8CrPsaK+nkhw7bHUHX2X+97oMNQIDAQAB AoGBAMd3YkZAc9LUsig8iDhYsJuAzUb4Qi7Cppj73EBjyqKR18BaM3Z+T1VoIpQ1 DeXkr39heCrN7aNCdTh1SiXGPG6+fkGj9HVw7LmjwXclp4UZwWp3fVbSAWfe3VRe LM/6p65qogEYuBRMhbSmsn9rBgz3tYVU0lDMZvWxQmUWWg7BAkEA6EbMJeCVdAYu nQsjwf4vhsHJTChKv/He6kT93Yr/rvq5ihIAPQK/hwcmWf05P9F6bdrA6JTOm3xu TvJsT/rIvQJBANv0yczI5pUQszw4s+LTzH+kZSb6asWp316BAMDedX+7ID4HaeKk e4JnBK//xHKVP7xmHuioKYtRlsnuHpWVtNkCQQDPru2+OE6pTRXEqT8xp3sLPJ4m ECi18yfjxAhRXIU9CUV4ZJv98UUbEJOEBtx3aW/UZbHyw4rwj5N511xtLsjpAkA9 p1XRYxbO/clfvf0ePYP621fHHzZChaUo1jwh07lXvloBSQ6zCqvcF4hG1Qh5ncAp zO4pBMnwVURRAb/s6fOxAkADv2Tilu1asafmqVzpnRsdfBZx2Xt4oPtquR9IN0Q1 ewRxOC13KZwoAWtkS7l0mY19WD27onF6iAaF7beuK/Va -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIECTCCA3KgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBujELMAkGA1UEBhMCVVMx EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxHzAdBgNVBAoT FkJsdXJkeWJsb29wIEluZHVzdHJpZXMxFjAUBgNVBAsTDUlTIERlcGFydG1lbnQx ITAfBgNVBAMTGEJvbWJhc3RpYyBULiBCbHVyZHlibG9vcDEoMCYGCSqGSIb3DQEJ ARYZYm9tYmFzdGljQGJsdXJkeWJsb29wLmNvbTAeFw0wMDA2MDYwMDUxMTRaFw0x MDA2MDQwMDUxMTRaMIG6MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv bjEQMA4GA1UEBxMHU2VhdHRsZTEfMB0GA1UEChMWQmx1cmR5Ymxvb3AgSW5kdXN0 cmllczEWMBQGA1UECxMNSVMgRGVwYXJ0bWVudDEhMB8GA1UEAxMYQm9tYmFzdGlj IFQuIEJsdXJkeWJsb29wMSgwJgYJKoZIhvcNAQkBFhlib21iYXN0aWNAYmx1cmR5 Ymxvb3AuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHkqs4YDbakYxR kYXIpY7xLXDQwULR5LW7xWVzuWmmZJOtzwlP7mN87g+aaiQzwXUVndaCw3Zm6cOG 4mytf20jPZq0tvWnjEB3763sorpfpOe/4VsnVBFjyQY6YdqYXNmjmzff5gTAecEX OcJ8CrPsaK+nkhw7bHUHX2X+97oMNQIDAQABo4IBGzCCARcwHQYDVR0OBBYEFD+g lcPrnpsSvIdkm/eol4sYYg09MIHnBgNVHSMEgd8wgdyAFD+glcPrnpsSvIdkm/eo l4sYYg09oYHApIG9MIG6MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv bjEQMA4GA1UEBxMHU2VhdHRsZTEfMB0GA1UEChMWQmx1cmR5Ymxvb3AgSW5kdXN0 cmllczEWMBQGA1UECxMNSVMgRGVwYXJ0bWVudDEhMB8GA1UEAxMYQm9tYmFzdGlj IFQuIEJsdXJkeWJsb29wMSgwJgYJKoZIhvcNAQkBFhlib21iYXN0aWNAYmx1cmR5 Ymxvb3AuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAwEEk JXpVXVaFTuG2VJGIzPOxQ+X3V1Cl86y4gM1bDbqlilOUdByUEG4YfSb8ILIn+eXk WzMAw63Ww5t0/jkO5JRs6i1SUt0Oy80DryNRJYLBVBi499WEduro8GCVD8HuSkDC yL1Rdq8qlNhWPsggcbhuhvpbEz4pAfzPkrWMBn4= -----END CERTIFICATE----- tkrat_2.2cvs20100105-dfsg.orig/imap/docs/Y2K000066400000000000000000000124031137544547100202120ustar00rootroot00000000000000QUESTION: Is c-client Y2K compliant? ANSWER: There are no known Y2K issues in c-client; nor have there ever been any known Y2K issues in c-client from its inception. Some older versions of c-client don't like the two-digit year "00", although the only impact of this is that messages with that year will sort before any other messages. Nobody should be using two-digit years in email messages any more (use "2000" instead of "00"). You may wish to read the document calendar.txt for more information about the Y4K, Y20K, and Y45K issues. Assuming that c-client is still around in 2000-43,000 years, someone will have to deal with these. Within the plausible lifetimes of people today, there are three known date-related issues in c-client which will have to be addressed in the future. If I am still alive when the first problem hits, I will be nearly 82 years old, and won't be maintaining c-client any more. Y2038: c-client, like most UNIX software, has Y2038 issues. On Tuesday, January 19, 2038 at 03:14:08 Coordinated Universal Time (also known as UTC, UT, or historically GMT), the clock on 32-bit UNIX systems will wrap around to a negative number; that is, from 0x7fffffff to 0x80000000. c-client uses an unsigned long for its 32-bit time; however the C library on most UNIX systems uses a signed long and will interpret that time as being Friday, December 13, 1901 at 20:45:52 UTC. Fixing this problem will require changing the C library to use either unsigned longs or a wider (e.g. 64-bit) value for time. Lots of work will need to be done on 32-bit UNIX systems as 2038 approaches. History suggests that most of the work will be done in the autumn of 2037... ;-) It's not known if anything is necessary to do to c-client other than just rebuild it with the new C library. Going to 32-bit unsigned longs means that there will be a Y2106 bug that someone will have to fix. Hopefully nobody will even think of using 32-bit systems by then. Y2070: c-client assumes that 2-digit years with values of 70 or greater are in the 20th century, and that 2-digit years with values of 69 or less are in the 21st century. Time for UNIX began on January 1, 1970 and email on ARPAnet happened between the first TENEX systems shortly after that; consequently there is no ambiguity with email data with 2-digit years prior to the year 2070. This is used only when parsing a 2-digit year. c-client never generates one. Fixing this problem requires convincing people not to use 2-digit years. This is a lesson that people should have figured out 70 years earlier with Y2K. Consequently, this may be a "non-problem." Otherwise, look in mail_parse_date() for the comment "two digit year" and change the statement as desired. [Note: do not change the definition of BASEYEAR since the UNIX port assumes that this matches when time began in the operating system.] Y2098: On January 1, 2098, the year in per-message internal dates will expire, since a 7-bit field is allocated for the year. c-client will mistakenly think that the day is January 1, 1970. Fortunately, it is easy to fix this problem. Just increase the width of "year" in MESSAGECACHE in mail.h. If you make it 8 bits, it'll be good until January 1, 2216; 9 bits makes it good until 2482. 10 bits will push it back that you'd worry about the Y2800 question before having to increase it again. If you ignore Y2800, 11 bits will push it it back to having to worry about Y4K first. Y2800: At this year, you will need to decide whether to keep the Gregorian calendar, which is one day slow every 20,000 years, or go to the more accurate Eastern Orthodox calendar which is one day slow every 45,000 years. The Gregorian and Eastern Orthodox calendars diverge at this year. There hasn't been any statement about how the international community will deal with the situation of the Orthodox calendar being one day ahead of the Gregorian calendar between 2800 and 2900. This will happen again between 3200 and 3300, and at gradually increasing intervals until 48,300 when the shift becomes permanent (assuming no Y20K or Y45K fixes). If you wish to make the transition to the Eastern Orthodox calendar, rebuild c-client with -DUSEORTHODOXCALENDAR=1. You can then ignore Y4K and Y20K! Y4K: A little-known rule in the Gregorian calendar is that years that are evenly divisible by 4000 are not leap years. Unlike the other rules, this rule hasn't had effect yet, and won't for another 2000 years. To fix the Y4K problem, just rebuild c-client with -DY4KBUGFIX=1. Y20K: Those of you who stuck with the Gregorian calendar have a problem; the calendar is now one day slow. The Pope has not made any statement about how this problem will be fixed. Maybe they'll declare that 20,004 is also not a leap year or something. There is no fix for this problem in c-client. Y45K: Greeks, Serbs, Russians, and other Eastern Orthodox have spent the past 41,000 years laughing at westerners' increasingly futile efforts to keep the Gregorian calendar in order. The day of reckoning has come; the Orthodox calendar is now one day slow. The Patriarch of Istanbul (nee Constantinople) has not made any statement about how this will be fixed. There is no fix for this problem in c-client. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/bugs.txt000066400000000000000000000227721137544547100213750ustar00rootroot00000000000000 KNOWN BUGS/MISFEATURES/DEFICIENCIES IN THE IMAP TOOLKIT Last Updated: 22 January 2004 The following are known problems/deficiencies in the imap-2004 toolkit: . Possible problems for some installations: . In some versions of Redhat Linux, SVR4-style timezone name lookup doesn't work properly due to a bug in glibc. The workaround is to edit os_lnx.c to include tz_bsd.c instead of tz_sv4.c. Note that other versions of Linux don't support BSD-style timezone name lookup, so don't make this change unless it's needed on your system. . In some systems, the OpenSSL distribution is installed other than at the standard /usr/local/ssl location. If this is the case on your system and you want to build with SSL support, you will need to set the SSLDIR variable, either by including a setting of EXTRASPECIALS in the make command line, e.g. build lnp SPECIALAUTHENTICATORS=ssl EXTRASPECIALS="SSLDIR=/usr/ssl" or by editing imap-2004/src/osdep/unix/Makefile . /tmp, /usr/tmp or /var/tmp (if present), and the mail spool directory must be protected 1777 (world write with sticky bit); otherwise mailbox locking and updates won't work. An alternative to 1777 on the mail spool directory is to install the mlock program that is bundled with the IMAP toolkit. . Multiple access protection locking does not work if the mailbox or /tmp are NFS mounted. . Shared access mailbox formats (mbx, mtx, mx, and tenex) do not work well with NFS and such usage is not supported. mmdf and unix formats are supported for use over NFS; however there won't be any multiple access locking protection. . Server startup delays may occur if a reverse DNS (IP address to name) lookup on the client's IP address does not complete in an expeditious fashion. This is actually a DNS problem and should be fixed in the DNS and/or the server's host table. A workaround exists (see the top-level Makefile for details) but is not recommended and can not be used at all with Kerberos. . At the insistance of the security gurus, SSL certification validation is now on by default. This means that you must now use the new /novalidate-cert switch if establishing an SSL connection to a server with a self-signed certificate; i.e. if "imap.blurdybloop.com" has a self-signed certificate, you must use a mailbox name such as {imap.blurdybloop.com/ssl/novalidate-cert}INBOX to get an SSL session instead of just {imap.blurdybloop.com/ssl}INBOX . GCC 8.x and above on SGI systems does not correctly pass/return structures which are smaller than 16 bytes and are not 8 bytes. The problem is that structures are padded at the wrong end; e.g. a 4 byte structure is loaded into the lower 4 bytes of the register when it should be loaded into the upper 4 bytes of the register. This affects IRIX 6 the most because it is a 64-bit system and 4 byte structures are common. This compiler bug impacts the use of inet_ntoa() in c-client and causes syslog messages to show IP addresses as 255.255.255.255 instead of the correct values. The fix is either to use SGI's C compiler instead of GCC or link with an implementation of inet_ntoa() that was built with GCC instead of the standard SGI C library version. . By default, the UNIX SSL build assumes that RSAREF is not needed, because RSA Security Inc. released the RSA public key encryption algorithm into the public domain on September 6, 2000. There is no longer any need to use RSAREF, and since RSAREF is slower than OpenSSL's RSA routines there's good reason not to. If for some reason you still want to use RSAREF, you will need to edit imap-2004/src/osdep/unix/Makefile to change SSLRSA to load libRSAglue and librsaref. . By default, the UNIX SSL build assumes that no name conflict exists between OpenSSL and Kerberos 5. If you are using an older version of Kerberos, you may need to edit imap-2004/src/osdep/unix/Makefile to change SSLCRYPTO so that it loads the OpenSSL libcrypto library explicitly as libcrypto.a. . By default, host names are canonicalized via gethostbyname() and gethostbyaddr() for everything except for SSL certificate validation. This can represent a security bug due to DNS spoofing, but is more likely to deliver results that users expect and also may be necessary to get Kerberos to work. Set variable "trustdns" in mail.c to NIL if you want to disable this. . Bugs: . It doesn't work to have a "}" character as a user name in /user= in a mailbox name, even if the user name is quoted. In other words, {blurdybloop.com/user="foo}bar"}zap won't work; foo will be interpreted as an unterminated quoted string and the remote mailbox name will be bar"}zap. . Case-independent searching only works for the 26 Roman alphabetics and not for any other characters . The experimental mx driver has performance problems and shouldn't be used . docs/internal.txt is out of date (again) . Annoyances: . Friendly host names (e.g. "server" instead of "server.foo.com") can't be used in a mailbox name with SSL certificate validation; you have to enter the fully-qualified domain name. This is a requirement established by the security gurus. . IMAP client limitations: . No SASL protection mechanisms (SASL authentication mechanisms are supported) . POP client limitations: . No SASL protection mechanisms (SASL authentication mechanisms are supported) . No POP3 UID support . SMTP client limitations: . No SASL protection mechanisms (SASL authentication mechanisms are supported) . No support for use of TURN, ETRN, and pipelining. . No support for enhanced status codes . UNIX limitations: . IPv6 is supported but is not the default; you have to use IP=6 in the make command . Supported local file formats: mbx, mh, mmdf, mtx, mx, news, phile, tenex, unix . Supported SASL mechanisms: CRAM-MD5, PLAIN, LOGIN, ANONYMOUS, GSSAPI . Sticky UIDs are not supported in the mh, mtx, and tenex drivers . Creation of keywords is not supported in the mh, mtx, and tenex drivers . Copy and append of keywords only works in the mbx driver. . Flat file formats (mbx, mmdf, mtx, phile, tenex, unix) do not permit mailboxes to have inferior names . SSL temporary key should be seeded better than it is. . Amiga limitations: . Supported local file formats: mbx, mh, mmdf, mtx, mx, news, phile, tenex, unix . Supported SASL mechanisms: CRAM-MD5, PLAIN, LOGIN, ANONYMOUS . Sticky UIDs are not supported in the mh, mtx, and tenex drivers . Creation of keywords is not supported in the mh, mtx, and tenex drivers . Copy and append of keywords only works in the mbx driver. . Flat file formats (mbx, mmdf, mtx, phile, tenex, unix) do not permit mailboxes to have inferior names . Win32 (Win9x/NT/Windows 2000) limitations: . IPv6 is supported in W2K builds but is not the default; you have to use IP=6 in the nmake command . Supported local file formats: mbx, mtx, tenex, unix . Supported SASL mechanisms: CRAM-MD5, PLAIN, LOGIN, ANONYMOUS, GSSAPI . No server SSL or TLS support. . No server authentication for GSSAPI . No server authentication for CRAM-MD5 on NT-based Windows (NT/2K/XP); it does work on DOS-based Windows (9x/Me). . Sticky UIDs are not supported in the mtxnt and tenexnt drivers . Creation of keywords is not supported in the mtxnt and tenexnt drivers . Copy and append of keywords only works in the mbxnt driver. . No support for TCP open timeouts . Flat file formats (mbx, mtx, tenex, unix) do not permit mailboxes to have inferior names . Win16 (Win3.1)/DOS limitations: . IPv6 not supported . Supported local file formats: bezerk, mtx . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS . Supported TCPs: B&W, Novell, PC-NFs, PC/TCP, Waterloo, Winsock . Sticky UIDs are not supported on local files . Creation of keywords are not supported on local files . Bezerk driver is read-only and does not handle LF-only newlines well . No support for any TCP timeouts on Waterloo DOS . No support for TCP open timeouts on Winsock and generic DOS . Flat file formats (bezerk, mtx) do not permit mailboxes to have inferior names . Does not work well unless a mailgets routine is armed when fetching texts. . Mac limitations: . IPv6 not supported . No local file drivers . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS . Does not output human-friendly time zone string . TOPS-20 limitations: . IPv6 not supported . No local file drivers . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS . No support for any TCP timeouts . VMS limitations: . IPv6 not supported . No local file drivers . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS . Supported TCPs: Multinet, Netlib . No support for any TCP timeouts on VMS Netlib . No support for TCP open timeouts on VMS Multinet . Time zone must be configured at build time . Does not output human-friendly time zone string . Windows CE limitations: . IPv6 not yet supported . No local file drivers . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS . No support for TCP open timeouts . Not finished, only builds c-client library . OS/2 limitations: . IPv6 not supported . Not finished, does not build tkrat_2.2cvs20100105-dfsg.orig/imap/docs/calendar.txt000066400000000000000000000340571137544547100222050ustar00rootroot00000000000000 ALL ABOUT CALENDARS Although one can never be sure of what will happen at some future time, there is strong historical precedent for presuming that the present Gregorian calendar will still be in effect within the useful lifetime of the IMAP toolkit. We have therefore chosen to adhere to these precedents. The purpose of a calendar is to reckon time in advance, to show how many days have to elapse until a certain event takes place in the future, such as the harvest or the release of a new version of Pine. The earliest calendars, naturally, were crude and tended to be based upon the seasons or the lunar cycle. ANCIENT CALENDARS The calendar of the Assyrians, for example, was based upon the phases of the moon. They knew that a lunation (the time from one full moon to the next) was 29 1/2 days long, so their lunar year had a duration of 354 days. This fell short of the solar year by about 11 days. (The exact time for the solar year is approximately 365 days, 5 hours, 48 minutes, and 46 seconds.) After 3 years, such a lunar calendar would be off by a whole month, so the Assyrians added an extra month from time to time to keep their calendar in synchronization with the seasons. The best approximation that was possible in antiquity was a 19-year period, with 7 of these 19 years having 13 months (leap months). This scheme was adopted as the basis for the lunar calendar used by the Hebrews. The Arabs also used this calendar until Mohammed forbade shifting from 12 months to 13 months; this causes the Muslim holy month of Ramadan to move backwards through the seasons, completing a cycle every 32 1/2 years. When Rome emerged as a world power, the difficulties of making a calendar were well known, but the Romans complicated their lives because of their superstition that even numbers were unlucky. Hence their months were 29 or 31 days long, with the exception of February, which had 28 days. Every second year, the Roman calendar included an extra month called Mercedonius of 22 or 23 days to keep up with the solar year. JULIAN CALENDAR Even this algorithm was very poor, so that in 45 BCE, Caesar, advised by the astronomer Sosigenes, ordered a sweeping reform. By imperial decree, the year 46 BCE was made 445 days long to bring the calendar back in step with the seasons. The new calendar, similar to the one we now use was called the Julian calendar (named after Julius Caesar). Months in the Julian calendar were 30 or 31 days in length and every fourth year was made a leap year (having 366 days) by adding a day to the end of the year. This leap year rule was not consistantly applied until 8 CE. The year-ending month of February, never a popular month, was presently shortened so that Julius Caesar and Emperor Augustus could each have long months named after them. Caesar also decreed that the year would start with the first of January, which since 153 BCE was the day that Roman consuls took office, and not the vernal equinox in late March. Not everyone accepted that part of his reform, as we shall see. GREGORIAN CALENDAR Caesar's year was 11 1/2 minutes short of the calculations recommended by Sosigenes and eventually the date of the vernal equinox began to drift. Roger Bacon became alarmed and sent a note to Pope Clement IV, who apparently was not impressed. Pope Sixtus IV later became convinced that another reform was needed and called the German astronomer, Regiomontanus, to Rome to advise him. Unfortunately, Regiomontanus died of the plague shortly thereafter and the plans died as well. In 1545, the Council of Trent authorized Pope Gregory XIII to reform the calendar once more. Most of the mathematical work was done by Father Christopher Clavius, S.J. The immediate correction that was adopted was that Thursday, October 4, 1582 was to be the last day of the Julian calendar. The next day was Friday, with the date of October 15. For long range accuracy, a formula suggested by the Vatican librarian Aloysius Giglio was adopted. It said that every fourth year is a leap year except for century years that are not divisible by 400. Thus 1700, 1800 and 1900 would not be leap years, but 2000 would be a leap year since 2000 is divisible by 400. This rule eliminates 3 leap years every 4 centuries, making the calendar sufficiently correct for most ordinary purposes. This calendar is known as the Gregorian calendar and is the one that we now use today. It is interesting to note that in 1582, all the Protestant princes ignored the papal decree and so many countries continued to use the Julian calendar until either 1698 or 1752. Britain and its American colonies went from Wednesday, September 2, 1752 to Thursday, September 14. Prior to the changeover, the British used March 25 as the start of the new year. In Russia, it needed the revolution to introduce the Gregorian calendar in 1918. Turkey didn't adopt it until 1927. NUMBERING OF YEARS The numbering of the year is generally done according to an "era", such as the year of a ruler's reign. In about 525, a monk named Dionysius Exiguus suggested that the calculated year of Jesus' birth be designated as year 1 in the Julian calendar. This suggestion was adopted over the next 500 years and subsequently followed in the Gregorian calendar. For the benefit of those who seek religious significance to the calendar millenium, note that year 1 is too late by at least 4 years. Herod the Great, named in the Christian Bible as having all children in Bethlehem put to death in an attempt to kill the infant Jesus, died in 4 BCE. Nothing particularly significant of an historic or religious nature happened in Gregorian year 1; however it has become a worldwide standard as the "common era." In modern times, the terms "CE" (common era) and "BCE" (before common era) are preferred over the earlier (and, as we have seen, less accurate) "AD" (anno Domini, "the year of the Lord") and "BC" (before Christ). The Hebrew lunar calendar begins at 3760 BCE, the year of creation in Jewish tradition. The Muslim lunar calendar begins on July 16, 622, when Mohammed fled from Mecca to Medina. The Japanese, Taiwanese, and North Koreans use the Gregorian calendar, but number the year by political era. In Japan, an era begins when an emperor succeeds to the throne; year 1 of the Heisei era was 1989 when Emperor Akihito ascended to the throne (the first few days of 1989 was year 64 of the Shouwa era). In Taiwan, year 1 is the first full year after the founding of the Republic of China in 1911. In North Korea, year 1 is the year of the Juche (self-reliance) ideal, corresponding to the birth year of founder Kim Il-Sung (1912). Thus, year 2000 is Heisei 12 (Japan), 89th year of the Republic (Taiwan), and Juche 89 (North Korea). FURTHER MODIFICATIONS TO THE GREGORIAN CALENDAR Despite the great accuracy of the Gregorian calendar, it still falls behind very slightly every few years. The most serious problem is that the earth's rotation is slowing gradually. If you are very concerned about this problem, we suggest that you tune in short wave radio station WWV or the Global Positioning System, which broadcasts official time signals for use in the United States. About once every 3 years, they declare a leap second at which time you should be careful to adjust your system clock. If you have trouble picking up their signals, we suggest you purchase an atomic clock (not part of the IMAP toolkit). Another problem is that the Gregorian calendar represents a year of 365.2425 days, whereas the actual time taken for the earth to rotate around the Sun is 365.2422 days. Thus, the Gregorian calendar is actually 25.92 seconds slow each year, resulting in the calendar being one day behind every 3,333 1/3 years. Consequently, the Gregorian calendar has been modified with a further rule, which is that years evenly divisible by 4000 are not leap years. Thus, the year 4000 will not be a leap year. Or, at least we assume that's what will happen assuming that the calendar remains unchanged for the next 2000 years. The modified Gregorian calendar represents a year of 365.24225 days. Thus, the modified Gregorian calendar is actually 4.32 seconds slow each year, resulting in the calendar being one day slow every 20,000 years. There is some dispute whether the modified Gregorian calendar was officially adopted, or if it's just a proposal. Other options (see below) exist; fortunately no decision needs to be made for several centuries yet. There is code in c-client to support the modified Gregorian calendar, although it is currently disabled. Sometime in the next 2000 years, someone will need to enable this code so that c-client is Y4K compiliant. Then, 18,000 years from now, someone will have to tear into c-client's code to fix the Y20K bug. EASTERN ORTHODOX MODIFICATION OF THE GREGORIAN CALENDAR The Eastern Orthodox church in 1923 established its own rules to correct the Julian calendar. In their calendar, century years modulo 900 must result in value of 200 or 600 to be considered a leap year. Both the Orthodox and Gregorian calendar agree that the years 2000 and 2400 will be leap years, and the years 1900, 2100, 2200, 2300, 2500, 2600, 2700 are not. However, the year 2800 will be a leap year in the Gregorian calendar but not in the Orthodox calendar; similarly, the year 2900 will be a leap year in the Orthodox calendar but not in the Gregorian calendar. Both calendars will agree that 3000 and 3100 are leap years, but will disagree again in 3200 and 3300. There is code in c-client to support the Orthodox calendar. It can be enabled by adding -DUSEORTHODOXCALENDAR=1 to the c-client CFLAGS, e.g. make xxx EXTRACFLAGS="-DUSEORTHODOXCALENDAR=1" The Orthodox calendar represents a year of 365.24222222... days. Thus, the Orthodox calendar is actually 1.91 seconds slow each year, resulting in the calendar being one day slow every 45,000 years. The Eastern Orthodox church has not yet made any statements on how the Y45K bug will be fixed. OTHER ISSUES AFFECTING THE CALENDAR IN THE FUTURE The effect of leap seconds also needs to be considered when looking at the Y20K and Y45K problems. Leap seconds put the clock back in line with the Earth's rotation, whereas leap years put the calendar back in line with the Earth's revolution. Since leap seconds slow down the clock (and hence the calendar), they actually bring the day of reckoning for the Gregorian and Orthodox calendars sooner. Another factor is that the next ice age (technically, the end of the current interglacial period; we are in the middle of an ice age now!) is due around Y25K. It is not known what perturbations this will cause on the Earth's rotation and revolution, nor what calendar adjustments will be necessary at that time. MEANINGS OF DAY NAMES The names of days of the week from a combination of Roman and Germanic names for celestial bodies: . Sunday Latin "dies solis" => "Sun's day" . Monday Latin "dies lunae" => "Moon's day" . Tuesday Germanic "Tiw's day" => "Mars' day" . Wednesday Germanic "Woden's day" => "Mercury's day" . Thursday Germanic "Thor's day" => "Jupiter's day" . Friday Germanic "Frigg's day" => "Venus' day" . Saturday Latin "dies Saturni" => "Saturn's day" MEANINGS OF MONTH NAMES The names of the months are from the Roman calendar: . January Janus, protector of doorways . February Februalia, a time for sacrifice to atone for sins . March Mars, god of war . April Latin "aperire" => "to open" buds . May Maia, goddess of plant growth . June Latin "juvenis" => "youth" . July Julius Caesar . August Augustus Caesar . September Latin "septem" => "seven" . October Latin "octo" => "eight" . November Latin "novem" => "nine" . December Latin "decem" => "ten" As you'll notice, the last four months are numbered 7 to 10, which is an artifact of the time when the new year started in March. INTERESTING FORMULAE There's another reason why the historical starting of the new year is significant. Starting with March, the length of months follows a mathematical series: 31 30 31 30 31 31 30 31 30 31 31 28 This means that you can calculate the day of week for any arbitrary day/month/year of the Gregorian calendar with the following formula (note all divisions are integral): _ _ | 7 + 31*(m - 1) y y y | dow = | d + -------------- + y + - - --- + --- | MOD 7 |_ 12 4 100 400_| where d := day of month (1..31) m := month in old style (March = 1..February = 12) y := year in old style dow := day of week (Tuesday = 0..Monday = 6) To convert from new style month/year to old style: if (m > 2) m -= 2; /* Mar-Dec: subtract 2 from month */ else m += 10,y--; /* Jan-Feb: months 11 & 12 of previous year */ Here's another fun formula. To find the number of days between two days, calculate a pair of calendar days with the formula (again, all divisions are integral), using new style month/year this time: m m + - 8 y y y d + 30 * (m - 1) + ----- + y * 365 + - - --- + --- - ld 2 4 100 400 where: d := day of month (1..31) m := month in new style (January = 1..December = 12) y := year in new style ld := leap day correction factor: 0 for January and February in non-leap years 1 for January and February in leap years 2 for all other months in all years In C code, the leap day correction factor is calculated as: (m < 3) ? !(y % 4) && ((y % 100) || !(y % 400)) : 2 It's up to you to figure out how to adapt these formulas for the Y4K bugfix and the Orthodox calendar. If you're really clever, try to use these formulae to implement the C library ctime(), gmtime(), and mktime() functions. Most C library implementations use a table of the number of days in a month. You don't need it. ACKNOWLEDGEMENT: The original version is from an old Digital Equipment Corporation SPR answer for VMS. Modifications for c-client, and additional information added by Mark Crispin. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/commndmt.txt000066400000000000000000000107021137544547100222410ustar00rootroot00000000000000[I wrote this tongue-in-cheek, but there's a lot here that people who build IMAP clients should take careful note. Most existing clients violate at least one, generally several, of these commandments. These are based on known user-visible problems that occur with various commonly used clients. Put another way, behind each commandment is a plethora of user (and server administrator) complaints caused by a violator.] Ten Commandments of How to Write an IMAP client Mark Crispin 1. Thou shalt not assume that it is alright to open multiple IMAP sessions selected on the same mailbox simultaneously, lest thou face the righteous wrath of mail stores that doth not permit such access. Instead, thou shalt labor mightily, even unto having to use thy brain to thinketh the matter through, such that thy client use existing sessions that are already open. 2. Thou shalt not abuse the STATUS command by using it to check for new mail on a mailbox that you already have selected in an IMAP session; for that session hath already told thou about new mail without thy having to ask. 3. Thou shalt remember the 30 minute inactivity timeout, and remember to speak to the IMAP server before that timeout expires. If thou useth the IDLE command, thou shalt send DONE from the IDLE before 29 minutes hath passed, and issue a new IDLE. If thou maketh no use of IDLE, then thou shalt send NOOP every few minutes, and the server shalt tell you about new mail, and there will be much rejoicing in the land. 4. Thou shalt not assume that all names are both the name of a mailbox and the name of a upper level of hierarchy that contains mailboxes; lest thou face the righteous wrath of mail stores in which a mailbox is a file and a level of hierarchy is a directory. Thou shalt pay diligent attention to the \NoSelect and \NoInferiors flags, so that your users may praise you with great praise. 5. Thou shalt learn and understand the unique features of IMAP, such as the unsolicited data model, the strict ascending rule of UIDs, how UIDs map to sequence numbers, the ENVELOPE and BODYSTRUCTURE structures; so that thou may use the IMAP protocol effectively. For a POP client hacked to babble IMAP protocol is still no more than a POP client. 6. Thou shalt remember untagged data sent by the server, and when thou needest data thou shalt consult your memory before asking the server. For those who must analyze thy protocol transactions are weak of stomach, and are likely to lose their recent meal should they see thou repeatedly re-fetch static data. 7. Thou shalt labor with great effort to work within the IMAP deleted/expunge model, even if thy own model is that of a trashcan; for interoperability is paramount and a trashcan model can be done entirely in the user interface. 8. Thou shalt not fear to open multiple IMAP sessions to the server; but thou shalt use this technique with wisdom. For verily it is true; if thou doth desire to monitor continuously five mailboxes for new mail, it is better to have five IMAP sessions continuously open on the mailboxes. It is generally not good to do a succession of five SELECT or STATUS commands on a periodic basis; and it is truly wretched to open and close five sessions to do a STATUS or SELECT on a periodic basis. The cost of opening and closing a session is great, especially if that session is SSL/TLS protected; and the cost of a STATUS or SELECT can also be great. By comparison, the cost of an open session doing an IDLE or getting a NOOP every few minutes is small. Great praise shall be given to thy wisdom in doing what is less costly instead of "common sense." 9. Thou shalt not abuse subscriptions, for verily the LIST command is the proper way to discover mailboxes on the server. Thou shalt not subscribe names to the user's subscription list without explicit instructions from the user; nor shalt thou assume that only subscribed names are valid. Rather, thou shalt treat subscribed names as akin to a bookmarks, or perhaps akin to how Windows shows the "My Documents" folder -- a set of names that are separate from the hierarchy, for they are such. 10. Thou shalt use the LIST "*" wildcard only with great care. If thou doth not fully comprehend the danger of "*", thou shalt use only "%" and forget about the existance of "*". Honor these commandments, and keep them holy in thy heart, so that thy users shalt maximize their pleasure, and the server administrators shalt sing thy praises and recommend thy work as a model for others to emulate. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/drivers.txt000066400000000000000000000161721137544547100221100ustar00rootroot00000000000000 c-client Driver Characteristics Mark Crispin 5 June 1999 Drivers are code modules that support different mailbox storage technologies. A mailbox storage technology may be implemented by 1) files and directories on the local system 2) a database 3) a network protocol. In the case of files and directories on the local system, a driver supports a particular mailbox format. Mailbox formats are discussed in more detail in the file formats.txt. As of the date this document was written, there was no bundled support for any databases in c-client. However, it should not be particularly difficult to write a driver that communicates with a database. Network protocols supported by c-client drivers are the Internet Mail Access Protocol (all versions: IMAP4rev1, IMAP4, IMAP2bis, and IMAP2); the Post Office Protocol (version 3); and the Network News Transport Protocol (NNTP). In addition, c-client also supports NNTP and the Simple Mail Transport Protocol (SMTP) for mailbox transport. By default, all drivers are enabled. There is little benefit to be gained by disabling a driver, with one exception. The mbox driver implements the behavior of automatically moving new mail from the spool directory to the "mbox" file on the user's home directory, if and *only* if the "mbox" exists and is in mailbox format. The mbox driver is listed under EXTRADRIVERS; if you wish to disable it just remove it from that list and rebuild. I. Special name "INBOX" The following rules to select INBOX and its format apply in the order given if "black box mode" is not in effect: 1) mbox format is selected if file ~/mbox exists, and is in unix format or is zero-length. 2) mx format is selected if file ~/INBOX/.mxindex exists. 3) mbx format is selected if file ~/INBOX exists and is in mbx format. 4) tenex format is selected if: a) file ~/mail.txt exists, and is in tenex format or is zero-length. b) file ~/INBOX exists and is in tenex format. 5) mtx format is selected if: a) file ~/INBOX.MTX exists, and is in mtx format or is zero-length. b) file ~/INBOX exists and is in mtx format. 6) mmdf format is selected if the spool directory file exists and is in mmdf format. 7) unix format is selected if the spool directory file exists and is in in unix format. 8) the dummy driver is selected if the spool directory file does not exist, or exists and is empty. If "black box mode" is not in effect, messages are automatically transferred ("snarfed") from the spool directory to an INBOX in mbox, mx, mbx, tenex, and mtx formats. The following rules to select INBOX and its format apply in the order given if "black box mode" is in effect: 1) mx format is selected if file ~/INBOX/.mxindex exists. 2) mbx format is selected if file ~/INBOX exists and is in mbx format. 3) tenex format is selected if file ~/INBOX exists and is in tenex format. 4) mtx format is selected if file ~/INBOX exists and is in mtx format. 5) mmdf format is selected if file ~/INBOX exists and is in mmdf format. 6) unix format is selected if file ~/INBOX exists and is in unix format. 7) the dummy driver is selected if ~/INBOX does not exist, or exists and is empty. II. Special Name #mhinbox #mhinbox always refers to the directory "inbox" in the MH path, which is declared in the ~/.mh_profile file. Messages are automatically transferred from the spool directory to #mhinbox mailbox. III. Special Prefix "#mh/" Any name prefixed with "#mh/" always refers to a directory in the MH path, which is declared in the ~/.mh_profile file. For example, the name "#mh/foo" refers to directory "foo" in the MH path. IV. Special prefix "#news." Any name prefixed with "#news" always refers to a newsgroup. For example, the name "#news.comp.mail.misc" refers to newsgroup "comp.mail.misc". V. All Other Names The driver is selected by generating a file name from the mailbox name, and then examining the data of the object with the resulting name. The formats are checked in order: mx, mbx, tenex, mtx, mmdf, unix, and phile. The dummy driver is selected if the file is empty. The file name is generated according to certain rules, based upon the prefix of the mailbox name. On UNIX, the following rules apply: Prefix Interpretation of Suffix ------ ------------------------ / [black box] preceeds a user name; "/foo/bar" means "black box user foo's mailbox bar" [not black box] preceeds an absolute path name. ~ [not black box] preceeds a user name; "~foo/bar" means "UNIX user foo's mailbox bar" #ftp/ preceeds UNIX user ftp's mailbox name #public/ preceeds UNIX user imappublic's mailbox name #shared/ preceeds UNIX user imapshared's mailbox name All other names are interpreted in the context of the UNIX user's home directory (not black box), the black box user's black box directory (black box), or UNIX user ftp's home directory (anonymous). The strings "..", "//", and /~ are forbidden in names in: black box mode #ftp, #public, or #shared names anonymous users Anonymous users may only access: INBOX (belonging to UNIX user ftp) files in or below UNIX user ftp's home directory #ftp, #news, and #public namespace VI. Driver Comparison The following information about the local file drivers is an elaboration of a table compiled by Osma Ahvenlampi. Driver CA CE UID Kwd Sub NFS Performance Layout ------ -- -- --- --- --- --- ----------- ------ unix no no yes yes no limited fair file ;;; traditional UNIX format mbox no no yes yes no limited fair file ;;; traditional UNIX format, INBOX only, using ~/mbox with automatic ;;; moving from the mail spool directory. mmdf no no yes yes no limited fair file ;;; default on SCO systems mbx yes yes yes yes no no very good prefile ;;; best performing local file driver; preferred format at UW tenex yes no no limited no no good prefile ;;; compatible with UNIX MM mtx yes no no limited no no very good prefile ;;; PC Pine standard format; compatible with TOPS-20; identical to tenex ;;; but instead CRLF newlines instead of LF mx yes buggy yes yes yes no poor ixdir ;;; fullest function; *not* recommended due to performance problems and bugs; ;;; to be redesigned/rewritten mh yes no no no yes yes very poor dir ;;; compatible with mh; #mhinbox for INBOX, #mh/ prefix for all other names news yes no yes no yes yes very poor ixdir ;;; local news spool access; #news. prefix for all names phile no no no no no yes good file ;;; reads arbitrary file as a single readonly message IMPORTANT: the "performance" ratings are relative to other drivers, and not necessarily to other software which implements those formats. They relate to the driver's performance in typical operations such as an IMAP "FETCH ALL". Key to headings: CA: concurrent read/write access CE: expunge permitted in concurrent read/write access UID: sticky UIDs Kwd: keyword flags Sub: subfolders NFS: usable over network filesystems (NFS, AFS, etc.) Layout: file - single file prefile - file with preallocated space for state dir - directory, messages are files ixdir - directory, messages are files, with helper index In addition, drivers imap, nntp, and pop3 support IMAP4rev1, NNTP, and POP3 protocols respectively. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/formats.txt000066400000000000000000000207421137544547100221030ustar00rootroot00000000000000 Mailbox Format Characteristics Mark Crispin 5 June 1999 When a mailbox storage technology uses local files and directories directly, the file(s) and directories are layed out in a mailbox format. I. Flat-File Formats In these formats, a mailbox and all the messages inside are a single file on the filesystem. The mailbox name is the name of the file in the filesystem, relative to the user's "mail home directory." A flat-file format mailbox is always a file, never a directory. This means that it is impossible to have a flat-file format mailbox that has inferior mailbox names under it (so-called "dual-usage" mailboxes). For some inexplicable reason, some people want this. The mail home directory is usually the same as the user login home directory if that concept is meaningful; otherwise, it is some other default directory (e.g. "C:\My Documents" on Windows 98). This can be redefined by modifying the c-client source code or in an application via the SET_HOMEDIR mail_parameters() call. For example, a mailbox named "project" is likely to be found in the file "project" in the user's home directory. Similarly, a mailbox named "test/trial1" (assuming a UNIX system) is likely to be found in the file "trial1" in the subdirectory "test" in the user's home directory. Note that the name "INBOX" has special semantics and rules, as described in the file naming.txt. The following flat-file formats are supported by c-client as of the time of this writing: . unix This is the traditional UNIX mailbox format, in use for nearly 30 years. It uses a line starting with "From " to indicate start of message, and stores the message status inside the RFC822 message header. unix is not particularly efficient; the entire mailbox file must be read when the mailbox is open, and when reading message texts it is necessary to convert the newline convention to Internet standard CR LF form. unix preserves UIDs, and allows the creation of keywords. Only one process may have a unix-format mailbox open read/write at a time. . mmdf This is the format used by the MMDF mailer. It uses a line consisting of 4 (0x01) characters to indicate start and end of message. Optionally, there may also be a unix format "From " line. It otherwise has the same characteristics as unix format. . mbx This is the current preferred mailbox format. It can be handled quite efficiently by c-client, without the problems that exist with unix and mmdf formats. Messages are stored in Internet standard CR LF format. mbx permits shared access, including shared expunge. It preserves UIDs, and allows the creation of keywords. . mtx This is supported for compatibility with the past. This is the old Tenex/TOPS-20 mail.txt format. It can be handled quite efficiently by c-client, and has most of the characteristics of mbx format. mtx is deficient in that it does not support shared expunge; it has no means to store UIDs; and it has no way to define keywords except through an external configuration file. . tenex This is supported for compatibility with the past. This is the old Columbia MM format. This is similar to mtx format, only it uses UNIX-style bare-LF newlines instead of CR LF newlines, thus incurring a performance penalty for newline conversion. . phile This is not strictly a format. Any file which is not in a recognized format is in phile format, which treats the entire contents of the file as a single message. II. File/Message Formats In these formats, a mailbox is a directory, and each the messages inside are separate files inside the directory. The file names of these files are generally the text form of a number, which also matches the UID of the message. In the case of mx, the mailbox name is the name of the directory in the filesystem, relative to the user's "mail home directory." In the case of news and mh, the mailbox name is in a separate namespace as described in the file naming.txt. A file/message format mailbox is always a directory. This means that it is possible to have a file/message format mailbox that has inferior mailbox names under it (so-called "dual-usage" mailboxes). For some inexplicable reason, some people want this. Note that the name "INBOX" has special semantics and rules, as described in the file naming.txt. The following file/message formats are supported by c-client as of the time of this writing: . mx This is an experimental format, and may be removed in a future release. An mx format mailbox has a .mxindex file which holds the message status and unique identifiers. Messages are stored in Internet standard CF LF form, so the file size of the message file equals the size of the message. mx is somewhat inefficient; the entire directory must be read and each file stat()'d. We found it intolerable for a moderate sized mailbox (2000 messages) and have more or less abandoned it. . mh This is supported for compatibility with the past. This is the format used by the old mh program. mh is very inefficient; the entire directory must be read and each file stat()'d, and in order to determine the size of a message, the entire file must be read and newline conversion performed. mh is deficient in that it does not support any permanent flags or keywords; and has no means to store UIDs (because the mh "compress" command renames all the files, that's why). . news This is an export of the local filesystem's news spool, e.g. /var/spool/news. Access to mailboxes in news format is read only; however, message "deleted" status is preserved in a .newsrc file in the user's home directory. There is no other status or keywords. news is very inefficient; the entire directory must be read and each file stat()'d, and in order to determine the size of a message, the entire file must be read and newline conversion performed. news is deficient in that it does not support permanent flags other than deleted; does not support keywords; and has no expunge. Soapbox on File/Message Formats If it sounds from the above descriptions that we're not putting too much effort into file/message formats, you are correct. There's a general reason why file/message formats are a bad idea. Just about every filesystem in existance serializes file creation and deletions because these manipulate the free space map. This turns out to be an enormous problem when you start creating/deleting more than a few messages per second; you spend all your time thrashing in the filesystem. It is also extremely slow to do a text search through a file/message format mailbox. All of those open()s and close()s really add up to major filesystem thrashing. What about Cyrus and Maildir? Both formats are vulnerable to the filesystem thrashing outlined above. The Cyrus format used by CMU's Cyrus server (and Esys' server) has a special associated flat file in each directory that contains extensive data (including pre-parsed ENVELOPEs and BODYSTRUCTUREs) about the messages. Put another way, it's a (considerably) more featureful form of mx. It also uses certain operating system facilities (e.g. file/memory mapping) which are not available on older systems, at a cost of much more limited portability than c-client. These considerably ameliorate the fundamental problems with file/message formats; in fact, Cyrus is halfway to being a database. Rather than support Cyrus format in c-client, you should run Cyrus or Esys if you want that format. The Maildir format used by qmail has all of the performance disadvantages of mh noted above, with the additional problem that the files are renamed in order to change their status so you end up having to rescan the directory frequently the current names (particularly in a shared mailbox scenario). It doesn't scale, and it represents a support nightmare; it will therefore never be supported in the official distribution. Maildir support code for c-client is available from third parties; but, if you use it, it is entirely at your own risk (read: don't complain about how poorly it performs or bugs). So what does this all mean? A database (such as used by Exchange) is really a much better approach if you want to move away from flat files. mx and especially Cyrus take a tenative step in that direction; mx failed mostly because it didn't go anywhere near far enough. Cyrus goes much further, and scores remarkable benefits from doing so. However, a well-designed pure database without the overhead of separate files would do even better. tkrat_2.2cvs20100105-dfsg.orig/imap/docs/imaprc.txt000066400000000000000000000640611137544547100217050ustar00rootroot00000000000000 .imaprc secrets revealed! Mark Crispin, June 17, 2002 The following information describes the format of the /etc/c-client.cf and ~/.imaprc file. The Columbia MM ~/.mminit file is also read by c-client; however, the only command that ~/.mminit has in common is set keywords. ********************************************************************** * DANGER! BEWARE! TAKE CARE! * ********************************************************************** * * * These files, and this documentation, are for internal UW usage * * only. This capability is for UW experimental tinkering, and most * * emphatically *not* for sorcerer's apprentices at other sites who * * feel that if a config file capability exists, they must write a * * config file whether or not there is any need for one. * * * * This information is subject to change without notice. Commands * * may be added, removed, or altered. The behavior of comamnds may * * change. Do not use any of this information without consulting me * * first. c-client's defaults have been carefully chosen to be right * * for general-purpose and most special-purpose configurations. If * * you tinker with these defaults, all hell may break loose. * * * * This is not an idle threat. There have been several instances of * * people who ignored these warnings and have gotten burned. * * * * Don't even trust this file to work. Many of the things which can * * be changed by this file can also be changed by the application, * * and it is totally unpredictable which will take precedence. It * * all depends upon how the application is coded. Not only that, you * * may cause the application to crash. * * * * In other words, keep your cotton-pickin' hands off my defaults. * * If it crashes and erases your mail, I don't want to hear about it. * * Consider 'em ``mandatory defaults''. Got a nice ring, eh? :-) If * * you must tinker with defaults, play with the .pinerc and pine.conf * * files in Pine. It's got options galore, all supported for you to * * have fun. They're also documented; so well documented, it takes * * two strong men to carry around all the documentation. ;-) ;-) * * * * Joking aside, you really shouldn't be fooling around with this * * capability. It's dangerous, and you can shoot yourself in the * * foot easily. If you need custom changes, you are better off with * * local source code modifications. Seriously. * * * * One last warning: don't believe anything that you read in this * * document. Every effort has been made to ensure that this document * * is incomplete and inaccurate, and I take no responsibility for any * * glimmers of correct information that may, by some fluke, be here. * * * ********************************************************************** The files are read in order: /etc/c-client.cf, ~/.mminit, ~/.imaprc, and an entry in a later file overrides the setting of an earlier file except as noted below. This ordering and overriding behavior may change without notice. Almost all of these facilities can also be set via the mail_parameters() call in the program. Whether the file overrides mail_parameters(), or mail_parameters() overrides the file, is indeterminate. It will vary from program to program, and it may be one way in one version and the other way in the next version. It's completely unpredictable, and so anything you do with these files has to be in complete knowledge of what the version of each program you're running is going to do. This is because the files do something for testing, but the real capability for configurability is put in the program instead. Are you getting the feeling that you shouldn't be messing with these files yet? The very first line of the file MUST start with the exact string "I accept the risk". This ensures that you have checked the file for correctness against this version of the IMAP toolkit. This enable string may change without notice in future versions, and the new string may or may not be accurately described in an updated version of this file. So any time you install software that uses the IMAP toolkit, you need to check the new version against these files (if you have insisted upon creating them in spite of all warnings). If two pieces of software use different versions of the IMAP toolkit with incompatible requirements, one of them won't work. Re-read the warning above about why you should not use these files. Subsequent lines are read from the file one at a time. Case does not matter. Unrecognized commands are ignored. 1) set new-folder-format sets what format new mailboxes are created in. This also controls default delivery via tmail and dmail. a) set new-folder-format same-as-inbox Folder is created using the same mailbox format as INBOX. If INBOX is empty, it defaults to system standard. b) set new-folder-format system-standard This is the default. Folder is created using the wired-in system standard format, which on most UNIX systems is ordinary UNIX /bin/mail format. On SCO systems, this is MMDF. c) set new-folder-format Folder is created using the given driver name, e.g. mbx, unix, mmdf, etc. There is no protection against setting this to a silly value (e.g. news, nntp, dummy) and doing so is a great way to screw things up. Setting this to mh does not do what you think it does. Setting this to tenex or mtx isn't particularly useful. 2) set empty-folder-format sets what format data is written into an empty mailbox file using mail_copy() or mail_append(). This also controls default delivery via tmail. a) set empty-folder-format same-as-inbox Data is written using the same mailbox format as INBOX. If INBOX is empty, it defaults to system standard. b) set empty-folder-format system-standard This is the default. Data is written using the wired-in system standard format, which on most UNIX systems is ordinary UNIX /bin/mail format. On SCO systems, this is MMDF. c) set-empty-folder-format Data is written using the given driver name, e.g. tenex, unix, mmdf, etc. There is no protection against setting this to a silly value (e.g. news, nntp, dummy) and doing so is a great way to screw things up. Setting this to mh, mbx, or mx does not work. 3) set keywords , , ... Sets the list of keyword flags (supported by tenex and mtx) to the given list. Up to 30 flags may be given. Since these names correspond to numeric bits, the order of the keywords can not be changed, nor can keywords be removed or inserted (you can append new keywords, up to the limit of 30). Set keywords is a deprecated command. It may not appear in future versions, or it may appear in a changed form. It exists only for compatibility with MM, and should only appear in ~/.mminit and not in the other files. It is likely to disappear entirely in IMAP4. There is no protection against setting these to silly values, and doing so is a great way to cause a crash. 4) set from-widget header-only Sets smart insertion of the > character in front of lines that begin with ``From ''. Only such lines that are also in UNIX mbox header file format will have a > character inserted. The default is to insert the > character in front of all lines which begin with ``From '', for the benefit of legacy tools that get confused otherwise. 5) set black-box-directory Sets the directory in which the user's data can be found. A user's folders can be found in a subdirectory of the black box directory named with the user's username. For example, if the blackbox directory is /usr/spool/folders/, user jones' data can be found in /usr/spool/folders/jones/. The user's black-box directory is the location of folders, .mminit, .imaprc, .newsrc, and all other files used by c-client; internally, it sets c-client's idea of the user's ``home directory'', overriding /etc/passwd. This command may not appear in ~/.mminit or ~/.imaprc In black-box mode, it is not permitted to access any folders outside of the user's personal blackbox directory. The breakouts ``/'', ``~'', and ``..'' are not permitted. In order to make this work without crashing, you must set another option which is not listed in this document. There is no protection against setting this to a silly value, and doing so is a great way to cause a crash. 6) set local-host Sets c-client's idea of the local host name. There is no protection against setting this to a silly value, and doing so is a great way to cause a crash. 7) set news-active-file Sets the location of the news active file, if it is not in the standard place. It is recommended to use a courtesy symbolic link instead. There is no protection against setting this to a silly value, and doing so is a great way to cause a crash. 8) set news-spool-directory Sets the location of the news spool, if it is not in the standard place. It is recommended to use a courtesy symbolic link instead. There is no protection against setting this to a silly value, and doing so is a great way to cause a crash. 9) set news-state-file Sets the location of the news state file (normally $(USER)/.newsrc). This is not very useful in /etc/c-client.cf because it is a file name. Setting this in /etc/c-client.cf would set all users to the same file as their newsrc, which is probably not what you want. There is no protection against setting this to a silly value, and doing so is a great way to cause a crash. 10) set system-inbox Sets the location of the "system inbox", if it is not in the standard place. This is the default location of INBOX, or the mail drop point from which mail is snarfed (e.g. in tenex, mtx, mbox, mh formats). This is not very useful in /etc/c-client.cf because it is a file name. Setting this in /etc/c-client.cf would set all users to the same file as their system inbox, which is probably not what you want. There is no protection against setting this to a silly value, and doing so is a great way to cause a crash. 11) set tcp-open-timeout Sets the number of seconds that the TCP routines will block on opening a TCP connection before timing out. If a timeout occurs, the connection attempt is aborted. The default is zero, meaning use the operating system default (75 seconds on most UNIX systems). There is no protection against setting this to an excessively small value, such as 1, and doing so is a great way to cause users extreme grief. 12) set tcp-read-timeout Sets the number of seconds that the TCP routines will block on reading data before calling the timeout routine. If no timeout routine is set by the program, the connection will be aborted on a timeout. The default is zero, meaning infinite. There is no protection against setting this to an excessively small value, such as 1, and doing so is a great way to cause users extreme grief. 13) set tcp-write-timeout Sets the number of seconds that the TCP routines will block on sending data before calling the timeout routine. If no timeout routine is set by the program, the connection will be aborted on a timeout. The default is zero, meaning infinite. There is no protection against setting this to an excessively small value, such as 1, and doing so is a great way to cause users extreme grief. 14) set rsh-timeout Sets the number of seconds that the rsh routines will block on opening an rimapd connection before timing out. If a timeout occurs, the rsh connection attempt is aborted. A zero timeout will disable rsh. The default is 15 seconds. There is no protection against setting this to an excessively small value, such as 1, and doing so is a great way to cause users extreme grief. 15) set maximum-login-trials Sets the number of iterations of asking the user, via mm_login(), for a user name and password, before cancelling the attempt. The default is 3. There is no protection against setting this to zero, and doing so is a great way to cause users extreme grief. 16) set lookahead Sets the number of envelopes that are looked ahead in IMAP, in mail_fetchstructure(). This is based on the guess that in such operations as drawing browser lines, if you get data for message n you are likely to want it for message n+1, n+2,... in short order. Lookahead preloads the c-client cache and saves unnecessary RTTs. The default is 20, a good number for a browser on a 24x80 screen, and small enough to usually have no significant real-time difference from a single message fetch. Setting it to 0 turns off lookahead. There is no protection against setting this ridiculously high and incurring performance penalties as a result. 17) set prefetch Sets the number of envelops which are automatically fetched for the messages which match in a search. This is based on the guess that in a browser that is "zoomed" on the results of a search, you are likely to want the envelope data for each of those messages in short order. Prefetching reloads the c-client cache, saves unnecessary RTTs, and avoids loading undesired envelopes due to lookahead (see above). The default is 20. Setting it to 0 turns off prefetch. There is no protection against setting this ridiculously high and incurring performance penalties as a result. 18) set close-on-error If non-zero, IMAP connections are closed if an EXAMINE or SELECT command fails. Otherwise, they are left half-open, and can be used again to select some other mailbox. The mailbox name in the stream is set to {serverhost} The default is zero (do not close on error). 19) set imap-port Set the TCP/IP contact port to use for IMAP. This overrides the wired-in setting and the setting from /etc/services, and can in turn be overridden by an explicit user specification in the mailbox name, e.g. {serverhost:143}foo The default is zero (use setting from /etc/services or the wired-in setting (143). There is no protection against setting this to a silly value, and doing so is a great way to cause users extreme grief. 20) set pop3-port Set the TCP/IP contact port to use for POP3. This overrides the wired-in setting and the setting from /etc/services, and can in turn be overridden by an explicit user specification in the mailbox name, e.g. {serverhost:110/pop3} The default is zero (use setting from /etc/services or the wired-in setting (110). There is no protection against setting this to a silly value, and doing so is a great way to cause users extreme grief. 21) set uid-lookahead Sets the number of UIDs that are looked ahead in IMAP in mail_uid(). Lookahead preloads the c-client cache and saves unnecessary RTTs. The default is 1000, small enough to usually have no significant real-time difference from a single message UID fetch. Setting it to 0 turns off lookahead. There is no protection against setting this ridiculously high and incurring performance penalties as a result. 22) set mailbox-protection Set the default protection for newly-created mailbox files. The default is 384. There is no protection against setting this to a silly value, and doing so is a great way to screw things up massively. 23) set directory-protection Set the default protection for newly-created directories. The default is 448. There is no protection against setting this to a silly value, and doing so is a great way to screw things up massively. 24) set lock-protection Set the default protection for lock files The default is 438, which is necessary if locks are to be respected by processes running as other UIDs. There is no protection against setting this to a silly value, and contrary to what you may think just about any value other than 438 turns out to be a silly value. 25) set disable-fcntl-locking This only applies to SVR4 systems. If non-zero, fnctl() locking is not attempted. In the past, this was used to avoid locking NFS files. If NFS is involved, the evil lockd/statd daemons get invoked. These daemons supposedly work over NFS, but really don't. You probably don't really want to do this, though, because now the flock() emulator (which calls fcntl()) now checks to see if the file is accessed via NFS and no-ops the lock. This is compatible with BSD. Disabling fcntl() locking loses a great deal of locking protection on local files as well as NFS files (which now never have locking protection). The default is zero (fcntl() locking is enabled). 26) set lock-EACCES-error If non-zero, a warning message is given if an attempt to create a lock file fails. Otherwise, EACCES is treated as a "silent failure", and it proceeds without trying to use the lock file. This is for the benefit of users on systems with paranoid /usr/spool/mail protections which don't let users create /usr/spool/mail/$(USER).lock files; these unfortunate users would be harassed with a flood of error messages otherwise. The problem is that on SVR4, if EACCES remains disabled and fcntl() locking is also disabled, then there is no locking at all which is doubleplus-ungood. If the site is paranoid on /usr/spool/mail protections AND if there is no fcntl() locking (SVR4) or usable flock() locking (e.g. NFS), then there is no way to win. Find a different system to use. The default is non-zero (report EACCESS as an error). 27) set list-maximum-level Sets the maximum depth of recursion that a * wildcard list will go down the directory tree. 0 means that no recursion is permitted, and * becomes like %. The default is 20. There is no protection against setting this to a ridiculously high value. Since LIST will follow symbolic links, it can effectively recurse infinitely, until the name strings get large enough that some name limit is exceeded. 28) set anonymous-home-directory Sets the location of the anonymous home directory, if it is not in the standard place. It is recommended to use a courtesy symbolic link instead. There is no protection against setting this to a silly value, and doing so is a great way to cause a crash. 29) set chroot-server This option is for closed server systems only. If defined, a chroot() call to the user's home directory is done as part of the login process. This has the effect of preventing access to any files outside of the user's home directory (including shared mailboxes). Shared mailboxes with other users can't possibly work with this option, because there is no way to export lock information to other users. This should be done ONLY on systems which do not permit users to have shell access This option should NEVER(!!) be set if users are allowed shell access. Doing so actually makes the system *less* secure, since the user could create an etc subdirectory which would be treated as real /etc by such programs as /bin/su. The default is zero (don't do chroot). This option is strongly *NOT* recommended. 30) set disable-automatic-shared-namespaces Never look up the "ftp", "imappublic", and "imapshared" users as posssible home directories for the #ftp, #public, and #shared namespaces. On some systems (reportedly including AIX 4.3.3) getpwnam() of an unknown user name is horrendously slow. Note that this does not remove the #ftp, #public, and #shared namespaces, and they can still be set up by other means. The default is zero (shared namespaces are automatic). 31) set advertise-the-world Include the UNIX root as a shared namespace. This is generally a bad idea, since certain IMAP clients (names withheld to protect the guilty) will take this as license to download the entire filesystem tree. The default is zero (don't advertise the world). 32) set mail-subdirectory Change the default connected directory from the user's home directory to the named subdirectory of the user's home directory. For example, setting MAILSUBDIR="mail" will cause the POP2 and IMAP servers to connect to the user's ~/mail subdirectory. This is equivalent to the env_unix.c edit described in Example 2 of the CONFIG file. Note that if the subdirectory does not exist, the result is undefined. It is probably an extremely bad idea to set this unless you can guarantee that the subdirectory exists for all users. If you can not guarantee this, then you should leave the default as the user's home directory and allow them to configure a personal default in their IMAP client. The default is not to use any subdirectory. 33) set allow-user-config Allow users to use ~/.imaprc and ~/.mminit files. The default is zero (don't allow user config files). 34) set allow-reverse-dns By default, the servers (ipop[23]d and imapd) will do gethostbyaddr() on the local and remote sockets so that imapd can identify itself properly (this is important when the same CPU hosts multiple virtual hosts on different IP addresss) and also includes the client's name when it writes to the syslog. There are also client gethostbyaddr() calls, used primarily by authentication mechanisms. Setting this option to zero disables all gethostbyaddr() calls. The returned "host name" string for the socket is just the bracketed [12.34.56.78] form, as if the reverse DNS lookup failed. WARNING: Some authentication mechanisms, e.g. Kerberos V, depend upon the host names being right, and if you set this option, it won't work. You should only do this if you are encountering server performance problems due to a misconfigured DNS, e.g. long startup delays or client timeouts. The default is non-zero (allow reverse DNS). 35) set disable-plaintext Disable plaintext password authentication (LOGIN command, AUTH=LOGIN, and AUTH=PLAIN). The default is zero (allow plaintext authentication). 36) set trust-dns By default, host names are canonicalized via gethostbyname() for everything except for SSL certificate validation. This can represent a security bug due to DNS spoofing, but is more likely to deliver results that users expect. It also may be necessary for SASL authentication to work right (e.g. generating a correct name for a Kerberos service principal) if the name entered by the user is a CNAME or not a fully-qualified domain name. If trust-dns is set to zero, no host name canonicalization is done. The user's actual entered name is used for SASL authentication and will appear in the mailbox name of the open stream. The default is non-zero (do DNS canonicalization). 37) set sasl-uses-ptr-name By default, if trust-dns is set, the host names used in authentication (e.g. to generate a Kerberos service principal) are canonicalized via gethostbyaddr() instead of by gethostbyname(). If gethostbyaddr() fails the gethostbyname() canonicalization is used. This represents an additional security bug due to DNS spoofing, over and above trust-dns. It also adds an additional DNS query to starting a session. It is necessary for sites which implement a server cluster with multiple A records for a cluster name (instead of a CNAME) but each cluster member has a unique PTR record which it expects for a Kerberos service principal. If sasl-uses-ptr-name is set to zero and trust-dns is set non-zero, the gethostbyname() canonicalized name is used for SASL authentication. The setting of sasl-uses-ptr-name is irrelevant if trust-dns is set to zero. The default is non-zero (use name from PTR record for SASL). 38) set network-filesystem-stat-bug By default, traditional UNIX mailbox files are only closed and reopened at checkpoint and expunge time. This ensures that, prior to rewriting the file, that any cached stat() data from a network filesystem is updated with current data. Very old versions of NFS, and reputedly also AFS, can get into a state in which the cached stat() data stays out-of-date, even across a close and reopen of the file. If network-filesystem-stat-bug is set non-zero, then the mailbox file is closed and reopened at ping time as a workaround for this bug in these network filesystems. This means that in imapd, the mailbox file is closed and reopened for every IMAP command. This is obviously something that should be avoided unless absolutely necessary. NFS and AFS are terrible ways to distribute mail. You use use IMAP servers with a local disk instead. The default is zero (only close/reopen at checkpoint and expunge time). Setting this option is a great way to ruin your system's performance. 39) set restrict-mailbox-access

.MIME */ f[k] = fetch_body_part_mime; } } else if (*v != ']') { /* better be the end if no IMAP4rev1 stuff */ fs_give ((void **) &ta);/* clean up */ response = "%.80s BAD Syntax error in section specifier\015\012"; return; } } if (*v != ']') { /* IMAP4rev1 stuff here? */ if (!strncmp (v,"HEADER",6)) { *v = '\0'; /* tie off in case top level */ v += 6; /* found [
.]HEADER */ f[k] = fetch_body_part_header; /* partial headers wanted? */ if (!strncmp (v,".FIELDS",7)) { v += 7; /* yes */ if (!strncmp (v,".NOT",4)) { v += 4; /* want to exclude named headers */ ta->flags |= FT_NOT; } if (*v || !(v = strtok (NIL,"\015\012")) || !(ta->lines = parse_stringlist (&v,&list))) { fs_give ((void **) &ta);/* clean up */ response = "%.80s BAD Syntax error in header fields\015\012"; return; } } } else if (!strncmp (v,"TEXT",4)) { *v = '\0'; /* tie off in case top level */ v += 4; /* found [
.]TEXT */ f[k] = fetch_body_part_text; } else { fs_give ((void **) &ta);/* clean up */ response = "%.80s BAD Unknown section text specifier\015\012"; return; } } } /* tie off section */ if (*v == ']') *v++ = '\0'; else { /* bogon */ if (ta->lines) mail_free_stringlist (&ta->lines); fs_give ((void **) &ta);/* clean up */ response = "%.80s BAD Section specifier not terminated\015\012"; return; } if ((*v == '<') && /* partial specifier? */ ((ta->binary & FTB_SIZE) || !(isdigit (v[1]) && ((ta->first = strtoul (v+1,(char **) &v,10)) || v) && (*v++ == '.') && (ta->last = strtoul (v,(char **) &v,10)) && (*v++ == '>')))) { if (ta->lines) mail_free_stringlist (&ta->lines); fs_give ((void **) &ta); response ="%.80s BAD Syntax error in partial text specifier\015\012"; return; } switch (*v) { /* what's there now? */ case ' ': /* more follows */ *--v = ' '; /* patch a space back in */ *--v = 'x'; /* and a hokey character before that */ strtok (v," "); /* reset strtok mechanism */ break; case '\0': /* none */ break; case ')': /* end of list */ if (list && !v[1]) { /* make sure of that */ list = NIL; strtok (v," "); /* reset strtok mechanism */ break; /* all done */ } /* otherwise it's a bogon, drop in */ default: /* bogon */ if (ta->lines) mail_free_stringlist (&ta->lines); fs_give ((void **) &ta); response = "%.80s BAD Syntax error after section specifier\015\012"; return; } /* make copy of section specifier */ if (s && *s) ta->section = cpystr (s); fa[k++] = (void *) ta; /* set argument */ } else { /* unknown attribute */ response = badatt; return; } } while ((s = strtok (NIL," ")) && (k < MAXFETCH) && list); else { response = misarg; /* missing attribute list */ return; } if (s) { /* too many attributes? */ response = "%.80s BAD Excessively complex FETCH attribute list\015\012"; return; } if (list) { /* too many attributes? */ response = "%.80s BAD Unterminated FETCH attribute list\015\012"; return; } f[k] = NIL; /* tie off attribute list */ /* c-client clobbers sequence, use spare */ for (i = 1; i <= nmsgs; i++) mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence; /* for each requested message */ for (i = 1; (i <= nmsgs) && (response != loseunknowncte); i++) if (mail_elt (stream,i)->spare) { /* parse envelope, set body, do warnings */ if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL); quell_events = T; /* can't do any events now */ PSOUT ("* "); /* leader */ pnum (i); PSOUT (" FETCH ("); (*f[0]) (i,fa[0]); /* do first attribute */ /* for each subsequent attribute */ for (k = 1; f[k] && (response != loseunknowncte); k++) { PBOUT (' '); /* delimit with space */ (*f[k]) (i,fa[k]); /* do that attribute */ } PSOUT (")\015\012"); /* trailer */ quell_events = NIL; /* events alright now */ } } /* Fetch message body structure (extensible) * Accepts: message number * extra argument */ void fetch_bodystructure (unsigned long i,void *args) { BODY *body; mail_fetchstructure (stream,i,&body); PSOUT ("BODYSTRUCTURE "); pbodystructure (body); /* output body */ } /* Fetch message body structure (non-extensible) * Accepts: message number * extra argument */ void fetch_body (unsigned long i,void *args) { BODY *body; mail_fetchstructure (stream,i,&body); PSOUT ("BODY "); /* output attribute */ pbody (body); /* output body */ } /* Fetch body part MIME header * Accepts: message number * extra argument */ void fetch_body_part_mime (unsigned long i,void *args) { TEXTARGS *ta = (TEXTARGS *) args; if (i) { /* do work? */ SIZEDTEXT st; unsigned long uid = mail_uid (stream,i); char *tmp = (char *) fs_get (100 + strlen (ta->section)); sprintf (tmp,"BODY[%s.MIME]",ta->section); /* try to use remembered text */ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst; else { /* get data */ st.data = (unsigned char *) mail_fetch_mime (stream,i,ta->section,&st.size,ta->flags); if (ta->first || ta->last) remember (uid,tmp,&st); } pbodypartstring (i,tmp,&st,ta); fs_give ((void **) &tmp); } else { /* clean up the arguments */ fs_give ((void **) &ta->section); fs_give ((void **) &args); } } /* Fetch body part contents * Accepts: message number * extra argument */ void fetch_body_part_contents (unsigned long i,void *args) { TEXTARGS *ta = (TEXTARGS *) args; if (i) { /* do work? */ SIZEDTEXT st; char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0)); unsigned long uid = mail_uid (stream,i); sprintf (tmp,"BODY[%s]",ta->section ? ta->section : ""); /* try to use remembered text */ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst; else { /* get data */ st.data = (unsigned char *) mail_fetch_body (stream,i,ta->section,&st.size,ta->flags); if (ta->first || ta->last) remember (uid,tmp,&st); } pbodypartstring (i,tmp,&st,ta); fs_give ((void **) &tmp); } else { /* clean up the arguments */ if (ta->section) fs_give ((void **) &ta->section); fs_give ((void **) &args); } } /* Fetch body part binary * Accepts: message number * extra argument */ void fetch_body_part_binary (unsigned long i,void *args) { TEXTARGS *ta = (TEXTARGS *) args; if (i) { /* do work? */ SIZEDTEXT st; SIZEDTEXT cst; BODY *body = mail_body (stream,i,ta->section); char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0)); unsigned long uid = mail_uid (stream,i); /* try to use remembered text */ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst; else { /* get data */ st.data = (unsigned char *) mail_fetch_body (stream,i,ta->section,&st.size,ta->flags); if (ta->first || ta->last) remember (uid,tmp,&st); } /* what encoding was used? */ if (body) switch (body->encoding) { case ENCBASE64: if (cst.data = rfc822_base64 (st.data,st.size,&cst.size)) break; fetch_uid (i,NIL); /* wrote a space, so must do something */ if (lsterr) fs_give ((void **) &lsterr); lsterr = cpystr ("Undecodable BASE64 contents"); response = loseunknowncte; fs_give ((void **) &tmp); return; case ENCQUOTEDPRINTABLE: if (cst.data = rfc822_qprint (st.data,st.size,&cst.size)) break; fetch_uid (i,NIL); /* wrote a space, so must do something */ if (lsterr) fs_give ((void **) &lsterr); lsterr = cpystr ("Undecodable QUOTED-PRINTABLE contents"); response = loseunknowncte; fs_give ((void **) &tmp); return; case ENC7BIT: /* no need to convert any of these */ case ENC8BIT: case ENCBINARY: cst.data = NIL; /* no converted data to free */ break; default: /* unknown encoding, oops */ fetch_uid (i,NIL); /* wrote a space, so must do something */ if (lsterr) fs_give ((void **) &lsterr); lsterr = cpystr ("Unknown Content-Transfer-Encoding"); response = loseunknowncte; fs_give ((void **) &tmp); return; } else { if (lsterr) fs_give ((void **) &lsterr); lsterr = cpystr ("Invalid body part"); response = loseunknowncte; fs_give ((void **) &tmp); return; } /* use decoded version if exists */ if (cst.data) memcpy ((void *) &st,(void *) &cst,sizeof (SIZEDTEXT)); if (ta->binary & FTB_SIZE) {/* just want size? */ sprintf (tmp,"BINARY.SIZE[%s] %lu",ta->section ? ta->section : "", st.size); PSOUT (tmp); } else { /* no, blat binary data */ int f = mail_elt (stream,i)->seen; if (st.data) { /* only if have useful data */ /* partial specifier */ if (ta->first || ta->last) sprintf (tmp,"BINARY[%s]<%lu> ", ta->section ? ta->section : "",ta->first); else sprintf (tmp,"BINARY[%s] ",ta->section ? ta->section : ""); /* in case first byte beyond end of text */ if (st.size <= ta->first) st.size = ta->first = 0; else { /* offset and truncate */ st.data += ta->first; /* move to desired position */ st.size -= ta->first; /* reduced size */ if (ta->last && (st.size > ta->last)) st.size = ta->last; } if (!st.size) PSOUT ("\"\""); else { /* write binary output */ sprintf (tmp + strlen (tmp),"{%lu}\015\012",st.size); PSOUT (tmp); if (PSOUTR (&st) == EOF) { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO, "%.80s, while writing binary user=%.80s host=%.80s", strerror (errno),user ? (char *) user : "???", tcp_clienthost ()); if (state == OPEN) stream = mail_close (stream); /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } } } else { sprintf (tmp,"BINARY[%s] NIL",ta->section ? ta->section : ""); PSOUT (tmp); } changed_flags (i,f); /* write changed flags */ } /* free converted data */ if (cst.data) fs_give ((void **) &cst.data); fs_give ((void **) &tmp); /* and temporary string */ } else { /* clean up the arguments */ if (ta->section) fs_give ((void **) &ta->section); fs_give ((void **) &args); } } /* Fetch MESSAGE/RFC822 body part header * Accepts: message number * extra argument */ void fetch_body_part_header (unsigned long i,void *args) { TEXTARGS *ta = (TEXTARGS *) args; unsigned long len = 100 + (ta->section ? strlen (ta->section) : 0); STRINGLIST *s; for (s = ta->lines; s; s = s->next) len += s->text.size + 1; if (i) { /* do work? */ SIZEDTEXT st; char *tmp = (char *) fs_get (len); PSOUT ("BODY["); /* output attribute */ if (ta->section && *ta->section) { PSOUT (ta->section); PBOUT ('.'); } PSOUT ("HEADER"); if (ta->lines) { PSOUT ((ta->flags & FT_NOT) ? ".FIELDS.NOT " : ".FIELDS "); pastringlist (ta->lines); } strcpy (tmp,"]"); /* close section specifier */ st.data = (unsigned char *) /* get data (no hope in using remember here) */ mail_fetch_header (stream,i,ta->section,ta->lines,&st.size,ta->flags); pbodypartstring (i,tmp,&st,ta); fs_give ((void **) &tmp); } else { /* clean up the arguments */ if (ta->lines) mail_free_stringlist (&ta->lines); if (ta->section) fs_give ((void **) &ta->section); fs_give ((void **) &args); } } /* Fetch MESSAGE/RFC822 body part text * Accepts: message number * extra argument */ void fetch_body_part_text (unsigned long i,void *args) { TEXTARGS *ta = (TEXTARGS *) args; if (i) { /* do work? */ SIZEDTEXT st; char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0)); unsigned long uid = mail_uid (stream,i); /* output attribute */ if (ta->section && *ta->section) sprintf (tmp,"BODY[%s.TEXT]",ta->section); else strcpy (tmp,"BODY[TEXT]"); /* try to use remembered text */ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst; else { /* get data */ st.data = (unsigned char *) mail_fetch_text (stream,i,ta->section,&st.size,ta->flags); if (ta->first || ta->last) remember (uid,tmp,&st); } pbodypartstring (i,tmp,&st,ta); fs_give ((void **) &tmp); } else { /* clean up the arguments */ if (ta->section) fs_give ((void **) &ta->section); fs_give ((void **) &args); } } /* Remember body part text for subsequent partial fetching * Accepts: message UID * body part id * text */ void remember (unsigned long uid,char *id,SIZEDTEXT *st) { lastuid = uid; /* remember UID */ if (lastid) fs_give ((void **) &lastid); lastid = cpystr (id); /* remember body part id */ if (lastst.data) fs_give ((void **) &lastst.data); /* remember text */ lastst.data = (unsigned char *) memcpy (fs_get (st->size + 1),st->data,st->size); lastst.size = st->size; } /* Fetch envelope * Accepts: message number * extra argument */ void fetch_envelope (unsigned long i,void *args) { ENVELOPE *env = mail_fetchenvelope (stream,i); PSOUT ("ENVELOPE "); /* output attribute */ penv (env); /* output envelope */ } /* Fetch matching header lines * Accepts: message number * extra argument */ void fetch_rfc822_header_lines (unsigned long i,void *args) { STRINGLIST *sa = (STRINGLIST *) args; if (i) { /* do work? */ SIZEDTEXT st; st.data = (unsigned char *) mail_fetch_header (stream,i,NIL,sa,&st.size,FT_PEEK); PSOUT ("RFC822.HEADER "); psizednstring (&st); /* output literal */ } else mail_free_stringlist (&sa); } /* Fetch not-matching header lines * Accepts: message number * extra argument */ void fetch_rfc822_header_lines_not (unsigned long i,void *args) { STRINGLIST *sa = (STRINGLIST *) args; if (i) { /* do work? */ SIZEDTEXT st; st.data = (unsigned char *) mail_fetch_header (stream,i,NIL,sa,&st.size,FT_NOT | FT_PEEK); PSOUT ("RFC822.HEADER "); psizednstring (&st); /* output literal */ } else mail_free_stringlist (&sa); } /* Fetch flags * Accepts: message number * extra argument */ void fetch_flags (unsigned long i,void *args) { unsigned long u; char *t,tmp[MAILTMPLEN]; int c = NIL; MESSAGECACHE *elt = mail_elt (stream,i); if (!elt->valid) { /* have valid flags yet? */ sprintf (tmp,"%lu",i); mail_fetch_flags (stream,tmp,NIL); } PSOUT ("FLAGS ("); /* output attribute */ /* output system flags */ if (elt->recent) put_flag (&c,"\\Recent"); if (elt->seen) put_flag (&c,"\\Seen"); if (elt->deleted) put_flag (&c,"\\Deleted"); if (elt->flagged) put_flag (&c,"\\Flagged"); if (elt->answered) put_flag (&c,"\\Answered"); if (elt->draft) put_flag (&c,"\\Draft"); if (u = elt->user_flags) do /* any user flags? */ if (t = stream->user_flags[find_rightmost_bit (&u)]) put_flag (&c,t); while (u); /* until no more user flags */ PBOUT (')'); /* end of flags */ elt->spare2 = NIL; /* we've sent the update */ } /* Output a flag * Accepts: pointer to current delimiter character * flag to output * Changes delimiter character to space */ void put_flag (int *c,char *s) { if (*c) PBOUT (*c); /* put delimiter */ PSOUT (s); /* dump flag */ *c = ' '; /* change delimiter if necessary */ } /* Output flags if was unseen * Accepts: message number * prior value of Seen flag */ void changed_flags (unsigned long i,int f) { /* was unseen, now seen? */ if (!f && mail_elt (stream,i)->seen) { PBOUT (' '); /* yes, delimit with space */ fetch_flags (i,NIL); /* output flags */ } } /* Fetch message internal date * Accepts: message number * extra argument */ void fetch_internaldate (unsigned long i,void *args) { char tmp[MAILTMPLEN]; MESSAGECACHE *elt = mail_elt (stream,i); if (!elt->day) { /* have internal date yet? */ sprintf (tmp,"%lu",i); mail_fetch_fast (stream,tmp,NIL); } PSOUT ("INTERNALDATE \""); PSOUT (mail_date (tmp,elt)); PBOUT ('"'); } /* Fetch unique identifier * Accepts: message number * extra argument */ void fetch_uid (unsigned long i,void *args) { PSOUT ("UID "); pnum (mail_uid (stream,i)); } /* Fetch complete RFC-822 format message * Accepts: message number * extra argument */ void fetch_rfc822 (unsigned long i,void *args) { if (i) { /* do work? */ int f = mail_elt (stream,i)->seen; #if 0 SIZEDTEXT st; st.data = (unsigned char *) mail_fetch_message (stream,i,&st.size,(long) args); PSOUT ("RFC822 "); psizednstring (&st); #else /* Yes, this version is bletcherous, but mail_fetch_message() requires too much memory */ SIZEDTEXT txt,hdr; char *s = mail_fetch_header (stream,i,NIL,NIL,&hdr.size,FT_PEEK); hdr.data = (unsigned char *) memcpy (fs_get (hdr.size),s,hdr.size); txt.data = (unsigned char *) mail_fetch_text (stream,i,NIL,&txt.size,(long) args); PSOUT ("RFC822 {"); pnum (hdr.size + txt.size); PSOUT ("}\015\012"); ptext (&hdr); ptext (&txt); fs_give ((void **) &hdr.data); #endif changed_flags (i,f); /* output changed flags */ } } /* Fetch RFC-822 header * Accepts: message number * extra argument */ void fetch_rfc822_header (unsigned long i,void *args) { SIZEDTEXT st; st.data = (unsigned char *) mail_fetch_header (stream,i,NIL,NIL,&st.size,FT_PEEK); PSOUT ("RFC822.HEADER "); psizednstring (&st); } /* Fetch RFC-822 message length * Accepts: message number * extra argument */ void fetch_rfc822_size (unsigned long i,void *args) { char tmp[MAILTMPLEN]; MESSAGECACHE *elt = mail_elt (stream,i); if (!elt->rfc822_size) { /* have message size yet? */ sprintf (tmp,"%lu",i); mail_fetch_fast (stream,tmp,NIL); } PSOUT ("RFC822.SIZE "); pnum (elt->rfc822_size); } /* Fetch RFC-822 text only * Accepts: message number * extra argument */ void fetch_rfc822_text (unsigned long i,void *args) { if (i) { /* do work? */ int f = mail_elt (stream,i)->seen; SIZEDTEXT st; st.data = (unsigned char *) mail_fetch_text (stream,i,NIL,&st.size,(long) args); PSOUT ("RFC822.TEXT "); psizednstring (&st); changed_flags (i,f); /* output changed flags */ } } /* Print envelope * Accepts: body */ void penv (ENVELOPE *env) { PBOUT ('('); /* delimiter */ if (env) { /* only if there is an envelope */ pnstring (env->date); /* output envelope fields */ PBOUT (' '); pnstring (env->subject); PBOUT (' '); paddr (env->from); PBOUT (' '); paddr (env->sender); PBOUT (' '); paddr (env->reply_to); PBOUT (' '); paddr (env->to); PBOUT (' '); paddr (env->cc); PBOUT (' '); paddr (env->bcc); PBOUT (' '); pnstring (env->in_reply_to); PBOUT (' '); pnstring (env->message_id); } /* no envelope */ else PSOUT ("NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL"); PBOUT (')'); /* end of envelope */ } /* Print body structure (extensible) * Accepts: body */ void pbodystructure (BODY *body) { PBOUT ('('); /* delimiter */ if (body) { /* only if there is a body */ PART *part; /* multipart type? */ if (body->type == TYPEMULTIPART) { /* print each part */ if (part = body->nested.part) for (; part; part = part->next) pbodystructure (&(part->body)); else pbodystructure (NIL); PBOUT (' '); /* space delimiter */ pstring (body->subtype); /* subtype */ PBOUT (' '); pparam (body->parameter); /* multipart body extension data */ PBOUT (' '); if (body->disposition.type) { PBOUT ('('); pstring (body->disposition.type); PBOUT (' '); pparam (body->disposition.parameter); PBOUT (')'); } else PSOUT ("NIL"); PBOUT (' '); pnstringorlist (body->language); PBOUT (' '); pnstring (body->location); } else { /* non-multipart body type */ pstring ((char *) body_types[body->type]); PBOUT (' '); pstring (body->subtype); PBOUT (' '); pparam (body->parameter); PBOUT (' '); pnstring (body->id); PBOUT (' '); pnstring (body->description); PBOUT (' '); pstring ((char *) body_encodings[body->encoding]); PBOUT (' '); pnum (body->size.bytes); switch (body->type) { /* extra stuff depends upon body type */ case TYPEMESSAGE: /* can't do this if not RFC822 */ if (strcmp (body->subtype,"RFC822")) break; PBOUT (' '); penv (body->nested.msg->env); PBOUT (' '); pbodystructure (body->nested.msg->body); case TYPETEXT: PBOUT (' '); pnum (body->size.lines); break; default: break; } PBOUT (' '); pnstring (body->md5); PBOUT (' '); if (body->disposition.type) { PBOUT ('('); pstring (body->disposition.type); PBOUT (' '); pparam (body->disposition.parameter); PBOUT (')'); } else PSOUT ("NIL"); PBOUT (' '); pnstringorlist (body->language); PBOUT (' '); pnstring (body->location); } } /* no body */ else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0 NIL NIL NIL NIL"); PBOUT (')'); /* end of body */ } /* Print body (non-extensible) * Accepts: body */ void pbody (BODY *body) { PBOUT ('('); /* delimiter */ if (body) { /* only if there is a body */ PART *part; /* multipart type? */ if (body->type == TYPEMULTIPART) { /* print each part */ if (part = body->nested.part) for (; part; part = part->next) pbody (&(part->body)); else pbody (NIL); PBOUT (' '); /* space delimiter */ pstring (body->subtype); /* and finally the subtype */ } else { /* non-multipart body type */ pstring ((char *) body_types[body->type]); PBOUT (' '); pstring (body->subtype); PBOUT (' '); pparam (body->parameter); PBOUT (' '); pnstring (body->id); PBOUT (' '); pnstring (body->description); PBOUT (' '); pstring ((char *) body_encodings[body->encoding]); PBOUT (' '); pnum (body->size.bytes); switch (body->type) { /* extra stuff depends upon body type */ case TYPEMESSAGE: /* can't do this if not RFC822 */ if (strcmp (body->subtype,"RFC822")) break; PBOUT (' '); penv (body->nested.msg ? body->nested.msg->env : NIL); PBOUT (' '); pbody (body->nested.msg ? body->nested.msg->body : NIL); case TYPETEXT: PBOUT (' '); pnum (body->size.lines); break; default: break; } } } /* no body */ else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0"); PBOUT (')'); /* end of body */ } /* Print parameter list * Accepts: paramter */ void pparam (PARAMETER *param) { if (param) { /* one specified? */ PBOUT ('('); do { pstring (param->attribute); PBOUT (' '); pstring (param->value); if (param = param->next) PBOUT (' '); } while (param); PBOUT (')'); /* end of parameters */ } else PSOUT ("NIL"); } /* Print address list * Accepts: address list */ void paddr (ADDRESS *a) { if (a) { /* have anything in address? */ PBOUT ('('); /* open the address list */ do { /* for each address */ PBOUT ('('); /* open the address */ pnstring (a->personal); /* personal name */ PBOUT (' '); pnstring (a->adl); /* at-domain-list */ PBOUT (' '); pnstring (a->mailbox); /* mailbox */ PBOUT (' '); pnstring (a->host); /* domain name of mailbox's host */ PBOUT (')'); /* terminate address */ } while (a = a->next); /* until end of address */ PBOUT (')'); /* close address list */ } else PSOUT ("NIL"); /* empty address */ } /* Print number * Accepts: number */ void pnum (unsigned long i) { char tmp[MAILTMPLEN]; sprintf (tmp,"%lu",i); PSOUT (tmp); } /* Print string * Accepts: string */ void pstring (char *s) { SIZEDTEXT st; st.data = (unsigned char *) s;/* set up sized text */ st.size = strlen (s); psizedstring (&st); /* print string */ } /* Print nstring * Accepts: string or NIL */ void pnstring (char *s) { if (s) pstring (s); /* print string */ else PSOUT ("NIL"); } /* Print atom or string * Accepts: astring */ void pastring (char *s) { char *t; if (!*s) PSOUT ("\"\""); /* empty string */ else { /* see if atom */ for (t = s; (*t > ' ') && !(*t & 0x80) && (*t != '"') && (*t != '\\') && (*t != '(') && (*t != ')') && (*t != '{') && (*t != '%') && (*t != '*'); t++); if (*t) pstring (s); /* not an atom */ else PSOUT (s); /* else plop down as atomic */ } } /* Print sized text as quoted * Accepts: sized text */ void psizedquoted (SIZEDTEXT *s) { PBOUT ('"'); /* use quoted string */ ptext (s); PBOUT ('"'); } /* Print sized text as literal * Accepts: sized text */ void psizedliteral (SIZEDTEXT *s) { PBOUT ('{'); /* print literal size */ pnum (s->size); PSOUT ("}\015\012"); ptext (s); } /* Print sized text as literal or quoted string * Accepts: sized text */ void psizedstring (SIZEDTEXT *s) { unsigned long i; for (i = 0; i < s->size; i++) /* check if must use literal */ if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) || (s->data[i] == '"') || (s->data[i] == '\\')) { psizedliteral (s); return; } psizedquoted (s); } /* Print sized nstring * Accepts: pointer to sized text or NIL */ void psizednstring (SIZEDTEXT *st) { if (st && st->data) psizedstring (st); else PSOUT ("NIL"); } /* Print sized text as literal or quoted string * Accepts: sized text */ void psizedastring (SIZEDTEXT *s) { unsigned long i; unsigned int atomp = s->size ? T : NIL; for (i = 0; i < s->size; i++){/* check if must use literal */ if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) || (s->data[i] == '"') || (s->data[i] == '\\')) { psizedliteral (s); return; } else switch (s->data[i]) { /* else see if any atom-specials */ case '(': case ')': case '{': case ' ': case '%': case '*': /* list-wildcards */ case ']': /* resp-specials */ /* CTL and quoted-specials in literal check */ atomp = NIL; /* not an atom */ } } if (atomp) ptext (s); /* print as atom */ else psizedquoted (s); /* print as quoted string */ } /* Print string list * Accepts: string list */ void pastringlist (STRINGLIST *s) { PBOUT ('('); /* start list */ do { psizedastring (&s->text); /* output list member */ if (s->next) PBOUT (' '); } while (s = s->next); PBOUT (')'); /* terminate list */ } /* Print nstring or list of strings * Accepts: string / string list */ void pnstringorlist (STRINGLIST *s) { if (!s) PSOUT ("NIL"); /* no argument given */ else if (s->next) { /* output list as list of strings*/ PBOUT ('('); /* start list */ do { psizedstring (&s->text); /* output list member */ if (s->next) PBOUT (' '); } while (s = s->next); PBOUT (')'); /* terminate list */ } else psizedstring (&s->text); /* and single-element list as nstring */ } /* Print body part string * Accepts: message number * body part id (note: must have space at end to append stuff) * sized text of string * text printing arguments */ void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,TEXTARGS *ta) { int f = mail_elt (stream,msgno)->seen; if (st->data) { /* only if have useful data */ /* partial specifier */ if (ta->first || ta->last) sprintf (id + strlen (id),"<%lu>",ta->first); /* in case first byte beyond end of text */ if (st->size <= ta->first) st->size = ta->first = 0; else { /* offset and truncate */ st->data += ta->first; /* move to desired position */ st->size -= ta->first; /* reduced size */ if (ta->last && (st->size > ta->last)) st->size = ta->last; } } PSOUT (id); PBOUT (' '); psizednstring (st); /* output nstring */ changed_flags (msgno,f); /* and changed flags */ } /* Print text * Accepts: pointer to text * pointer to size of text */ void ptext (SIZEDTEXT *txt) { /* RFC 3501 technically forbids NULs in literals. Normally, the delivering * MTA would take care of MIME converting the message text so that it is * NUL-free. If it doesn't, then we have the choice of either violating * IMAP by sending NULs, corrupting the data, or going to lots of work to do * MIME conversion in the IMAP server. */ unsigned char c; unsigned char *s = txt->data; unsigned char *t = s + txt->size; while ((s < t) && ((PBOUT ((c = *s++) ? c : 0x80) != EOF))); if (s == t) return; /* check for completion */ alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"%.80s, while writing text user=%.80s host=%.80s", strerror (errno),user ? (char *) user : "???",tcp_clienthost ()); if (state == OPEN) stream = mail_close (stream); /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } /* Print thread * Accepts: thread */ void pthread (THREADNODE *thr) { THREADNODE *t; while (thr) { /* for each branch */ PBOUT ('('); /* open branch */ if (thr->num) { /* first node message number */ pnum (thr->num); if (t = thr->next) { /* any subsequent nodes? */ PBOUT (' '); while (t) { /* for each subsequent node */ if (t->branch) { /* branches? */ pthread (t); /* yes, recurse to do branch */ t = NIL; /* done */ } else { /* just output this number */ pnum (t->num); t = t->next; /* and do next message */ } if (t) PBOUT (' '); /* delimit if more to come */ } } } else pthread (thr->next); /* nest for dummy */ PBOUT (')'); /* done with this branch */ thr = thr->branch; /* do next branch */ } } /* Print capabilities * Accepts: option flag */ void pcapability (long flag) { unsigned long i; char *s; struct stat sbuf; AUTHENTICATOR *auth; THREADER *thr = (THREADER *) mail_parameters (NIL,GET_THREADERS,NIL); /* always output protocol level */ PSOUT ("CAPABILITY IMAP4REV1 LITERAL+"); #ifdef NETSCAPE_BRAIN_DAMAGE PSOUT (" X-NETSCAPE"); #endif if (flag >= 0) { /* want post-authentication capabilities? */ PSOUT (" IDLE NAMESPACE MAILBOX-REFERRALS BINARY UNSELECT SCAN SORT"); while (thr) { /* threaders */ PSOUT (" THREAD="); PSOUT (thr->name); thr = thr->next; } if (!anonymous) PSOUT (" MULTIAPPEND"); } if (flag <= 0) { /* want pre-authentication capabilities? */ PSOUT (" SASL-IR LOGIN-REFERRALS"); if (s = ssl_start_tls (NIL)) fs_give ((void *) &s); else PSOUT (" STARTTLS"); /* disable plaintext */ if (i = (unsigned long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)) PSOUT (" LOGINDISABLED"); for (auth = mail_lookup_auth (1); auth; auth = auth->next) if (auth->server && (!i || (auth->flags & AU_SECURE))) { PSOUT (" AUTH="); PSOUT (auth->name); } if (!stat (ANOFILE,&sbuf)) PSOUT (" AUTH=ANONYMOUS"); } } /* Anonymous users may only use these mailboxes in these namespaces */ char *oktab[] = {"#news.", "#ftp/", "#public/", 0}; /* Check if mailbox name is OK * Accepts: reference name * mailbox name */ long nameok (char *ref,char *name) { int i; unsigned char *s,*t; if (!name) return NIL; /* failure if missing name */ if (!anonymous) return T; /* otherwise OK if not anonymous */ /* validate reference */ if (ref && ((*ref == '#') || (*ref == '{'))) for (i = 0; oktab[i]; i++) { for (s = ref, t = oktab[i]; *t && (*s + (isupper (*s) ? 'a'-'A' : 0)) == *t; s++, t++); if (!*t) { /* reference OK */ if (*name == '#') break;/* check name if override */ else return T; /* otherwise done */ } } /* ordinary names are OK */ if ((*name != '#') && (*name != '{')) return T; for (i = 0; oktab[i]; i++) { /* validate mailbox */ for (s = name, t = oktab[i]; *t && (*s + (isupper (*s) ? 'a'-'A' : 0)) == *t; s++, t++); if (!*t) return T; /* name is OK */ } response = "%.80s NO Anonymous may not %.80s this name\015\012"; return NIL; } /* Convert possible BBoard name to actual name * Accepts: command * mailbox name * Returns: maibox name */ char *bboardname (char *cmd,char *name) { if (cmd[0] == 'B') { /* want bboard? */ char *s = litstk[litsp++] = (char *) fs_get (strlen (name) + 9); sprintf (s,"#public/%s",(*name == '/') ? name+1 : name); name = s; } return name; } /* Test if name is news proxy * Accepts: name * Returns: T if news proxy, NIL otherwise */ long isnewsproxy (char *name) { return (nntpproxy && (name[0] == '#') && ((name[1] == 'N') || (name[1] == 'n')) && ((name[2] == 'E') || (name[2] == 'e')) && ((name[3] == 'W') || (name[3] == 'w')) && ((name[4] == 'S') || (name[4] == 's')) && (name[5] == '.')) ? LONGT : NIL; } /* News proxy generate canonical pattern * Accepts: reference * pattern * buffer to return canonical pattern * Returns: T on success with pattern in buffer, NIL on failure */ long newsproxypattern (char *ref,char *pat,char *pattern,long flag) { if (!nntpproxy) return NIL; if (strlen (ref) > NETMAXMBX) { sprintf (pattern,"Invalid reference specification: %.80s",ref); mm_log (pattern,ERROR); return NIL; } if (strlen (pat) > NETMAXMBX) { sprintf (pattern,"Invalid pattern specification: %.80s",pat); mm_log (pattern,ERROR); return NIL; } if (flag) { /* prepend proxy specifier */ sprintf (pattern,"{%.300s/nntp}",nntpproxy); pattern += strlen (pattern); } if (*ref) { /* have a reference */ strcpy (pattern,ref); /* copy reference to pattern */ /* # overrides mailbox field in reference */ if (*pat == '#') strcpy (pattern,pat); /* pattern starts, reference ends, with . */ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.')) strcat (pattern,pat + 1); /* append, omitting one of the period */ else strcat (pattern,pat); /* anything else is just appended */ } else strcpy (pattern,pat); /* just have basic name */ return isnewsproxy (pattern); } /* IMAP4rev1 Authentication responder * Accepts: challenge * length of challenge * pointer to response length return location if non-NIL * Returns: response */ #define RESPBUFLEN 8*MAILTMPLEN char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen) { unsigned long i,j; unsigned char *t,resp[RESPBUFLEN]; if (initial) { /* initial response given? */ if (clen) return NIL; /* not permitted */ /* set up response */ t = (unsigned char *) initial; initial = NIL; /* no more initial response */ return (char *) rfc822_base64 (t,strlen ((char *) t),rlen ? rlen : &i); } PSOUT ("+ "); for (t = rfc822_binary ((void *) challenge,clen,&i),j = 0; j < i; j++) if (t[j] > ' ') PBOUT (t[j]); fs_give ((void **) &t); CRLF; PFLUSH (); /* dump output buffer */ /* slurp response buffer */ slurp ((char *) resp,RESPBUFLEN); if (!(t = (unsigned char *) strchr ((char *) resp,'\012'))) return flush (); if (t[-1] == '\015') --t; /* remove CR */ *t = '\0'; /* tie off buffer */ if (resp[0] == '*') { cancelled = T; return NIL; } return (char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i); } /* Proxy copy across mailbox formats * Accepts: mail stream * sequence to copy on this stream * destination mailbox * option flags * Returns: T if success, else NIL */ long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { MAILSTREAM *ts; STRING st; MSGDATA md; char tmp[MAILTMPLEN]; unsigned long i,j; md.stream = stream; md.msgno = 0; md.flags = md.date = NIL; md.message = &st; if (!((options & CP_UID) ? /* validate sequence */ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; response = win; /* cancel previous errors */ if (lsterr) fs_give ((void **) &lsterr); /* c-client clobbers sequence, use spare */ for (i = 1,j = 0; i <= nmsgs; i++) if ((mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence) && !j) md.msgno = (j = i) - 1; /* only if at least one message to copy */ if (j && !mail_append_multiple (NIL,mailbox,proxy_append,(void *) &md)) { response = trycreate ? losetry : lose; return NIL; } response = win; /* stomp any previous babble */ if (md.msgno) { /* get new driver name if was dummy */ sprintf (tmp,"Cross-format (%.80s -> %.80s) COPY completed", stream->dtb->name,(ts = mail_open (NIL,mailbox,OP_PROTOTYPE)) ? ts->dtb->name : "unknown"); mm_log (tmp,NIL); } return LONGT; } /* Proxy append message callback * Accepts: MAIL stream * append data package * pointer to return initial flags * pointer to return message internal date * pointer to return stringstruct of message or NIL to stop * Returns: T if success (have message or stop), NIL if error */ long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message) { MESSAGECACHE *elt; unsigned long i; char *s,*t,tmp[MAILTMPLEN]; MSGDATA *md = (MSGDATA *) data; if (md->flags) fs_give ((void **) &md->flags); if (md->date) fs_give ((void **) &md->date); *message = NIL; /* assume all done */ *flags = *date = NIL; while (++md->msgno <= nmsgs) if ((elt = mail_elt (md->stream,md->msgno))->spare) { if (!(elt->valid && elt->day)) { sprintf (tmp,"%lu",md->msgno); mail_fetch_fast (md->stream,tmp,NIL); } memset (s = tmp,0,MAILTMPLEN); /* copy flags */ if (elt->seen) strcat (s," \\Seen"); if (elt->deleted) strcat (s," \\Deleted"); if (elt->flagged) strcat (s," \\Flagged"); if (elt->answered) strcat (s," \\Answered"); if (elt->draft) strcat (s," \\Draft"); if (i = elt->user_flags) do if ((t = md->stream->user_flags[find_rightmost_bit (&i)]) && *t && (strlen (t) < ((size_t) (MAILTMPLEN-((s += strlen (s))+2-tmp))))) { *s++ = ' '; /* space delimiter */ strcpy (s,t); } while (i); /* until no more user flags */ *message = md->message; /* set up return values */ *flags = md->flags = cpystr (tmp + 1); *date = md->date = cpystr (mail_date (tmp,elt)); INIT (md->message,msg_string,(void *) md,elt->rfc822_size); break; /* process this message */ } return LONGT; } /* Append message callback * Accepts: MAIL stream * append data package * pointer to return initial flags * pointer to return message internal date * pointer to return stringstruct of message or NIL to stop * Returns: T if success (have message or stop), NIL if error */ long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message) { unsigned long i,j; char *t; APPENDDATA *ad = (APPENDDATA *) data; unsigned char *arg = ad->arg; /* flush text of previous message */ if (t = ad->flags) fs_give ((void **) &ad->flags); if (t = ad->date) fs_give ((void **) &ad->date); if (t = ad->msg) fs_give ((void **) &ad->msg); *flags = *date = NIL; /* assume no flags or date */ if (t) { /* have previous message? */ if (!*arg) { /* if least one message, and no more coming */ *message = NIL; /* set stop */ return LONGT; /* return success */ } else if (*arg++ != ' ') { /* must have a delimiter to next argument */ response = misarg; /* oops */ return NIL; } } *message = ad->message; /* return pointer to message stringstruct */ if (*arg == '(') { /* parse optional flag list */ t = ++arg; /* pointer to flag list contents */ while (*arg && (*arg != ')')) arg++; if (*arg) *arg++ = '\0'; if (*arg == ' ') arg++; *flags = ad->flags = cpystr (t); } /* parse optional date */ if (*arg == '"') *date = ad->date = cpystr (snarf (&arg)); if (!arg || (*arg != '{')) /* parse message */ response = "%.80s BAD Missing literal in %.80s\015\012"; else if (!isdigit (arg[1])) response = "%.80s BAD Missing message to %.80s\015\012"; else if (!(i = strtoul (arg+1,&t,10))) response = "%.80s NO Empty message to %.80s\015\012"; else if (i > 0x7fffffff) /* maybe relax this a little */ response = "%.80s NO Excessively large message to %.80s\015\012"; else if (((*t == '+') && (t[1] == '}') && !t[2]) || ((*t == '}') && !t[1])) { /* get a literal buffer */ inliteral (ad->msg = (char *) fs_get (i+1),i); /* get new command tail */ slurp (ad->arg,CMDLEN - (ad->arg - cmdbuf)); if (strchr (ad->arg,'\012')) { /* reset strtok mechanism, tie off if done */ if (!strtok (ad->arg,"\015\012")) *ad->arg = '\0'; /* possible litplus? */ if (((j = strlen (ad->arg)) > 3) && (ad->arg[j - 1] == '}') && (ad->arg[j - 2] == '+') && isdigit (ad->arg[j - 3])) { /* back over possible count */ for (j -= 4; j && isdigit (ad->arg[j]); j--); if (ad->arg[j] == '{') litplus = strtoul (ad->arg + j + 1,NIL,10); } /* initialize stringstruct */ INIT (ad->message,mail_string,(void *) ad->msg,i); return LONGT; /* ready to go */ } flush (); /* didn't find end of line? */ fs_give ((void **) &ad->msg); } else response = badarg; /* not a literal */ return NIL; /* error */ } /* Got a referral * Accepts: MAIL stream * URL * referral type code */ #define REFPREFIX "[REFERRAL " #define REFSUFFIX "] Try specified URL" char *referral (MAILSTREAM *stream,char *url,long code) { if (lstref) fs_give ((void **) &lstref); lstref = (char *) fs_get (sizeof (REFPREFIX) + strlen (url) + sizeof (REFSUFFIX) - 1); sprintf (lstref,"[REFERRAL %.900s] Try specified URL",url); if (code == REFAUTH) response = altwin; return NIL; /* don't chase referrals for now */ } /* Co-routines from MAIL library */ /* Message matches a search * Accepts: MAIL stream * message number */ void mm_searched (MAILSTREAM *s,unsigned long msgno) { /* nothing to do here */ } /* Message exists (i.e. there are that many messages in the mailbox) * Accepts: MAIL stream * message number */ void mm_exists (MAILSTREAM *s,unsigned long number) { /* note change in number of messages */ if ((s != tstream) && (nmsgs != number)) { nmsgs = number; /* always update number of messages */ if (quell_events) existsquelled = T; else { PSOUT ("* "); pnum (nmsgs); PSOUT (" EXISTS\015\012"); } recent = 0xffffffff; /* make sure update recent too */ } } /* Message expunged * Accepts: MAIL stream * message number */ void mm_expunged (MAILSTREAM *s,unsigned long number) { if (quell_events) fatal ("Impossible EXPUNGE event"); if (s != tstream) { PSOUT ("* "); pnum (number); PSOUT (" EXPUNGE\015\012"); } nmsgs--; existsquelled = T; /* do EXISTS when command done */ } /* Message status changed * Accepts: MAIL stream * message number */ void mm_flags (MAILSTREAM *s,unsigned long number) { if (s != tstream) mail_elt (s,number)->spare2 = T; } /* Mailbox found * Accepts: hierarchy delimiter * mailbox name * attributes */ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) { mm_list_work ("LIST",delimiter,name,attributes); } /* Subscribed mailbox found * Accepts: hierarchy delimiter * mailbox name * attributes */ void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) { mm_list_work ("LSUB",delimiter,name,attributes); } /* Mailbox status * Accepts: MAIL stream * mailbox name * mailbox status */ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { if (!quell_events) { char tmp[MAILTMPLEN]; tmp[0] = tmp[1] = '\0'; if (status->flags & SA_MESSAGES) sprintf (tmp + strlen (tmp)," MESSAGES %lu",status->messages); if (status->flags & SA_RECENT) sprintf (tmp + strlen (tmp)," RECENT %lu",status->recent); if (status->flags & SA_UNSEEN) sprintf (tmp + strlen (tmp)," UNSEEN %lu",status->unseen); if (status->flags & SA_UIDNEXT) sprintf (tmp + strlen (tmp)," UIDNEXT %lu",status->uidnext); if (status->flags & SA_UIDVALIDITY) sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",status->uidvalidity); PSOUT ("* STATUS "); pastring (mailbox); PSOUT (" ("); PSOUT (tmp+1); PBOUT (')'); CRLF; } } /* Worker routine for LIST and LSUB * Accepts: name of response * hierarchy delimiter * mailbox name * attributes */ void mm_list_work (char *what,int delimiter,char *name,long attributes) { char *s; if (!quell_events) { char tmp[MAILTMPLEN]; if (finding) { PSOUT ("* MAILBOX "); PSOUT (name); } /* new form */ else if ((cmd[0] == 'R') || !(attributes & LATT_REFERRAL)) { PSOUT ("* "); PSOUT (what); PSOUT (" ("); tmp[0] = tmp[1] = '\0'; if (attributes & LATT_NOINFERIORS) strcat (tmp," \\NoInferiors"); if (attributes & LATT_NOSELECT) strcat (tmp," \\NoSelect"); if (attributes & LATT_MARKED) strcat (tmp," \\Marked"); if (attributes & LATT_UNMARKED) strcat (tmp," \\UnMarked"); PSOUT (tmp+1); switch (delimiter) { case '\\': /* quoted delimiter */ case '"': PSOUT (") \"\\"); PBOUT (delimiter); PBOUT ('"'); break; case '\0': /* no delimiter */ PSOUT (") NIL"); break; default: /* unquoted delimiter */ PSOUT (") \""); PBOUT (delimiter); PBOUT ('"'); break; } PBOUT (' '); /* output mailbox name */ if (proxylist && (s = strchr (name,'}'))) pastring (s+1); else pastring (name); } CRLF; } } /* Notification event * Accepts: MAIL stream * string to log * error flag */ void mm_notify (MAILSTREAM *stream,char *string,long errflg) { SIZEDTEXT msg; char *s,*code; if (!quell_events && (!tstream || (stream != tstream))) { switch (errflg) { case NIL: /* information message, set as OK response */ if ((string[0] == '[') && ((string[1] == 'T') || (string[1] == 't')) && ((string[2] == 'R') || (string[2] == 'r')) && ((string[3] == 'Y') || (string[3] == 'y')) && ((string[4] == 'C') || (string[4] == 'c')) && ((string[5] == 'R') || (string[5] == 'r')) && ((string[6] == 'E') || (string[6] == 'e')) && ((string[7] == 'A') || (string[7] == 'a')) && ((string[8] == 'T') || (string[8] == 't')) && ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']')) trycreate = T; case BYE: /* some other server signing off */ case PARSE: /* parse glitch, output unsolicited OK */ code = "* OK "; break; case WARN: /* warning, output unsolicited NO (kludge!) */ code = "* NO "; break; case ERROR: /* error that broke command */ default: /* default should never happen */ code = "* BAD "; break; } PSOUT (code); msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string), "\015\012")) ? (s - string) : strlen (string); PSOUTR (&msg); CRLF; PFLUSH (); /* let client see it immediately */ } } /* Log an event for the user to see * Accepts: string to log * error flag */ void mm_log (char *string,long errflg) { SIZEDTEXT msg; char *s; msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ? (s - string) : strlen (string); if (!quell_events) switch (errflg) { case NIL: /* information message, set as OK response */ if (response == win) { /* only if no other response yet */ response = altwin; /* switch to alternative win message */ if (lsterr) fs_give ((void **) &lsterr); lsterr = cpystr (string); /* copy string for later use */ if (s) lsterr[s - string] = NIL; } break; case PARSE: /* parse glitch, output unsolicited OK */ PSOUT ("* OK [PARSE] "); PSOUTR (&msg); CRLF; PFLUSH (); /* let client see it immediately */ break; case WARN: /* warning, output unsolicited NO */ /* ignore "Mailbox is empty" (KLUDGE!) */ if (strcmp (string,"Mailbox is empty")) { if (lstwrn) { /* have previous warning? */ PSOUT ("* NO "); PSOUT (lstwrn); CRLF; PFLUSH (); /* make sure client sees it immediately */ fs_give ((void **) &lstwrn); } lstwrn = cpystr (string); /* note last warning */ if (s) lstwrn[s - string] = NIL; } break; case ERROR: /* error that broke command */ default: /* default should never happen */ response = trycreate ? losetry : lose; if (lsterr) fs_give ((void **) &lsterr); /* note last error */ lsterr = cpystr (string); /* note last error */ if (s) lsterr[s - string] = NIL; break; } } /* Return last error */ char *lasterror (void) { if (lsterr) return lsterr; if (lstwrn) return lstwrn; return ""; } /* Log an event to debugging telemetry * Accepts: string to log */ void mm_dlog (char *string) { mm_log (string,WARN); /* shouldn't happen normally */ } /* Get user name and password for this host * Accepts: parse of network user name * where to return user name * where to return password * trial count */ void mm_login (NETMBX *mb,char *username,char *password,long trial) { /* set user name */ strncpy (username,*mb->user ? mb->user : (char *) user,NETMAXUSER); strncpy (password,pass,256); /* and password */ } /* About to enter critical code * Accepts: stream */ void mm_critical (MAILSTREAM *s) { ++critical; } /* About to exit critical code * Accepts: stream */ void mm_nocritical (MAILSTREAM *s) { /* go non-critical, pending death? */ if (!--critical && (state == LOGOUT)) { /* clean up */ if (s && !s->lock) s = mail_close (s); if (stream && (stream != s) && !stream->lock) stream = mail_close (stream); /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); /* die die die */ } } /* Disk error found * Accepts: stream * system error code * flag indicating that mailbox may be clobbered * Returns: abort flag */ long mm_diskerror (MAILSTREAM *s,long errcode,long serious) { if (serious) { /* try your damnest if clobberage likely */ mm_notify (s,"Retrying to fix probable mailbox damage!",ERROR); PFLUSH (); /* dump output buffer */ syslog (LOG_ALERT, "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s", user ? (char *) user : "???",tcp_clienthost (), (stream && stream->mailbox) ? stream->mailbox : "???", strerror (errcode)); alarm (0); /* make damn sure timeout disabled */ sleep (60); /* give it some time to clear up */ return NIL; } if (!quell_events) { /* otherwise die before more damage is done */ PSOUT ("* NO Disk error: "); PSOUT (strerror (errcode)); CRLF; } return T; } /* Log a fatal error event * Accepts: string to log */ void mm_fatal (char *string) { SIZEDTEXT msg; char *s; msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ? (s - string) : strlen (string); if (!quell_events) { PSOUT ("* BYE [ALERT] IMAP4rev1 server crashing: "); PSOUTR (&msg); CRLF; } syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s", user ? (char *) user : "???",tcp_clienthost (), (stream && stream->mailbox) ? stream->mailbox : "???",string); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/imapd/imapd.def000066400000000000000000000002771137544547100223730ustar00rootroot00000000000000; ; IMAPD.DEF - def file for IMAP server ; NAME IMAPD DESCRIPTION 'IMAP4 Server Application' ;PROTMODE ;CODE PRELOAD MOVEABLE DISCARDABLE ;DATA PRELOAD MOVEABLE tkrat_2.2cvs20100105-dfsg.orig/imap/src/imapd/makefile.nt000066400000000000000000000023361137544547100227370ustar00rootroot00000000000000# Program: IMAPD Makefile for Windows 9x and Windows NT # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 5 November 1990 # Last Edited: 29 June 2004 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. ALERT=\\imapd.alert USERALERT=alert.txt SHUTDOWN=\\nologin ANO=\\anonymous.newsgroups NNTP=\\imapd.nntp C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\" imapd: $(CCLIENTLIB) imapd.obj LINK /NOLOGO /DEF:imapd.def imapd.obj $(LIBS) imapd.obj: $C\mail.h $C\misc.h $C\osdep.h $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/imapd/makefile.ntk000066400000000000000000000025011137544547100231040ustar00rootroot00000000000000# Program: IMAPD Makefile for Windows 9x and Windows NT + Kerberos # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 5 November 1990 # Last Edited: 29 June 2004 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. ALERT=\\imapd.alert USERALERT=alert.txt SHUTDOWN=\\nologin ANO=\\anonymous.newsgroups NNTP=\\imapd.nntp C = ..\c-client CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib LIBS = $(CCLIENTLIB) $(K5LIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\" imapd: $(CCLIENTLIB) imapd.obj LINK /NOLOGO /DEF:imapd.def imapd.obj $(LIBS) imapd.obj: $C\mail.h $C\misc.h $C\osdep.h $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/imapd/makefile.w2k000066400000000000000000000023241137544547100230160ustar00rootroot00000000000000# Program: IMAPD Makefile for Windows 2000 # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 5 November 1990 # Last Edited: 29 June 2004 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. ALERT=\\imapd.alert USERALERT=alert.txt SHUTDOWN=\\nologin ANO=\\anonymous.newsgroups NNTP=\\imapd.nntp C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib CFLAGS= -I$C /MT /W3 /DWIN32 -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\" imapd: $(CCLIENTLIB) imapd.obj LINK /NOLOGO /DEF:imapd.def imapd.obj $(LIBS) imapd.obj: $C\mail.h $C\misc.h $C\osdep.h $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/000077500000000000000000000000001137544547100206345ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/Makefile000066400000000000000000000021511137544547100222730ustar00rootroot00000000000000# Program: IPOPD client Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 28 October 1990 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ../c-client CCLIENTLIB = $C/c-client.a SHELL = /bin/sh # Get local definitions from c-client directory CC = `cat $C/CCTYPE` CFLAGS = -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` ipopd: ipop2d ipop3d ipop2d: $(CCLIENTLIB) ipop2d.o $(CC) $(CFLAGS) -o ipop2d ipop2d.o $(LDFLAGS) ipop3d: $(CCLIENTLIB) ipop3d.o $(CC) $(CFLAGS) -o ipop3d ipop3d.o $(LDFLAGS) ipop2d.o: $C/mail.h $C/misc.h $C/osdep.h ipop3d.o: $C/mail.h $C/misc.h $C/osdep.h $(CCLIENTLIB): cd $C;make clean: rm -f *.o ipop2d ipop3d || true # A monument to a hack of long ago and far away... love: @echo 'not war?' tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/ipop2d.c000066400000000000000000000476461137544547100222160ustar00rootroot00000000000000/* * Program: IPOP2D - IMAP to POP2 conversion server * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 28 October 1990 * Last Edited: 16 September 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Parameter files */ #include #include #include extern int errno; /* just in case */ #include #include #include "c-client.h" /* Autologout timer */ #define KODTIMEOUT 60*5 #define LOGINTIMEOUT 60*3 #define TIMEOUT 60*30 /* Size of temporary buffers */ #define TMPLEN 1024 /* Server states */ #define LISN 0 #define AUTH 1 #define MBOX 2 #define ITEM 3 #define NEXT 4 #define DONE 5 /* Global storage */ char *version = "2004.69"; /* server version */ short state = LISN; /* server state */ short critical = NIL; /* non-zero if in critical code */ MAILSTREAM *stream = NIL; /* mailbox stream */ long idletime = 0; /* time we went idle */ unsigned long nmsgs = 0; /* number of messages */ unsigned long current = 1; /* current message number */ unsigned long size = 0; /* size of current message */ char status[MAILTMPLEN]; /* space for status string */ char *user = ""; /* user name */ char *pass = ""; /* password */ unsigned long *msg = NIL; /* message translation vector */ logouthook_t lgoh = NIL; /* logout hook */ /* Function prototypes */ int main (int argc,char *argv[]); void clkint (); void kodint (); void hupint (); void trmint (); short c_helo (char *t,int argc,char *argv[]); short c_fold (char *t); short c_read (char *t); short c_retr (char *t); short c_acks (char *t); short c_ackd (char *t); short c_nack (char *t); /* Main program */ int main (int argc,char *argv[]) { char *s,*t; char cmdbuf[TMPLEN]; char *pgmname = (argc && argv[0]) ? (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ? s+1 : argv[0]) : "ipop2d"; /* set service name before linkage */ mail_parameters (NIL,SET_SERVICENAME,(void *) "pop"); #include "linkage.c" if (mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)) { printf ("- POP2 server disabled on this system\015\012"); fflush (stdout); _exit (1); } /* initialize server */ server_init (pgmname,"pop",NIL,clkint,kodint,hupint,trmint); /* There are reports of POP2 clients which get upset if anything appears * between the "+" and the "POP2" in the greeting. */ printf ("+ POP2 %s v%s server ready\015\012",tcp_serverhost (),version); fflush (stdout); /* dump output buffer */ state = AUTH; /* initial server state */ while (state != DONE) { /* command processing loop */ idletime = time (0); /* get a command under timeout */ alarm ((state != AUTH) ? TIMEOUT : LOGINTIMEOUT); clearerr (stdin); /* clear stdin errors */ while (!fgets (cmdbuf,TMPLEN-1,stdin)) { if (ferror (stdin) && (errno == EINTR)) clearerr (stdin); else { char *e = ferror (stdin) ? strerror (errno) : "Command stream end of file"; alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"%s while reading line user=%.80s host=%.80s", e,user ? user : "???",tcp_clienthost ()); state = DONE; mail_close (stream); /* try to close the stream gracefully */ stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } } alarm (0); /* make sure timeout disabled */ idletime = 0; /* no longer idle */ /* find end of line */ if (!strchr (cmdbuf,'\012')) { server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); fputs ("- Command line too long\015\012",stdout); state = DONE; } else if (!(s = strtok (cmdbuf," \015\012"))) { server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); fputs ("- Missing or null command\015\012",stdout); state = DONE; } else { /* dispatch based on command */ ucase (s); /* canonicalize case */ /* snarf argument */ t = strtok (NIL,"\015\012"); if ((state == AUTH) && !strcmp (s,"HELO")) state = c_helo (t,argc,argv); else if ((state == MBOX || state == ITEM) && !strcmp (s,"FOLD")) state = c_fold (t); else if ((state == MBOX || state == ITEM) && !strcmp (s,"READ")) state = c_read (t); else if ((state == ITEM) && !strcmp (s,"RETR")) state = c_retr (t); else if ((state == NEXT) && !strcmp (s,"ACKS")) state = c_acks (t); else if ((state == NEXT) && !strcmp (s,"ACKD")) state = c_ackd (t); else if ((state == NEXT) && !strcmp (s,"NACK")) state = c_nack (t); else if ((state == AUTH || state == MBOX || state == ITEM) && !strcmp (s,"QUIT")) { server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); state = DONE; /* done in either case */ if (t) fputs ("- Bogus argument given to QUIT\015\012",stdout); else { /* expunge the stream */ if (stream && nmsgs) stream = mail_close_full (stream,CL_EXPUNGE); stream = NIL; /* don't repeat it */ /* acknowledge the command */ fputs ("+ Sayonara\015\012",stdout); } } else { /* some other or inappropriate command */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); printf ("- Bogus or out of sequence command - %s\015\012",s); state = DONE; } } fflush (stdout); /* make sure output blatted */ } /* clean up the stream */ if (stream) mail_close (stream); syslog (LOG_INFO,"Logout user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); exit (0); /* all done */ return 0; /* stupid compilers */ } /* Clock interrupt */ void clkint () { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); fputs ("- Autologout; idle for too long\015\012",stdout); syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); fflush (stdout); /* make sure output blatted */ state = DONE; /* mark state done in either case */ if (!critical) { /* badly host if in critical code */ if (stream && !stream->lock) mail_close (stream); stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); /* die die die */ } } /* Kiss Of Death interrupt */ void kodint () { /* only if in command wait */ if (idletime && ((time (0) - idletime) > KODTIMEOUT)) { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); fputs ("- Received Kiss of Death\015\012",stdout); syslog (LOG_INFO,"Killed (lost mailbox lock) user=%.80s host=%.80s", user ? user : "???",tcp_clienthost ()); fflush (stdout); /* make sure output blatted */ state = DONE; /* mark state done in either case */ if (!critical) { /* badly host if in critical code */ if (stream && !stream->lock) mail_close (stream); stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); /* die die die */ } } } /* Hangup interrupt */ void hupint () { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"Hangup user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); state = DONE; /* mark state done in either case */ if (!critical) { /* badly host if in critical code */ if (stream && !stream->lock) mail_close (stream); stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); /* die die die */ } } /* Termination interrupt */ void trmint () { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); fputs ("- Killed\015\012",stdout); syslog (LOG_INFO,"Killed user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); fflush (stdout); /* make sure output blatted */ if (critical) state = DONE; /* mark state done in either case */ /* Make no attempt at graceful closure since a shutdown may be in * progress, and we won't have any time to do mail_close() actions. */ else _exit (1); /* die die die */ } /* Parse HELO command * Accepts: pointer to command argument * Returns: new state */ short c_helo (char *t,int argc,char *argv[]) { char *s,*u,*p; char tmp[TMPLEN]; if ((!(t && *t && (u = strtok (t," ")) && (p = strtok (NIL,"\015\012")))) || (strlen (p) >= TMPLEN)) { /* get user name and password */ fputs ("- Missing user or password\015\012",stdout); return DONE; } /* copy password, handle quoting */ for (s = tmp; *p; p++) *s++ = (*p == '\\') ? *++p : *p; *s = '\0'; /* tie off string */ pass = cpystr (tmp); if (!(s = strchr (u,':'))) { /* want remote mailbox? */ /* no, delimit user from possible admin */ if (s = strchr (u,'*')) *s++ = '\0'; if (server_login (user = cpystr (u),pass,s,argc,argv)) { syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s",s ? "Admin " : "", user,tcp_clienthost ()); return c_fold ("INBOX"); /* local; select INBOX */ } } #ifndef DISABLE_POP_PROXY /* can't do if can't log in as anonymous */ else if (anonymous_login (argc,argv)) { *s++ = '\0'; /* separate host name from user name */ user = cpystr (s); /* note user name */ syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",u,user, tcp_clienthost ()); /* initially remote INBOX */ sprintf (tmp,"{%.128s/user=%.128s}INBOX",u,user); /* disable rimap just in case */ mail_parameters (NIL,SET_RSHTIMEOUT,0); return c_fold (tmp); } #endif fputs ("- Bad login\015\012",stdout); return DONE; } /* Parse FOLD command * Accepts: pointer to command argument * Returns: new state */ short c_fold (char *t) { unsigned long i,j,flags; char *s = NIL,tmp[2*TMPLEN]; NETMBX mb; if (!(t && *t)) { /* make sure there's an argument */ fputs ("- Missing mailbox name\015\012",stdout); return DONE; } myusername_full (&flags); /* get user type flags */ /* expunge old stream */ if (stream && nmsgs) mail_expunge (stream); nmsgs = 0; /* no more messages */ if (msg) fs_give ((void **) &msg); #ifndef DISABLE_POP_PROXY if (flags == MU_ANONYMOUS) { /* don't permit proxy to leave IMAP */ if (stream) { /* not first time */ if (!(stream->mailbox && (s = strchr (stream->mailbox,'}')))) fatal ("bad previous mailbox name"); strncpy (tmp,stream->mailbox,i = (++s - stream->mailbox)); if (i >= TMPLEN) fatal ("ridiculous network prefix"); strcpy (tmp+i,t); /* append mailbox to initial spec */ t = tmp; } /* must be net name first time */ else if (!mail_valid_net_parse (t,&mb)) fatal ("anonymous folder bogon"); } #endif /* open mailbox, note # of messages */ if (j = (stream = mail_open (stream,t,NIL)) ? stream->nmsgs : 0) { sprintf (tmp,"1:%lu",j); /* fetch fast information for all messages */ mail_fetch_fast (stream,tmp,NIL); msg = (unsigned long *) fs_get ((stream->nmsgs + 1) * sizeof (unsigned long)); for (i = 1; i <= j; i++) /* find undeleted messages, add to vector */ if (!mail_elt (stream,i)->deleted) msg[++nmsgs] = i; } #ifndef DISABLE_POP_PROXY if (!stream && (flags == MU_ANONYMOUS)) { fputs ("- Bad login\015\012",stdout); return DONE; } #endif printf ("#%lu messages in %s\015\012",nmsgs,stream ? stream->mailbox : ""); return MBOX; } /* Parse READ command * Accepts: pointer to command argument * Returns: new state */ short c_read (char *t) { MESSAGECACHE *elt = NIL; if (t && *t) { /* have a message number argument? */ /* validity check message number */ if (((current = strtoul (t,NIL,10)) < 1) || (current > nmsgs)) { fputs ("- Invalid message number given to READ\015\012",stdout); return DONE; } } else if (current > nmsgs) { /* at end of mailbox? */ fputs ("=0 No more messages\015\012",stdout); return MBOX; } /* set size if message valid and exists */ size = msg[current] ? (elt = mail_elt(stream,msg[current]))->rfc822_size : 0; if (elt) sprintf (status,"Status: %s%s\015\012", elt->seen ? "R" : " ",elt->recent ? " " : "O"); else status[0] = '\0'; /* no status */ size += strlen (status); /* update size to reflect status */ /* display results */ printf ("=%lu characters in message %lu\015\012",size + 2,current); return ITEM; } /* Parse RETR command * Accepts: pointer to command argument * Returns: new state */ short c_retr (char *t) { if (t) { /* disallow argument */ fputs ("- Bogus argument given to RETR\015\012",stdout); return DONE; } if (size) { /* message size valid? */ unsigned long i,j; t = mail_fetch_header (stream,msg[current],NIL,NIL,&i,FT_PEEK); if (i > 2) { /* only if there is something */ i -= 2; /* lop off last two octets */ while (i) { /* blat the header */ j = fwrite (t,sizeof (char),i,stdout); if (i -= j) t += j; /* advance to incomplete data */ } } fputs (status,stdout); /* yes, output message */ fputs ("\015\012",stdout); /* delimit header from text */ t = mail_fetch_text (stream,msg[current],NIL,&i,NIL); while (i) { /* blat the text */ j = fwrite (t,sizeof (char),i,stdout); if (i -= j) t += j; /* advance to incomplete data */ } fputs ("\015\012",stdout); /* trailer to coddle PCNFS' NFSMAIL */ } else return DONE; /* otherwise go away */ return NEXT; } /* Parse ACKS command * Accepts: pointer to command argument * Returns: new state */ short c_acks (char *t) { char tmp[TMPLEN]; if (t) { /* disallow argument */ fputs ("- Bogus argument given to ACKS\015\012",stdout); return DONE; } /* mark message as seen */ sprintf (tmp,"%lu",msg[current++]); mail_setflag (stream,tmp,"\\Seen"); return c_read (NIL); /* end message reading transaction */ } /* Parse ACKD command * Accepts: pointer to command argument * Returns: new state */ short c_ackd (char *t) { char tmp[TMPLEN]; if (t) { /* disallow argument */ fputs ("- Bogus argument given to ACKD\015\012",stdout); return DONE; } /* mark message as seen and deleted */ sprintf (tmp,"%lu",msg[current]); mail_setflag (stream,tmp,"\\Seen \\Deleted"); msg[current++] = 0; /* mark message as deleted */ return c_read (NIL); /* end message reading transaction */ } /* Parse NACK command * Accepts: pointer to command argument * Returns: new state */ short c_nack (char *t) { if (t) { /* disallow argument */ fputs ("- Bogus argument given to NACK\015\012",stdout); return DONE; } return c_read (NIL); /* end message reading transaction */ } /* Co-routines from MAIL library */ /* Message matches a search * Accepts: MAIL stream * message number */ void mm_searched (MAILSTREAM *stream,unsigned long msgno) { /* Never called */ } /* Message exists (i.e. there are that many messages in the mailbox) * Accepts: MAIL stream * message number */ void mm_exists (MAILSTREAM *stream,unsigned long number) { /* Can't use this mechanism. POP has no means of notifying the client of new mail during the session. */ } /* Message expunged * Accepts: MAIL stream * message number */ void mm_expunged (MAILSTREAM *stream,unsigned long number) { if (state != DONE) { /* ignore if closing */ /* this should never happen */ fputs ("- Mailbox expunged from under me!\015\012",stdout); if (stream && !stream->lock) mail_close (stream); stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } } /* Message status changed * Accepts: MAIL stream * message number */ void mm_flags (MAILSTREAM *stream,unsigned long number) { /* This isn't used */ } /* Mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* This isn't used */ } /* Subscribe mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* This isn't used */ } /* Mailbox status * Accepts: MAIL stream * mailbox name * mailbox status */ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { /* This isn't used */ } /* Notification event * Accepts: MAIL stream * string to log * error flag */ void mm_notify (MAILSTREAM *stream,char *string,long errflg) { mm_log (string,errflg); /* just do mm_log action */ } /* Log an event for the user to see * Accepts: string to log * error flag */ void mm_log (char *string,long errflg) { switch (errflg) { case NIL: /* information message */ case PARSE: /* parse glitch */ break; /* too many of these to log */ case WARN: /* warning */ syslog (LOG_DEBUG,"%s",string); break; case BYE: /* driver broke connection */ if (state != DONE) { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"Mailbox closed (%.80s) user=%.80s host=%.80s", string,user ? user : "???",tcp_clienthost ()); /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } break; case ERROR: /* error that broke command */ default: /* default should never happen */ syslog (LOG_NOTICE,"%s",string); break; } } /* Log an event to debugging telemetry * Accepts: string to log */ void mm_dlog (char *string) { /* Not doing anything here for now */ } /* Get user name and password for this host * Accepts: parse of network mailbox name * where to return user name * where to return password * trial count */ void mm_login (NETMBX *mb,char *username,char *password,long trial) { /* set user name */ strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1); strncpy (password,pass,255); /* and password */ username[NETMAXUSER] = password[255] = '\0'; } /* About to enter critical code * Accepts: stream */ void mm_critical (MAILSTREAM *stream) { ++critical; } /* About to exit critical code * Accepts: stream */ void mm_nocritical (MAILSTREAM *stream) { --critical; } /* Disk error found * Accepts: stream * system error code * flag indicating that mailbox may be clobbered * Returns: abort flag */ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { if (serious) { /* try your damnest if clobberage likely */ syslog (LOG_ALERT, "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s", user,tcp_clienthost (), (stream && stream->mailbox) ? stream->mailbox : "???", strerror (errcode)); alarm (0); /* make damn sure timeout disabled */ sleep (60); /* give it some time to clear up */ return NIL; } syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s", user,tcp_clienthost (), (stream && stream->mailbox) ? stream->mailbox : "???", strerror (errcode)); return T; } /* Log a fatal error event * Accepts: string to log */ void mm_fatal (char *string) { mm_log (string,ERROR); /* shouldn't happen normally */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/ipop2d.def000066400000000000000000000003001137544547100225020ustar00rootroot00000000000000; ; IPOP2D.DEF - def file for POP2 server ; NAME IPOP2D DESCRIPTION 'POP2 Server Application' ;PROTMODE ;CODE PRELOAD MOVEABLE DISCARDABLE ;DATA PRELOAD MOVEABLE tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/ipop3d.c000066400000000000000000000722241137544547100222050ustar00rootroot00000000000000/* * Program: IPOP3D - IMAP to POP3 conversion server * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 November 1990 * Last Edited: 16 September 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Parameter files */ #include #include #include extern int errno; /* just in case */ #include #include #include "c-client.h" #define CRLF PSOUT ("\015\012") /* primary output terpri */ /* Autologout timer */ #define KODTIMEOUT 60*5 #define LOGINTIMEOUT 60*3 #define TIMEOUT 60*10 /* Size of temporary buffers */ #define TMPLEN 1024 /* Server states */ #define AUTHORIZATION 0 #define TRANSACTION 1 #define UPDATE 2 #define LOGOUT 3 /* Eudora food */ #define STATUS "Status: %s%s\015\012" #define SLEN (sizeof (STATUS)-3) /* Global storage */ char *version = "2004.89"; /* server version */ short state = AUTHORIZATION; /* server state */ short critical = NIL; /* non-zero if in critical code */ MAILSTREAM *stream = NIL; /* mailbox stream */ long idletime = 0; /* time we went idle */ unsigned long nmsgs = 0; /* current number of messages */ unsigned long ndele = 0; /* number of deletes */ unsigned long last = 0; /* highest message accessed */ unsigned long il = 0; /* initial last message */ char challenge[128]; /* challenge */ char *host = NIL; /* remote host name */ char *user = NIL; /* user name */ char *pass = NIL; /* password */ char *initial = NIL; /* initial response */ long *msg = NIL; /* message translation vector */ logouthook_t lgoh = NIL; /* logout hook */ char *sayonara = "+OK Sayonara\015\012"; /* Function prototypes */ int main (int argc,char *argv[]); void clkint (); void kodint (); void hupint (); void trmint (); int pass_login (char *t,int argc,char *argv[]); char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]); char *responder (void *challenge,unsigned long clen,unsigned long *rlen); int mbxopen (char *mailbox); long blat (char *text,long lines,unsigned long size); void rset (); /* Main program */ int main (int argc,char *argv[]) { unsigned long i,j,k; char *s,*t; char tmp[TMPLEN]; time_t autologouttime; char *pgmname = (argc && argv[0]) ? (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ? s+1 : argv[0]) : "ipop3d"; /* set service name before linkage */ mail_parameters (NIL,SET_SERVICENAME,(void *) "pop"); #include "linkage.c" /* initialize server */ server_init (pgmname,"pop3","pop3s",clkint,kodint,hupint,trmint); challenge[0] = '\0'; /* find the CRAM-MD5 authenticator */ if (i = mail_lookup_auth_name ("CRAM-MD5",NIL)) { AUTHENTICATOR *a = mail_lookup_auth (i); if (a->server) { /* have an MD5 enable file? */ /* build challenge -- less than 128 chars */ sprintf (challenge,"<%lx.%lx@%.64s>",(unsigned long) getpid (), (unsigned long) time (0),tcp_serverhost ()); } } /* There are reports of POP3 clients which get upset if anything appears * between the "+OK" and the "POP3" in the greeting. */ PSOUT ("+OK POP3 "); if (!challenge[0]) { /* if no MD5 enable, output host name */ PSOUT (tcp_serverhost ()); PBOUT (' '); } PSOUT (version); PSOUT (" server ready"); if (challenge[0]) { /* if MD5 enable, output challenge here */ PBOUT (' '); PSOUT (challenge); } CRLF; PFLUSH (); /* dump output buffer */ autologouttime = time (0) + LOGINTIMEOUT; /* command processing loop */ while ((state != UPDATE) && (state != LOGOUT)) { idletime = time (0); /* get a command under timeout */ alarm ((state == TRANSACTION) ? TIMEOUT : LOGINTIMEOUT); clearerr (stdin); /* clear stdin errors */ while (!PSIN (tmp,TMPLEN)){ /* read command line */ /* ignore if some interrupt */ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin); else { char *e = ferror (stdin) ? strerror (errno) : "Command stream end of file"; alarm (0); /* disable all interrupts */ syslog (LOG_INFO,"%s while reading line user=%.80s host=%.80s", e,user ? user : "???",tcp_clienthost ()); rset (); /* try to gracefully close the stream */ if (state == TRANSACTION) mail_close (stream); stream = NIL; state = LOGOUT; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } } alarm (0); /* make sure timeout disabled */ idletime = 0; /* no longer idle */ if (!strchr (tmp,'\012')) /* find end of line */ PSOUT ("-ERR Command line too long\015\012"); else if (!(s = strtok (tmp," \015\012"))) PSOUT ("-ERR Null command\015\012"); else { /* dispatch based on command */ ucase (s); /* canonicalize case */ /* snarf argument */ t = strtok (NIL,"\015\012"); /* QUIT command always valid */ if (!strcmp (s,"QUIT")) state = UPDATE; else if (!strcmp (s,"CAPA")) { AUTHENTICATOR *auth; PSOUT ("+OK Capability list follows:\015\012"); PSOUT ("TOP\015\012LOGIN-DELAY 180\015\012UIDL\015\012"); if (s = ssl_start_tls (NIL)) fs_give ((void *) &s); else PSOUT ("STLS\015\012"); if (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)) PSOUT ("USER\015\012"); /* display secure server authenticators */ for (auth = mail_lookup_auth (1), s = "SASL"; auth; auth = auth->next) if (auth->server && (i || (auth->flags & AU_SECURE))) { if (s) { PSOUT (s); s = NIL; } PBOUT (' '); PSOUT (auth->name); } PSOUT (s ? ".\015\012" : "\015\012.\015\012"); } else switch (state) { /* else dispatch based on state */ case AUTHORIZATION: /* waiting to get logged in */ if (!strcmp (s,"AUTH")) { if (t && *t) { /* mechanism given? */ if (host) fs_give ((void **) &host); if (user) fs_give ((void **) &user); if (pass) fs_give ((void **) &pass); s = strtok (t," "); /* get mechanism name */ /* get initial response */ initial = strtok (NIL,"\015\012"); if (!(user = cpystr (mail_auth (s,responder,argc,argv)))) { PSOUT ("-ERR Bad authentication\015\012"); syslog (LOG_INFO,"AUTHENTICATE %s failure host=%.80s",s, tcp_clienthost ()); } else if ((state = mbxopen ("INBOX")) == TRANSACTION) syslog (LOG_INFO,"Auth user=%.80s host=%.80s nmsgs=%ld/%ld", user,tcp_clienthost (),nmsgs,stream->nmsgs); else syslog (LOG_INFO,"Auth user=%.80s host=%.80s no mailbox", user,tcp_clienthost ()); } else { AUTHENTICATOR *auth; PSOUT ("+OK Supported authentication mechanisms:\015\012"); i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL); for (auth = mail_lookup_auth (1); auth; auth = auth->next) if (auth->server && (i || (auth->flags & AU_SECURE))) { PSOUT (auth->name); CRLF; } PBOUT ('.'); CRLF; } } else if (!strcmp (s,"APOP")) { if (challenge[0]) { /* can do it if have an MD5 challenge */ if (host) fs_give ((void **) &host); if (user) fs_give ((void **) &user); if (pass) fs_give ((void **) &pass); /* get user name */ if (!(t && *t && (s = strtok (t," ")) && (t = strtok(NIL,"\012")))) PSOUT ("-ERR Missing APOP argument\015\012"); else if (!(user = apop_login (challenge,s,t,argc,argv))) PSOUT ("-ERR Bad APOP\015\012"); else if ((state = mbxopen ("INBOX")) == TRANSACTION) syslog (LOG_INFO,"APOP user=%.80s host=%.80s nmsgs=%ld/%ld", user,tcp_clienthost (),nmsgs,stream->nmsgs); else syslog (LOG_INFO,"APOP user=%.80s host=%.80s no mailbox", user,tcp_clienthost ()); } else PSOUT ("-ERR Not supported\015\012"); } /* (chuckle) */ else if (!strcmp (s,"RPOP")) PSOUT ("-ERR Nice try, bunkie\015\012"); else if (!strcmp (s,"STLS")) { if (t = ssl_start_tls (pgmname)) { PSOUT ("-ERR STLS failed: "); PSOUT (t); CRLF; } else PSOUT ("+OK STLS completed\015\012"); } else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) && !strcmp (s,"USER")) { if (host) fs_give ((void **) &host); if (user) fs_give ((void **) &user); if (pass) fs_give ((void **) &pass); if (t && *t) { /* if user name given */ /* skip leading whitespace (bogus clients!) */ while (*t == ' ') ++t; /* remote user name? */ if (s = strchr (t,':')) { *s++ = '\0'; /* tie off host name */ host = cpystr (t);/* copy host name */ user = cpystr (s);/* copy user name */ } /* local user name */ else user = cpystr (t); PSOUT ("+OK User name accepted, password please\015\012"); } else PSOUT ("-ERR Missing username argument\015\012"); } else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) && user && *user && !strcmp (s,"PASS")) state = pass_login (t,argc,argv); else PSOUT ("-ERR Unknown AUTHORIZATION state command\015\012"); break; case TRANSACTION: /* logged in */ if (!strcmp (s,"STAT")) { for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) { /* message still exists? */ j++; /* count one more undeleted message */ k += mail_elt (stream,msg[i])->rfc822_size + SLEN; } sprintf (tmp,"+OK %lu %lu\015\012",j,k); PSOUT (tmp); } else if (!strcmp (s,"LIST")) { if (t && *t) { /* argument do single message */ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) { sprintf (tmp,"+OK %lu %lu\015\012",i, mail_elt(stream,msg[i])->rfc822_size + SLEN); PSOUT (tmp); } else PSOUT ("-ERR No such message\015\012"); } else { /* entire mailbox */ PSOUT ("+OK Mailbox scan listing follows\015\012"); for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) { sprintf (tmp,"%lu %lu\015\012",i, mail_elt (stream,msg[i])->rfc822_size + SLEN); PSOUT (tmp); } PBOUT ('.'); /* end of list */ CRLF; } } else if (!strcmp (s,"UIDL")) { if (t && *t) { /* argument do single message */ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) { sprintf (tmp,"+OK %lu %08lx%08lx\015\012",i,stream->uid_validity, mail_uid (stream,msg[i])); PSOUT (tmp); } else PSOUT ("-ERR No such message\015\012"); } else { /* entire mailbox */ PSOUT ("+OK Unique-ID listing follows\015\012"); for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) { sprintf (tmp,"%lu %08lx%08lx\015\012",i,stream->uid_validity, mail_uid (stream,msg[i])); PSOUT (tmp); } PBOUT ('.'); /* end of list */ CRLF; } } else if (!strcmp (s,"RETR")) { if (t && *t) { /* must have an argument */ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) { MESSAGECACHE *elt; /* update highest message accessed */ if (i > last) last = i; sprintf (tmp,"+OK %lu octets\015\012", (elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN); PSOUT (tmp); /* output header */ t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK); blat (t,-1,k); /* output status */ sprintf (tmp,STATUS,elt->seen ? "R" : " ", elt->recent ? " " : "O"); PSOUT (tmp); CRLF; /* delimit header and text */ /* output text */ t = mail_fetch_text (stream,msg[i],NIL,&k,NIL); blat (t,-1,k); CRLF; /* end of list */ PBOUT ('.'); CRLF; } else PSOUT ("-ERR No such message\015\012"); } else PSOUT ("-ERR Missing message number argument\015\012"); } else if (!strcmp (s,"DELE")) { if (t && *t) { /* must have an argument */ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) { /* update highest message accessed */ if (i > last) last = i; /* delete message */ sprintf (tmp,"%ld",msg[i]); mail_setflag (stream,tmp,"\\Deleted"); msg[i] = -msg[i]; /* note that we deleted this message */ PSOUT ("+OK Message deleted\015\012"); ndele++; /* one more message deleted */ } else PSOUT ("-ERR No such message\015\012"); } else PSOUT ("-ERR Missing message number argument\015\012"); } else if (!strcmp (s,"NOOP")) PSOUT ("+OK No-op to you too!\015\012"); else if (!strcmp (s,"LAST")) { sprintf (tmp,"+OK %lu\015\012",last); PSOUT (tmp); } else if (!strcmp (s,"RSET")) { rset (); /* reset the mailbox */ PSOUT ("+OK Reset state\015\012"); } else if (!strcmp (s,"TOP")) { if (t && *t && (i =strtoul (t,&s,10)) && (i <= nmsgs) && (msg[i] > 0)) { /* skip whitespace */ while (*s == ' ') s++; /* make sure line count argument good */ if ((*s >= '0') && (*s <= '9')) { MESSAGECACHE *elt = mail_elt (stream,msg[i]); j = strtoul (s,NIL,10); /* update highest message accessed */ if (i > last) last = i; PSOUT ("+OK Top of message follows\015\012"); /* output header */ t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK); blat (t,-1,k); /* output status */ sprintf (tmp,STATUS,elt->seen ? "R" : " ", elt->recent ? " " : "O"); PSOUT (tmp); CRLF; /* delimit header and text */ if (j) { /* want any text lines? */ /* output text */ t = mail_fetch_text (stream,msg[i],NIL,&k,FT_PEEK); /* tie off final line if full text output */ if (j -= blat (t,j,k)) CRLF; } PBOUT ('.'); /* end of list */ CRLF; } else PSOUT ("-ERR Bad line count argument\015\012"); } else PSOUT ("-ERR Bad message number argument\015\012"); } else if (!strcmp (s,"XTND")) PSOUT ("-ERR Sorry I can't do that\015\012"); else PSOUT ("-ERR Unknown TRANSACTION state command\015\012"); break; default: PSOUT ("-ERR Server in unknown state\015\012"); break; } } PFLUSH (); /* make sure output finished */ if (autologouttime) { /* have an autologout in effect? */ /* cancel if no longer waiting for login */ if (state != AUTHORIZATION) autologouttime = 0; /* took too long to login */ else if (autologouttime < time (0)) { PSOUT ("-ERR Autologout\015\012"); syslog (LOG_INFO,"Autologout host=%.80s",tcp_clienthost ()); PFLUSH (); /* make sure output blatted */ state = LOGOUT; /* sayonara */ } } } if (stream && (state == UPDATE)) { mail_expunge (stream); syslog (LOG_INFO,"Logout user=%.80s host=%.80s nmsgs=%ld ndele=%ld", user,tcp_clienthost (),stream->nmsgs,ndele); mail_close (stream); } else syslog (LOG_INFO,"Logout user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); PSOUT (sayonara); /* "now it's time to say sayonara..." */ PFLUSH (); /* make sure output finished */ /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); exit (0); /* all done */ return 0; /* stupid compilers */ } /* Clock interrupt */ void clkint () { PSOUT ("-ERR Autologout; idle for too long\015\012"); syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); PFLUSH (); /* make sure output blatted */ if (critical) state = LOGOUT; /* badly hosed if in critical code */ else { /* try to gracefully close the stream */ if ((state == TRANSACTION) && !stream->lock) { rset (); mail_close (stream); } state = LOGOUT; stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); /* die die die */ } } /* Kiss Of Death interrupt */ void kodint () { /* only if idle */ if (idletime && ((time (0) - idletime) > KODTIMEOUT)) { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); PSOUT ("-ERR Received Kiss of Death\015\012"); syslog (LOG_INFO,"Killed (lost mailbox lock) user=%.80s host=%.80s", user ? user : "???",tcp_clienthost ()); if (critical) state =LOGOUT;/* must defer if in critical code */ else { /* try to gracefully close the stream */ if ((state == TRANSACTION) && !stream->lock) { rset (); mail_close (stream); } state = LOGOUT; stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); /* die die die */ } } } /* Hangup interrupt */ void hupint () { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"Hangup user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); if (critical) state = LOGOUT; /* must defer if in critical code */ else { /* try to gracefully close the stream */ if ((state == TRANSACTION) && !stream->lock) { rset (); mail_close (stream); } state = LOGOUT; stream = NIL; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); /* die die die */ } } /* Termination interrupt */ void trmint () { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); PSOUT ("-ERR Killed\015\012"); syslog (LOG_INFO,"Killed user=%.80s host=%.80s",user ? user : "???", tcp_clienthost ()); if (critical) state = LOGOUT; /* must defer if in critical code */ /* Make no attempt at graceful closure since a shutdown may be in * progress, and we won't have any time to do mail_close() actions. */ else _exit (1); /* die die die */ } /* Parse PASS command * Accepts: pointer to command argument * Returns: new state */ int pass_login (char *t,int argc,char *argv[]) { char tmp[TMPLEN]; /* flush old passowrd */ if (pass) fs_give ((void **) &pass); if (!(t && *t)) { /* if no password given */ PSOUT ("-ERR Missing password argument\015\012"); return AUTHORIZATION; } pass = cpystr (t); /* copy password argument */ if (!host) { /* want remote mailbox? */ /* no, delimit user from possible admin */ if (t = strchr (user,'*')) *t++ ='\0'; /* attempt the login */ if (server_login (user,pass,t,argc,argv)) { int ret = mbxopen ("INBOX"); if (ret == TRANSACTION) /* mailbox opened OK? */ syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s nmsgs=%ld/%ld", t ? "Admin " : "",user,tcp_clienthost (),nmsgs,stream->nmsgs); else syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s no mailbox", t ? "Admin " : "",user,tcp_clienthost ()); return ret; } } #ifndef DISABLE_POP_PROXY /* remote; build remote INBOX */ else if (anonymous_login (argc,argv)) { syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host, user,tcp_clienthost ()); sprintf (tmp,"{%.128s/user=%.128s}INBOX",host,user); /* disable rimap just in case */ mail_parameters (NIL,SET_RSHTIMEOUT,0); return mbxopen (tmp); } #endif /* vague error message to confuse crackers */ PSOUT ("-ERR Bad login\015\012"); return AUTHORIZATION; } /* Authentication responder * Accepts: challenge * length of challenge * pointer to response length return location if non-NIL * Returns: response */ #define RESPBUFLEN 8*MAILTMPLEN char *responder (void *challenge,unsigned long clen,unsigned long *rlen) { unsigned long i,j; unsigned char *t,resp[RESPBUFLEN]; if (initial) { /* initial response given? */ if (clen) return NIL; /* not permitted */ /* set up response */ t = (unsigned char *) initial; initial = NIL; /* no more initial response */ return (char *) rfc822_base64 (t,strlen ((char *) t),rlen ? rlen : &i); } PSOUT ("+ "); for (t = rfc822_binary (challenge,clen,&i),j = 0; j < i; j++) if (t[j] > ' ') PBOUT (t[j]); fs_give ((void **) &t); CRLF; PFLUSH (); /* dump output buffer */ resp[RESPBUFLEN-1] = '\0'; /* last buffer character is guaranteed NUL */ alarm (LOGINTIMEOUT); /* get a response under timeout */ clearerr (stdin); /* clear stdin errors */ /* read buffer */ while (!PSIN ((char *) resp,RESPBUFLEN)) { /* ignore if some interrupt */ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin); else { char *e = ferror (stdin) ? strerror (errno) : "Command stream end of file"; alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"%s, while reading authentication host=%.80s", e,tcp_clienthost ()); state = UPDATE; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } } if (!(t = (unsigned char *) strchr ((char *) resp,'\012'))) { int c; while ((c = PBIN ()) != '\012') if (c == EOF) { /* ignore if some interrupt */ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin); else { char *e = ferror (stdin) ? strerror (errno) : "Command stream end of file"; alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"%s, while reading auth char user=%.80s host=%.80s", e,user ? user : "???",tcp_clienthost ()); state = UPDATE; /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } } return NIL; } alarm (0); /* make sure timeout disabled */ if (t[-1] == '\015') --t; /* remove CR */ *t = '\0'; /* tie off buffer */ return (resp[0] != '*') ? (char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i) : NIL; } /* Select mailbox * Accepts: mailbox name * Returns: new state */ int mbxopen (char *mailbox) { unsigned long i,j; char tmp[TMPLEN]; MESSAGECACHE *elt; nmsgs = 0; /* no messages yet */ if (msg) fs_give ((void **) &msg); /* open mailbox */ if (stream = mail_open (stream,mailbox,NIL)) { if (!stream->rdonly) { /* make sure not readonly */ if (j = stream->nmsgs) { /* if mailbox non-empty */ sprintf (tmp,"1:%lu",j);/* fetch fast information for all messages */ mail_fetch_fast (stream,tmp,NIL); msg = (long *) fs_get ((stream->nmsgs + 1) * sizeof (long)); for (i = 1; i <= j; i++) if (!(elt = mail_elt (stream,i))->deleted) { msg[++nmsgs] = i; /* note the presence of this message */ if (elt->seen) il = last = nmsgs; } } sprintf (tmp,"+OK Mailbox open, %lu messages\015\012",nmsgs); PSOUT (tmp); return TRANSACTION; } else sayonara = "-ERR Can't get lock. Mailbox in use\015\012"; } else sayonara = "-ERR Unable to open user's INBOX\015\012"; syslog (LOG_INFO,"Error opening or locking INBOX user=%.80s host=%.80s", user,tcp_clienthost ()); return UPDATE; } /* Blat a string with dot checking * Accepts: string * maximum number of lines if greater than zero * maximum number of bytes to output * Returns: number of lines output * * This routine is uglier and kludgier than it should be, just to be robust * in the case of a message which doesn't end in a newline. Yes, this routine * does truncate the last two bytes from the text. Since it is normally a * newline and the main routine adds it back, it usually does not make a * difference. But if it isn't, since the newline is required and the octet * counts have to match, there's no choice but to truncate. */ long blat (char *text,long lines,unsigned long size) { char c,d,e; long ret = 0; /* no-op if zero lines or empty string */ if (!(lines && (size-- > 2))) return 0; c = *text++; d = *text++; /* collect first two bytes */ if (c == '.') PBOUT ('.'); /* double string-leading dot if necessary */ while (lines && --size) { /* copy loop */ e = *text++; /* get next byte */ PBOUT (c); /* output character */ if (c == '\012') { /* end of line? */ ret++; --lines; /* count another line */ /* double leading dot as necessary */ if (lines && size && (d == '.')) PBOUT ('.'); } c = d; d = e; /* move to next character */ } return ret; } /* Reset mailbox */ void rset () { unsigned long i; char tmp[20]; if (nmsgs) { /* undelete and unmark all of our messages */ for (i = 1; i <= nmsgs; i++) { /* */ if (msg[i] < 0) { /* ugly and inefficient, but trustworthy */ sprintf (tmp,"%ld",msg[i] = -msg[i]); mail_clearflag (stream,tmp,i <= il ? "\\Deleted" : "\\Deleted \\Seen"); } else if (i > il) { sprintf (tmp,"%ld",msg[i]); mail_clearflag (stream,tmp,"\\Seen"); } } last = il; } ndele = 0; /* no more deleted messages */ } /* Co-routines from MAIL library */ /* Message matches a search * Accepts: MAIL stream * message number */ void mm_searched (MAILSTREAM *stream,unsigned long msgno) { /* Never called */ } /* Message exists (i.e. there are that many messages in the mailbox) * Accepts: MAIL stream * message number */ void mm_exists (MAILSTREAM *stream,unsigned long number) { /* Can't use this mechanism. POP has no means of notifying the client of new mail during the session. */ } /* Message expunged * Accepts: MAIL stream * message number */ void mm_expunged (MAILSTREAM *stream,unsigned long number) { unsigned long i = number + 1; msg[number] = 0; /* I bet that this will annoy someone */ while (i <= nmsgs) --msg[i++]; } /* Message flag status change * Accepts: MAIL stream * message number */ void mm_flags (MAILSTREAM *stream,unsigned long number) { /* This isn't used */ } /* Mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* This isn't used */ } /* Subscribe mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* This isn't used */ } /* Mailbox status * Accepts: MAIL stream * mailbox name * mailbox status */ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { /* This isn't used */ } /* Notification event * Accepts: MAIL stream * string to log * error flag */ void mm_notify (MAILSTREAM *stream,char *string,long errflg) { mm_log (string,errflg); /* just do mm_log action */ } /* Log an event for the user to see * Accepts: string to log * error flag */ void mm_log (char *string,long errflg) { switch (errflg) { case NIL: /* information message */ case PARSE: /* parse glitch */ break; /* too many of these to log */ case WARN: /* warning */ syslog (LOG_DEBUG,"%s",string); break; case BYE: /* driver broke connection */ if (state != UPDATE) { alarm (0); /* disable all interrupts */ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); syslog (LOG_INFO,"Mailbox closed (%.80s) user=%.80s host=%.80s", string,user ? user : "???",tcp_clienthost ()); /* do logout hook if needed */ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL)) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL)); _exit (1); } break; case ERROR: /* error that broke command */ default: /* default should never happen */ syslog (LOG_NOTICE,"%s",string); break; } } /* Log an event to debugging telemetry * Accepts: string to log */ void mm_dlog (char *string) { /* Not doing anything here for now */ } /* Get user name and password for this host * Accepts: parse of network mailbox name * where to return user name * where to return password * trial count */ void mm_login (NETMBX *mb,char *username,char *password,long trial) { /* set user name */ strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1); strncpy (password,pass,255); /* and password */ username[NETMAXUSER] = password[255] = '\0'; } /* About to enter critical code * Accepts: stream */ void mm_critical (MAILSTREAM *stream) { ++critical; } /* About to exit critical code * Accepts: stream */ void mm_nocritical (MAILSTREAM *stream) { --critical; } /* Disk error found * Accepts: stream * system error code * flag indicating that mailbox may be clobbered * Returns: abort flag */ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { if (serious) { /* try your damnest if clobberage likely */ syslog (LOG_ALERT, "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s", user,tcp_clienthost (), (stream && stream->mailbox) ? stream->mailbox : "???", strerror (errcode)); alarm (0); /* make damn sure timeout disabled */ sleep (60); /* give it some time to clear up */ return NIL; } syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s", user,tcp_clienthost (), (stream && stream->mailbox) ? stream->mailbox : "???", strerror (errcode)); return T; } /* Log a fatal error event * Accepts: string to log */ void mm_fatal (char *string) { mm_log (string,ERROR); /* shouldn't happen normally */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/ipop3d.def000066400000000000000000000003001137544547100225030ustar00rootroot00000000000000; ; IPOP3D.DEF - def file for POP3 server ; NAME IPOP3D DESCRIPTION 'POP3 Server Application' ;PROTMODE ;CODE PRELOAD MOVEABLE DISCARDABLE ;DATA PRELOAD MOVEABLE tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/ipopd.8000066400000000000000000000023521137544547100220420ustar00rootroot00000000000000.TH IPOPD 8 "May 18, 2004" .UC 5 .SH NAME IPOPd \- Post Office Protocol server .SH SYNOPSIS .B /usr/etc/ipop2d .PP .B /usr/etc/ipop3d .SH DESCRIPTION .I ipop2d and .I ipop3d are servers which support the .B POP2 and .B POP3 remote mail access protocols respectively. .I ipop2d and .I ipop3d can also be used by .B POP2 and .B POP3 clients respecitively to access mailboxes on .B IMAP servers by specifying a login user name in the form : e.g., .B SERVER.WASHINGTON.EDU:SMITH. .PP These daemons contain CRAM-MD5 and APOP support. See the md5.txt documentation file for additional information. .PP .I ipop2d and .I ipop3d are invoked by the internet server (see .IR inetd (8)), normally for requests to connect to the .B POP port as indicated by the .I /etc/services file (see .IR services (5)). .SH "SEE ALSO" imapd(8) .SH BUGS The .B POP2 and .B POP3 protocols are intrinsically less flexible than .B IMAP and do not maintain `read' vs `unread' state on the server. As a result, most .B POP based software transfers all the mail from the server to the client and deletes it from the server. This necessarily locks the user into using only a single client. .PP .B POP3 does not allow you to specify an alternate folder from the user's default. tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/makefile.nt000066400000000000000000000022071137544547100227550ustar00rootroot00000000000000# Program: IPOPD Makefile for Windows 9x and Windows NT # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 28 October 1990 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) ipopd: ipop2d ipop3d ipop2d: $(CCLIENTLIB) ipop2d.obj LINK /NOLOGO /DEF:ipop2d.def ipop2d.obj $(LIBS) ipop3d: $(CCLIENTLIB) ipop3d.obj LINK /NOLOGO /DEF:ipop3d.def ipop3d.obj $(LIBS) ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/makefile.ntk000066400000000000000000000023501137544547100231270ustar00rootroot00000000000000# Program: IPOPD Makefile for Windows 9x and Windows NT + Kerberos # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 28 October 1990 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib LIBS = $(CCLIENTLIB) $(K5LIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) ipopd: ipop2d ipop3d ipop2d: $(CCLIENTLIB) ipop2d.obj LINK /NOLOGO /DEF:ipop2d.def ipop2d.obj $(LIBS) ipop3d: $(CCLIENTLIB) ipop3d.obj LINK /NOLOGO /DEF:ipop3d.def ipop3d.obj $(LIBS) ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/ipopd/makefile.w2k000066400000000000000000000021741137544547100230420ustar00rootroot00000000000000# Program: IPOPD Makefile for Windows 2000 # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 28 October 1990 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib CFLAGS= -I$C /MT /W3 /DWIN32 -nologo $(EXTRACFLAGS) ipopd: ipop2d ipop3d ipop2d: $(CCLIENTLIB) ipop2d.obj LINK /NOLOGO /DEF:ipop2d.def ipop2d.obj $(LIBS) ipop3d: $(CCLIENTLIB) ipop3d.obj LINK /NOLOGO /DEF:ipop3d.def ipop3d.obj $(LIBS) ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/000077500000000000000000000000001137544547100213415ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/Makefile000066400000000000000000000017241137544547100230050ustar00rootroot00000000000000# Program: mailutil Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 2 February 1993 # Last Edited: 18 November 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ../c-client CCLIENTLIB = $C/c-client.a SHELL = /bin/sh # Get local definitions from c-client directory CC = `cat $C/CCTYPE` CFLAGS = -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` mailutil: $(CCLIENTLIB) mailutil.o $(CC) $(CFLAGS) -o mailutil mailutil.o $(LDFLAGS) mailutil.o: $C/mail.h $C/misc.h $C/osdep.h $(CCLIENTLIB): cd $C;make clean: rm -f *.o mailutil # A monument to a hack of long ago and far away... love: @echo 'not war?' tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/mailutil.1000066400000000000000000000146541137544547100232550ustar00rootroot00000000000000.TH mailutil 1 "May 18, 2004" .SH NAME mailutil - mail utility program .nh .SH SYNTAX .B mailutil check [-debug] [-verbose] .B [mailbox] .PP .B mailutil create [-debug] [-verbose] .B new_mailbox .PP .B mailutil delete [-debug] [-verbose] .B mailbox .PP .B mailutil rename [-debug] [-verbose] .B [-rwcopy] old new .PP .B mailutil copy [-debug] [-verbose] .B [-rwcopy] old new .PP .B mailutil move [-debug] [-verbose] .B [-rwcopy] old new .PP .B mailutil append [-debug] [-verbose] .B [-rwcopy] src dst .PP .B mailutil appenddelete [-debug] [-verbose] .B [-rwcopy] src dst .PP .B mailutil prune [-debug] [-verbose] .B mailbox criteria .PP .B mailutil transfer [-debug] [-verbose] .B [-merge m] [-rwcopy] src dst .SH DESCRIPTION .B mailutil replaces the old chkmail, imapcopy, imapmove, imapxfer, mbxcopy, mbxcreat, and mbxcvt programs. .PP .B mailutil check determines whether new mail exists in the given mailbox (the default is INBOX). The number of new messages is defined as the number of messages that have "Recent" status set. If the mailbox contains no new messages, .B mailutil check will indicate that no new mail is present; otherwise, it will report the number of new messages. In either case, it will also indicate the canonical form of the name of the mailbox. .PP .B mailutil create creates a new .I mailbox with the given name. The mailbox name must not already exist. A mailbox can be created in a particular format by prefixing the name with .I #driver. followed by the format name and a .I / character. For example, the command .br mailutil create #driver.mbx/junkmail .br will create a new mailbox named "junkmail" in mbx format. .PP .B mailutil delete deletes an existing .I mailbox with the given name. .PP .B mailutil rename renames an existing mailbox to a new name (which must not already exist). This only works if the old and new names are in the same mail store. A more general means to rename a mailbox is to do a .B mailutil copy of the old name to the new name, followed by a .B mailutil delete of the old name. .PP .B mailutil copy creates a new mailbox and copies messages from the old mailbox to the new mailbox. As in .B mailutil create a mailbox format can be specified with the new mailbox. For example, the command .br mailutil copy INBOX #driver.mbx/INBOX .br will copy messages from your existing INBOX to an mbx-format INBOX. .PP .B mailutil move is similar to .B mailutil copy but in addition will also remove (delete and expunge) the messages from the old mailbox after copying them to the new mailbox. .PP .B mailutil append and .B mailutil appenddelete are similar to .B mailutil copy and .B mailutil move respecitively except that they do not create the destination mailbox. .PP .B mailutil prune prunes the mailbox of messages which match certain criteria, which are in the form of IMAP2 (RFC 1176) SEARCH arguments. For example, the command. .br mailutil prune INBOX "before 1-jan-2004" .br will delete and expunge all messages written before January 1, 2004. .PP .B mailutil transfer copies an entire hierarchy of mailboxes from the named source to the named destination. Mailboxes are created on the destination as needed. Any error in copying messages will cause the transfer to stop. .PP Normally, any error in creation will cause the transfer to stop. However, if .B -merge mode is specified, a merging transfer is performed. The .B mode argument the type of merge. .PP .B -merge prompt indicates that the user should be asked for an alternative name to create. If creating the new name fails, the user will be asked again. .PP .B -merge append indicates that it's alright to copy the messages into an existing mailbox with that name. If the mailbox does not exist, the user will be prompted for an alternative name. .PP .B -merge suffix=XXXX where XXXX is any string, indicates that an alternative name should be built by appending the given suffix to the name. It that alternative name can't be created, then the user will be prompted for an alternative name. .PP The source hierarchy consists of all mailboxes which start with the given source name. With the exception of a remote system specification (within "{}" braces), the source name is used as the name of the destination. The destination hierarchy is a prefix applied to any new names being created. For example, .br mailutil transfer foo bar .br will copy all mailboxes with names beginning with "foo" to names beginning with "bar" (hence "foobar" will be copied to "barfoobar"). Similarly, .br mailutil transfer "{imap.foo.com}" "{imap.bar.com}old/" .br will copy all mailboxes from the imap.foo.com IMAP server to equivalent names starting with "old/" on the imap.bar.com IMAP server. .SH FLAGS The .B -debug flag prints full debugging telemetry including protocol operations. .PP The .B -verbose flag prints verbose (non-error) telemetry. .PP The .B -rwcopy flag causes the source mailbox to be open in readwrite mode rather than readonly mode. Normally, mailutil tries to use readonly mode to avoid altering any flags in the source mailbox, but some mailbox types, e.g. POP3, can't be open in readonly mode. .SH ARGUMENTS The arguments are standard c-client mailbox names. A variety of mailbox name formats and types of mailboxes are supported by c-client; examples of the most common forms of names are: .PP .I .IP Name 15 .I Meaning .IP INBOX primary incoming mail folder on the local system .IP archive/tx-project mail folder named "tx-project" in "archive" subdirectory of local filesystem home directory .IP {imapserver.foo.com}INBOX primary incoming mail folder on IMAP server system "imapserver.foo.com" .IP {imapserver.foo.com}archive/tx-project mail folder named "tx-project" in "archive" subdirectory on IMAP server system "imapserver.foo.com" .IP #news.comp.mail.misc newsgroup "comp.mail.misc" on local filesystem .IP {newserver.foo.com/nntp}comp.mail.misc newsgroup "comp.mail.misc" on NNTP server system "newserver.foo.com" .IP {popserver.foo.com/pop3} mail folder on POP3 server system "popserver.foo.com" .LP See your system manager for more information about the types of mailboxes which are available on your system. .SH RESTRICTIONS You must surround a .I {host}mailbox argument with quotation marks if you run .B mailutil from .IR csh (1) or another shell for which braces have special meaning. .PP You must surround a .I #driver.format/mailbox argument with quotation marks if you run .B mailutil from a shell in which "#" is the comment character. .SH AUTHOR Mark Crispin, MRC@CAC.Washington.EDU tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/mailutil.c000066400000000000000000000522131137544547100233300ustar00rootroot00000000000000/* * Program: Mail utility * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 February 1994 * Last Edited: 27 January 2005 * * The IMAP tools software provided in this Distribution is * Copyright 1988-2005 by the University of Washington * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include "misc.h" #include "linkage.h" /* Globals */ int debugp = NIL; /* flag saying debug */ int verbosep = NIL; /* flag saying verbose */ int rwcopyp = NIL; /* flag saying readwrite copy (for POP) */ int critical = NIL; /* flag saying in critical code */ int trycreate = NIL; /* [TRYCREATE] seen */ char *suffix = NIL; /* suffer merge mode suffix text */ int ddelim = -1; /* destination delimiter */ FILE *f = NIL; /* Merge modes */ #define mPROMPT 1 #define mAPPEND 2 #define mSUFFIX 3 /* Function prototypes */ void ms_init (STRING *s,void *data,unsigned long size); char ms_next (STRING *s); void ms_setpos (STRING *s,unsigned long i); int main (int argc,char *argv[]); int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del, int mode); long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message); /* Append package */ typedef struct append_package { MAILSTREAM *stream; /* source stream */ unsigned long msgno; /* current message number */ unsigned long msgmax; /* maximum message number */ char *flags; /* current flags */ char *date; /* message internal date */ STRING *message; /* stringstruct of message */ } APPENDPACKAGE; /* Message string driver for message stringstructs */ STRINGDRIVER mstring = { ms_init, /* initialize string structure */ ms_next, /* get next byte in string structure */ ms_setpos /* set position in string structure */ }; /* Initialize file string structure for file stringstruct * Accepts: string structure * pointer to message data structure * size of string */ void ms_init (STRING *s,void *data,unsigned long size) { APPENDPACKAGE *md = (APPENDPACKAGE *) data; s->data = data; /* note stream/msgno and header length */ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PREFETCHTEXT); #if 0 s->size = size; /* message size */ #else /* This kludge is necessary because of broken IMAP servers (sigh!) */ mail_fetchtext_full (md->stream,md->msgno,&s->size,NIL); s->size += s->data1; /* header + body size */ #endif SETPOS (s,0); } /* Get next character from file stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ char ms_next (STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for file stringstruct * Accepts: string structure * new position */ void ms_setpos (STRING *s,unsigned long i) { APPENDPACKAGE *md = (APPENDPACKAGE *) s->data; if (i < s->data1) { /* want header? */ s->chunk = mail_fetchheader (md->stream,md->msgno); s->chunksize = s->data1; /* header length */ s->offset = 0; /* offset is start of message */ } else if (i < s->size) { /* want body */ s->chunk = mail_fetchtext (md->stream,md->msgno); s->chunksize = s->size - s->data1; s->offset = s->data1; /* offset is end of header */ } else { /* off end of message */ s->chunk = NIL; /* make sure that we crack on this then */ s->chunksize = 1; /* make sure SNX cracks the right way... */ s->offset = i; } /* initial position and size */ s->curpos = s->chunk + (i -= s->offset); s->cursize = s->chunksize - i; } /* Main program */ int main (int argc,char *argv[]) { MAILSTREAM *source = NIL; MAILSTREAM *dest = NIL; SEARCHPGM *criteria; char c,*s,**args,*dp,*t,*t1,tmp[MAILTMPLEN],mbx[MAILTMPLEN]; unsigned long m,len,curlen,start,last; int nargs,i; int merge = NIL; int ret = 1; char *cmd = NIL; char *src = NIL; char *dst = NIL; char *pgm = argc ? argv[0] : "mailutil"; #include "linkage.c" for (nargs = argc ? argc - 1 : 0,args = argv + 1; nargs; args++,nargs--) { if (*(s = *args) == '-') { /* parse switches */ if (!strcmp (s,"-debug") || !strcmp (s,"-d")) debugp = T; else if (!strcmp (s,"-verbose") || !strcmp (s,"-v")) verbosep = T; else if (!strcmp (s,"-rwcopy") || !strcmp (s,"-rw")) rwcopyp = T; else if ((nargs > 1) && (!strcmp (s,"-merge") || !strcmp (s,"-m"))) { args++,nargs--; /* advance to next argument */ if (!strcmp (s = *args,"prompt")) merge = mPROMPT; else if (!strcmp (s,"append")) merge = mAPPEND; else if (!strncmp (s,"suffix=",7) && s[7]) { merge = mSUFFIX; suffix = cpystr (s+7); } else { printf ("unknown merge option: %s\n",s); exit (ret); } } else { printf ("unknown switch: %s\n",s); exit (ret); } } else if (!cmd) cmd = s; /* first non-switch is command */ else if (!src) src = s; /* second non-switch is source */ else if (!dst) dst = s; /* third non-switch is destination */ else { printf ("unknown argument: %s\n",s); exit (ret); } } if (!cmd) cmd = ""; /* prevent SEGV */ if (!strcmp (cmd,"check")) { /* check for new messages */ if (!src) src = "INBOX"; if (dst || merge || rwcopyp) printf ("usage: %s check [-debug] [-verbose] [mailbox]\n",pgm); else if (mail_status (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL, src,SA_MESSAGES | SA_RECENT | SA_UNSEEN)) ret = 0; } else if (!strcmp (cmd,"create")) { if (!src || dst || merge || rwcopyp) printf ("usage: %s create [-debug] [-verbose] mailbox\n",pgm); else if (mail_create (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL,src)) ret = 0; } else if (!strcmp (cmd,"delete")) { if (!src || dst || merge || rwcopyp) printf ("usage: %s delete [-debug] [-verbose] mailbox\n",pgm); else if (mail_delete (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL,src)) ret = 0; } else if (i = !strcmp (cmd,"rename")) { if (!src || !dst || merge || rwcopyp) printf ("usage: %s %s [-debug] [-verbose] source destination\n",pgm,cmd); else if (mail_rename (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL,src,dst)) ret = 0; } else if ((i = !strcmp (cmd,"move")) || !strcmp (cmd,"copy")) { if (!src || !dst || merge) printf ("usage: %s %s [-debug] [-verbose] source destination\n",pgm,cmd); else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) | (debugp ? OP_DEBUG : NIL))) { dest = NIL; /* open destination stream if network */ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))) { if (mbxcopy (source,dest,dst,T,i,merge)) ret = 0; } } } else if ((i = !strcmp (cmd,"appenddelete")) || !strcmp (cmd,"append")) { if (!src || !dst || merge) printf ("usage: %s %s [-debug] [-verbose] source destination\n",pgm,cmd); else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) | (debugp ? OP_DEBUG : NIL))) { dest = NIL; /* open destination stream if network */ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))) { if (mbxcopy (source,dest,dst,NIL,i,merge)) ret = 0; } } } else if (!strcmp (cmd,"prune")) { if (!src || !dst || merge || rwcopyp || !(criteria = mail_criteria (dst))) printf ("usage: %s prune [-debug] [-verbose] mailbox search_criteria\n", pgm); else if ((source = mail_open (NIL,src,(debugp ? OP_DEBUG : NIL))) && mail_search_full (source,NIL,criteria,SE_FREE)) { for (m = 1, s = t = NIL, len = start = last = 0; m <= source->nmsgs; m++) if (mail_elt (source,m)->searched) { if (s) { /* continuing a range? */ if (m == last + 1) last = m; else { /* no, end of previous range? */ if (last != start) sprintf (t,":%lu,%lu",last,m); /* no, just this message */ else sprintf (t,",%lu",m); start = last = m; /* either way, start new range */ /* running out of space? */ if ((len - (curlen = (t += strlen (t)) - s)) < 20) { fs_resize ((void **) &s,len += MAILTMPLEN); t = s + curlen; /* relocate current pointer */ } } } else { /* first time, start new buffer */ s = (char *) fs_get (len = MAILTMPLEN); sprintf (s,"%lu",start = last = m); t = s + strlen (s); /* end of buffer */ } } /* finish last range if necessary */ if (last != start) sprintf (t,":%lu",last); if (s) { /* delete/expunge any matching messages */ mail_flag (source,s,"\\Deleted",ST_SET); m = source->nmsgs; /* get number of messages before purge */ mail_expunge (source); printf ("%lu message(s) purged\n",m - source->nmsgs); fs_give ((void **) &s); /* flush buffer */ } else puts ("No matching messages, so nothing purged"); source = mail_close (source); } } else if (!strcmp (cmd,"transfer")) { if (!src || !dst) printf ("usage: %s transfer [-debug] [-verbose] source destination\n", pgm); else if ((*src == '{') && /* open source mailbox */ !(source = mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))); else if ((*dst == '{') && /* open destination server */ !(dest = mail_open (NIL,dst,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))); else if (!(f = tmpfile ())) puts ("can't open temporary file"); else { if (verbosep) puts ("Listing mailboxes..."); if (dest) strcpy (strchr (strcpy (tmp,dest->mailbox),'}') + 1, dp = strchr (dst,'}') + 1); else { dp = dst; tmp[0] = '\0'; } mail_list (dest,tmp,""); rewind (f); /* list all mailboxes matching prefix */ if (ddelim < 0) { /* if server failed to give delimiter */ puts ("warning: unable to get destination hierarchy delimiter!"); ddelim = 0; /* default to none */ } if (source) strcpy (strchr (strcpy (tmp,source->mailbox),'}') + 1, strchr (src,'}') + 1); else strcpy (tmp,src); mail_list (source,tmp,"*"); rewind (f); /* read back mailbox names */ while (ret && (fgets (tmp,MAILTMPLEN-1,f))) { if (t = strchr (tmp+1,'\n')) *t = '\0'; for (t = mbx,t1 = dest ? dest->mailbox : "",c = NIL; (c != '}') && *t1; *t++ = c= *t1++); for (t1 = dp; *t1; *t++ = *t1++); /* point to name without delim or netspec */ t1 = source ? (strchr (tmp+1,'}') + 1) : tmp + 1; /* src and mbx have different delimiters? */ if (ddelim && (ddelim != tmp[0])) while (c = *t1++) { /* swap delimiters then */ if (c == ddelim) c = tmp[0] ? tmp[0] : 'x'; else if (c == tmp[0]) c = ddelim; *t++ = c; } /* easy case */ else while (*t1) *t++ = *t1++; *t++ = '\0'; if (verbosep) { printf ("Copying %s\n => %s\n",tmp+1,mbx); fflush (stdout); } if (source = mail_open (source,tmp+1,(debugp ? OP_DEBUG : NIL) | (rwcopyp ? NIL : OP_READONLY))) { ret = mbxcopy (source,dest,mbx,T,NIL,merge); if (source->dtb->flags & DR_LOCAL) source = mail_close (source); } else printf ("can't open source mailbox %s\n",tmp+1); } } } else { printf ("usage: %s check [-debug] [-verbose] [mailbox]\n",pgm); puts (" ;; report number of messages and new messages"); printf (" %s create [-debug] [-verbose] new_mailbox\n",pgm); puts (" ;; create new mailbox"); printf (" %s delete [-debug] [-verbose] mailbox\n",pgm); puts (" ;; delete existing mailbox"); printf (" %s rename [-debug] [-verbose] source destination\n",pgm); puts (" ;; rename mailbox to a new name"); printf (" %s (copy | move) [-debug] [-verbose] old_mailbox new_mailbox\n",pgm); puts (" ;; create new mailbox and copy/move messages"); printf (" %s (append | appenddelete) [-debug] [-verbose] source destination\n",pgm); puts (" ;; copy/move messages to existing mailbox"); printf (" %s prune [-debug] [-verbose] mailbox search_criteria\n", pgm); puts (" ;; prune mailbox of messages matching criteria"); printf (" %s transfer [-debug] [-verbose] [-merge mode] source destination\n",pgm); puts (" ;; make copy of source hierarchy to destination"); puts (" ;; -merge modes are prompt, append, or suffix=xxxx"); } /* close streams */ if (source) mail_close (source); if (dest) mail_close (dest); exit (ret); return ret; /* stupid compilers */ } /* Copy mailbox * Accepts: stream open on source * halfopen stream for destination or NIL * destination mailbox name * non-zero to create destination mailbox * non-zero to delete messages from source after copying * merge mode * Returns: T if success, NIL if error */ int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del, int mode) { char *s,tmp[MAILTMPLEN]; APPENDPACKAGE ap; STRING st; char *ndst = NIL; int ret = NIL; trycreate = NIL; /* no TRYCREATE yet */ if (create) while (!mail_create (dest,dst) && (mode != mAPPEND)) { switch (mode) { case mPROMPT: /* prompt user for new name */ tmp[0] = '\0'; while (!tmp[0]) { /* read name */ fputs ("alternative name: ",stdout); fflush (stdout); fgets (tmp,MAILTMPLEN-1,stdin); if (s = strchr (tmp,'\n')) *s = '\0'; } if (ndst) fs_give ((void **) &ndst); ndst = cpystr (tmp); break; case mSUFFIX: /* try again with new suffix */ if (ndst) fs_give ((void **) &ndst); sprintf (ndst = (char *) fs_get (strlen (dst) + strlen (suffix) + 1), "%s%s",dst,suffix); printf ("retry to create %s\n",ndst); mode = mPROMPT; /* switch to prompt mode if name fails */ break; case NIL: /* not merging */ return NIL; } if (ndst) dst = ndst; /* if alternative name given, use it */ } if (source->nmsgs) { /* non-empty source */ if (verbosep) printf ("%s [%lu message(s)] => %s\n", source->mailbox,source->nmsgs,dst); ap.stream = source; /* prepare append package */ ap.msgno = 0; ap.msgmax = source->nmsgs; ap.flags = ap.date = NIL; ap.message = &st; /* make sure we have all messages */ sprintf (tmp,"1:%lu",ap.msgmax); mail_fetchfast (source,tmp); if (mail_append_multiple (dest,dst,mm_append,(void *) &ap)) { --ap.msgno; /* make sure user knows it won */ if (verbosep) printf ("[Ok %lu messages(s)]\n",ap.msgno); if (del && ap.msgno) { /* delete source messages */ sprintf (tmp,"1:%lu",ap.msgno); mail_flag (source,tmp,"\\Deleted",ST_SET); /* flush moved messages */ mail_expunge (source); } ret = T; } else if ((mode == mAPPEND) && trycreate) ret = mbxcopy (source,dest,dst,create,del,mPROMPT); else if (verbosep) puts ("[Failed]"); } else { /* empty source */ if (verbosep) printf ("%s [empty] => %s\n",source->mailbox,dst); ret = T; } if (ndst) fs_give ((void **) &ndst); return ret; } /* Append callback * Accepts: mail stream * append package * pointer to return flags * pointer to return date * pointer to return message stringstruct * Returns: T on success */ long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message) { char *t,*t1,tmp[MAILTMPLEN]; unsigned long u; MESSAGECACHE *elt; APPENDPACKAGE *ap = (APPENDPACKAGE *) data; *flags = *date = NIL; /* assume no flags or date */ if (ap->flags) fs_give ((void **) &ap->flags); if (ap->date) fs_give ((void **) &ap->date); mail_gc (ap->stream,GC_TEXTS); if (++ap->msgno <= ap->msgmax) { /* initialize flag string */ memset (t = tmp,0,MAILTMPLEN); /* output system flags */ if ((elt = mail_elt (ap->stream,ap->msgno))->seen) strcat (t," \\Seen"); if (elt->deleted) strcat (t," \\Deleted"); if (elt->flagged) strcat (t," \\Flagged"); if (elt->answered) strcat (t," \\Answered"); if (elt->draft) strcat (t," \\Draft"); if (u = elt->user_flags) do /* any user flags? */ if ((t1 = ap->stream->user_flags[find_rightmost_bit (&u)]) && (MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long) (2 + strlen (t1))){ *t++ = ' '; /* space delimiter */ strcpy (t,t1); /* copy the user flag */ } while (u); /* until no more user flags */ *flags = ap->flags = cpystr (tmp + 1); *date = ap->date = cpystr (mail_date (tmp,elt)); *message = ap->message; /* message stringstruct */ INIT (ap->message,mstring,(void *) ap,elt->rfc822_size); } else *message = NIL; /* all done */ return LONGT; } /* Co-routines from MAIL library */ /* Message matches a search * Accepts: MAIL stream * message number */ void mm_searched (MAILSTREAM *stream,unsigned long msgno) { /* dummy routine */ } /* Message exists (i.e. there are that many messages in the mailbox) * Accepts: MAIL stream * message number */ void mm_exists (MAILSTREAM *stream,unsigned long number) { /* dummy routine */ } /* Message expunged * Accepts: MAIL stream * message number */ void mm_expunged (MAILSTREAM *stream,unsigned long number) { /* dummy routine */ } /* Message flags update seen * Accepts: MAIL stream * message number */ void mm_flags (MAILSTREAM *stream,unsigned long number) { /* dummy routine */ } /* Mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* note destination delimiter */ if (ddelim < 0) ddelim = delimiter; /* if got a selectable name */ else if (!(attributes & LATT_NOSELECT) && *name) fprintf (f,"%c%s\n",delimiter,name); } /* Subscribe mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* dummy routine */ } /* Mailbox status * Accepts: MAIL stream * mailbox name * mailbox status */ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { if (status->recent || status->unseen) printf ("%lu new message(s) (%lu unseen),",status->recent,status->unseen); else fputs ("No new messages,",stdout); printf (" %lu total in %s\n",status->messages,mailbox); } /* Notification event * Accepts: MAIL stream * string to log * error flag */ void mm_notify (MAILSTREAM *stream,char *string,long errflg) { if (!errflg && (string[0] == '[') && ((string[1] == 'T') || (string[1] == 't')) && ((string[2] == 'R') || (string[2] == 'r')) && ((string[3] == 'Y') || (string[3] == 'y')) && ((string[4] == 'C') || (string[4] == 'c')) && ((string[5] == 'R') || (string[5] == 'r')) && ((string[6] == 'E') || (string[6] == 'e')) && ((string[7] == 'A') || (string[7] == 'a')) && ((string[8] == 'T') || (string[8] == 't')) && ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']')) trycreate = T; mm_log (string,errflg); /* just do mm_log action */ } /* Log an event for the user to see * Accepts: string to log * error flag */ void mm_log (char *string,long errflg) { switch (errflg) { case BYE: case NIL: /* no error */ if (verbosep) fprintf (stderr,"[%s]\n",string); break; case PARSE: /* parsing problem */ case WARN: /* warning */ fprintf (stderr,"warning: %s\n",string); break; case ERROR: /* error */ default: fprintf (stderr,"%s\n",string); break; } } /* Log an event to debugging telemetry * Accepts: string to log */ void mm_dlog (char *string) { fprintf (stderr,"%s\n",string); } /* Get user name and password for this host * Accepts: parse of network mailbox name * where to return user name * where to return password * trial count */ void mm_login (NETMBX *mb,char *username,char *password,long trial) { char *s; if (*mb->user) strcpy (username,mb->user); else { printf ("{%s/%s} username: ",mb->host,mb->service); fgets (username,NETMAXUSER-1,stdin); username[NETMAXUSER-1] = '\0'; if (s = strchr (username,'\n')) *s = '\0'; } strcpy (password,getpass ("password: ")); } /* About to enter critical code * Accepts: stream */ void mm_critical (MAILSTREAM *stream) { critical = T; /* note in critical code */ } /* About to exit critical code * Accepts: stream */ void mm_nocritical (MAILSTREAM *stream) { critical = NIL; /* note not in critical code */ } /* Disk error found * Accepts: stream * system error code * flag indicating that mailbox may be clobbered * Returns: T if user wants to abort */ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { return T; } /* Log a fatal error event * Accepts: string to log */ void mm_fatal (char *string) { fprintf (stderr,"FATAL: %s\n",string); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/mailutil.def000066400000000000000000000003041137544547100236360ustar00rootroot00000000000000; ; MAILUTIL.DEF - def file for mailbox utilities ; NAME mailutil DESCRIPTION 'Mailbox Utilities' ;PROTMODE ;CODE PRELOAD MOVEABLE DISCARDABLE ;DATA PRELOAD MOVEABLE tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/makefile.nt000066400000000000000000000020251137544547100234600ustar00rootroot00000000000000# Program: MAILUTIL Makefile for Windows 9x and Windows NT # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) mailutil: $(CCLIENTLIB) mailutil.obj LINK /NOLOGO /DEF:mailutil.def mailutil.obj $(LIBS) mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/makefile.ntk000066400000000000000000000021661137544547100236410ustar00rootroot00000000000000# Program: MAILUTIL Makefile for Windows 9x and Windows NT + Kerberos # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib LIBS = $(CCLIENTLIB) $(K5LIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) mailutil: $(CCLIENTLIB) mailutil.obj LINK /NOLOGO /DEF:mailutil.def mailutil.obj $(LIBS) mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/makefile.os2000066400000000000000000000020341137544547100235420ustar00rootroot00000000000000# Program: MTEST Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Thanks to Nicholas Paul Sheppard who contributed the original version CC = gcc CFLAGS = -O2 -Zomf LD = gcc LDFLAGS = -s -Zomf -Zcrtdll C = ..\c-client CCLIENTLIB = $C\\c-client.lib LIBS = $(CCLIENTLIB) -l socket mtest.exe: $(CCLIENTLIB) mtest.obj $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) $(CCLIENTLIB): @echo Make c-client first false mtest.obj: mtest.c $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h $(CC) $(CFLAGS) -I$C -o $@ -c $< clean: if exist *.obj del *.obj # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mailutil/makefile.w2k000066400000000000000000000020121137544547100235360ustar00rootroot00000000000000# Program: MAILUTIL Makefile for Windows 2000 # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib CFLAGS= -I$C /MT /W3 /DWIN32 -nologo $(EXTRACFLAGS) mailutil: $(CCLIENTLIB) mailutil.obj LINK /NOLOGO /DEF:mailutil.def mailutil.obj $(LIBS) mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mlock/000077500000000000000000000000001137544547100206265ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/mlock/Makefile000066400000000000000000000015661137544547100222760ustar00rootroot00000000000000# Program: MLOCK Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 8 February 1999 # Last Edited: 18 November 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ../c-client SHELL = /bin/sh # Get local definitions from c-client directory CC = `cat $C/CCTYPE` CFLAGS = `cat $C/CFLAGS` all: mlock mlock: mlock.o $(CC) $(CFLAGS) -o mlock mlock.o install: mlock chgrp mail mlock chmod 3711 mlock cp -p mlock /etc/mlock clean: rm -f *.o mlock || true # A monument to a hack of long ago and far away... love: @echo 'not war?' tkrat_2.2cvs20100105-dfsg.orig/imap/src/mlock/mlock.c000066400000000000000000000104121137544547100220750ustar00rootroot00000000000000/* * Program: Standalone Mailbox Lock program * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 8 February 1999 * Last Edited: 21 June 2004 * * The IMAP tools software provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define LOCKPROTECTION 0775 #ifndef MAXHOSTNAMELEN /* Solaris still sucks */ #define MAXHOSTNAMELEN 256 #endif /* Fatal error * Accepts: Message string * exit code * Never returns */ void die (char *msg,int code) { syslog (LOG_NOTICE,"(%u) %s",code,msg); write (1,"?",1); /* indicate "impossible" failure */ _exit (code); /* sayonara */ } int main (int argc,char *argv[]) { int ld; int tries = LOCKTIMEOUT * 60 - 1; size_t len; char *s,*lock,*hitch,tmp[1024]; time_t t; struct stat sb,fsb; struct group *grp = getgrnam ("mail"); /* get syslog */ openlog (argv[0],LOG_PID,LOG_MAIL); if (!grp || (grp->gr_gid != getegid ())) die ("not setgid mail",EX_USAGE); if (argc != 3) die ("invalid arguments",EX_USAGE); for (s = argv[1]; *s; s++) if (!isdigit (*s)) die ("invalid fd",EX_USAGE); len = strlen (argv[2]); /* make buffers */ lock = (char *) malloc (len + 6); hitch = (char *) malloc (len + 6 + 40 + MAXHOSTNAMELEN); if (!lock || !hitch) die ("malloc failure",errno); /* get device/inode of file descriptor */ if (fstat (atoi (argv[1]),&fsb)) die ("fstat failure",errno); /* better be a regular file */ if ((fsb.st_mode & S_IFMT) != S_IFREG) die ("fd not regular file",EX_USAGE); /* now get device/inode of file */ if (lstat (argv[2],&sb)) die ("lstat failure",errno); /* does it match? */ if ((sb.st_mode & S_IFMT) != S_IFREG) die ("name not regular file",EX_USAGE); if ((sb.st_dev != fsb.st_dev) || (sb.st_ino != fsb.st_ino)) die ("fd and name different",EX_USAGE); /* build lock filename */ sprintf (lock,"%s.lock",argv[2]); if (!lstat (lock,&sb) && ((sb.st_mode & S_IFMT) != S_IFREG)) die ("existing lock not regular file",EX_NOPERM); do { /* until OK or out of tries */ t = time (0); /* get the time now */ /* SUN-OS had an NFS * As kludgy as an albatross; * And everywhere that it was installed, * It was a total loss. * -- MRC 9/25/91 */ /* build hitching post file name */ sprintf (hitch,"%s.%lu.%lu.",lock,(unsigned long) time (0), (unsigned long) getpid ()); len = strlen (hitch); /* append local host name */ gethostname (hitch + len,MAXHOSTNAMELEN); /* try to get hitching-post file */ if ((ld = open (hitch,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) { /* make sure others can break the lock */ chmod (hitch,LOCKPROTECTION); close (ld); /* close the hitching-post */ link (hitch,lock); /* tie hitching-post to lock, ignore failure */ stat (hitch,&sb); /* get its data */ unlink (hitch); /* flush hitching post */ /* If link count .ne. 2, hitch failed. Set ld to -1 as if open() failed so we try again. If extant lock file and time now is .gt. file time plus timeout interval, flush the lock so can win next time around. */ if ((ld = (sb.st_nlink != 2) ? -1 : 0) && (!stat (lock,&sb)) && (t > sb.st_ctime + LOCKTIMEOUT * 60)) unlink (lock); } /* give up immediately if protection failure */ else if (errno == EACCES) tries = 0; if (ld < 0) { /* lock failed */ if (tries--) sleep (1); /* sleep 1 second and try again */ else { write (1,"-",1); /* hard failure */ _exit (EX_CANTCREAT); } } } while (ld < 0); write (1,"+",1); /* indicate that all is well */ read (0,tmp,1); /* read continue signal from parent */ unlink (lock); /* flush the lock file */ return EX_OK; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/000077500000000000000000000000001137544547100206555ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/Makefile000066400000000000000000000017631137544547100223240ustar00rootroot00000000000000# Program: MTEST Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ../c-client CCLIENTLIB = $C/c-client.a SHELL = /bin/sh # Get local definitions from c-client directory CC = `cat $C/CCTYPE` CFLAGS = -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` all: mtest mtest: $(CCLIENTLIB) mtest.o $(CC) $(CFLAGS) -o mtest mtest.o $(LDFLAGS) mtest.o: $C/mail.h $C/misc.h $C/osdep.h $C/rfc822.h $C/smtp.h $C/nntp.h $(CCLIENTLIB): cd $C;make clean: rm -f *.o mtest || true # A monument to a hack of long ago and far away... love: @echo 'not war?' tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/makefile.nt000066400000000000000000000020001137544547100227650ustar00rootroot00000000000000# Program: MTEST Makefile for Windows 9x and Windows NT # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) mtest: $(CCLIENTLIB) mtest.obj LINK /NOLOGO /DEF:mtest.def mtest.obj $(LIBS) mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/makefile.ntk000066400000000000000000000021411137544547100231460ustar00rootroot00000000000000# Program: MTEST Makefile for Windows 9x and Windows NT + Kerberos # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib LIBS = $(CCLIENTLIB) $(K5LIB) wsock32.lib winmm.lib advapi32.lib CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) mtest: $(CCLIENTLIB) mtest.obj LINK /NOLOGO /DEF:mtest.def mtest.obj $(LIBS) mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/makefile.os2000066400000000000000000000020341137544547100230560ustar00rootroot00000000000000# Program: MTEST Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Thanks to Nicholas Paul Sheppard who contributed the original version CC = gcc CFLAGS = -O2 -Zomf LD = gcc LDFLAGS = -s -Zomf -Zcrtdll C = ..\c-client CCLIENTLIB = $C\\c-client.lib LIBS = $(CCLIENTLIB) -l socket mtest.exe: $(CCLIENTLIB) mtest.obj $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) $(CCLIENTLIB): @echo Make c-client first false mtest.obj: mtest.c $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h $(CC) $(CFLAGS) -I$C -o $@ -c $< clean: if exist *.obj del *.obj # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/makefile.w2k000066400000000000000000000017651137544547100230700ustar00rootroot00000000000000# Program: MTEST Makefile for Windows 2000 # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 25 February 1996 # Last Edited: 21 August 2002 # # The IMAP toolkit provided in this Distribution is # Copyright 2002 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) wsock32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib CFLAGS= -I$C /MT /W3 /DWIN32 -nologo $(EXTRACFLAGS) mtest: $(CCLIENTLIB) mtest.obj LINK /NOLOGO /DEF:mtest.def mtest.obj $(LIBS) mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c $(CCLIENTLIB): @echo Make c-client first false clean: del *.obj *.exe *.lib *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/mtest.c000066400000000000000000000541121137544547100221600ustar00rootroot00000000000000/* * Program: Mail library test program * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 8 July 1988 * Last Edited: 7 April 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. * * This original version of this file is * Copyright 1988 Stanford University * and was developed in the Symbolic Systems Resources Group of the Knowledge * Systems Laboratory at Stanford University in 1987-88, and was funded by the * Biomedical Research Technology Program of the NationalInstitutes of Health * under grant number RR-00785. */ #include #include #include #include "c-client.h" #include "imap4r1.h" /* Excellent reasons to hate ifdefs, and why my real code never uses them */ #ifndef unix # define unix 0 #endif #if unix # define UNIXLIKE 1 # define MACOS 0 # include #else # define UNIXLIKE 0 # ifdef noErr # define MACOS 1 # include # else # define MACOS 0 # endif #endif char *curhst = NIL; /* currently connected host */ char *curusr = NIL; /* current login user */ char personalname[MAILTMPLEN]; /* user's personal name */ static char *hostlist[] = { /* SMTP server host list */ "mailhost", "localhost", NIL }; static char *newslist[] = { /* Netnews server host list */ "news", NIL }; int main (void); void mm (MAILSTREAM *stream,long debug); void overview_header (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov, unsigned long msgno); void header (MAILSTREAM *stream,long msgno); void display_body (BODY *body,char *pfx,long i); void status (MAILSTREAM *stream); void prompt (char *msg,char *txt); void smtptest (long debug); /* Main program - initialization */ int main () { MAILSTREAM *stream = NIL; void *sdb = NIL; char *s,tmp[MAILTMPLEN]; long debug; #include "linkage.c" #if MACOS { size_t *base = (size_t *) 0x000908; /* increase stack size on a Mac */ SetApplLimit ((Ptr) (*base - (size_t) 65535L)); } #endif curusr = cpystr (((s = myusername ()) && *s) ? s : "somebody"); #if UNIXLIKE { char *suffix; struct passwd *pwd = getpwnam (curusr); if (pwd) { strcpy (tmp,pwd->pw_gecos); /* dyke out the office and phone poop */ if (suffix = strchr (tmp,',')) suffix[0] = '\0'; strcpy (personalname,tmp);/* make a permanent copy of it */ } else personalname[0] = '\0'; } #else personalname[0] = '\0'; #endif curhst = cpystr (mylocalhost ()); puts ("MTest -- C client test program"); if (!*personalname) prompt ("Personal name: ",personalname); /* user wants protocol telemetry? */ prompt ("Debug protocol (y/n)?",tmp); ucase (tmp); debug = (tmp[0] == 'Y') ? T : NIL; do { prompt ("Mailbox ('?' for help): ",tmp); if (!strcmp (tmp,"?")) { puts ("Enter INBOX, mailbox name, or IMAP mailbox as {host}mailbox"); puts ("Known local mailboxes:"); mail_list (NIL,NIL,"%"); if (s = sm_read (&sdb)) { puts ("Local subscribed mailboxes:"); do (mm_lsub (NIL,NIL,s,NIL)); while (s = sm_read (&sdb)); } puts ("or just hit return to quit"); } else if (tmp[0]) stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL); } while (!stream && tmp[0]); mm (stream,debug); /* run user interface if opened */ #if MACOS /* clean up resolver */ if (resolveropen) CloseResolver (); #endif return NIL; } /* MM command loop * Accepts: MAIL stream */ void mm (MAILSTREAM *stream,long debug) { void *sdb = NIL; char cmd[MAILTMPLEN]; char *s,*arg; unsigned long i; unsigned long last = 0; BODY *body; status (stream); /* first report message status */ while (stream) { prompt ("MTest>",cmd); /* prompt user, get command */ /* get argument */ if (arg = strchr (cmd,' ')) *arg++ = '\0'; switch (*ucase (cmd)) { /* dispatch based on command */ case 'B': /* Body command */ if (arg) last = atoi (arg); else if (!last) { puts ("?Missing message number"); break; } if (last && (last <= stream->nmsgs)) { mail_fetchstructure (stream,last,&body); if (body) display_body (body,NIL,(long) 0); else puts ("%No body information available"); } else puts ("?Bad message number"); break; case 'C': /* Check command */ mail_check (stream); status (stream); break; case 'D': /* Delete command */ if (arg) last = atoi (arg); else { if (last == 0) { puts ("?Missing message number"); break; } arg = cmd; sprintf (arg,"%lu",last); } if (last && (last <= stream->nmsgs)) mail_setflag (stream,arg,"\\DELETED"); else puts ("?Bad message number"); break; case 'E': /* Expunge command */ mail_expunge (stream); last = 0; break; case 'F': /* Find command */ if (!arg) { arg = "%"; if (s = sm_read (&sdb)) { puts ("Local network subscribed mailboxes:"); do if (*s == '{') (mm_lsub (NIL,NIL,s,NIL)); while (s = sm_read (&sdb)); } } puts ("Subscribed mailboxes:"); mail_lsub (((arg[0] == '{') && (*stream->mailbox == '{')) ? stream : NIL, NIL,arg); puts ("Known mailboxes:"); mail_list (((arg[0] == '{') && (*stream->mailbox == '{')) ? stream : NIL, NIL,arg); break; case 'G': mail_gc (stream,GC_ENV|GC_TEXTS|GC_ELT); break; case 'H': /* Headers command */ if (arg) { if (!(last = atoi (arg))) { mail_search (stream,arg); for (i = 1; i <= stream->nmsgs; ++i) if (mail_elt (stream,i)->searched) header (stream,i); break; } } else if (last == 0) { puts ("?Missing message number"); break; } if (last && (last <= stream->nmsgs)) header (stream,last); else puts ("?Bad message number"); break; case 'L': /* Literal command */ if (arg) last = atoi (arg); else if (!last) { puts ("?Missing message number"); break; } if (last && (last <= stream->nmsgs)) puts (mail_fetch_message (stream,last,NIL,NIL)); else puts ("?Bad message number"); break; case 'M': mail_status (NIL,arg ? arg : stream->mailbox, SA_MESSAGES|SA_RECENT|SA_UNSEEN|SA_UIDNEXT|SA_UIDVALIDITY); break; case 'N': /* New mailbox command */ if (!arg) { puts ("?Missing mailbox"); break; } /* get the new mailbox */ while (!(stream = mail_open (stream,arg,debug))) { prompt ("Mailbox: ",arg); if (!arg[0]) break; } last = 0; status (stream); break; case 'O': /* Overview command */ if (!arg) { puts ("?Missing UID"); break; } mail_fetch_overview (stream,arg,overview_header); break; case 'P': /* Ping command */ mail_ping (stream); status (stream); break; case 'Q': /* Quit command */ mail_close (stream); stream = NIL; break; case 'S': /* Send command */ smtptest (debug); break; case '\0': /* null command (type next message) */ if (!last || (last++ >= stream->nmsgs)) { puts ("%No next message"); break; } case 'T': /* Type command */ if (arg) last = atoi (arg); else if (!last) { puts ("?Missing message number"); break; } if (last && (last <= stream->nmsgs)) { STRINGLIST *lines = mail_newstringlist (); STRINGLIST *cur = lines; cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) cpystr ("Date"))); cur = cur->next = mail_newstringlist (); cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) cpystr ("From"))); cur = cur->next = mail_newstringlist (); cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) cpystr (">From"))); cur = cur->next = mail_newstringlist (); cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) cpystr ("Subject"))); cur = cur->next = mail_newstringlist (); cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) cpystr ("To"))); cur = cur->next = mail_newstringlist (); cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) cpystr ("cc"))); cur = cur->next = mail_newstringlist (); cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) cpystr ("Newsgroups"))); printf ("%s",mail_fetchheader_full (stream,last,lines,NIL,NIL)); puts (mail_fetchtext (stream,last)); mail_free_stringlist (&lines); } else puts ("?Bad message number"); break; case 'U': /* Undelete command */ if (arg) last = atoi (arg); else { if (!last) { puts ("?Missing message number"); break; } arg = cmd; sprintf (arg,"%lu",last); } if (last > 0 && last <= stream->nmsgs) mail_clearflag (stream,arg,"\\DELETED"); else puts ("?Bad message number"); break; case 'X': /* Xit command */ mail_expunge (stream); mail_close (stream); stream = NIL; break; case '+': mail_debug (stream); debug = T; break; case '-': mail_nodebug (stream); debug = NIL; break; case '?': /* ? command */ puts ("Body, Check, Delete, Expunge, Find, GC, Headers, Literal,"); puts (" MailboxStatus, New Mailbox, Overview, Ping, Quit, Send, Type,"); puts ("Undelete, Xit, +, -, or for next message"); break; default: /* bogus command */ printf ("?Unrecognized command: %s\n",cmd); break; } } } /* MM display header * Accepts: IMAP2 stream * message number */ void overview_header (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov, unsigned long msgno) { if (ov) { unsigned long i; char *t,tmp[MAILTMPLEN]; ADDRESS *adr; MESSAGECACHE *elt = mail_elt (stream,msgno); MESSAGECACHE selt; tmp[0] = elt->recent ? (elt->seen ? 'R': 'N') : ' '; tmp[1] = (elt->recent | elt->seen) ? ' ' : 'U'; tmp[2] = elt->flagged ? 'F' : ' '; tmp[3] = elt->answered ? 'A' : ' '; tmp[4] = elt->deleted ? 'D' : ' '; mail_parse_date (&selt,ov->date); sprintf (tmp+5,"%4lu) ",elt->msgno); mail_date (tmp+11,&selt); tmp[17] = ' '; tmp[18] = '\0'; memset (tmp+18,' ',(size_t) 20); tmp[38] = '\0'; /* tie off with null */ /* get first from address from envelope */ for (adr = ov->from; adr && !adr->host; adr = adr->next); if (adr) { /* if a personal name exists use it */ if (!(t = adr->personal)) sprintf (t = tmp+400,"%s@%s",adr->mailbox,adr->host); memcpy (tmp+18,t,(size_t) min (20,(long) strlen (t))); } strcat (tmp," "); if (i = elt->user_flags) { strcat (tmp,"{"); while (i) { strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]); if (i) strcat (tmp," "); } strcat (tmp,"} "); } sprintf (tmp + strlen (tmp),"%.25s (%lu chars)", ov->subject ? ov->subject : " ",ov->optional.octets); puts (tmp); } else printf ("%%No overview for UID %lu\n",uid); } /* MM display header * Accepts: IMAP2 stream * message number */ void header (MAILSTREAM *stream,long msgno) { unsigned long i; char tmp[MAILTMPLEN]; char *t; MESSAGECACHE *cache = mail_elt (stream,msgno); mail_fetchstructure (stream,msgno,NIL); tmp[0] = cache->recent ? (cache->seen ? 'R': 'N') : ' '; tmp[1] = (cache->recent | cache->seen) ? ' ' : 'U'; tmp[2] = cache->flagged ? 'F' : ' '; tmp[3] = cache->answered ? 'A' : ' '; tmp[4] = cache->deleted ? 'D' : ' '; sprintf (tmp+5,"%4lu) ",cache->msgno); mail_date (tmp+11,cache); tmp[17] = ' '; tmp[18] = '\0'; mail_fetchfrom (tmp+18,stream,msgno,(long) 20); strcat (tmp," "); if (i = cache->user_flags) { strcat (tmp,"{"); while (i) { strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]); if (i) strcat (tmp," "); } strcat (tmp,"} "); } mail_fetchsubject (t = tmp + strlen (tmp),stream,msgno,(long) 25); sprintf (t += strlen (t)," (%lu chars)",cache->rfc822_size); puts (tmp); } /* MM display body * Accepts: BODY structure pointer * prefix string * index */ void display_body (BODY *body,char *pfx,long i) { char tmp[MAILTMPLEN]; char *s = tmp; PARAMETER *par; PART *part; /* multipart doesn't have a row to itself */ if (body->type == TYPEMULTIPART) { /* if not first time, extend prefix */ if (pfx) sprintf (tmp,"%s%ld.",pfx,++i); else tmp[0] = '\0'; for (i = 0,part = body->nested.part; part; part = part->next) display_body (&part->body,tmp,i++); } else { /* non-multipart, output oneline descriptor */ if (!pfx) pfx = ""; /* dummy prefix if top level */ sprintf (s," %s%ld %s",pfx,++i,body_types[body->type]); if (body->subtype) sprintf (s += strlen (s),"/%s",body->subtype); if (body->description) sprintf (s += strlen (s)," (%s)",body->description); if (par = body->parameter) do sprintf (s += strlen (s),";%s=%s",par->attribute,par->value); while (par = par->next); if (body->id) sprintf (s += strlen (s),", id = %s",body->id); switch (body->type) { /* bytes or lines depending upon body type */ case TYPEMESSAGE: /* encapsulated message */ case TYPETEXT: /* plain text */ sprintf (s += strlen (s)," (%lu lines)",body->size.lines); break; default: sprintf (s += strlen (s)," (%lu bytes)",body->size.bytes); break; } puts (tmp); /* output this line */ /* encapsulated message? */ if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822") && (body = body->nested.msg->body)) { if (body->type == TYPEMULTIPART) display_body (body,pfx,i-1); else { /* build encapsulation prefix */ sprintf (tmp,"%s%ld.",pfx,i); display_body (body,tmp,(long) 0); } } } } /* MM status report * Accepts: MAIL stream */ void status (MAILSTREAM *stream) { unsigned long i; char *s,date[MAILTMPLEN]; THREADER *thr; AUTHENTICATOR *auth; rfc822_date (date); puts (date); if (stream) { if (stream->mailbox) printf (" %s mailbox: %s, %lu messages, %lu recent\n", stream->dtb->name,stream->mailbox,stream->nmsgs,stream->recent); else puts ("%No mailbox is open on this stream"); if (stream->user_flags[0]) { printf ("Keywords: %s",stream->user_flags[0]); for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i) printf (", %s",stream->user_flags[i]); puts (""); } if (!strcmp (stream->dtb->name,"imap")) { if (LEVELIMAP4rev1 (stream)) s = "IMAP4rev1 (RFC 3501)"; else if (LEVEL1730 (stream)) s = "IMAP4 (RFC 1730)"; else if (LEVELIMAP2bis (stream)) s = "IMAP2bis"; else if (LEVEL1176 (stream)) s = "IMAP2 (RFC 1176)"; else s = "IMAP2 (RFC 1064)"; printf ("%s server %s\n",s,imap_host (stream)); if (LEVELIMAP4 (stream)) { if (i = imap_cap (stream)->auth) { s = ""; printf ("Mutually-supported SASL mechanisms:"); while (auth = mail_lookup_auth (find_rightmost_bit (&i) + 1)) { printf (" %s",auth->name); if (!strcmp (auth->name,"PLAIN")) s = "\n [LOGIN will not be listed here if PLAIN is supported]"; } puts (s); } printf ("Supported standard extensions:\n"); if (LEVELACL (stream)) puts (" Access Control lists (RFC 2086)"); if (LEVELQUOTA (stream)) puts (" Quotas (RFC 2087)"); if (LEVELLITERALPLUS (stream)) puts (" Non-synchronizing literals (RFC 2088)"); if (LEVELIDLE (stream)) puts (" IDLE unsolicited update (RFC 2177)"); if (LEVELMBX_REF (stream)) puts (" Mailbox referrals (RFC 2193)"); if (LEVELLOG_REF (stream)) puts (" Login referrals (RFC 2221)"); if (LEVELANONYMOUS (stream)) puts (" Anonymous access (RFC 2245)"); if (LEVELNAMESPACE (stream)) puts (" Multiple namespaces (RFC 2342)"); if (LEVELUIDPLUS (stream)) puts (" Extended UID behavior (RFC 2359)"); if (LEVELSTARTTLS (stream)) puts (" Transport Layer Security (RFC 2595)"); if (LEVELLOGINDISABLED (stream)) puts (" LOGIN command disabled (RFC 2595)"); if (LEVELID (stream)) puts (" Implementation identity negotiation (RFC 2971)"); if (LEVELCHILDREN (stream)) puts (" LIST children announcement (RFC 3348)"); if (LEVELMULTIAPPEND (stream)) puts (" Atomic multiple APPEND (RFC 3502)"); if (LEVELBINARY (stream)) puts (" Binary body content (RFC 3516)"); puts ("Supported draft extensions:"); if (LEVELUNSELECT (stream)) puts (" Mailbox unselect"); if (LEVELSASLIR (stream)) puts (" SASL initial client response"); if (LEVELSORT (stream)) puts (" Server-based sorting"); if (LEVELTHREAD (stream)) { printf (" Server-based threading:"); for (thr = imap_cap (stream)->threader; thr; thr = thr->next) printf (" %s",thr->name); putchar ('\n'); } if (LEVELSCAN (stream)) puts (" Mailbox text scan"); if (i = imap_cap (stream)->extlevel) { printf ("Supported BODYSTRUCTURE extensions:"); switch (i) { case BODYEXTLOC: printf (" location"); case BODYEXTLANG: printf (" language"); case BODYEXTDSP: printf (" disposition"); case BODYEXTMD5: printf (" MD5\n"); } } } else putchar ('\n'); } } } /* Prompt user for input * Accepts: pointer to prompt message * pointer to input buffer */ void prompt (char *msg,char *txt) { printf ("%s",msg); gets (txt); } /* Interfaces to C-client */ void mm_searched (MAILSTREAM *stream,unsigned long number) { } void mm_exists (MAILSTREAM *stream,unsigned long number) { } void mm_expunged (MAILSTREAM *stream,unsigned long number) { } void mm_flags (MAILSTREAM *stream,unsigned long number) { } void mm_notify (MAILSTREAM *stream,char *string,long errflg) { mm_log (string,errflg); } void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes) { putchar (' '); if (delimiter) putchar (delimiter); else fputs ("NIL",stdout); putchar (' '); fputs (mailbox,stdout); if (attributes & LATT_NOINFERIORS) fputs (", no inferiors",stdout); if (attributes & LATT_NOSELECT) fputs (", no select",stdout); if (attributes & LATT_MARKED) fputs (", marked",stdout); if (attributes & LATT_UNMARKED) fputs (", unmarked",stdout); putchar ('\n'); } void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes) { putchar (' '); if (delimiter) putchar (delimiter); else fputs ("NIL",stdout); putchar (' '); fputs (mailbox,stdout); if (attributes & LATT_NOINFERIORS) fputs (", no inferiors",stdout); if (attributes & LATT_NOSELECT) fputs (", no select",stdout); if (attributes & LATT_MARKED) fputs (", marked",stdout); if (attributes & LATT_UNMARKED) fputs (", unmarked",stdout); putchar ('\n'); } void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { printf (" Mailbox %s",mailbox); if (status->flags & SA_MESSAGES) printf (", %lu messages",status->messages); if (status->flags & SA_RECENT) printf (", %lu recent",status->recent); if (status->flags & SA_UNSEEN) printf (", %lu unseen",status->unseen); if (status->flags & SA_UIDVALIDITY) printf (", %lu UID validity", status->uidvalidity); if (status->flags & SA_UIDNEXT) printf (", %lu next UID",status->uidnext); printf ("\n"); } void mm_log (char *string,long errflg) { switch ((short) errflg) { case NIL: printf ("[%s]\n",string); break; case PARSE: case WARN: printf ("%%%s\n",string); break; case ERROR: printf ("?%s\n",string); break; } } void mm_dlog (char *string) { puts (string); } void mm_login (NETMBX *mb,char *user,char *pwd,long trial) { char tmp[MAILTMPLEN]; if (curhst) fs_give ((void **) &curhst); curhst = (char *) fs_get (1+strlen (mb->host)); strcpy (curhst,mb->host); if (*mb->user) { strcpy (user,mb->user); sprintf (tmp,"{%s/%s/user=\"%s\"} password: ",mb->host,mb->service,mb->user); } else { sprintf (tmp,"{%s/%s} username: ",mb->host,mb->service); prompt (tmp,user); strcpy (tmp,"Password: "); } if (curusr) fs_give ((void **) &curusr); curusr = cpystr (user); strcpy (pwd,getpass (tmp)); } void mm_critical (MAILSTREAM *stream) { } void mm_nocritical (MAILSTREAM *stream) { } long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { #if UNIXLIKE kill (getpid (),SIGSTOP); #else abort (); #endif return NIL; } void mm_fatal (char *string) { printf ("?%s\n",string); } /* SMTP tester */ void smtptest (long debug) { SENDSTREAM *stream = NIL; char line[MAILTMPLEN]; char *text = (char *) fs_get (8*MAILTMPLEN); ENVELOPE *msg = mail_newenvelope (); BODY *body = mail_newbody (); msg->from = mail_newaddr (); msg->from->personal = cpystr (personalname); msg->from->mailbox = cpystr (curusr); msg->from->host = cpystr (curhst); msg->return_path = mail_newaddr (); msg->return_path->mailbox = cpystr (curusr); msg->return_path->host = cpystr (curhst); prompt ("To: ",line); rfc822_parse_adrlist (&msg->to,line,curhst); if (msg->to) { prompt ("cc: ",line); rfc822_parse_adrlist (&msg->cc,line,curhst); } else { prompt ("Newsgroups: ",line); if (*line) msg->newsgroups = cpystr (line); else { mail_free_body (&body); mail_free_envelope (&msg); fs_give ((void **) &text); return; } } prompt ("Subject: ",line); msg->subject = cpystr (line); puts (" Msg (end with a line with only a '.'):"); body->type = TYPETEXT; *text = '\0'; while (gets (line)) { if (line[0] == '.') { if (line[1] == '\0') break; else strcat (text,"."); } strcat (text,line); strcat (text,"\015\012"); } body->contents.text.data = (unsigned char *) text; body->contents.text.size = strlen (text); rfc822_date (line); msg->date = (char *) fs_get (1+strlen (line)); strcpy (msg->date,line); if (msg->to) { puts ("Sending..."); if (stream = smtp_open (hostlist,debug)) { if (smtp_mail (stream,"MAIL",msg,body)) puts ("[Ok]"); else printf ("[Failed - %s]\n",stream->reply); } } else { puts ("Posting..."); if (stream = nntp_open (newslist,debug)) { if (nntp_mail (stream,msg,body)) puts ("[Ok]"); else printf ("[Failed - %s]\n",stream->reply); } } if (stream) smtp_close (stream); else puts ("[Can't open connection to any server]"); mail_free_envelope (&msg); mail_free_body (&body); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/mtest/mtest.def000066400000000000000000000003241137544547100224700ustar00rootroot00000000000000; ; MTEST.DEF - def file for c-client test application mtest ; NAME mtest DESCRIPTION 'C-Client Test Application' ;PROTMODE ;CODE PRELOAD MOVEABLE DISCARDABLE ;DATA PRELOAD MOVEABLE tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/000077500000000000000000000000001137544547100206335ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/000077500000000000000000000000001137544547100217115ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/Makefile000066400000000000000000000131171137544547100233540ustar00rootroot00000000000000# Program: C client makefile for Amiga # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 14 October 2003 # # The IMAP toolkit provided in this Distribution is # Copyright 2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Command line build parameters EXTRAAUTHENTICATORS= EXTRADRIVERS=mbox PASSWDTYPE=std # Build parameters normally set by the individual port AMICFLAGS=-O -DNO_INLINE_STDARG -Dunix AMILDFLAGS=/pine/libc.a -lamiga -lauto CHECKPW=std LOGINPW=std ACTIVEFILE=/UULib/News/Active SPOOLDIR=/usr/spool MAILSPOOL=/AmiTCP/Mail NEWSSPOOL=/UUNews # Default formats for creating new mailboxes and for empty mailboxes in the # default namespace; must be set to the associated driver's prototype. # # The CREATEPROTO is the default format for new mailbox creation. # The EMPTYPROTO is the default format for handling zero-byte files. # # Normally, this is set by the individual port. # # NOTE: namespace formats (e.g. mh and news) can not be set as a default format # since they do not exist in the default namespace. Also, it is meaningless to # set certain other formats (e.g. mbx and mx) as the EMPTYPROTO since these # formats can never be empty files. CREATEPROTO=unixproto EMPTYPROTO=unixproto # Commands possibly overriden by the individual port ARRC=ar rc CC=cc LN=cp RANLIB=ranlib RM=rm -f # Standard distribution build parameters DEFAULTAUTHENTICATORS=md5 pla log DEFAULTDRIVERS=imap nntp pop3 mh mx mbx tenex mtx mmdf unix news phile # Normally no need to change any of these ARCHIVE=c-client.a BINARIES=mail.o misc.o newsrc.o smanager.o osdep.o utf8.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o CFLAGS=$(BASECFLAGS) $(EXTRACFLAGS) MAKE=make MV=mv SHELL=/bin/sh # Primary build command BUILDOPTIONS= EXTRACFLAGS=$(EXTRACFLAGS) EXTRALDFLAGS=$(EXTRALDFLAGS)\ EXTRADRIVERS=$(EXTRADRIVERS) EXTRAAUTHENTICATORS=$(EXTRAAUTHENTICATORS)\ PASSWDTYPE=$(PASSWDTYPE) BUILD=$(MAKE) build $(BUILDOPTIONS) $(SPECIALS) # Here if no make argument established missing: osdep.h $(MAKE) $(ARCHIVE) CC=`cat CCTYPE` CFLAGS="`cat CFLAGS`" osdep.h: @echo You must specify what type of system @false # Current ports ami: # AmigaDOS $(BUILD) OS=$@ \ BASECFLAGS="-DOLD $(AMICFLAGS)" \ BASELDFLAGS="$(AMILDFLAGS) -lamitcp000" \ CC=gcc am2: # AmigaDOS with a 68020+ $(BUILD) OS=ami \ BASECFLAGS="-DOLD -m68020 $(AMICFLAGS)" \ BASELDFLAGS="$(AMILDFLAGS) -lamitcp" \ CC=gcc amn: # AmigaDOS with a 680x0 using "new" socket library $(BUILD) OS=ami \ BASELDFLAGS="$(AMILDFLAGS) -lnewamitcp000" \ CC=gcc ama: # AmigaDOS using AS225R2 $(BUILD) OS=ami \ MAILSPOOL=/INet/Mail \ BASECFLAGS="-m68020 $(AMICFLAGS)" \ BASELDFLAGS="$(AMILDFLAGS) -las225r2" \ CC=gcc # Build it! build: clean once ckp$(PASSWDTYPE) $(EXTRAAUTHENTICATORS) $(ARCHIVE) $(ARCHIVE): $(BINARIES) $(RM) $(ARCHIVE) || true $(ARRC) $(ARCHIVE) $(BINARIES) $(RANLIB) $(ARCHIVE) # Cleanup clean: $(RM) *.o linkage.[ch] auths.c $(ARCHIVE) osdep.* *TYPE *FLAGS || true # Dependencies dummy.o: mail.h misc.h osdep.h dummy.h fdstring.o: mail.h misc.h osdep.h fdstring.h flstring.o: mail.h misc.h osdep.h flstring.h imap4r1.o: mail.h misc.h osdep.h imap4r1.h rfc822.h mail.o: mail.h misc.h osdep.h rfc822.h linkage.h mbx.o: mail.h misc.h osdep.h mbx.h dummy.h mh.o: mail.h misc.h osdep.h mh.h dummy.h mx.o: mail.h misc.h osdep.h mx.h dummy.h misc.o: mail.h misc.h osdep.h mmdf.o: mail.h misc.h osdep.h pseudo.h dummy.h mtx.o: mail.h misc.h osdep.h dummy.h netmsg.o: mail.h misc.h osdep.h netmsg.h news.o: mail.h misc.h osdep.h newsrc.o: mail.h misc.h osdep.h newsrc.h nntp.o: mail.h misc.h osdep.h netmsg.h smtp.h nntp.h rfc822.h phile.o: mail.h misc.h osdep.h rfc822.h dummy.h pseudo.o: pseudo.h pop3.o: mail.h misc.h osdep.h pop3.h rfc822.h smanager.o: mail.h misc.h osdep.h smtp.o: mail.h misc.h osdep.h smtp.h rfc822.h rfc822.o: mail.h misc.h osdep.h rfc822.h tenex.o: mail.h misc.h osdep.h dummy.h unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h utf8.o: mail.h misc.h osdep.h utf8.h # OS-dependent osdep.o:mail.h misc.h env.h fs.h ftl.h nl.h tcp.h \ osdep.h env_ami.h tcp_ami.h \ osdep.c env_ami.c fs_ami.c ftl_ami.c nl_ami.c tcp_ami.c \ auths.c gethstid.c \ gr_waitp.c \ auth_log.c auth_md5.c auth_pla.c \ pmatch.c scandir.c \ tz_bsd.c \ write.c \ strerror.c strpbrk.c strstr.c strtok.c strtoul.c \ OSCFLAGS $(CC) $(CFLAGS) `cat OSCFLAGS` -c osdep.c osdep.c: osdepbas.c osdepckp.c osdeplog.c osdepssl.c $(RM) osdep.c || true cat osdepbas.c osdepckp.c osdeplog.c osdepssl.c > osdep.c # Once-only environment setup once: @echo Once-only environment setup... ./drivers $(EXTRADRIVERS) $(DEFAULTDRIVERS) dummy ./mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS) echo $(CC) > CCTYPE echo $(CFLAGS) > CFLAGS echo -DCREATEPROTO=$(CREATEPROTO) -DEMPTYPROTO=$(EMPTYPROTO) \ -DMAILSPOOL=\"$(MAILSPOOL)\" \ -DACTIVEFILE=\"$(ACTIVEFILE)\" -DNEWSSPOOL=\"$(NEWSSPOOL)\" \ -DANONYMOUSHOME=\"$(MAILSPOOL)/anonymous\" > OSCFLAGS echo $(BASELDFLAGS) $(EXTRALDFLAGS) > LDFLAGS $(LN) os_$(OS).h osdep.h $(LN) os_$(OS).c osdepbas.c $(LN) log_$(LOGINPW).c osdeplog.c $(LN) ssl_none.c osdepssl.c # Password checkers ckpstd: # Port standard $(LN) ckp_$(CHECKPW).c osdepckp.c # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/Makefile.md5000066400000000000000000000012631137544547100240370ustar00rootroot00000000000000# Program: CRAM-MD5 makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Extended flags needed for additional authenticators. You may need to modify. MD5CFLAGS= -DMD5ENABLE=\"/etc/cram-md5.pwd\" md5: # CRAM-MD5 flags echo $(MD5CFLAGS) >> OSCFLAGS tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/ckp_std.c000066400000000000000000000016731137544547100235130ustar00rootroot00000000000000/* * Program: Standard check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { return (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] && !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ? pw : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/drivers000077500000000000000000000014061137544547100233160ustar00rootroot00000000000000#!/bin/sh # # Program: Driver Linkage Generator # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 October 1989 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Erase old driver linkage rm -f linkage.[ch] # Now define the new list for driver do echo "extern DRIVER "$driver"driver;" >> linkage.h echo " mail_link (&"$driver"driver); /* link in the $driver driver */" | cat >> linkage.c done tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/dummy.c000066400000000000000000000531011137544547100232100ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 10 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include "dummy.h" #include "misc.h" #include "mx.h" /* highly unfortunate */ /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level); long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents); long dummy_subscribe (MAILSTREAM *stream,char *mailbox); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ dummy_subscribe, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { /* indeterminate clearbox INBOX */ if (!*s) return &dummydriver; else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { case S_IFREG: case S_IFDIR: return &dummydriver; } /* blackbox INBOX does not exist yet */ else if (!compare_cstring (name,"INBOX")) return &dummydriver; } return NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = dummy_file ((char *) value,"INBOX"); break; } return ret; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i; if (!pat || !*pat) { /* empty pattern? */ if (dummy_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'/')) *++s = '\0'; else test[0] = '\0'; dummy_listed (stream,'/',test,LATT_NOSELECT,NIL); } } /* get canonical form of name */ else if (dummy_canonicalize (test,ref,pat)) { /* found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test,i = s - test); file[i] = '\0'; /* tie off */ } else strcpy (file,test); /* use just that name then */ if (s = strrchr (file,'/')){/* find directory name */ *++s = '\0'; /* found, tie off at that point */ s = file; } /* silly case */ else if ((file[0] == '~') || (file[0] == '#')) s = file; /* do the work */ dummy_list_work (stream,s,test,contents,0); /* always an INBOX */ if (pmatch ("INBOX",ucase (test))) dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents); } } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { dummy_scan (stream,ref,pat,NIL); } /* Dummy list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { void *sdb = NIL; char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN]; int showuppers = pat[strlen (pat) - 1] == '%'; /* get canonical form of name */ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do if (*s != '{') { if (!compare_cstring (s,"INBOX") && pmatch ("INBOX",ucase (strcpy (tmp,test)))) mm_lsub (stream,NIL,s,LATT_NOINFERIORS); else if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL); else while (showuppers && (t = strrchr (s,'/'))) { *t = '\0'; /* tie off the name */ if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,LATT_NOSELECT); } } while (s = sm_read (&sdb)); /* until no more subscriptions */ } /* Dummy subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long dummy_subscribe (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) #if 0 /* disable this temporarily for Netscape */ && ((sbuf.st_mode & S_IFMT) == S_IFREG) #endif ) return sm_subscribe (mailbox); sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* Dummy list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * string to scan * search level */ void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level) { DIR *dp; struct direct *d; struct stat sbuf; int ismx; char tmp[MAILTMPLEN]; /* punt if bogus name */ if (!mailboxdir (tmp,dir,NIL)) return; if (dp = opendir (tmp)) { /* do nothing if can't open directory */ /* list it if not at top-level */ if (!level && dir && pmatch_full (dir,pat,'/')) dummy_listed (stream,'/',dir,LATT_NOSELECT,contents); /* scan directory, ignore . and .. */ ismx = (!stat (strcat (tmp,MXINDEXNAME),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)); if (!dir || dir[strlen (dir) - 1] == '/') while (d = readdir (dp)) if (((d->d_name[0] != '.') || (((int) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL : (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]) && strcmp (d->d_name+1,MXINDEXNAME+2))))) && (strlen (d->d_name) <= NETMAXMBX)) { /* see if name is useful */ if (dir) sprintf (tmp,"%s%s",dir,d->d_name); else strcpy (tmp,d->d_name); /* make sure useful and can get info */ if ((pmatch_full (tmp,pat,'/') || pmatch_full (strcat (tmp,"/"),pat,'/') || dmatch (tmp,pat,'/')) && mailboxdir (tmp,dir,d->d_name) && tmp[0] && !stat (tmp,&sbuf)) { /* now make name we'd return */ if (dir) sprintf (tmp,"%s%s",dir,d->d_name); else strcpy (tmp,d->d_name); /* only interested in file type */ switch (sbuf.st_mode & S_IFMT) { case S_IFDIR: /* directory? */ if (pmatch_full (tmp,pat,'/')) { if (!dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents)) break; strcat (tmp,"/"); /* set up for dmatch call */ } /* try again with trailing / */ else if (pmatch_full (strcat (tmp,"/"),pat,'/') && !dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents)) break; if (dmatch (tmp,pat,'/') && (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) dummy_list_work (stream,tmp,pat,contents,level+1); break; case S_IFREG: /* ordinary name */ /* ignore all-digit names from mx */ /* Must use ctime for systems that don't update mtime properly */ if (!(ismx && mx_select (d)) && pmatch_full (tmp,pat,'/') && compare_cstring (tmp,"INBOX")) dummy_listed (stream,'/',tmp,LATT_NOINFERIORS + ((sbuf.st_size && (sbuf.st_atime < sbuf.st_ctime))? LATT_MARKED : LATT_UNMARKED),contents); break; } } } closedir (dp); /* all done, flush directory */ } } /* Scan file for contents * Accepts: file name * desired contents * Returns: NIL if contents not found, T if found */ #define BUFSIZE 4*MAILTMPLEN long dummy_scan_contents (char *name,char *contents,unsigned long csiz, unsigned long fsiz) { int fd; unsigned long ssiz,bsiz; char *buf; /* forget it if can't select or open */ if ((fd = open (name,O_RDONLY,NIL)) >= 0) { /* get buffer including slop */ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1); memset (buf,'\0',ssiz); /* no slop area the first time */ while (fsiz) { /* until end of file */ read (fd,buf+ssiz,bsiz = min (fsiz,BUFSIZE)); if (search ((unsigned char *) buf,bsiz+ssiz, (unsigned char *) contents,csiz)) break; memcpy (buf,buf+BUFSIZE,ssiz); fsiz -= bsiz; /* note that we read that much */ } fs_give ((void **) &buf); /* flush buffer */ close (fd); /* finished with file */ if (fsiz) return T; /* found */ } return NIL; /* not found */ } /* Mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * attributes * contents to search before calling mm_list() * Returns: NIL if should abort hierarchy search, else T (currently always) */ long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents) { DRIVER *d = NIL; unsigned long csiz; struct stat sbuf; char *s,tmp[MAILTMPLEN]; /* don't \NoSelect dir if it has a driver */ if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) && (d != &dummydriver)) attributes &= ~LATT_NOSELECT; if (!contents || /* notify main program */ (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) && (s = mailboxfile (tmp,name)) && (*s || (s = mail_parameters (NIL,GET_INBOXPATH,tmp))) && !stat (s,&sbuf) && (csiz <= sbuf.st_size) && SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size))) mm_list (stream,delimiter,name,attributes); return T; } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; long ret = NIL; /* validate name */ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); MM_LOG (tmp,ERROR); } /* create the name, done if made directory */ else if ((ret = dummy_create_path (stream,tmp,get_dir_protection(mailbox)))&& (s = strrchr (s,'/')) && !s[1]) return T; return ret ? set_mbx_protections (mailbox,tmp) : NIL; } /* Dummy create path * Accepts: mail stream * path name to create * directory mode * Returns: T on success, NIL on failure */ long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN]; int fd; long ret = NIL; char *t = strrchr (path,'/'); int wantdir = t && !t[1]; int mask = umask (0); if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */ if (s = strrchr (path,'/')) { /* found superior to this name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (path,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,path,dirmode)) { umask (mask); /* restore mask */ return NIL; } *s = c; /* restore full name */ } if (wantdir) { /* want to create directory? */ ret = !mkdir (path,(int) dirmode); *t = '/'; /* restore directory delimiter */ } /* create file */ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL, (int) mail_parameters(NIL,GET_MBXPROTECTION,NIL))) >= 0) ret = !close (fd); if (!ret) { /* error? */ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,strerror (errno)); MM_LOG (tmp,ERROR); } umask (mask); /* restore mask */ return ret; /* return status */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { struct stat sbuf; char *s,tmp[MAILTMPLEN]; if (!(s = dummy_file (tmp,mailbox))) { sprintf (tmp,"Can't delete - invalid name: %.80s",s); MM_LOG (tmp,ERROR); } /* no trailing / (workaround BSD kernel bug) */ if ((s = strrchr (tmp,'/')) && !s[1]) *s = '\0'; if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? rmdir (tmp) : unlink (tmp)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } return T; /* return success */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; /* no trailing / allowed */ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || ((s = strrchr (s,'/')) && !s[1])) { sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); MM_LOG (mbx,ERROR); return NIL; } if (s) { /* found superior to destination name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create (stream,mbx)) return NIL; *s = c; /* restore full name */ } /* rename of non-ex INBOX creates dest */ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } return T; /* return success */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { int fd; char err[MAILTMPLEN],tmp[MAILTMPLEN]; struct stat sbuf; /* OP_PROTOTYPE call */ if (!stream) return &dummyproto; err[0] = '\0'; /* no error message yet */ /* can we open the file? */ if (!dummy_file (tmp,stream->mailbox)) sprintf (err,"Can't open this name: %.80s",stream->mailbox); else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { /* no, error unless INBOX */ if (compare_cstring (stream->mailbox,"INBOX")) sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox); } else { /* file had better be empty then */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); if ((sbuf.st_mode & S_IFMT) != S_IFREG) sprintf (err,"Can't open %.80s: not a selectable mailbox", stream->mailbox); else if (sbuf.st_size) /* bogus format if non-empty */ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format", stream->mailbox,tmp); } if (err[0]) { /* if an error happened */ MM_LOG (err,stream->silent ? WARN : ERROR); return NIL; } else if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); /* and certainly no recent ones! */ stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long dummy_ping (MAILSTREAM *stream) { MAILSTREAM *test; if (time (0) >= /* time to do another test? */ ((time_t) (stream->gensym + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) { /* has mailbox format changed? */ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) && (test->dtb != stream->dtb) && (test = mail_open (NIL,stream->mailbox,NIL))) { /* preserve some resources */ test->original_mailbox = stream->original_mailbox; stream->original_mailbox = NIL; test->sparep = stream->sparep; stream->sparep = NIL; test->sequence = stream->sequence; mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */ memcpy (fs_get (sizeof (MAILSTREAM)),stream, sizeof (MAILSTREAM))); /* swap the streams */ memcpy (stream,test,sizeof (MAILSTREAM)); fs_give ((void **) &test);/* flush test now that copied */ /* make sure application knows */ mail_exists (stream,stream->recent = stream->nmsgs); } /* still hasn't changed */ else stream->gensym = time (0); } return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd = -1; int e; char tmp[MAILTMPLEN]; MAILSTREAM *ts = default_proto (T); if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) { if ((e = errno) == ENOENT) /* failed, was it no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox); MM_LOG (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } if (fd >= 0) { /* found file? */ fstat (fd,&sbuf); /* get its size */ close (fd); /* toss out the fd */ if (sbuf.st_size) ts = NIL; /* non-empty file? */ } if (ts) return (*ts->dtb->append) (stream,mailbox,af,data); sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* Dummy mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *dummy_file (char *dst,char *name) { char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? strcpy (dst,sysinbox ()) : s; } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { if (ref) { /* preliminary reference check */ if (*ref == '{') return NIL;/* remote reference not allowed */ else if (!*ref) ref = NIL; /* treat empty reference as no reference */ } switch (*pat) { case '#': /* namespace name */ if (mailboxfile (tmp,pat)) strcpy (tmp,pat); else return NIL; /* unknown namespace */ break; case '{': /* remote names not allowed */ return NIL; case '/': /* rooted name */ case '~': /* home directory name */ if (!ref || (*ref != '#')) {/* non-namespace reference? */ strcpy (tmp,pat); /* yes, ignore */ break; } /* fall through */ default: /* apply reference for all other names */ if (!ref) strcpy (tmp,pat); /* just copy if no namespace */ else if ((*ref != '#') || mailboxfile (tmp,ref)) { /* wants root of name? */ if (*pat == '/') strcpy (strchr (strcpy (tmp,ref),'/'),pat); /* otherwise just append */ else sprintf (tmp,"%s%s",ref,pat); } else return NIL; /* unknown namespace */ } return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/dummy.h000066400000000000000000000021051137544547100232130ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/env_ami.c000066400000000000000000001200241137544547100234720ustar00rootroot00000000000000/* * Program: Amiga environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 8 July 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include /* c-client environment parameters */ static char *myUserName = NIL; /* user name */ static char *myHomeDir = NIL; /* home directory name */ static char *myMailboxDir = NIL;/* mailbox directory name */ static char *myLocalHost = NIL; /* local host name */ static char *myNewsrc = NIL; /* newsrc file name */ static char *mailsubdir = NIL; /* mail subdirectory name */ static char *sysInbox = NIL; /* system inbox name */ static char *newsActive = NIL; /* news active file */ static char *newsSpool = NIL; /* news spool */ /* anonymous home directory */ static char *anonymousHome = NIL; static char *ftpHome = NIL; /* ftp export home directory */ static char *publicHome = NIL; /* public home directory */ static char *sharedHome = NIL; /* shared home directory */ static short anonymous = NIL; /* is anonymous */ static short restrictBox = NIL; /* is a restricted box */ static short has_no_life = NIL; /* is a cretin with no life */ static short hideDotFiles = NIL;/* hide files whose names start with . */ /* advertise filesystem root */ static short advertisetheworld = NIL; /* disable automatic shared namespaces */ static short noautomaticsharedns = NIL; static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ static short netfsstatbug = NIL;/* compensate for broken stat() on network * filesystems (AFS and old NFS). Don't do * this unless you really have to! */ /* 1 = disable plaintext, 2 = if not SSL */ static long disablePlaintext = NIL; static long list_max_level = 20;/* maximum level of list recursion */ /* default file protection */ static long mbx_protection = 0600; /* default directory protection */ static long dir_protection = 0700; /* default lock file protection */ static long lock_protection = MANDATORYLOCKPROT; /* default ftp file protection */ static long ftp_protection = 0644; static long ftp_dir_protection = 0755; /* default public file protection */ static long public_protection = 0666; static long public_dir_protection = 0777; /* default shared file protection */ static long shared_protection = 0660; static long shared_dir_protection = 0770; static long locktimeout = 5; /* default lock timeout */ /* default prototypes */ static MAILSTREAM *createProto = NIL; static MAILSTREAM *appendProto = NIL; /* default user flags */ static char *userFlags[NUSERFLAGS] = {NIL}; static NAMESPACE *nslist[3]; /* namespace list */ static int logtry = 3; /* number of server login tries */ /* block notification */ static blocknotify_t mailblocknotify = mm_blocknotify; /* Amiga namespaces */ /* personal mh namespace */ static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL}; static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf}; /* home namespace */ static NAMESPACE nshome = {"",'/',NIL,&nsmh}; /* Amiga other user namespace */ static NAMESPACE nsamigaother = {"~",'/',NIL,NIL}; /* public (anonymous OK) namespace */ static NAMESPACE nspublic = {"#public/",'/',NIL,NIL}; /* netnews namespace */ static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic}; /* FTP export namespace */ static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews}; /* shared (no anonymous) namespace */ static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp}; /* world namespace */ static NAMESPACE nsworld = {"/",'/',NIL,&nsshared}; #include "write.c" /* include safe writing routines */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Get all authenticators */ #include "auths.c" /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_NAMESPACE: ret = (void *) nslist; break; case SET_USERNAME: if (myUserName) fs_give ((void **) &myUserName); myUserName = cpystr ((char *) value); case GET_USERNAME: ret = (void *) myUserName; break; case SET_HOMEDIR: if (myHomeDir) fs_give ((void **) &myHomeDir); myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: if (myLocalHost) fs_give ((void **) &myLocalHost); myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: ret = (void *) myNewsrc; break; case SET_NEWSACTIVE: if (newsActive) fs_give ((void **) &newsActive); newsActive = cpystr ((char *) value); case GET_NEWSACTIVE: ret = (void *) newsActive; break; case SET_NEWSSPOOL: if (newsSpool) fs_give ((void **) &newsSpool); newsSpool = cpystr ((char *) value); case GET_NEWSSPOOL: ret = (void *) newsSpool; break; case SET_ANONYMOUSHOME: if (anonymousHome) fs_give ((void **) &anonymousHome); anonymousHome = cpystr ((char *) value); case GET_ANONYMOUSHOME: if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME); ret = (void *) anonymousHome; break; case SET_FTPHOME: if (ftpHome) fs_give ((void **) &ftpHome); ftpHome = cpystr ((char *) value); case GET_FTPHOME: ret = (void *) ftpHome; break; case SET_PUBLICHOME: if (publicHome) fs_give ((void **) &publicHome); publicHome = cpystr ((char *) value); case GET_PUBLICHOME: ret = (void *) publicHome; break; case SET_SHAREDHOME: if (sharedHome) fs_give ((void **) &sharedHome); sharedHome = cpystr ((char *) value); case GET_SHAREDHOME: ret = (void *) sharedHome; break; case SET_SYSINBOX: if (sysInbox) fs_give ((void **) &sysInbox); sysInbox = cpystr ((char *) value); case GET_SYSINBOX: ret = (void *) sysInbox; break; case SET_LISTMAXLEVEL: list_max_level = (long) value; case GET_LISTMAXLEVEL: ret = (void *) list_max_level; break; case SET_MBXPROTECTION: mbx_protection = (long) value; case GET_MBXPROTECTION: ret = (void *) mbx_protection; break; case SET_DIRPROTECTION: dir_protection = (long) value; case GET_DIRPROTECTION: ret = (void *) dir_protection; break; case SET_LOCKPROTECTION: lock_protection = (long) value; case GET_LOCKPROTECTION: ret = (void *) lock_protection; break; case SET_FTPPROTECTION: ftp_protection = (long) value; case GET_FTPPROTECTION: ret = (void *) ftp_protection; break; case SET_PUBLICPROTECTION: public_protection = (long) value; case GET_PUBLICPROTECTION: ret = (void *) public_protection; break; case SET_SHAREDPROTECTION: shared_protection = (long) value; case GET_SHAREDPROTECTION: ret = (void *) shared_protection; break; case SET_FTPDIRPROTECTION: ftp_dir_protection = (long) value; case GET_FTPDIRPROTECTION: ret = (void *) ftp_dir_protection; break; case SET_PUBLICDIRPROTECTION: public_dir_protection = (long) value; case GET_PUBLICDIRPROTECTION: ret = (void *) public_dir_protection; break; case SET_SHAREDDIRPROTECTION: shared_dir_protection = (long) value; case GET_SHAREDDIRPROTECTION: ret = (void *) shared_dir_protection; break; case SET_LOCKTIMEOUT: locktimeout = (long) value; case GET_LOCKTIMEOUT: ret = (void *) locktimeout; break; case SET_HIDEDOTFILES: hideDotFiles = value ? T : NIL; case GET_HIDEDOTFILES: ret = (void *) (hideDotFiles ? VOIDT : NIL); break; case SET_DISABLEPLAINTEXT: disablePlaintext = (long) value; case GET_DISABLEPLAINTEXT: ret = (void *) disablePlaintext; break; case SET_ADVERTISETHEWORLD: advertisetheworld = value ? T : NIL; case GET_ADVERTISETHEWORLD: ret = (void *) (advertisetheworld ? VOIDT : NIL); break; case SET_DISABLEAUTOSHAREDNS: noautomaticsharedns = value ? T : NIL; case GET_DISABLEAUTOSHAREDNS: ret = (void *) (noautomaticsharedns ? VOIDT : NIL); break; case SET_DISABLE822TZTEXT: no822tztext = value ? T : NIL; case GET_DISABLE822TZTEXT: ret = (void *) (no822tztext ? VOIDT : NIL); break; case SET_USERHASNOLIFE: has_no_life = value ? T : NIL; case GET_USERHASNOLIFE: ret = (void *) (has_no_life ? VOIDT : NIL); break; case SET_NETFSSTATBUG: netfsstatbug = value ? T : NIL; case GET_NETFSSTATBUG: ret = (void *) (netfsstatbug ? VOIDT : NIL); break; case SET_BLOCKNOTIFY: mailblocknotify = (blocknotify_t) value; case GET_BLOCKNOTIFY: ret = (void *) mailblocknotify; break; } return ret; } /* Write current time * Accepts: destination string * optional format of day-of-week prefix * format of date and time * flag whether to append symbolic timezone */ static void do_date (char *date,char *prefix,char *fmt,int suffix) { time_t tn = time (0); struct tm *t = gmtime (&tn); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&tn); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; if (prefix) { /* want day of week? */ sprintf (date,prefix,days[t->tm_wday]); date += strlen (date); /* make next sprintf append */ } /* output the date */ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); /* append timezone suffix if desired */ if (suffix) rfc822_timezone (date,(void *) t); } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d", no822tztext ? NIL : T); } /* Write current time in fixed-width RFC 822 format * Accepts: destination string */ void rfc822_fixed_date (char *date) { do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL); } /* Initialize server * Accepts: server name for syslog or NIL * /etc/services service name or NIL * alternate /etc/services service name or NIL * clock interrupt handler * kiss-of-death interrupt handler * hangup interrupt handler * termination interrupt handler */ void server_init (char *server,char *service,char *sslservice, void *clkint,void *kodint,void *hupint,void *trmint) { /* only do this if for init call */ if (server && service && sslservice) { long port; struct servent *sv; struct sockaddr_in sin; int i = sizeof (struct sockaddr_in); /* Don't use tcp_clienthost() since reverse DNS problems may slow down the * greeting message and cause the client to time out. */ char *client = getpeername (0,(struct sockaddr *) &sin,(void *) &i) ? "UNKNOWN" : ((sin.sin_family == AF_INET) ? inet_ntoa (sin.sin_addr) : "NON-IPv4"); /* set server name in syslog */ openlog (server,LOG_PID,LOG_MAIL); fclose (stderr); /* possibly save a process ID */ /* Use SSL if SSL service, or if server starts with "s" and not service */ if (((port = tcp_serverport ()) >= 0)) { if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port))) syslog (LOG_DEBUG,"%s service init from %s",service,client); else if ((sv = getservbyname (sslservice,"tcp")) && (port == ntohs (sv->s_port))) { syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,client); ssl_server_init (server); } else { /* not service or SSL service port */ syslog (LOG_DEBUG,"port %ld service init from %s",port,client); if (*server == 's') ssl_server_init (server); } } switch (i = umask (022)) { /* check old umask */ case 0: /* definitely unreasonable */ case 022: /* don't need to change it */ break; default: /* already was a reasonable value */ umask (i); /* so change it back */ } } signal (SIGALRM,clkint); /* prepare for clock interrupt */ signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */ signal (SIGHUP,hupint); /* prepare for hangup */ signal (SIGTERM,trmint); /* prepare for termination */ } /* Wait for stdin input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long server_input_wait (long seconds) { fd_set rfd,efd; struct timeval tmo; FD_ZERO (&rfd); FD_ZERO (&efd); FD_SET (0,&rfd); FD_SET (0,&efd); tmo.tv_sec = seconds; tmo.tv_usec = 0; return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL; } /* Return Amiga password entry for user name * Accepts: user name string * Returns: password entry * * Tries all-lowercase form of user name if given user name fails */ static struct passwd *pwuser (unsigned char *user) { unsigned char *s; struct passwd *pw = getpwnam (user); if (!pw) { /* failed, see if any uppercase characters */ for (s = user; *s && !isupper (*s); s++); if (*s) { /* yes, try all lowercase form */ pw = getpwnam (s = lcase (cpystr (user))); fs_give ((void **) &s); } } return pw; } /* Validate password for user name * Accepts: user name string * password string * argument count * argument vector * Returns: password entry if validated * * Tries password+1 if password fails and starts with space */ static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[]) { char *s; struct passwd *pw; struct passwd *ret = NIL; if (auth_md5.server) { /* using CRAM-MD5 authentication? */ if (s = auth_md5_pwd (user)) { if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1))) ret = pwuser (user); /* validated, get passwd entry for user */ memset (s,0,strlen (s)); /* erase sensitive information */ fs_give ((void **) &s); } } else if (pw = pwuser (user)) {/* can get user? */ s = cpystr (pw->pw_name); /* copy returned name in case we need it */ if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) && (*pwd == ' ') && pwd[1] && (ret = pwuser (s))) ret = checkpw (pw,pwd+1,argc,argv); fs_give ((void **) &s); /* don't need copy of name any more */ } return ret; } /* Server log in * Accepts: user name string * password string * authenticating user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[]) { struct passwd *pw = NIL; int level = LOG_NOTICE; char *err = "failed"; /* cretins still haven't given up */ if ((strlen (user) >= NETMAXUSER) || (authuser && (strlen (authuser) >= NETMAXUSER))) { level = LOG_ALERT; /* escalate this alert */ err = "SYSTEM BREAK-IN ATTEMPT"; logtry = 0; /* render this session useless */ } else if (logtry-- <= 0) err = "excessive login failures"; else if (disablePlaintext) err = "disabled"; else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv); else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user); if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T; syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err, user,(authuser && *authuser) ? authuser : user,tcp_clienthost ()); sleep (3); /* slow down possible cracker */ return NIL; } /* Authenticated server log in * Accepts: user name string * authenticating user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ long authserver_login (char *user,char *authuser,int argc,char *argv[]) { return pw_login (pwuser (user),authuser,user,NIL,argc,argv); } /* Log in as anonymous daemon * Accepts: argument count * argument vector * Returns: T if successful, NIL if error */ long anonymous_login (int argc,char *argv[]) { /* log in Mr. A. N. Onymous */ return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL, (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL), argc,argv); } /* Finish log in and environment initialization * Accepts: passwd struct for loginpw() * optional authentication user name * user name (NIL for anonymous) * home directory (NIL to use directory from passwd struct) * argument count * argument vector * Returns: T if successful, NIL if error */ long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc, char *argv[]) { struct group *gr; char **t; long ret = NIL; if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */ /* make safe copies of user and home */ if (user) user = cpystr (pw->pw_name); home = cpystr (home ? home : pw->pw_dir); /* authorization ID .NE. authentication ID? */ if (user && auser && *auser && compare_cstring (auser,user)) { /* scan list of mail administrators */ if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret) if (!compare_cstring (auser,*t++)) ret = pw_login (pw,NIL,user,home,argc,argv); syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s", ret ? "Admin" : "Failed",auser,user,tcp_clienthost ()); } /* normal login */ else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) && (ret = env_init (user,home))) chdir (myhomedir ()); fs_give ((void **) &home); /* clean up */ if (user) fs_give ((void **) &user); } return ret; /* return status */ } /* Initialize environment * Accepts: user name (NIL for anonymous) * home directory name * Returns: T, always */ long env_init (char *user,char *home) { extern MAILSTREAM CREATEPROTO; extern MAILSTREAM EMPTYPROTO; struct passwd *pw; struct stat sbuf; char tmp[MAILTMPLEN]; if (myUserName) fatal ("env_init called twice!"); /* set up user name */ myUserName = cpystr (user ? user : ANONYMOUSUSER); if (user) { /* remember user name and home directory */ nslist[0] = &nshome; /* home namespace */ nslist[1] = &nsamigaother; nslist[2] = advertisetheworld ? &nsworld : &nsshared; } else { /* anonymous user */ nslist[0] = nslist[1] = NIL,nslist[2] = &nsftp; sprintf (tmp,"%s/INBOX", home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL)); sysInbox = cpystr (tmp); /* make system INBOX */ anonymous = T; /* flag as anonymous */ } myHomeDir = cpystr (home); /* set home directory */ if (!noautomaticsharedns) { /* #ftp namespace */ if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir); /* #public namespace */ if (!publicHome && (pw = getpwnam ("imappublic"))) publicHome = cpystr (pw->pw_dir); /* #shared namespace */ if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared"))) sharedHome = cpystr (pw->pw_dir); } if (!myLocalHost) mylocalhost (); if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc")); if (!newsActive) newsActive = cpystr (ACTIVEFILE); if (!newsSpool) newsSpool = cpystr (NEWSSPOOL); /* force default prototype to be set */ if (!createProto) createProto = &CREATEPROTO; if (!appendProto) appendProto = &EMPTYPROTO; /* re-do open action to get flags */ (*createProto->dtb->open) (NIL); endpwent (); /* close pw database */ return T; } /* Return my user name * Accepts: pointer to optional flags * Returns: my user name */ char *myusername_full (unsigned long *flags) { char *ret = UNLOGGEDUSER; if (!myUserName) { /* get user name if don't have it yet */ struct passwd *pw; struct stat sbuf; unsigned long euid = geteuid (); char *s = (char *) (euid ? getlogin () : NIL); /* look up getlogin() user name or EUID */ if (!((s && *s && (strlen (s) < NETMAXUSER) && (pw = getpwnam (s)) && (pw->pw_uid == euid)) || (pw = getpwuid (euid)))) fatal ("Unable to look up user name"); /* init environment if not root */ if (euid) env_init (pw->pw_name,((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ? s : pw->pw_dir); else ret = pw->pw_name; /* in case UID 0 user is other than root */ } if (myUserName) { /* logged in? */ if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN; ret = myUserName; /* return user name */ } else if (flags) *flags = MU_NOTLOGGEDIN; return ret; } /* Return my local host name * Returns: my local host name */ char *mylocalhost () { char tmp[MAILTMPLEN]; struct hostent *host_name; if (!myLocalHost) { gethostname(tmp,MAILTMPLEN);/* get local host name */ myLocalHost = cpystr ((host_name = gethostbyname (tmp)) ? host_name->h_name : tmp); } return myLocalHost; } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { if (!myHomeDir) myusername ();/* initialize if first time */ return myHomeDir ? myHomeDir : ""; } /* Return my home mailbox name * Returns: my home directory name */ static char *mymailboxdir () { char *home = myhomedir (); if (!myMailboxDir && home) { /* initialize if first time */ if (mailsubdir) { char tmp[MAILTMPLEN]; sprintf (tmp,"%s/%s",home,mailsubdir); myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */ } else myMailboxDir = cpystr (home); } return myMailboxDir ? myMailboxDir : ""; } /* Return system standard INBOX * Accepts: buffer string */ char *sysinbox () { char tmp[MAILTMPLEN]; if (!sysInbox) { /* initialize if first time */ sprintf (tmp,"%s/%s",MAILSPOOL,myusername ()); sysInbox = cpystr (tmp); /* system inbox is from mail spool */ } return sysInbox; } /* Return mailbox directory name * Accepts: destination buffer * directory prefix * name in directory * Returns: file name or NIL if error */ char *mailboxdir (char *dst,char *dir,char *name) { char tmp[MAILTMPLEN]; if (dir || name) { /* if either argument provided */ if (dir) { if (strlen (dir) > NETMAXMBX) return NIL; strcpy (tmp,dir); /* write directory prefix */ } else tmp[0] = '\0'; /* otherwise null string */ if (name) { if (strlen (name) > NETMAXMBX) return NIL; strcat (tmp,name); /* write name in directory */ } /* validate name, return its name */ if (!mailboxfile (dst,tmp)) return NIL; } /* no arguments, wants mailbox directory */ else strcpy (dst,mymailboxdir ()); return dst; /* return the name */ } /* Return mailbox file name * Accepts: destination buffer * mailbox name * Returns: file name or empty string for driver-selected INBOX or NIL if error */ char *mailboxfile (char *dst,char *name) { struct passwd *pw; char *s; if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) || ((anonymous || restrictBox || (*name == '#')) && (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~")))) dst = NIL; /* invalid name */ else switch (*name) { /* determine mailbox type based upon name */ case '#': /* namespace name */ /* #ftp/ namespace */ if (((name[1] == 'f') || (name[1] == 'F')) && ((name[2] == 't') || (name[2] == 'T')) && ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5); /* #public/ and #shared/ namespaces */ else if ((((name[1] == 'p') || (name[1] == 'P')) && ((name[2] == 'u') || (name[2] == 'U')) && ((name[3] == 'b') || (name[3] == 'B')) && ((name[4] == 'l') || (name[4] == 'L')) && ((name[5] == 'i') || (name[5] == 'I')) && ((name[6] == 'c') || (name[6] == 'C')) && (name[7] == '/') && (s = publicHome)) || (!anonymous && ((name[1] == 's') || (name[1] == 'S')) && ((name[2] == 'h') || (name[2] == 'H')) && ((name[3] == 'a') || (name[3] == 'A')) && ((name[4] == 'r') || (name[4] == 'R')) && ((name[5] == 'e') || (name[5] == 'E')) && ((name[6] == 'd') || (name[6] == 'D')) && (name[7] == '/') && (s = sharedHome))) sprintf (dst,"%s/%s",s,compare_cstring (name,"INBOX") ? name : "INBOX"); else dst = NIL; /* unknown namespace */ break; case '/': /* root access */ if (anonymous) dst = NIL; /* anonymous forbidden to do this */ else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ())) dst = NIL; /* restricted and not access to sysinbox */ else strcpy (dst,name); /* unrestricted, copy root name */ break; case '~': /* other user access */ /* bad syntax or anonymous can't win */ if (!*++name || anonymous) dst = NIL; /* ~/ equivalent to ordinary name */ else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1); /* other user forbidden if restricted */ else if (restrictBox & RESTRICTOTHERUSER) dst = NIL; else { /* clear box other user */ /* copy user name */ for (s = dst; *name && (*name != '/'); *s++ = *name++); *s++ = '\0'; /* tie off user name, look up in passwd file */ if ((pw = getpwnam (dst)) && pw->pw_dir) { if (*name) name++; /* skip past the slash */ /* canonicalize case of INBOX */ if (!compare_cstring (name,"INBOX")) name = "INBOX"; /* remove trailing / from directory */ if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0'; /* don't allow ~root/ if restricted root */ if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL; /* build final name w/ subdir if needed */ else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name); else sprintf (dst,"%s/%s",pw->pw_dir,name); } else dst = NIL; /* no such user */ } break; case 'I': case 'i': /* possible INBOX */ if (!compare_cstring (name+1,"NBOX")) { /* if anonymous, use INBOX in mailbox dir */ if (anonymous) sprintf (dst,"%s/INBOX",mymailboxdir ()); else *dst = '\0'; /* otherwise driver selects the name */ break; } /* drop into to ordinary name case */ default: /* ordinary name is easy */ sprintf (dst,"%s/%s",mymailboxdir (),name); break; } return dst; /* return final name */ } /* Dot-lock file locker * Accepts: file name to lock * destination buffer for lock file name * open file description on file name to lock * Returns: T if success, NIL if failure */ long dotlock_lock (char *file,DOTLOCK *base,int fd) { int i = locktimeout * 60; int j,mask,retry,pi[2],po[2]; char *s,tmp[MAILTMPLEN]; struct stat sb; /* flush absurd file name */ if (strlen (file) > 512) return NIL; /* build lock filename */ sprintf (base->lock,"%s.lock",file); /* assume no pipe */ base->pipei = base->pipeo = -1; do { /* make sure not symlink */ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL; /* time out if file older than 5 minutes */ if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0; /* try to create the lock */ if ((j = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) lock_protection)) >= 0) { close (i); /* make the file, now close it */ chmod (base->lock,(int) lock_protection); return LONGT; } if (errno == EEXIST) { /* already locked? */ retry = -1; /* can try again */ if (!(i%15)) { /* time to notify? */ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...", file,i); mm_log (tmp,WARN); } sleep (1); /* wait 1 second before next try */ } else retry = i = 0; /* hard failure, no more retries */ } while (i--); /* until out of retries */ if (retry < 0) { /* still returning retry after locktimeout? */ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL; if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) { sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock", (long) (time (0) - sb.st_ctime)); mm_log (tmp,WARN); } mask = umask (0); unlink (base->lock); /* try to remove the old file */ /* seize the lock */ if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) lock_protection)) >= 0) { close (i); /* don't need descriptor any more */ sprintf (tmp,"Mailbox %.80s lock overridden",file); mm_log (tmp,NIL); chmod (base->lock,(int) lock_protection); umask (mask) /* restore old umask */ return LONGT; } umask (mask) /* restore old umask */ } if (fd >= 0) switch (errno) { case EACCES: /* protection failure? */ /* make command pipes */ if (!stat (LOCKPGM,&sb) && (pipe (pi) >= 0)) { if (pipe (po) >= 0) { if (!(j = fork ())) { /* make inferior process */ if (!fork ()) { /* make grandchild so it's inherited by init */ char *argv[4]; /* prepare argument vector */ sprintf (tmp,"%d",fd); argv[0] = LOCKPGM; argv[1] = tmp; argv[2] = file; argv[3] = NIL; /* set parent's I/O to my O/I */ dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0); /* close all unnecessary descriptors */ for (j = max (20,max (max (pi[0],pi[1]),max(po[0],po[1]))); j >= 3; --j) if (j != fd) close (j); /* be our own process group */ setpgrp (0,getpid ()); /* now run it */ execv (argv[0],argv); } _exit (1); /* child is done */ } else if (j > 0) { /* reap child; grandchild now owned by init */ grim_pid_reap (j,NIL); /* read response from locking program */ if ((read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) { /* success, record pipes */ base->pipei = pi[0]; base->pipeo = po[1]; /* close child's side of the pipes */ close (pi[1]); close (po[0]); return LONGT; } } close (po[0]); close (po[1]); } close (pi[0]); close (pi[1]); } /* find directory/file delimiter */ if (s = strrchr (base->lock,'/')) { *s = '\0'; /* tie off at directory */ sprintf(tmp, /* generate default message */ "Mailbox vulnerable - directory %.80s must have 1777 protection", base->lock); /* definitely not 1777 if can't stat */ mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777); *s = '/'; /* restore lock name */ if (mask != 1777) { /* default warning if not 1777 */ MM_LOG (tmp,WARN); break; } } default: sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s", base->lock,strerror (errno)); mm_log (tmp,WARN); /* this is probably not good */ break; } base->lock[0] = '\0'; /* don't use lock files */ return NIL; } /* Dot-lock file unlocker * Accepts: lock file name * Returns: T if success, NIL if failure */ long dotlock_unlock (DOTLOCK *base) { long ret = LONGT; if (base && base->lock[0]) { if (base->pipei >= 0) { /* if running through a pipe unlocker */ ret = (write (base->pipeo,"+",1) == 1); /* nuke the pipes */ close (base->pipei); close (base->pipeo); } else ret = !unlink (base->lock); } return ret; } /* Lock file name * Accepts: scratch buffer * file name * type of locking operation (LOCK_SH or LOCK_EX) * pointer to return PID of locker * Returns: file descriptor of lock or negative if error */ int lockname (char *lock,char *fname,int op,long *pid) { struct stat sbuf; *pid = 0; /* no locker PID */ return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid); } /* Lock file descriptor * Accepts: file descriptor * lock file name buffer * type of locking operation (LOCK_SH or LOCK_EX) * Returns: file descriptor of lock or negative if error */ int lockfd (int fd,char *lock,int op) { struct stat sbuf; return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL); } /* Lock file name worker * Accepts: lock file name * pointer to stat() buffer * type of locking operation (LOCK_SH or LOCK_EX) * pointer to return PID of locker * Returns: file descriptor of lock or negative if error */ int lock_work (char *lock,void *sb,int op,long *pid) { struct stat lsb,fsb; struct stat *sbuf = (struct stat *) sb; char tmp[MAILTMPLEN]; long i; int fd; int mask = umask (0); if (pid) *pid = 0; /* initialize return PID */ /* make temporary lock file name */ sprintf (lock,"%s/.%lx.%lx","/tmp", (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino); while (T) { /* until get a good lock */ do switch ((int) chk_notsymlink (lock,&lsb)) { case 1: /* exists just once */ if (((fd = open (lock,O_RDWR,lock_protection)) >= 0) || (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break; case -1: /* name doesn't exist */ fd = open (lock,O_RDWR|O_CREAT|O_EXCL,lock_protection); break; default: /* multiple hard links */ mm_log ("hard link to lock name",ERROR); syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock); case 0: /* symlink (already did syslog) */ umask (mask); /* restore old mask */ return -1; /* fail: no lock file */ } while ((fd < 0) && (errno == EEXIST)); if (fd < 0) { /* failed to get file descriptor */ syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock, strerror (errno)); if (stat ("/tmp",&lsb)) syslog (LOG_CRIT,"SYSTEM ERROR: no /tmp: %s",strerror (errno)); else if ((lsb.st_mode & 01777) != 01777) mm_log ("Can't lock for write: /tmp must have 1777 protection",WARN); umask (mask); /* restore old mask */ return -1; /* fail: can't open lock file */ } /* non-blocking form */ if (op & LOCK_NB) i = flock (fd,op); else { /* blocking form */ (*mailblocknotify) (BLOCK_FILELOCK,NIL); i = flock (fd,op); (*mailblocknotify) (BLOCK_NONE,NIL); } if (i) { /* failed, get other process' PID */ if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) && (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0)) *pid = i; close (fd); /* failed, give up on lock */ umask (mask); /* restore old mask */ return -1; /* fail: can't lock */ } /* make sure this lock is good for us */ if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) && !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) && (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break; close (fd); /* lock not right, drop fd and try again */ } /* make sure mode OK (don't use fchmod()) */ chmod (lock,(int) lock_protection); umask (mask); /* restore old mask */ return fd; /* success */ } /* Check to make sure not a symlink * Accepts: file name * stat buffer * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links */ long chk_notsymlink (char *name,void *sb) { struct stat *sbuf = (struct stat *) sb; /* name exists? */ if (lstat (name,sbuf)) return -1; /* forbid symbolic link */ if ((sbuf->st_mode & S_IFMT) == S_IFLNK) { mm_log ("symbolic link on lock name",ERROR); syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s", name); return NIL; } return (long) sbuf->st_nlink; /* return number of hard links */ } /* Unlock file descriptor * Accepts: file descriptor * lock file name from lockfd() */ void unlockfd (int fd,char *lock) { /* delete the file if no sharers */ if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock); flock (fd,LOCK_UN); /* unlock it */ close (fd); /* close it */ } /* Set proper file protection for mailbox * Accepts: mailbox name * actual file path name * Returns: T, always */ long set_mbx_protections (char *mailbox,char *path) { struct stat sbuf; int mode = (int) mbx_protection; if (*mailbox == '#') { /* possible namespace? */ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) && ((mailbox[2] == 't') || (mailbox[2] == 'T')) && ((mailbox[3] == 'p') || (mailbox[3] == 'P')) && (mailbox[4] == '/')) mode = (int) ftp_protection; else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) && ((mailbox[2] == 'u') || (mailbox[2] == 'U')) && ((mailbox[3] == 'b') || (mailbox[3] == 'B')) && ((mailbox[4] == 'l') || (mailbox[4] == 'L')) && ((mailbox[5] == 'i') || (mailbox[5] == 'I')) && ((mailbox[6] == 'c') || (mailbox[6] == 'C')) && (mailbox[7] == '/')) mode = (int) public_protection; else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) && ((mailbox[2] == 'h') || (mailbox[2] == 'H')) && ((mailbox[3] == 'a') || (mailbox[3] == 'A')) && ((mailbox[4] == 'r') || (mailbox[4] == 'R')) && ((mailbox[5] == 'e') || (mailbox[5] == 'E')) && ((mailbox[6] == 'd') || (mailbox[6] == 'D')) && (mailbox[7] == '/')) mode = (int) shared_protection; } /* if a directory */ if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) { /* set owner search if allow read or write */ if (mode & 0600) mode |= 0100; if (mode & 060) mode |= 010;/* set group search if allow read or write */ if (mode & 06) mode |= 01; /* set world search if allow read or write */ /* preserve directory SGID bit */ if (sbuf.st_mode & S_ISGID) mode |= S_ISGID; } chmod (path,mode); /* set the new protection, ignore failure */ return LONGT; } /* Get proper directory protection * Accepts: mailbox name * Returns: directory mode, always */ long get_dir_protection (char *mailbox) { if (*mailbox == '#') { /* possible namespace? */ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) && ((mailbox[2] == 't') || (mailbox[2] == 'T')) && ((mailbox[3] == 'p') || (mailbox[3] == 'P')) && (mailbox[4] == '/')) return ftp_dir_protection; else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) && ((mailbox[2] == 'u') || (mailbox[2] == 'U')) && ((mailbox[3] == 'b') || (mailbox[3] == 'B')) && ((mailbox[4] == 'l') || (mailbox[4] == 'L')) && ((mailbox[5] == 'i') || (mailbox[5] == 'I')) && ((mailbox[6] == 'c') || (mailbox[6] == 'C')) && (mailbox[7] == '/')) return public_dir_protection; else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) && ((mailbox[2] == 'h') || (mailbox[2] == 'H')) && ((mailbox[3] == 'a') || (mailbox[3] == 'A')) && ((mailbox[4] == 'r') || (mailbox[4] == 'R')) && ((mailbox[5] == 'e') || (mailbox[5] == 'E')) && ((mailbox[6] == 'd') || (mailbox[6] == 'D')) && (mailbox[7] == '/')) return shared_dir_protection; } return dir_protection; } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { myusername (); /* make sure initialized */ /* return default driver's prototype */ return type ? appendProto : createProto; } /* Set up user flags for stream * Accepts: MAIL stream * Returns: MAIL stream with user flags set up */ MAILSTREAM *user_flags (MAILSTREAM *stream) { int i; myusername (); /* make sure initialized */ for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i) if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]); return stream; } /* Return nth user flag * Accepts: user flag number * Returns: flag */ char *default_user_flag (unsigned long i) { myusername (); /* make sure initialized */ return userFlags[i]; } /* Default block notify routine * Accepts: reason for calling * data * Returns: data */ void *mm_blocknotify (int reason,void *data) { void *ret = data; switch (reason) { case BLOCK_SENSITIVE: /* entering sensitive code */ ret = (void *) alarm (0); break; case BLOCK_NONSENSITIVE: /* exiting sensitive code */ if ((unsigned int) data) alarm ((unsigned int) data); break; default: /* ignore all other reasons */ break; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/env_ami.h000066400000000000000000000056731137544547100235130ustar00rootroot00000000000000/* * Program: UNIX environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 22 February 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ typedef struct dotlock_base { char lock[MAILTMPLEN]; int pipei; int pipeo; } DOTLOCK; /* Bits that can be set in restrictBox */ #define RESTRICTROOT 0x1 /* restricted box doesn't allow root */ #define RESTRICTOTHERUSER 0x2 /* restricted box doesn't allow other user */ /* Subscription definitions for UNIX */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s/.mailboxlist",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/.mlbxlsttmp",myhomedir ()) /* dorc() options */ #define RISKPHRASE "I accept the risk" #define SYSCONFIG "/etc/c-client.cf" /* Special users */ #define ANONYMOUSUSER "nobody" /* anonymous user */ #define UNLOGGEDUSER "root" /* unlogged-in user */ #define ADMINGROUP "mailadm" /* mail administrator group */ /* * Attention: all sorcerer's apprentices who think that 0666 is a mistake. * You are wrong. Read the FAQ. Do not meddle in the affairs of wizards, * for they are subtle and quick to anger. */ #define MANDATORYLOCKPROT 0666 /* don't change this */ /* Function prototypes */ #include "env.h" void rfc822_fixed_date (char *date); long env_init (char *user,char *home); char *myusername_full (unsigned long *flags); #define MU_LOGGEDIN 0 #define MU_NOTLOGGEDIN 1 #define MU_ANONYMOUS 2 #define myusername() \ myusername_full (NIL) char *sysinbox (); char *mailboxdir (char *dst,char *dir,char *name); long dotlock_lock (char *file,DOTLOCK *base,int fd); long dotlock_unlock (DOTLOCK *base); int lockname (char *lock,char *fname,int op,long *pid); int lockfd (int fd,char *lock,int op); int lock_work (char *lock,void *sbuf,int op,long *pid); long chk_notsymlink (char *name,void *sbuf); void unlockfd (int fd,char *lock); long set_mbx_protections (char *mailbox,char *path); long get_dir_protection (char *mailbox); MAILSTREAM *user_flags (MAILSTREAM *stream); char *default_user_flag (unsigned long i); void dorc (char *file,long flag); long path_create (MAILSTREAM *stream,char *mailbox); void grim_pid_reap_status (int pid,int killreq,void *status); #define grim_pid_reap(pid,killreq) \ grim_pid_reap_status (pid,killreq,NIL) long safe_write (int fd,char *buf,long nbytes); void *arm_signal (int sig,void *action); struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]); long loginpw (struct passwd *pw,int argc,char *argv[]); long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc, char *argv[]); void *mm_blocknotify (int reason,void *data); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/fdstring.c000066400000000000000000000050171137544547100237000ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "mail.h" #include "osdep.h" #include "misc.h" #include "fdstring.h" /* String driver for fd stringstructs */ static void fd_string_init (STRING *s,void *data,unsigned long size); static char fd_string_next (STRING *s); static void fd_string_setpos (STRING *s,unsigned long i); STRINGDRIVER fd_string = { fd_string_init, /* initialize string structure */ fd_string_next, /* get next byte in string structure */ fd_string_setpos /* set position in string structure */ }; /* Initialize string structure for fd stringstruct * Accepts: string structure * pointer to string * size of string */ static void fd_string_init (STRING *s,void *data,unsigned long size) { FDDATA *d = (FDDATA *) data; s->data = (void *) d->fd; /* note fd */ s->data1 = d->pos; /* note file offset */ s->size = size; /* note size */ s->curpos = s->chunk = d->chunk; s->chunksize = (unsigned long) d->chunksize; s->offset = 0; /* initial position */ /* and size of data */ s->cursize = min (s->chunksize,size); /* move to that position in the file */ lseek (d->fd,d->pos,L_SET); read (d->fd,s->chunk,(size_t) s->cursize); } /* Get next character from fd stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ static char fd_string_next (STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for fd stringstruct * Accepts: string structure * new position */ static void fd_string_setpos (STRING *s,unsigned long i) { if (i > s->size) i = s->size; /* don't permit setting beyond EOF */ s->offset = i; /* set new offset */ s->curpos = s->chunk; /* reset position */ /* set size of data */ if (s->cursize = min (s->chunksize,SIZE (s))) { /* move to that position in the file */ lseek ((int) s->data,s->data1 + s->offset,L_SET); read ((int) s->data,s->curpos,(size_t) s->cursize); } } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/fdstring.h000066400000000000000000000015131137544547100237020ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Driver-dependent data passed to init method */ typedef struct fd_data { int fd; /* file descriptor */ unsigned long pos; /* initial position */ char *chunk; /* I/O buffer chunk */ unsigned long chunksize; /* I/O buffer chunk length */ } FDDATA; extern STRINGDRIVER fd_string; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/fs_ami.c000066400000000000000000000031711137544547100233150ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); (*bn) (BLOCK_NONSENSITIVE,data); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); (*bn) (BLOCK_NONSENSITIVE,data); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); free (*block); *block = NIL; (*bn) (BLOCK_NONSENSITIVE,data); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/ftl_ami.c000066400000000000000000000013271137544547100234730ustar00rootroot00000000000000/* * Program: DOS/VMS/TOPS-20 crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/gethstid.c000066400000000000000000000013531137544547100236720ustar00rootroot00000000000000/* * Program: Get host ID emulator * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for BSD gethostid() call * Returns: unique identifier for this machine */ long gethostid (void) { /* No gethostid() here, so just fake it and hope things turn out okay. */ return 0xdeadface; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/gr_waitp.c000066400000000000000000000015251137544547100236740ustar00rootroot00000000000000/* * Program: UNIX Grim PID Reaper -- waitpid() version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 November 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Grim PID reaper * Accepts: process ID * kill request flag * status return value */ void grim_pid_reap_status (int pid,int killreq,void *status) { if (killreq) kill(pid,SIGHUP);/* kill if not already dead */ while ((waitpid (pid,status,NIL) < 0) && (errno != ECHILD)); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/log_std.c000066400000000000000000000016321137544547100235120ustar00rootroot00000000000000/* * Program: Standard login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { uid_t uid = pw->pw_uid; char *name = cpystr (pw->pw_name); long ret = !(setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) || setuid (uid)); fs_give ((void **) &name); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mbx.c000066400000000000000000001646131137544547100226560ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "mbx.h" #include "misc.h" #include "dummy.h" /* Kludge to make Cygwin happy */ #ifndef O_BINARY #define O_BINARY 0 #endif /* MBX I/O stream local data */ typedef struct mbx_local { unsigned int flagcheck: 1; /* if ping should sweep for flags */ unsigned int expok: 1; /* if expunging OK in ping */ unsigned int expunged : 1; /* if one or more expunged messages */ int fd; /* file descriptor for I/O */ int ld; /* lock file descriptor */ int ffuserflag; /* first free user flag */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned long lastpid; /* PID of last writer */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ char lock[MAILTMPLEN]; /* buffer to write lock name */ } MBXLOCAL; /* Convenient access to local data */ #define LOCAL ((MBXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mbx_valid (char *name); int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp); void *mbx_parameters (long function,void *value); void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mbx_list (MAILSTREAM *stream,char *ref,char *pat); void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mbx_create (MAILSTREAM *stream,char *mailbox); long mbx_delete (MAILSTREAM *stream,char *mailbox); long mbx_rename (MAILSTREAM *stream,char *old,char *newname); long mbx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mbx_open (MAILSTREAM *stream); void mbx_close (MAILSTREAM *stream,long options); void mbx_abort (MAILSTREAM *stream); void mbx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mbx_ping (MAILSTREAM *stream); void mbx_check (MAILSTREAM *stream); void mbx_expunge (MAILSTREAM *stream); void mbx_snarf (MAILSTREAM *stream); long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); char *mbx_file (char *dst,char *name); long mbx_parse (MAILSTREAM *stream); MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok); unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mbx_update_header (MAILSTREAM *stream); void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags); unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr); unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags); long mbx_flaglock (MAILSTREAM *stream); /* MBX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mbxdriver = { "mbx", /* driver name */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING, /* driver flags */ (DRIVER *) NIL, /* next driver */ mbx_valid, /* mailbox is valid for us */ mbx_parameters, /* manipulate parameters */ mbx_scan, /* scan mailboxes */ mbx_list, /* list mailboxes */ mbx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mbx_create, /* create mailbox */ mbx_delete, /* delete mailbox */ mbx_rename, /* rename mailbox */ mbx_status, /* status of mailbox */ mbx_open, /* open mailbox */ mbx_close, /* close mailbox */ mbx_flags, /* fetch message "fast" attributes */ mbx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mbx_header, /* fetch message header */ mbx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mbx_flag, /* modify flags */ mbx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mbx_ping, /* ping mailbox to see if still alive */ mbx_check, /* check for new messages */ mbx_expunge, /* expunge deleted messages */ mbx_copy, /* copy messages to another mailbox */ mbx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mbxproto = {&mbxdriver}; /* MBX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mbx_valid (char *name) { char tmp[MAILTMPLEN]; return mbx_isvalid (NIL,name,tmp) ? &mbxdriver : NIL; } /* MBX mail test for valid mailbox * Accepts: returned stream with valid mailbox keywords * mailbox name * scratch buffer * Returns: T if valid, NIL otherwise */ int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp) { int fd; int ret = NIL; unsigned long i; unsigned char *s,*t,hdr[HDRSIZE]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) && ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) >= 0)) { errno = -1; /* bogus format */ /* I love cretinous C compilers -- don't you? */ if (read (fd,hdr,HDRSIZE) == HDRSIZE) if ((hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8])) if (isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (hdr[15]) && isxdigit (hdr[16])) if (isxdigit (hdr[17]) && isxdigit (hdr[18]) && isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) && isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) ret = T; if (stream) { /* stream specified? */ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0, sizeof (MAILSTREAM)); for (i = 0, s = hdr + 25; /* parse user flags */ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (strlen (s) <= MAXUSERFLAG) (*stream)->user_flags[i] = cpystr (s); } } close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (tmp,tp); /* set the times */ } } /* in case INBOX but not mbx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MBX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mbx_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = mbx_file ((char *) value,"INBOX"); break; case SET_ONETIMEEXPUNGEATPING: if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T; case GET_ONETIMEEXPUNGEATPING: if (value) ret = (void *) (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL); break; } return ret; } /* MBX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MBX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MBX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MBX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mbx_create (MAILSTREAM *stream,char *mailbox) { char *s,*t,mbx[MAILTMPLEN],tmp[HDRSIZE]; long ret = NIL; int i,fd; if (!(s = mbx_file (mbx,mailbox))) { sprintf (mbx,"Can't create %.80s: invalid name",mailbox); MM_LOG (mbx,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) { /* done if made directory */ if ((s = strrchr (s,'/')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY|O_BINARY, (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else { memset (tmp,'\0',HDRSIZE);/* initialize header */ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012", (unsigned long) time (0)); for (i = 0; i < NUSERFLAGS; ++i) { t = (stream && stream->user_flags[i]) ? stream->user_flags[i] : ((t = default_user_flag (i)) ? t : ""); sprintf (s += strlen (s),"%s\015\012",t); } if (write (fd,tmp,HDRSIZE) != HDRSIZE) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s", mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ close (fd); /* close file */ } } /* set proper protections */ return ret ? set_mbx_protections (mailbox,mbx) : NIL; } /* MBX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mbx_delete (MAILSTREAM *stream,char *mailbox) { return mbx_rename (stream,mailbox,NIL); } /* MBX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mbx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!mbx_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); MM_LOG (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); MM_LOG (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if (s = strrchr (tmp,'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* superior name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) ret = NIL; else *s = c; /* restore full name */ } /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } } else if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } flock (fd,LOCK_UN); /* release lock on the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX"); return ret; /* return success */ } /* MBX Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long mbx_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; /* calculate post-snarf results */ if (!status.recent && stream->inbox && (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* MBX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mbx_open (MAILSTREAM *stream) { int fd,ld; short silent; char tmp[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mbxproto); if (stream->local) fatal ("mbx recycle stream"); /* canonicalize the mailbox name */ if (!mbx_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->ld = -1; /* no flaglock */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get parse/append permission */ if ((ld = lockfd (LOCAL->fd,tmp,LOCK_EX)) < 0) { MM_LOG ("Unable to lock open mailbox",ERROR); return NIL; } (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* lock the file */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */ /* time not set up yet */ LOCAL->lastsnarf = LOCAL->filetime = 0; LOCAL->expok = LOCAL->flagcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; silent = stream->silent; /* defer events */ stream->silent = T; if (mbx_ping (stream) && !stream->nmsgs) MM_LOG ("Mailbox is empty",(long) NIL); stream->silent = silent; /* now notify upper level */ mail_exists (stream,stream->nmsgs); mail_recent (stream,stream->recent); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T; /* can we create new user flags? */ return stream; /* return stream to caller */ } /* MBX mail close * Accepts: MAIL stream * close options */ void mbx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ /* do an expunge if requested */ if (options & CL_EXPUNGE) mbx_expunge (stream); else { /* otherwise do a checkpoint to purge */ LOCAL->expok = T; /* possible expunged messages */ mbx_ping (stream); } stream->silent = silent; /* restore previous status */ mbx_abort (stream); } } /* MBX mail abort stream * Accepts: MAIL stream */ void mbx_abort (MAILSTREAM *stream) { if (stream && LOCAL) { /* only if a file is open */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MBX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mbx_flags (MAILSTREAM *stream,char *sequence,long flags) { MESSAGECACHE *elt; unsigned long i; if (mbx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence && !elt->valid) mbx_elt (stream,i,NIL); } /* MBX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i; char *s; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get header position, possibly header */ i = mbx_hdrpos (stream,msgno,length,&s); if (!s) { /* mbx_hdrpos() returned header? */ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */ /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->buf,*length); } s[*length] = '\0'; /* tie off string */ return s; } /* MBX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; char *s = LOCAL->text.data; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = mbx_elt (stream,msgno,NIL); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mbx_update_status (stream,msgno,NIL); MM_FLAGS (stream,msgno); /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } if (!LOCAL) i = 0; /* mbx_flaglock() could have aborted */ /* in case previous text cached */ if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mbx_hdrpos (stream,msgno,&j,NIL); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* MBX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { time_t tp[2]; struct stat sbuf; unsigned long oldpid = LOCAL->lastpid; /* make sure the update takes */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) { fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; /* we are the last flag updater */ LOCAL->lastpid = (unsigned long) getpid (); /* update header if needed */ if (((LOCAL->ffuserflag < NUSERFLAGS) && stream->user_flags[LOCAL->ffuserflag]) || (oldpid != LOCAL->lastpid)) mbx_update_header (stream); tp[0] = time (0); /* make sure read comes after all that */ utime (stream->mailbox,tp); unlockfd (LOCAL->ld,LOCAL->lock); LOCAL->ld = -1; } } /* MBX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL); } /* MBX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mbx_ping (MAILSTREAM *stream) { unsigned long i,pos; long ret = NIL; int ld; char lock[MAILTMPLEN]; MESSAGECACHE *elt; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ int snarf = stream->inbox && !stream->rdonly; ret = LONGT; /* assume OK */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ /* allow expunge if permitted at ping */ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T; /* if external modification */ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; /* upgrade to flag checking */ /* new mail or flagcheck handling needed? */ if (((i = (sbuf.st_size - LOCAL->filesize)) || LOCAL->flagcheck || !stream->nmsgs || snarf) && ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) { if (LOCAL->flagcheck) { /* sweep mailbox for changed message status */ if (ret = mbx_parse (stream)) { LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ) if (mbx_elt (stream,i,LOCAL->expok)) ++i; LOCAL->flagcheck =NIL;/* got all the updates */ } } else if (i) ret = mbx_parse (stream); if (ret && snarf) { /* snarf new messages if still OK */ mbx_snarf (stream); /* parse snarfed messages */ ret = mbx_parse (stream); } unlockfd (ld,lock); /* release shared parse/append permission */ } if (ret) { /* must still be alive */ if (!LOCAL->expunged) /* look for holes if none known yet */ for (i = 1, pos = HDRSIZE; !LOCAL->expunged && (i <= stream->nmsgs); i++, pos += elt->private.special.text.size + elt->rfc822_size) if ((elt = mail_elt (stream,i))->private.special.offset != pos) LOCAL->expunged = T;/* found a hole */ /* burp any holes */ if (LOCAL->expunged && !stream->rdonly) { if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check"); if (i) { /* any space reclaimed? */ LOCAL->expunged = NIL;/* no more pending expunge */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i); MM_LOG (LOCAL->buf,(long) NIL); } } LOCAL->expok = NIL; /* no more expok */ } } return ret; /* return result of the parse */ } /* MBX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mbx_check (MAILSTREAM *stream) { if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */ if (mbx_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* MBX mail expunge mailbox * Accepts: MAIL stream */ void mbx_expunge (MAILSTREAM *stream) { unsigned long nexp,reclaimed; if (!mbx_ping (stream)); /* do nothing if stream dead */ else if (stream->rdonly) /* won't do on readonly files! */ MM_LOG ("Expunge ignored on readonly mailbox",WARN); /* if expunged any messages */ else if (nexp = mbx_rewrite (stream,&reclaimed,T)) { sprintf (LOCAL->buf,"Expunged %lu messages",nexp); MM_LOG (LOCAL->buf,(long) NIL); } else if (reclaimed) { /* or if any prior expunged space reclaimed */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed); MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); } /* MBX mail snarf messages from system inbox * Accepts: MAIL stream, already locked */ void mbx_snarf (MAILSTREAM *stream) { unsigned long i = 0; unsigned long j,r,hdrlen,txtlen; struct stat sbuf; char *hdr,*txt,tmp[MAILTMPLEN]; MESSAGECACHE *elt; MAILSTREAM *sysibx = NIL; /* give up if can't get exclusive permission */ if ((time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && strcmp (sysinbox (),stream->mailbox)) { MM_CRITICAL (stream); /* go critical */ /* sizes match and anything in sysinbox? */ if (!stat (sysinbox (),&sbuf) && sbuf.st_size && !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { /* yes, go to end of file in our mailbox */ lseek (LOCAL->fd,sbuf.st_size,L_SET); /* for each message in sysibx mailbox */ while (r && (++i <= sysibx->nmsgs)) { /* snarf message from system INBOX */ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL)); txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK); /* if have a message */ if (j = hdrlen + txtlen) { /* build header line */ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i)); sprintf (LOCAL->buf + strlen (LOCAL->buf), ",%lu;00000000%04x-00000000\015\012",j,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* copy message */ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) || (write (LOCAL->fd,hdr,hdrlen) < 0) || (write (LOCAL->fd,txt,txtlen) < 0)) r = 0; } fs_give ((void **) &hdr); } /* make sure all the updates take */ if (fsync (LOCAL->fd)) r = 0; if (r) { /* delete all the messages we copied */ if (r == 1) strcpy (tmp,"1"); else sprintf (tmp,"1:%lu",r); mail_setflag (sysibx,tmp,"\\Deleted"); mail_expunge (sysibx); /* now expunge all those messages */ } else { sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (LOCAL->fd,sbuf.st_size); } fstat (LOCAL->fd,&sbuf); /* yes, get current file size */ LOCAL->filetime = sbuf.st_mtime; } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ LOCAL->lastsnarf = time (0);/* note time of last snarf */ } } /* MBX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; time_t tp[2]; MESSAGECACHE *elt; unsigned long i,j,k,m; long ret = LONGT; int fd,ld; char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (mbx_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock copy mailbox",ERROR); MM_NOCRITICAL (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); mail_date(LOCAL->buf,elt);/* build target header */ /* get target keyword mask */ for (j = elt->user_flags, k = 0; j; ) if (s = stream->user_flags[find_rightmost_bit (&j)]) for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++) if (!compare_cstring (s,t) && (k |= 1 << m)) break; sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-00000000\015\012", elt->rfc822_size,k,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* write target header */ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){ read (LOCAL->fd,LOCAL->buf,j); ret = write (fd,LOCAL->buf,j) >= 0; } } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) { for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) { /* mark message deleted */ mbx_elt (stream,i,NIL)->deleted = T; /* recalculate status */ mbx_update_status (stream,i,NIL); } /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } return ret; } /* MBX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; time_t tp[2]; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = NIL; MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mbx_create (dstream = stream ? stream : user_flags (&mbxproto),"INBOX"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (dstream,data,&flags,&date,&message)); /* open destination mailbox */ else if (((fd = open (mbx_file (file,mailbox), O_WRONLY|O_APPEND|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); } /* get parse/append permission */ else if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock append mailbox",ERROR); close (fd); } else { MM_CRITICAL (dstream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; for (ret = LONGT; ret && message; ) { if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (dstream,flags,&uf); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%08lx%04lx-00000000\015\012",tmp, i = SIZE (message),uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !MM_APPEND (af) (dstream,data,&flags,&date,&message)) ret = NIL; } } /* if error... */ if (!ret || (fflush (df) == EOF)) { /* revert file */ ftruncate (fd,sbuf.st_size); close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); } ret = NIL; } /* set atime to now-1 if successful copy */ if (ret) tp[0] = time (0) - 1; /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (dstream); /* release critical */ } if (dstream != stream) mail_close (dstream); return ret; } /* Internal routines */ /* MBX mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *mbx_file (char *dst,char *name) { char *s = mailboxfile (dst,name); return (s && !*s) ? mailboxfile (dst,"~/INBOX") : s; } /* MBX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mbx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j,k,m; off_t curpos = LOCAL->filesize; unsigned long nmsgs = stream->nmsgs; unsigned long recent = stream->recent; unsigned long lastuid = 0; short dirty = NIL; short added = NIL; short silent = stream->silent; short uidwarn = T; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } lseek (LOCAL->fd,0,L_SET); /* rewind file */ /* read internal header */ read (LOCAL->fd,LOCAL->buf,HDRSIZE); LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */ c = LOCAL->buf[15]; /* save first character of last UID */ LOCAL->buf[15] = '\0'; /* parse UID validity */ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16); LOCAL->buf[15] = c; /* restore first character of last UID */ /* parse last UID */ i = strtoul (LOCAL->buf + 15,NIL,16); stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i; /* parse user flags */ for (i = 0, s = LOCAL->buf + 25; (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG)) stream->user_flags[i] = cpystr (s); } LOCAL->ffuserflag = (int) i; /* first free user flag */ /* get current last flag updater PID */ i = (isxdigit (LOCAL->buf[HDRSIZE-10]) && isxdigit (LOCAL->buf[HDRSIZE-9]) && isxdigit (LOCAL->buf[HDRSIZE-8]) && isxdigit (LOCAL->buf[HDRSIZE-7]) && isxdigit (LOCAL->buf[HDRSIZE-6]) && isxdigit (LOCAL->buf[HDRSIZE-5]) && isxdigit (LOCAL->buf[HDRSIZE-4]) && isxdigit (LOCAL->buf[HDRSIZE-3]) && (LOCAL->buf[HDRSIZE-2] == '\015') && (LOCAL->buf[HDRSIZE-1] == '\012'))? strtoul (LOCAL->buf + HDRSIZE - 8,NIL,16) : 0; /* set flagcheck if lastpid changed */ if (LOCAL->lastpid && (LOCAL->lastpid != i)) LOCAL->flagcheck = T; LOCAL->lastpid = i; /* set as last PID */ stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s", (unsigned long) curpos,i,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) && isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) && isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) && isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) { sprintf (tmp,"Unable to parse message flags at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } if ((t[13] != '-') || t[22] || !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) && isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) && isxdigit (t[20]) && isxdigit (t[21]))) { sprintf (tmp,"Unable to parse message UID at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } *s++ = '\0'; *t++ = '\0'; /* break up fields */ /* get message size */ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) { sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s", (unsigned long) curpos,(char *) LOCAL->buf,(char *) s, (char *) t); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } /* make sure didn't run off end of file */ if (((off_t) (curpos + i + j)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", (unsigned long) curpos,(unsigned long) (curpos + i + j), (unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } /* parse UID */ if ((m = strtoul (t+13,NIL,16)) && ((m <= lastuid) || (m > stream->uid_last))) { if (uidwarn) { sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs", m,nmsgs+1); MM_LOG (tmp,WARN); uidwarn = NIL; /* restart UID validity */ stream->uid_validity = time (0); } m = 0; /* lose this UID */ dirty = T; /* mark dirty, set new lastuid */ stream->uid_last = lastuid; } t[12] = '\0'; /* parse system flags */ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) { if (m) lastuid = m; /* expunge message, update last UID seen */ else { /* no UID assigned? */ lastuid = ++stream->uid_last; dirty = T; } } else { /* not expunged, swell the cache */ added = T; /* note that a new message was added */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; /* parse the date */ if (!mail_parse_date (elt,LOCAL->buf)) { sprintf (tmp,"Unable to parse message date at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } /* note file offset of header */ elt->private.special.offset = curpos; /* and internal header size */ elt->private.special.text.size = i; /* header size not known yet */ elt->private.msg.header.text.size = 0; elt->rfc822_size = j; /* note message size */ /* calculate system flags */ if (k & fSEEN) elt->seen = T; if (k & fDELETED) elt->deleted = T; if (k & fFLAGGED) elt->flagged = T; if (k & fANSWERED) elt->answered = T; if (k & fDRAFT) elt->draft = T; t[8] = '\0'; /* get user flags value */ elt->user_flags = strtoul (t,NIL,16); /* UID already assigned? */ if (!(elt->private.uid = m)) { elt->recent = T; /* no, mark as recent */ ++recent; /* count up a new recent message */ dirty = T; /* and must rewrite header */ /* assign new UID */ elt->private.uid = ++stream->uid_last; mbx_update_status (stream,elt->msgno,NIL); } /* update last parsed UID */ lastuid = elt->private.uid; } curpos += i + j; /* update position */ } if (dirty && !stream->rdonly){/* update header */ mbx_update_header (stream); fsync (LOCAL->fd); /* make sure all the UID updates take */ } /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ time_t tp[2]; tp[0] = time (0); tp[1] = LOCAL->filetime; utime (stream->mailbox,tp); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MBX get cache element with status updating from file * Accepts: MAIL stream * message number * expunge OK flag * Returns: cache element */ MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; /* get new flags */ if (mbx_read_flags (stream,elt) && expok) { mail_expunged (stream,elt->msgno); return NIL; /* return this message was expunged */ } if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) MM_FLAGS (stream,msgno); /* let top level know */ return elt; } /* MBX read flags from file * Accepts: MAIL stream * cache element * Returns: non-NIL if message expunged */ unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i; struct stat sbuf; fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } LOCAL->buf[13] = '\0'; /* tie off buffer */ /* calculate system flags */ i = strtoul (LOCAL->buf+9,NIL,16); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->expunged |= i & fEXPUNGED ? T : NIL; LOCAL->buf[9] = '\0'; /* tie off flags */ /* get user flags value */ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16); elt->valid = T; /* have valid flags now */ return i & fEXPUNGED; } /* MBX update header * Accepts: MAIL stream */ #ifndef CYGKLUDGEOFFSET #define CYGKLUDGEOFFSET 0 #endif void mbx_update_header (MAILSTREAM *stream) { int i; char *s = LOCAL->buf; memset (s,'\0',HDRSIZE); /* initialize header */ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012", stream->uid_validity,stream->uid_last); for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]); LOCAL->ffuserflag = i; /* first free user flag */ /* can we create more user flags? */ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL; /* write reserved lines */ while (i++ < NUSERFLAGS) strcat (s,"\015\012"); sprintf (LOCAL->buf + HDRSIZE - 10,"%08lx\015\012",LOCAL->lastpid); while (T) { /* rewind file */ lseek (LOCAL->fd,CYGKLUDGEOFFSET,L_SET); /* write new header */ if (write (LOCAL->fd,LOCAL->buf + CYGKLUDGEOFFSET, HDRSIZE - CYGKLUDGEOFFSET) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } } /* MBX update status string * Accepts: MAIL stream * message number * flags */ void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags) { struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); /* readonly */ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt); else { /* readwrite */ fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } /* print new flag string */ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned) (((elt->deleted && flags) ? fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft)),elt->private.uid); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 23,L_SET); /* write new flags and UID */ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } } } /* MBX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * pointer to possible returned header * Returns: position of header in file */ #define HDRBUFLEN 4096 /* good enough for most headers */ #define SLOP 4 /* CR LF CR LF */ unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr) { unsigned long siz,done; long i; unsigned char *s,*t,*te; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; if (hdr) *hdr = NIL; /* assume no header returned */ /* is header size known? */ if (*size = elt->private.msg.header.text.size) return ret; /* paranoia check */ if (LOCAL->buflen < (HDRBUFLEN + SLOP)) fatal ("LOCAL->buf smaller than HDRBUFLEN"); lseek (LOCAL->fd,ret,L_SET); /* get to header position */ /* read HDRBUFLEN chunks with 4 byte slop */ for (done = siz = 0, s = LOCAL->buf; (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) && (read (LOCAL->fd,s,i) == i); done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) { te = (t = s + i) - 12; /* calculate end of fast scan */ /* fast scan for CR */ for (s = LOCAL->buf; s < te;) if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } for (te = t - 3; (s < te);) /* final character-at-a-time scan */ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } if (i <= SLOP) break; /* end of data */ /* slide over last 4 bytes */ memmove (LOCAL->buf,t - SLOP,SLOP); hdr = NIL; /* can't return header this way */ } /* not found: header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; if (hdr) *hdr = LOCAL->buf; /* possibly return header too */ return ret; } /* MBX mail rewrite mailbox * Accepts: MAIL stream * pointer to return reclaimed size * flags (non-NIL to do expunge) * Returns: number of expunged messages */ unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags) { time_t tp[2]; struct stat sbuf; off_t pos,ppos; int ld; unsigned long i,j,k,m,n,delta; unsigned long recent = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* The cretins who designed flock() created a window of vulnerability in * upgrading locks from shared to exclusive or downgrading from exclusive * to shared. Rather than maintain the lock at shared status at a minimum, * flock() actually *releases* the former lock. Obviously they never talked * to any database guys. Fortunately, we have the parse/append permission * lock. If we require this lock before going exclusive on the mailbox, * another process can not sneak in and steal the exclusive mailbox lock on * us, because it will block on trying to get parse/append permission first. */ /* get parse/append permission */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock mailbox for rewrite",ERROR); return *reclaimed = 0; } fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime && !LOCAL->flagcheck && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */ unlockfd (ld,lock); /* failed?? */ return *reclaimed = 0; } if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */ LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL); LOCAL->flagcheck = NIL; } /* get exclusive access */ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { MM_CRITICAL (stream); /* go critical */ for (i = 1,n = delta = *reclaimed = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) { /* note if message not at predicted location */ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) { ppos = elt->private.special.offset; *reclaimed += m; /* note reclaimed message space */ delta += m; /* and as expunge delta */ } /* number of bytes to smash or preserve */ ppos += (k = elt->private.special.text.size + elt->rfc822_size); /* if deleted */ if (flags && elt->deleted) { delta += k; /* number of bytes to delete */ mail_expunged(stream,i);/* notify upper levels */ n++; /* count up one more expunged message */ } else { /* preserved message */ i++; /* count this message */ if (elt->recent) ++recent; if (delta) { /* moved, note first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages yet */ else pos = elt->private.special.offset + k; } } /* deltaed file size match position? */ if (m = (LOCAL->filesize -= delta) - pos) { *reclaimed += m; /* probably an fEXPUNGED msg */ LOCAL->filesize = pos; /* set correct size */ } /* truncate file after last message */ ftruncate (LOCAL->fd,LOCAL->filesize); fsync (LOCAL->fd); /* force disk update */ MM_NOCRITICAL (stream); /* release critical */ (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ } else { /* can't get exclusive */ (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ /* checkpoint while shared? */ if (!flags) n = *reclaimed = 0; /* no, do hide-expunge */ else for (i = 1,n = *reclaimed = 0; i <= stream->nmsgs; ) { if (elt = mbx_elt (stream,i,T)) { if (elt->deleted) { /* make deleted message invisible */ mbx_update_status (stream,elt->msgno,LONGT); /* notify upper levels */ mail_expunged (stream,i); n++; /* count up one more expunged message */ } else { i++; /* preserved message */ if (elt->recent) ++recent; } } else n++; /* count up one more expunged message */ } fsync (LOCAL->fd); /* force disk update */ } fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* reset atime to now */ utime (stream->mailbox,tp); /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); return n; /* return number of expunged messages */ } /* MBX mail lock for flag updating * Accepts: stream * Returns: T if successful, NIL if failure */ long mbx_flaglock (MAILSTREAM *stream) { struct stat sbuf; unsigned long i; int ld; char lock[MAILTMPLEN]; /* no-op if readonly or already locked */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) { /* lock now */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) return NIL; if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */ if (LOCAL->filetime) { /* know previous time? */ fstat (LOCAL->fd,&sbuf);/* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } if (!mbx_parse (stream)) {/* parse mailbox */ unlockfd (ld,lock); /* shouldn't happen */ return NIL; } if (LOCAL->flagcheck) /* invalidate cache if flagcheck */ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL; } LOCAL->ld = ld; /* copy to stream for subseuent calls */ memcpy (LOCAL->lock,lock,MAILTMPLEN); } return LONGT; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mbx.h000066400000000000000000000012211137544547100226440ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define HDRSIZE 2048 /* Private driver flags, should be in mail.h? */ #define fEXPUNGED 32768 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mh.c000066400000000000000000001031541137544547100224650ustar00rootroot00000000000000/* * Program: MH mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 23 February 1992 * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "mh.h" #include "misc.h" #include "dummy.h" /* MH I/O stream local data */ typedef struct mh_local { char *dir; /* spool directory name */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long cachedtexts; /* total size of all cached texts */ time_t scantime; /* last time directory scanned */ } MHLOCAL; /* Convenient access to local data */ #define LOCAL ((MHLOCAL *) stream->local) /* Function prototypes */ DRIVER *mh_valid (char *name); int mh_isvalid (char *name,char *tmp,long synonly); void *mh_parameters (long function,void *value); void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mh_list (MAILSTREAM *stream,char *ref,char *pat); void mh_lsub (MAILSTREAM *stream,char *ref,char *pat); void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level); long mh_subscribe (MAILSTREAM *stream,char *mailbox); long mh_unsubscribe (MAILSTREAM *stream,char *mailbox); long mh_create (MAILSTREAM *stream,char *mailbox); long mh_delete (MAILSTREAM *stream,char *mailbox); long mh_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *mh_open (MAILSTREAM *stream); void mh_close (MAILSTREAM *stream,long options); void mh_fast (MAILSTREAM *stream,char *sequence,long flags); char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); long mh_ping (MAILSTREAM *stream); void mh_check (MAILSTREAM *stream); void mh_expunge (MAILSTREAM *stream); long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long options); long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int mh_select (struct direct *name); int mh_numsort (const void *d1,const void *d2); char *mh_file (char *dst,char *name); long mh_canonicalize (char *pattern,char *ref,char *pat); void mh_setdate (char *file,MESSAGECACHE *elt); /* MH mail routines */ /* Driver dispatch used by MAIL */ DRIVER mhdriver = { "mh", /* driver name */ /* driver flags */ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ mh_valid, /* mailbox is valid for us */ mh_parameters, /* manipulate parameters */ mh_scan, /* scan mailboxes */ mh_list, /* find mailboxes */ mh_lsub, /* find subscribed mailboxes */ mh_subscribe, /* subscribe to mailbox */ mh_unsubscribe, /* unsubscribe from mailbox */ mh_create, /* create mailbox */ mh_delete, /* delete mailbox */ mh_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mh_open, /* open mailbox */ mh_close, /* close mailbox */ mh_fast, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mh_header, /* fetch message header */ mh_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mh_ping, /* ping mailbox to see if still alive */ mh_check, /* check for new messages */ mh_expunge, /* expunge deleted messages */ mh_copy, /* copy messages to another mailbox */ mh_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mhproto = {&mhdriver}; /* MH mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mh_valid (char *name) { char tmp[MAILTMPLEN]; return mh_isvalid (name,tmp,T) ? &mhdriver : NIL; } /* MH mail test for valid mailbox * Accepts: mailbox name * temporary buffer to use * syntax only test flag * Returns: T if valid, NIL otherwise */ static char *mh_profile = NIL; /* holds MH profile */ static char *mh_path = NIL; /* holds MH path name */ static long mh_once = 0; /* already through this code */ int mh_isvalid (char *name,char *tmp,long synonly) { struct stat sbuf; /* name must be #MHINBOX or #mh/... */ if ((name[0] != '#') || ((name[1] != 'm') && name[1] != 'M') || ((name[2] != 'h') && name[2] != 'H') || ((name[3] != '/') && compare_cstring (name+3,"INBOX"))) { errno = EINVAL; /* bogus name */ return NIL; } if (!mh_path) { /* have MH path yet? */ char *s,*t,*v; int fd; if (mh_once++) return NIL; /* only do this code once */ if (!mh_profile) { /* have MH profile? */ sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE); mh_profile = cpystr (tmp); } if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { strcat (tmp," not found, mh format names disabled"); mm_log (tmp,WARN); return NIL; } fstat (fd,&sbuf); /* yes, get size and read file */ read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size); close (fd); /* don't need the file any more */ t[sbuf.st_size] = '\0'; /* tie it off */ /* parse profile file */ for (s = strtok (t,"\r\n"); s && *s; s = strtok (NIL,"\r\n")) { /* found space in line? */ if (v = strpbrk (s," \t")) { *v++ = '\0'; /* tie off, is keyword "Path:"? */ if (!strcmp (lcase (s),"path:")) { /* skip whitespace */ while ((*v == ' ') || (*v == '\t')) ++v; if (*v == '/') s = v; /* absolute path? */ else sprintf (s = tmp,"%s/%s",myhomedir (),v); mh_path = cpystr (s); /* copy name */ break; /* don't need to look at rest of file */ } } } fs_give ((void **) &t); /* flush profile text */ if (!mh_path) { /* default path if not in the profile */ sprintf (tmp,"%s/%s",myhomedir (),MHPATH); mh_path = cpystr (tmp); } } if (synonly) return T; /* all done if syntax only check */ errno = NIL; /* zap error */ /* validate name as directory */ return ((stat (mh_file (tmp,name),&sbuf) == 0) && (sbuf.st_mode & S_IFMT) == S_IFDIR); } /* MH manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mh_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_MHPROFILE: if (mh_profile) fs_give ((void **) &mh_profile); mh_profile = cpystr ((char *) value); case GET_MHPROFILE: ret = (void *) mh_profile; break; case SET_MHPATH: if (mh_path) fs_give ((void **) &mh_path); mh_path = cpystr ((char *) value); case GET_MHPATH: ret = (void *) mh_path; break; } return ret; } /* MH scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char tmp[MAILTMPLEN]; if (mh_canonicalize (tmp,ref,pat)) mm_log ("Scan not valid for mh mailboxes",ERROR); } /* MH list mailboxes * Accepts: mail stream * reference * pattern to search */ void mh_list (MAILSTREAM *stream,char *ref,char *pat) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i = 0; if (!pat || !*pat) { /* empty pattern? */ if (mh_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'/')) *++s = '\0'; else test[0] = '\0'; mm_list (stream,'/',test,LATT_NOSELECT); } } /* get canonical form of name */ else if (mh_canonicalize (test,ref,pat)) { if (test[3] == '/') { /* looking down levels? */ /* yes, found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test+4,i = s - (test+4)); file[i] = '\0'; /* tie off */ } else strcpy (file,test+4);/* use just that name then */ /* find directory name */ if (s = strrchr (file,'/')) { *s = '\0'; /* found, tie off at that point */ s = file; } /* do the work */ mh_list_work (stream,s,test,0); } /* always an INBOX */ if (!compare_cstring (test,"#MHINBOX")) mm_list (stream,NIL,"#MHINBOX",LATT_NOINFERIORS); } } /* MH list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mh_lsub (MAILSTREAM *stream,char *ref,char *pat) { void *sdb = NIL; char *s,test[MAILTMPLEN]; /* get canonical form of name */ if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) { do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL); while (s = sm_read (&sdb)); /* until no more subscriptions */ } } /* MH list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * search level */ void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) { DIR *dp; struct direct *d; struct stat sbuf; char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN]; /* build MH name to search */ if (dir) sprintf (name,"#mh/%s/",dir); else strcpy (name,"#mh/"); /* make directory name, punt if bogus */ if (!mh_file (curdir,name)) return; cp = curdir + strlen (curdir);/* end of directory name */ np = name + strlen (name); /* end of MH name */ if (dp = opendir (curdir)) { /* open directory */ while (d = readdir (dp)) /* scan, ignore . and numeric names */ if ((d->d_name[0] != '.') && !mh_select (d)) { strcpy (cp,d->d_name); /* make directory name */ if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) { strcpy (np,d->d_name);/* make mh name of directory name */ /* yes, an MH name if full match */ if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL); /* check if should recurse */ if (dmatch (name,pat,'/') && (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) mh_list_work (stream,name+4,pat,level+1); } } closedir (dp); /* all done, flush directory */ } } /* MH mail subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long mh_subscribe (MAILSTREAM *stream,char *mailbox) { return sm_subscribe (mailbox); } /* MH mail unsubscribe to mailbox * Accepts: mail stream * mailbox to delete from subscription list * Returns: T on success, NIL on failure */ long mh_unsubscribe (MAILSTREAM *stream,char *mailbox) { return sm_unsubscribe (mailbox); } /* MH mail create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long mh_create (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; /* assume error */ sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox); if (mailbox[0] == '#' && (mailbox[1] == 'm' || mailbox[1] == 'M') && (mailbox[2] == 'h' || mailbox[2] == 'H') && mailbox[3] == '/') /* make sure valid name */ for (s = mailbox + 4; s && *s;) { if (isdigit (*s)) s++; /* digit, check this node further... */ /* all digit node, barf */ else if (*s == '/') s = NIL; /* non-digit in node, skip to next node */ else if (s = strchr (s+1,'/')) s++; else tmp[0] = NIL; /* no more nodes, good name */ } if (tmp[0]) { /* was there an error in the name? */ mm_log (tmp,ERROR); /* yes, log it */ return NIL; } /* must not already exist */ if (mh_isvalid (mailbox,tmp,NIL)) { sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox); mm_log (tmp,ERROR); return NIL; } if (!mh_path) return NIL; /* sorry */ if (!(mh_file (tmp,mailbox) &&/* try to make it */ dummy_create_path (stream,strcat (tmp,"/"), get_dir_protection (mailbox)))) { sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno)); mm_log (tmp,ERROR); return NIL; } return T; /* return success */ } /* MH mail delete mailbox * mailbox name to delete * Returns: T on success, NIL on failure */ long mh_delete (MAILSTREAM *stream,char *mailbox) { DIR *dirp; struct direct *d; int i; char tmp[MAILTMPLEN]; if (!(mailbox[0] == '#' && (mailbox[1] == 'm' || mailbox[1] == 'M') && (mailbox[2] == 'h' || mailbox[2] == 'H') && mailbox[3] == '/')) { sprintf (tmp,"Can't delete mailbox %.80s: invalid MH-format name",mailbox); mm_log (tmp,ERROR); return NIL; } /* is mailbox valid? */ if (!mh_isvalid (mailbox,tmp,NIL)){ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox); mm_log (tmp,ERROR); return NIL; } /* get name of directory */ i = strlen (mh_file (tmp,mailbox)); if (dirp = opendir (tmp)) { /* open directory */ tmp[i++] = '/'; /* now apply trailing delimiter */ while (d = readdir (dirp)) /* massacre all numeric or comma files */ if (mh_select (d) || (*d->d_name == ',') || !strcmp (d->d_name,MHSEQUENCE)) { strcpy (tmp + i,d->d_name); unlink (tmp); /* sayonara */ } closedir (dirp); /* flush directory */ } /* try to remove the directory */ if (rmdir (mh_file (tmp,mailbox))) { sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno)); mm_log (tmp,WARN); } return T; /* return success */ } /* MH mail rename mailbox * Accepts: MH mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long mh_rename (MAILSTREAM *stream,char *old,char *newname) { char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN]; struct stat sbuf; if (!(old[0] == '#' && (old[1] == 'm' || old[1] == 'M') && (old[2] == 'h' || old[2] == 'H') && old[3] == '/')) sprintf (tmp,"Can't delete mailbox %.80s: invalid MH-format name",old); /* old mailbox name must be valid */ else if (!mh_isvalid (old,tmp,NIL)) sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old); else if (!(newname[0] == '#' && (newname[1] == 'm' || newname[1] == 'M') && (newname[2] == 'h' || newname[2] == 'H') && newname[3] == '/')) sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name", newname); /* new mailbox name must not be valid */ else if (mh_isvalid (newname,tmp,NIL)) sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists", newname); /* success if can rename the directory */ else { /* found superior to destination name? */ if (s = strrchr (mh_file (tmp1,newname),'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp1,get_dir_protection (newname))) return NIL; *s = c; /* restore full name */ } if (!rename (mh_file (tmp,old),tmp1)) return T; sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s", old,newname,strerror (errno)); } mm_log (tmp,ERROR); /* something failed */ return NIL; } /* MH mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mh_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */ if (stream->local) fatal ("mh recycle stream"); stream->local = fs_get (sizeof (MHLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"#MHINBOX"); mh_file (tmp,stream->mailbox);/* get directory name */ LOCAL->dir = cpystr (tmp); /* copy directory name for later */ /* make temporary buffer */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); LOCAL->scantime = 0; /* not scanned yet */ LOCAL->cachedtexts = 0; /* no cached texts */ stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (!mh_ping (stream)) return NIL; if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",(long) NIL); return stream; /* return stream to caller */ } /* MH mail close * Accepts: MAIL stream * close options */ void mh_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mh_expunge (stream); if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); /* free local scratch buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ stream->silent = silent; /* reset silent state */ } } /* MH mail fetch fast information * Accepts: MAIL stream * sequence * option flags */ void mh_fast (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i,j; /* ugly and slow */ if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) mh_header (stream,i,&j,NIL); } /* MH mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i,hdrsize; int fd; unsigned char *t; struct stat sbuf; struct tm *tm; MESSAGECACHE *elt; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get elt */ if (!elt->private.msg.header.text.data) { /* purge cache if too big */ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) { mail_gc (stream,GC_TEXTS);/* just can't keep that much */ LOCAL->cachedtexts = 0; } /* build message file name */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return ""; fstat (fd,&sbuf); /* get size of message */ /* make plausible IMAPish date string */ tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ /* find end of header */ for (i = 0,t = LOCAL->buf; *t && !(i && (*t == '\n')); i = (*t++ == '\n')); /* number of header bytes */ hdrsize = (*t ? ++t : t) - LOCAL->buf; elt->rfc822_size = /* size of entire message in CRLF form */ (elt->private.msg.header.text.size = strcrlfcpy (&elt->private.msg.header.text.data,&i,LOCAL->buf, hdrsize)) + (elt->private.msg.text.text.size = strcrlfcpy (&elt->private.msg.text.text.data,&i,t, sbuf.st_size - hdrsize)); /* add to cached size */ LOCAL->cachedtexts += elt->rfc822_size; } *length = elt->private.msg.header.text.size; return (char *) elt->private.msg.header.text.data; } /* MH mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL on failure */ long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get elt */ /* snarf message if don't have it yet */ if (!elt->private.msg.text.text.data) { mh_header (stream,msgno,&i,flags); if (!elt->private.msg.text.text.data) return NIL; } if (!(flags & FT_PEEK)) { /* mark as seen */ mail_elt (stream,msgno)->seen = T; mm_flags (stream,msgno); } if (!elt->private.msg.text.text.data) return NIL; INIT (bs,mail_string,elt->private.msg.text.text.data, elt->private.msg.text.text.size); return T; } /* MH mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long mh_ping (MAILSTREAM *stream) { MAILSTREAM *sysibx = NIL; MESSAGECACHE *elt,*selt; struct stat sbuf; char *s,tmp[MAILTMPLEN]; int fd; unsigned long i,j,r,old; long nmsgs = stream->nmsgs; long recent = stream->recent; int silent = stream->silent; if (stat (LOCAL->dir,&sbuf)) { /* directory exists? */ if (stream->inbox) return T; sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox); mm_log (tmp,ERROR); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ if (sbuf.st_ctime != LOCAL->scantime) { struct direct **names = NIL; long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort); if (nfiles < 0) nfiles = 0; /* in case error */ old = stream->uid_last; /* note scanned now */ LOCAL->scantime = sbuf.st_ctime; /* scan directory */ for (i = 0; i < nfiles; ++i) { /* if newly seen, add to list */ if ((j = atoi (names[i]->d_name)) > old) { mail_exists (stream,++nmsgs); stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j; elt->valid = T; /* note valid flags */ if (old) { /* other than the first pass? */ elt->recent = T; /* yup, mark as recent */ recent++; /* bump recent count */ } else { /* see if already read */ sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name); stat (tmp,&sbuf); /* get inode poop */ if (sbuf.st_atime > sbuf.st_mtime) elt->seen = T; } } fs_give ((void **) &names[i]); } /* free directory */ if (s = (void *) names) fs_give ((void **) &s); } /* if INBOX, snarf from system INBOX */ if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) { old = stream->uid_last; mm_critical (stream); /* go critical */ stat (sysinbox (),&sbuf); /* see if anything there */ /* can get sysinbox mailbox? */ if (sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */ /* build file name we will use */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old); /* snarf message from Berkeley mailbox */ selt = mail_elt (sysibx,i); if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL, S_IREAD|S_IWRITE)) >= 0) && (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) && (write (fd,s,j) == j) && (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) && (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) { /* swell the cache */ mail_exists (stream,++nmsgs); stream->uid_last = /* create new elt, note its file number */ (elt = mail_elt (stream,nmsgs))->private.uid = old; recent++; /* bump recent count */ /* set up initial flags and date */ elt->valid = elt->recent = T; elt->seen = selt->seen; elt->deleted = selt->deleted; elt->flagged = selt->flagged; elt->answered = selt->answered; elt->draft = selt->draft; elt->day = selt->day;elt->month = selt->month;elt->year = selt->year; elt->hours = selt->hours;elt->minutes = selt->minutes; elt->seconds = selt->seconds; elt->zhours = selt->zhours; elt->zminutes = selt->zminutes; mh_setdate (LOCAL->buf,elt); } else { /* failed to snarf */ if (fd) { /* did it ever get opened? */ mm_log ("Message copy to MH mailbox failed",ERROR); close (fd); /* close descriptor */ unlink (LOCAL->buf);/* flush this file */ } else { sprintf (tmp,"Can't add message: %s",strerror (errno)); mm_log (tmp,ERROR); } stream->silent = silent; return NIL; /* note that something is badly wrong */ } sprintf (tmp,"%lu",i); /* delete it from the sysinbox */ mail_flag (sysibx,tmp,"\\Deleted",ST_SET); } stat (LOCAL->dir,&sbuf); /* update scan time */ LOCAL->scantime = sbuf.st_ctime; mail_expunge (sysibx); /* now expunge all those messages */ } if (sysibx) mail_close (sysibx); mm_nocritical (stream); /* release critical */ } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */ mail_recent (stream,recent); return T; /* return that we are alive */ } /* MH mail check mailbox * Accepts: MAIL stream */ void mh_check (MAILSTREAM *stream) { /* Perhaps in the future this will preserve flags */ if (mh_ping (stream)) mm_log ("Check completed",(long) NIL); } /* MH mail expunge mailbox * Accepts: MAIL stream */ void mh_expunge (MAILSTREAM *stream) { MESSAGECACHE *elt; unsigned long i = 1; unsigned long n = 0; unsigned long recent = stream->recent; mm_critical (stream); /* go critical */ while (i <= stream->nmsgs) { /* for each message */ /* if deleted, need to trash it */ if ((elt = mail_elt (stream,i))->deleted) { sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if (unlink (LOCAL->buf)) {/* try to delete the message */ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i, strerror (errno)); mm_log (LOCAL->buf,(long) NIL); break; } /* note uncached */ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ? elt->private.msg.header.text.size : 0) + (elt->private.msg.text.text.data ? elt->private.msg.text.text.size : 0)); mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS); if (elt->recent) --recent;/* if recent, note one less recent message */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else i++; /* otherwise try next message */ } if (n) { /* output the news if any expunged */ sprintf (LOCAL->buf,"Expunged %lu messages",n); mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); mm_nocritical (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); } /* MH mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { STRING st; MESSAGECACHE *elt; struct stat sbuf; int fd; unsigned long i; char flags[MAILTMPLEN],date[MAILTMPLEN]; /* copy the messages */ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL; fstat (fd,&sbuf); /* get size of message */ if (!elt->day) { /* make plausible IMAPish date string */ struct tm *tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; } /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size); /* init flag string */ flags[0] = flags[1] = '\0'; if (elt->seen) strcat (flags," \\Seen"); if (elt->deleted) strcat (flags," \\Deleted"); if (elt->flagged) strcat (flags," \\Flagged"); if (elt->answered) strcat (flags," \\Answered"); if (elt->draft) strcat (flags," \\Draft"); flags[0] = '('; /* open list */ strcat (flags,")"); /* close list */ mail_date (date,elt); /* generate internal date */ if (!mail_append_full (NIL,mailbox,flags,date,&st)) return NIL; if (options & CP_MOVE) elt->deleted = T; } return T; /* return success */ } /* MH mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct direct **names; int fd; char c,*flags,*date,*s,tmp[MAILTMPLEN]; STRING *message; MESSAGECACHE elt; long i,size,last,nfiles; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &mhproto; /* make sure valid mailbox */ if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"#mhinbox")) mh_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) { /* largest number */ last = atoi (names[nfiles-1]->d_name); for (i = 0; i < nfiles; ++i) /* free directory */ fs_give ((void **) &names[i]); } else last = 0; /* no messages here yet */ if (s = (void *) names) fs_give ((void **) &s); mm_critical (stream); /* go critical */ do { if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } if (date) { /* want to preserve date? */ /* yes, parse date into an elt */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; break; } } mh_file (tmp,mailbox); /* build file name we will use */ sprintf (tmp + strlen (tmp),"/%ld",++last); if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { sprintf (tmp,"Can't open append message: %s",strerror (errno)); mm_log (tmp,ERROR); ret = NIL; break; } /* copy the data w/o CR's */ for (size = 0,i = SIZE (message),s = (char *) fs_get (i + 1); i; --i) if ((c = SNX (message)) != '\015') s[size++] = c; /* write the data */ if ((write (fd,s,size) < 0) || fsync (fd)) { unlink (tmp); /* delete message */ sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); ret = NIL; } fs_give ((void **) &s); /* flush the buffer */ close (fd); /* close the file */ if (ret) { /* set the date for this message */ if (date) mh_setdate (tmp,&elt); /* get next message */ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); mm_nocritical (stream); /* release critical */ return ret; } /* Internal routines */ /* MH file name selection test * Accepts: candidate directory entry * Returns: T to use file name, NIL to skip it */ int mh_select (struct direct *name) { char c; char *s = name->d_name; while (c = *s++) if (!isdigit (c)) return NIL; return T; } /* MH file name comparision * Accepts: first candidate directory entry * second candidate directory entry * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2 */ int mh_numsort (const void *d1,const void *d2) { return atoi ((*(struct direct **) d1)->d_name) - atoi ((*(struct direct **) d2)->d_name); } /* MH mail build file name * Accepts: destination string * source * Returns: destination */ char *mh_file (char *dst,char *name) { char *s; /* build composite name */ sprintf (dst,"%s/%.900s",mh_path, compare_cstring (name,"#MHINBOX") ? name + 4 : "inbox"); /* tie off unnecessary trailing / */ if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0'; return dst; } /* MH canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long mh_canonicalize (char *pattern,char *ref,char *pat) { char tmp[MAILTMPLEN]; if (ref && *ref) { /* have a reference */ strcpy (pattern,ref); /* copy reference to pattern */ /* # overrides mailbox field in reference */ if (*pat == '#') strcpy (pattern,pat); /* pattern starts, reference ends, with / */ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/')) strcat (pattern,pat + 1); /* append, omitting one of the period */ else strcat (pattern,pat); /* anything else is just appended */ } else strcpy (pattern,pat); /* just have basic name */ return (mh_isvalid (pattern,tmp,T)); } /* Set date for message * Accepts: file name * elt containing date */ void mh_setdate (char *file,MESSAGECACHE *elt) { time_t tp[2]; tp[0] = time (0); /* atime is now */ tp[1] = mail_longdate (elt); /* modification time */ utime (file,tp); /* set the times */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mh.h000066400000000000000000000012141137544547100224640ustar00rootroot00000000000000/* * Program: MH mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 23 February 1992 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define MHPROFILE ".mh_profile" #define MHSEQUENCE ".mh_sequence" #define MHPATH "Mail" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mkauths000077500000000000000000000017411137544547100233160ustar00rootroot00000000000000#!/bin/sh # # Program: Authenticator Linkage Generator # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 5 December 1995 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Erase old authenticators list rm -f auths.c touch auths.c # Now define the new list for authenticator do if [ -f Makefile."$authenticator" ]; then make -f Makefile."$authenticator" `cat SPECIALS` fi echo "extern AUTHENTICATOR auth_"$authenticator";" >> linkage.h echo " auth_link (&auth_"$authenticator"); /* link in the $authenticator authenticator */" | cat >> linkage.c echo "#include \"auth_"$authenticator".c\"" >> auths.c done tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mmdf.c000066400000000000000000002423271137544547100230120ustar00rootroot00000000000000/* * Program: MMDF mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include #include "mail.h" #include "osdep.h" #include #include #include "pseudo.h" #include "fdstring.h" #include "misc.h" #include "dummy.h" /* Supposedly, this page has everything the MMDF driver needs to know about * the MMDF delimiter. By changing these macros, the MMDF driver should * change with it. Note that if you change the length of MMDFHDRTXT you * also need to change the ISMMDF and RETIFMMDFWRD macros to reflect the new * size. */ /* Useful MMDF constants */ #define MMDFCHR '\01' /* MMDF character */ #define MMDFCHRS 0x01010101 /* MMDF header character spread in a word */ /* MMDF header text */ #define MMDFHDRTXT "\01\01\01\01\n" /* length of MMDF header text */ #define MMDFHDRLEN (sizeof (MMDFHDRTXT) - 1) /* Validate MMDF header * Accepts: pointer to candidate string to validate as an MMDF header * Returns: T if valid; else NIL */ #define ISMMDF(s) \ ((*(s) == MMDFCHR) && ((s)[1] == MMDFCHR) && ((s)[2] == MMDFCHR) && \ ((s)[3] == MMDFCHR) && ((s)[4] == '\n')) /* Return if a 32-bit word has the start of an MMDF header * Accepts: pointer to word of four bytes to validate as an MMDF header * Returns: pointer to MMDF header, else proceeds */ #define RETIFMMDFWRD(s) { \ if (s[3] == MMDFCHR) { \ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == MMDFCHR) && \ (s[7] == '\n')) return s + 3; \ else if (s[2] == MMDFCHR) { \ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == '\n')) \ return s + 2; \ else if (s[1] == MMDFCHR) { \ if ((s[4] == MMDFCHR) && (s[5] == '\n')) return s + 1; \ else if ((*s == MMDFCHR) && (s[4] == '\n')) return s; \ } \ } \ } \ } /* Validate line * Accepts: pointer to candidate string to validate as a From header * return pointer to end of date/time field * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') * return pointer to offset from t of time zone (if non-zero) * Returns: t,ti,zn set if valid From string, else ti is NIL */ #define VALID(s,x,ti,zn) { \ ti = 0; \ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ (s[4] == ' ')) { \ for (x = s + 5; *x && *x != '\n'; x++); \ if (*x) { \ if (x - s >= 41) { \ for (zn = -1; x[zn] != ' '; zn--); \ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ x += zn - 12; \ } \ if (x - s >= 27) { \ if (x[-5] == ' ') { \ if (x[-8] == ':') zn = 0,ti = -5; \ else if (x[-9] == ' ') ti = zn = -9; \ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ ti = zn = -11; \ } \ else if (x[-4] == ' ') { \ if (x[-9] == ' ') zn = -4,ti = -9; \ } \ else if (x[-6] == ' ') { \ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ zn = -6,ti = -11; \ } \ if (ti && !((x[ti - 3] == ':') && \ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ (x[ti - 11] == ' '))) ti = 0; \ } \ } \ } \ } /* You are not expected to understand this macro, but read the next page if * you are not faint of heart. * * Known formats to the VALID macro are: * From user Wed Dec 2 05:53 1992 * BSD From user Wed Dec 2 05:53:22 1992 * SysV From user Wed Dec 2 05:53 PST 1992 * rn From user Wed Dec 2 05:53:22 PST 1992 * From user Wed Dec 2 05:53 -0700 1992 * emacs From user Wed Dec 2 05:53:22 -0700 1992 * From user Wed Dec 2 05:53 1992 PST * From user Wed Dec 2 05:53:22 1992 PST * From user Wed Dec 2 05:53 1992 -0700 * Solaris From user Wed Dec 2 05:53:22 1992 -0700 * * Plus all of the above with `` remote from xxx'' after it. Thank you very * much, smail and Solaris, for making my life considerably more complicated. */ /* * What? You want to understand the VALID macro anyway? Alright, since you * insist. Actually, it isn't really all that difficult, provided that you * take it step by step. * * Line 1 Initializes the return ti value to failure (0); * Lines 2-3 Validates that the 1st-5th characters are ``From ''. * Lines 4-5 Validates that there is an end of line and points x at it. * Lines 6-13 First checks to see if the line is at least 41 characters long. * If so, it scans backwards to find the rightmost space. From * that point, it scans backwards to see if the string matches * `` remote from''. If so, it sets x to point to the space at * the start of the string. * Line 14 Makes sure that there are at least 27 characters in the line. * Lines 15-20 Checks if the date/time ends with the year (there is a space * five characters back). If there is a colon three characters * further back, there is no timezone field, so zn is set to 0 * and ti is set in front of the year. Otherwise, there must * either to be a space four characters back for a three-letter * timezone, or a space six characters back followed by a + or - * for a numeric timezone; in either case, zn and ti become the * offset of the space immediately before it. * Lines 21-23 Are the failure case for line 14. If there is a space four * characters back, it is a three-letter timezone; there must be a * space for the year nine characters back. zn is the zone * offset; ti is the offset of the space. * Lines 24-27 Are the failure case for line 20. If there is a space six * characters back, it is a numeric timezone; there must be a * space eleven characters back and a + or - five characters back. * zn is the zone offset; ti is the offset of the space. * Line 28-31 If ti is valid, make sure that the string before ti is of the * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise * invalidate ti. There must be a colon three characters back * and a space six or nine characters back (depending upon * whether or not the character six characters back is a colon). * There must be a space three characters further back (in front * of the day), one seven characters back (in front of the month), * and one eleven characters back (in front of the day of week). * ti is set to be the offset of the space before the time. * * Why a macro? It gets invoked a *lot* in a tight loop. On some of the * newer pipelined machines it is faster being open-coded than it would be if * subroutines are called. * * Why does it scan backwards from the end of the line, instead of doing the * much easier forward scan? There is no deterministic way to parse the * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to * see if unquoted spaces were possible. They are, and I've encountered enough * evil mail to be totally unwilling to trust that ``it will never happen''. */ /* Build parameters */ #define KODRETRY 15 /* kiss-of-death retry in seconds */ #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define CHUNK 16384 /* read-in chunk size */ /* MMDF I/O stream local data */ typedef struct mmdf_local { unsigned int dirty : 1; /* disk copy needs updating */ unsigned int pseudo : 1; /* uses a pseudo message */ int fd; /* mailbox file descriptor */ int ld; /* lock file descriptor */ char *lname; /* lock file name */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ unsigned long textlen; /* current text length */ char *line; /* returned line */ } MMDFLOCAL; /* Convenient access to local data */ #define LOCAL ((MMDFLOCAL *) stream->local) /* MMDF protected file structure */ typedef struct mmdf_file { MAILSTREAM *stream; /* current stream */ off_t curpos; /* current file position */ off_t protect; /* protected position */ off_t filepos; /* current last written file position */ char *buf; /* overflow buffer */ size_t buflen; /* current overflow buffer length */ char *bufpos; /* current buffer position */ } MMDFFILE; /* Function prototypes */ DRIVER *mmdf_valid (char *name); long mmdf_isvalid (char *name,char *tmp); long mmdf_isvalid_fd (int fd,char *tmp); void *mmdf_parameters (long function,void *value); void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mmdf_list (MAILSTREAM *stream,char *ref,char *pat); void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat); long mmdf_create (MAILSTREAM *stream,char *mailbox); long mmdf_delete (MAILSTREAM *stream,char *mailbox); long mmdf_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *mmdf_open (MAILSTREAM *stream); void mmdf_close (MAILSTREAM *stream,long options); char *mmdf_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mmdf_ping (MAILSTREAM *stream); void mmdf_check (MAILSTREAM *stream); void mmdf_check (MAILSTREAM *stream); void mmdf_expunge (MAILSTREAM *stream); long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int mmdf_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void mmdf_abort (MAILSTREAM *stream); char *mmdf_file (char *dst,char *name); int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op); void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock); int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op); char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr); unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag); long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock); long mmdf_extend (MAILSTREAM *stream,unsigned long size); void mmdf_write (MMDFFILE *f,char *s,unsigned long i); void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size); /* MMDF mail routines */ /* Driver dispatch used by MAIL */ DRIVER mmdfdriver = { "mmdf", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ mmdf_valid, /* mailbox is valid for us */ mmdf_parameters, /* manipulate parameters */ mmdf_scan, /* scan mailboxes */ mmdf_list, /* list mailboxes */ mmdf_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mmdf_create, /* create mailbox */ mmdf_delete, /* delete mailbox */ mmdf_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mmdf_open, /* open mailbox */ mmdf_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mmdf_header, /* fetch message header */ mmdf_text, /* fetch message text */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ mmdf_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mmdf_ping, /* ping mailbox to see if still alive */ mmdf_check, /* check for new messages */ mmdf_expunge, /* expunge deleted messages */ mmdf_copy, /* copy messages to another mailbox */ mmdf_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mmdfproto = {&mmdfdriver}; char *mmdfhdr = MMDFHDRTXT; /* MMDF header */ /* MMDF mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mmdf_valid (char *name) { char tmp[MAILTMPLEN]; return mmdf_isvalid (name,tmp) ? &mmdfdriver : NIL; } /* MMDF mail test for valid mailbox name * Accepts: mailbox name * scratch buffer * Returns: T if valid, NIL otherwise */ long mmdf_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *t,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* must be non-empty file */ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { /* error -1 for invalid format */ if (!(ret = mmdf_isvalid_fd (fd,tmp))) errno = -1; close (fd); /* close the file */ /* \Marked status? */ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } return ret; /* return what we should */ } /* MMDF mail test for valid mailbox * Accepts: file descriptor * scratch buffer * Returns: T if valid, NIL otherwise */ long mmdf_isvalid_fd (int fd,char *tmp) { int ret = NIL; memset (tmp,'\0',MAILTMPLEN); if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = ISMMDF (tmp) ? T : NIL; return ret; /* return what we should */ } /* MMDF manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mmdf_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = dummy_file ((char *) value,"INBOX"); break; } return ret; } /* MMDF mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MMDF mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mmdf_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MMDF mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MMDF mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mmdf_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN]; long ret = NIL; int i,fd; time_t ti = time (0); if (!(s = dummy_file (mbx,mailbox))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); MM_LOG (tmp,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) { /* done if made directory */ if ((s = strrchr (s,'/')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY, (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } /* in case a whiner with no life */ else if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T; else { /* initialize header */ memset (tmp,'\0',MAILTMPLEN); sprintf (tmp,"%sFrom %s %sDate: ",mmdfhdr,pseudo_from,ctime (&ti)); rfc822_date (s = tmp + strlen (tmp)); sprintf (s += strlen (s), /* write the pseudo-header */ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000", pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) ti); for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i)) sprintf (s += strlen (s)," %s",default_user_flag (i)); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr); if ((write (fd,tmp,strlen (tmp)) < 0) || close (fd)) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx, strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ } close (fd); /* close file, set proper protections */ } return ret ? set_mbx_protections (mailbox,mbx) : NIL; } /* MMDF mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mmdf_delete (MAILSTREAM *stream,char *mailbox) { return mmdf_rename (stream,mailbox,NIL); } /* MMDF mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mmdf_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = NIL; char c,*s = NIL; char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; DOTLOCK lockx; int fd,ld; long i; struct stat sbuf; MM_CRITICAL (stream); /* get the c-client lock */ if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); /* lock out other c-clients */ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0) sprintf (tmp,"Mailbox %.80s is in use by another process",old); else { if ((fd = mmdf_lock (file,O_RDWR,S_IREAD|S_IWRITE,&lockx,LOCK_EX)) < 0) sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno)); else { if (newname) { /* want rename? */ /* found superior to destination name? */ if (s = strrchr (s,'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) { mmdf_unlock (fd,NIL,&lockx); mmdf_unlock (ld,NIL,NIL); unlink (lock); MM_NOCRITICAL (stream); return ret; /* return success or failure */ } *s = c; /* restore full name */ } if (rename (file,tmp)) sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); else ret = T; /* set success */ } else if (unlink (file)) sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); else ret = T; /* set success */ mmdf_unlock (fd,NIL,&lockx); } mmdf_unlock (ld,NIL,NIL); /* flush the lock */ unlink (lock); } MM_NOCRITICAL (stream); /* no longer critical */ if (!ret) MM_LOG (tmp,ERROR); /* log error */ return ret; /* return success or failure */ } /* MMDF mail open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *mmdf_open (MAILSTREAM *stream) { long i; int fd; char tmp[MAILTMPLEN]; DOTLOCK lock; long retry; /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mmdfproto); retry = stream->silent ? 1 : KODRETRY; if (stream->local) fatal ("mmdf recycle stream"); stream->local = memset (fs_get (sizeof (MMDFLOCAL)),0,sizeof (MMDFLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); /* canonicalize the stream mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); MM_LOG (tmp,ERROR); return NIL; } /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); stream->sequence++; /* bump sequence number */ /* make lock for read/write access */ if (!stream->rdonly) while (retry) { /* try to lock file */ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) { if (retry-- == KODRETRY) {/* no, first time through? */ if (i) { /* learned the other guy's PID? */ kill ((int) i,SIGUSR2); sprintf (tmp,"Trying to get mailbox lock from process %ld",i); MM_LOG (tmp,WARN); } else retry = 0; /* give up */ } if (!stream->silent) { /* nothing if silent stream */ if (retry) sleep (1); /* wait a second before trying again */ else MM_LOG ("Mailbox is open by another process, access is readonly", WARN); } } else { /* got the lock, nobody else can alter state */ LOCAL->ld = fd; /* note lock's fd and name */ LOCAL->lname = cpystr (tmp); /* make sure mode OK (don't use fchmod()) */ chmod (LOCAL->lname,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL)); if (stream->silent) i = 0;/* silent streams won't accept KOD */ else { /* note our PID in the lock */ sprintf (tmp,"%d",getpid ()); write (fd,tmp,(i = strlen (tmp))+1); } ftruncate (fd,i); /* make sure tied off */ fsync (fd); /* make sure it's available */ retry = 0; /* no more need to try */ } } /* parse mailbox */ stream->nmsgs = stream->recent = 0; /* will we be able to get write access? */ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) { MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); flock (LOCAL->ld,LOCK_UN); /* release the lock */ close (LOCAL->ld); /* close the lock file */ LOCAL->ld = -1; /* no more lock fd */ unlink (LOCAL->lname); /* delete it */ } /* reset UID validity */ stream->uid_validity = stream->uid_last = 0; if (stream->silent && !stream->rdonly && (LOCAL->ld < 0)) mmdf_abort (stream); /* abort if can't get RW silent stream */ /* parse mailbox */ else if (mmdf_parse (stream,&lock,LOCK_SH)) { mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ } if (!LOCAL) return NIL; /* failure if stream died */ /* make sure upper level knows readonly */ stream->rdonly = (LOCAL->ld < 0); /* notify about empty mailbox */ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL); if (!stream->rdonly) { /* flags stick if readwrite */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = T; if (!stream->uid_nosticky) {/* users with lives get permanent keywords */ stream->perm_user_flags = 0xffffffff; /* and maybe can create them too! */ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T; } } return stream; /* return stream alive to caller */ } /* MMDF mail close * Accepts: MAIL stream * close options */ void mmdf_close (MAILSTREAM *stream,long options) { int silent = stream->silent; stream->silent = T; /* go silent */ /* expunge if requested */ if (options & CL_EXPUNGE) mmdf_expunge (stream); /* else dump final checkpoint */ else if (LOCAL->dirty) mmdf_check (stream); stream->silent = silent; /* restore old silence state */ mmdf_abort (stream); /* now punt the file and local data */ } /* MMDF mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ /* lines to filter from header */ static STRINGLIST *mmdf_hlines = NIL; char *mmdf_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { MESSAGECACHE *elt; unsigned char *s,*t,*tl; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get cache */ if (!mmdf_hlines) { /* once only code */ STRINGLIST *lines = mmdf_hlines = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Keywords")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-UID")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAP")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAPbase")); } /* go to header position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.header.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.header.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.header.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; } else { /* need to make a CRLF version */ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1), elt->private.msg.header.text.size); /* tie off string, and convert to CRLF */ s[elt->private.msg.header.text.size] = '\0'; *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, elt->private.msg.header.text.size); fs_give ((void **) &s); /* free readin buffer */ } *length = mail_filter (LOCAL->buf,*length,mmdf_hlines,FT_NOT); return LOCAL->buf; /* return processed copy */ } /* MMDF mail fetch message text * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL if failure */ long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get cache element */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { /* mark message seen and dirty */ elt->seen = elt->private.dirty = LOCAL->dirty = T; MM_FLAGS (stream,msgno); } s = mmdf_text_work (stream,elt,&i,flags); INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* MMDF mail fetch message text worker routine * Accepts: MAIL stream * message cache element * pointer to returned header text length * option flags */ char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags) { FDDATA d; STRING bs; unsigned char *s,*t,*tl,tmp[CHUNK]; /* go to text position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.text.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.text.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.text.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; return LOCAL->buf; } /* have it cached already? */ if (elt->private.uid != LOCAL->uid) { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->text.size) { /* excessively conservative, but the right thing is too hard to do */ fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); } d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = elt->private.special.offset + elt->private.msg.text.offset; d.chunk = tmp; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) { case '\r': /* carriage return seen */ *s++ = SNX (&bs); /* copy it and any succeeding LF */ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); break; case '\n': *s++ = '\r'; /* insert a CR */ default: *s++ = SNX (&bs); /* copy characters */ } *s = '\0'; /* tie off buffer */ /* calculate length of cached data */ LOCAL->textlen = s - LOCAL->text.data; } *length = LOCAL->textlen; /* return from cache */ return (char *) LOCAL->text.data; } /* MMDF per-message modify flag * Accepts: MAIL stream * message cache element */ void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { /* only after finishing */ if (elt->valid) elt->private.dirty = LOCAL->dirty = T; } /* MMDF mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long mmdf_ping (MAILSTREAM *stream) { DOTLOCK lock; struct stat sbuf; long reparse; /* big no-op if not readwrite */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) { if (stream->rdonly) { /* does he want to give up readwrite? */ /* checkpoint if we changed something */ if (LOCAL->dirty) mmdf_check (stream); flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ close (LOCAL->ld); /* close the readwrite lock file */ LOCAL->ld = -1; /* no more readwrite lock fd */ unlink (LOCAL->lname); /* delete the readwrite lock file */ } else { /* see if need to reparse */ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) { /* get current mailbox size */ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); else stat (stream->mailbox,&sbuf); reparse = (sbuf.st_size != LOCAL->filesize); } /* parse if mailbox changed */ if (reparse && mmdf_parse (stream,&lock,LOCK_SH)) { /* unlock mailbox */ mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* and stream */ /* done with critical */ MM_NOCRITICAL (stream); } } } return LOCAL ? LONGT : NIL; /* return if still alive */ } /* MMDF mail check mailbox * Accepts: MAIL stream */ void mmdf_check (MAILSTREAM *stream) { DOTLOCK lock; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && mmdf_parse (stream,&lock,LOCK_EX)) { /* any unsaved changes? */ if (LOCAL->dirty && mmdf_rewrite (stream,NIL,&lock)) { if (!stream->silent) MM_LOG ("Checkpoint completed",NIL); } /* no checkpoint needed, just unlock */ else mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ } } /* MMDF mail expunge mailbox * Accepts: MAIL stream */ void mmdf_expunge (MAILSTREAM *stream) { unsigned long i; DOTLOCK lock; char *msg = NIL; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && mmdf_parse (stream,&lock,LOCK_EX)) { /* count expunged messages if not dirty */ if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; if (!LOCAL->dirty) { /* not dirty and no expunged messages */ mmdf_unlock (LOCAL->fd,stream,&lock); msg = "No messages deleted, so no update needed"; } else if (mmdf_rewrite (stream,&i,&lock)) { if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); else msg = "Mailbox checkpointed, but no messages expunged"; } /* rewrite failed */ else mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ if (msg && !stream->silent) MM_LOG (msg,NIL); } else if (!stream->silent) MM_LOG ("Expunge ignored on readonly mailbox",WARN); } /* MMDF mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; int fd; char *s,file[MAILTMPLEN]; DOTLOCK lock; time_t tp[2]; unsigned long i,j; MESSAGECACHE *elt; long ret = T; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* make sure valid mailbox */ if (!mmdf_isvalid (mailbox,file)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { if (pc) return (*pc) (stream,sequence,mailbox,options); mmdf_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MMDF-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MMDF-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } LOCAL->buf[0] = '\0'; MM_CRITICAL (stream); /* go critical */ if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); /* log the error */ return NIL; /* failed */ } fstat (fd,&sbuf); /* get current file size */ /* write all requested messages to mailbox */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL; else { /* internal header succeeded */ s = mmdf_header (stream,i,&j,FT_INTERNAL); /* header size, sans trailing newline */ if (j && (s[j - 2] == '\n')) j--; if (write (fd,s,j) < 0) ret = NIL; else { /* message header succeeded */ j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* message status succeeded */ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL); if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0)) ret = NIL; } } } } if (!ret || fsync (fd)) { /* force out the update */ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); ftruncate (fd,sbuf.st_size); ret = NIL; } tp[1] = time (0); /* set mtime to now */ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ else tp[0] = /* else preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; utime (file,tp); /* set the times */ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ MM_NOCRITICAL (stream); /* release critical */ /* log the error */ if (!ret) MM_LOG (LOCAL->buf,ERROR); /* delete if requested message */ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = elt->private.dirty = LOCAL->dirty = T; return ret; } /* MMDF mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ #define BUFLEN 8*MAILTMPLEN long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN]; time_t tp[2]; FILE *sf,*df; MESSAGECACHE elt; DOTLOCK lock; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) { /* stream specified? */ stream = &mmdfproto; /* no, default stream to prototype */ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) fs_give ((void **) &stream->user_flags[i]); stream->kwd_create = T; /* allow new flags */ } /* make sure valid mailbox */ if (!mmdf_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { mmdf_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } unlink (tmp); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR); else if (!mmdf_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); } /* get next message */ else if (MM_APPEND (af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ MM_CRITICAL (stream); /* go critical */ if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ rewind (sf); for (; i && ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) && (fwrite (buf,1,j,df) == j)); i -= j); fclose (sf); /* done with scratch file */ tp[1] = time (0); /* set mtime to now */ /* make sure append wins, fsync() necessary */ if (i || (fflush (df) == EOF) || fsync (fd)) { sprintf (buf,"Message append failed: %s",strerror (errno)); MM_LOG (buf,ERROR); ftruncate (fd,sbuf.st_size); tp[0] = /* preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; ret = NIL; /* return error */ } else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ utime (file,tp); /* set the times */ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ fclose (df); MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */ int mmdf_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg) { unsigned long i,uf; int c; char tmp[MAILTMPLEN]; int hdrp = T; long f = mail_parse_flags (stream,flags,&uf); /* build initial header */ if ((fprintf (sf,"%sFrom %s@%s %sStatus: ", mmdfhdr,myusername (),mylocalhost (),date) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if (putc ('\n',sf) == EOF) return NIL; while (SIZE (msg)) { /* copy text to scratch file */ /* disregard CRs */ while ((c = (SIZE (msg)) ? (c = 0xff & SNX (msg)) : '\n') == '\r'); /* see if line needs special treatment */ if (hdrp && ((c == 'S') || (c == 'X'))) { /* copy line to buffer */ for (i = 1,tmp[0] = c; (c != '\n') && (i < MAILTMPLEN); ) if ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') != '\r') tmp[i++] = c; /* insert X- before metadata header */ if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && (tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && (tmp[5] == 's') && (tmp[6] == ':')) || (((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && (tmp[5] == ':')) || ((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && (tmp[4] == 'A') && (tmp[5] == 'P') && ((tmp[6] == ':') || ((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && (tmp[8] == 's') && (tmp[9] == 'e') && (tmp[10] == ':')))) || ((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && (tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && (tmp[7] == 's') && (tmp[8] == ':')) || ((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && (tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && (tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && (tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF)) return NIL; /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; /* set up to copy remainder of line */ if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); else continue; /* end of line or end of message */ } /* check for end of header */ else if (hdrp && (c == '\n')) hdrp = NIL; do switch (c) { /* copy line */ case MMDFCHR: /* flush CTRL/A */ case '\r': /* and CR */ break; default: /* any other character */ if (putc (c,sf) == EOF) return NIL; } while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* write trailer and return */ return (fputs (mmdfhdr,sf) == EOF) ? NIL : T; } /* Internal routines */ /* MMDF mail abort stream * Accepts: MAIL stream */ void mmdf_abort (MAILSTREAM *stream) { if (LOCAL) { /* only if a file is open */ if (LOCAL->fd >= 0) close (LOCAL->fd); if (LOCAL->ld >= 0) { /* have a mailbox lock? */ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ close (LOCAL->ld); /* close the lock file */ unlink (LOCAL->lname); /* and delete it */ } if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); /* free local text buffers */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MMDF open and lock mailbox * Accepts: file name to open/lock * file open mode * destination buffer for lock file name * type of locking operation (LOCK_SH or LOCK_EX) */ int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op) { int fd; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_FILELOCK,NIL); /* try locking the easy way */ if (dotlock_lock (file,lock,-1)) { /* got dotlock file, easy open */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } /* no dot lock file, open file now */ else if ((fd = open (file,flags,mode)) >= 0) { /* try paranoid way to make a dot lock file */ if (dotlock_lock (file,lock,fd)) { close (fd); /* get fresh fd in case of timing race */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } else flock (fd,op); /* paranoid way failed, just flock() it */ } (*bn) (BLOCK_NONE,NIL); return fd; } /* MMDF unlock and close mailbox * Accepts: file descriptor * (optional) mailbox stream to check atime/mtime * (optional) lock file name */ void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock) { if (stream) { /* need to muck with times? */ struct stat sbuf; time_t tp[2]; time_t now = time (0); fstat (fd,&sbuf); /* get file times */ if (LOCAL->ld >= 0) { /* yes, readwrite session? */ tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else if (stream->recent) { /* readonly with recent messages */ if ((sbuf.st_atime >= sbuf.st_mtime) || (sbuf.st_atime >= sbuf.st_ctime)) /* keep past mtime, whack back atime */ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; else now = 0; /* no time change needed */ } /* readonly with no recent messages */ else if ((sbuf.st_atime < sbuf.st_mtime) || (sbuf.st_atime < sbuf.st_ctime)) { tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else now = 0; /* no time change needed */ /* set the times, note change */ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; } flock (fd,LOCK_UN); /* release flock'ers */ if (!stream) close (fd); /* close the file if no stream */ dotlock_unlock (lock); /* flush the lock file if any */ } /* MMDF mail parse and lock mailbox * Accepts: MAIL stream * space to write lock file name * type of locking operation * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure */ int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op) { int ti,zn,m; unsigned long i,j,k; unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; int retain = T; unsigned long nmsgs = stream->nmsgs; unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; unsigned long recent = stream->recent; unsigned long oldnmsgs = stream->nmsgs; short silent = stream->silent; short pseudoseen = NIL; struct stat sbuf; STRING bs; FDDATA d; MESSAGECACHE *elt; mail_lock (stream); /* guard against recursion or pingers */ /* toss out previous descriptor */ if (LOCAL->fd >= 0) close (LOCAL->fd); MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */ if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ? O_RDWR : O_RDONLY,NIL,lock,op)) < 0) { sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (tmp,ERROR); mmdf_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } fstat (LOCAL->fd,&sbuf); /* get status */ /* validate change in size */ if (sbuf.st_size < LOCAL->filesize) { sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); /* this is pretty bad */ mmdf_unlock (LOCAL->fd,stream,lock); mmdf_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } /* new data? */ else if (i = sbuf.st_size - LOCAL->filesize) { d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = LOCAL->filesize; /* get to that position in the file */ d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ /* skip leading whitespace for broken MTAs */ while (((c = CHR (&bs)) == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) SNX (&bs); if (SIZE (&bs)) { /* read new data */ /* remember internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = mmdf_mbxline (stream,&bs,&i); stream->silent = T; /* quell main program new message events */ do { /* read MMDF header */ if (!(i && ISMMDF (s))){/* see if valid MMDF header */ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", (char *) s); /* see if we can back up to a line */ if (i && (j > MMDFHDRLEN)) { SETPOS (&bs,j -= MMDFHDRLEN); /* read previous line */ s = mmdf_mbxline (stream,&bs,&i); /* kill the error if it looks good */ if (i && ISMMDF (s)) tmp[0] = '\0'; } if (tmp[0]) { MM_LOG (tmp,ERROR); mmdf_unlock (LOCAL->fd,stream,lock); mmdf_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); return NIL; } } /* instantiate first new message */ mail_exists (stream,++nmsgs); (elt = mail_elt (stream,nmsgs))->valid = T; recent++; /* assume recent by default */ elt->recent = T; /* note position/size of internal header */ elt->private.special.offset = j; elt->private.special.text.size = i; s = mmdf_mbxline (stream,&bs,&i); ti = 0; /* assume not a valid date */ zn = 0,t = NIL; if (i) VALID (s,t,ti,zn); if (ti) { /* generate plausible IMAPish date string */ /* this is also part of header */ elt->private.special.text.size += i; date[2] = date[6] = date[20] = '-'; date[11] = ' '; date[14] = date[17] = ':'; /* dd */ date[0] = t[ti - 2]; date[1] = t[ti - 1]; /* mmm */ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; /* hh */ date[12] = t[ti + 1]; date[13] = t[ti + 2]; /* mm */ date[15] = t[ti + 4]; date[16] = t[ti + 5]; if (t[ti += 6]==':'){ /* ss */ date[18] = t[++ti]; date[19] = t[++ti]; ti++; /* move to space */ } else date[18] = date[19] = '0'; /* yy -- advance over timezone if necessary */ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4); date[7] = t[ti + 1]; date[8] = t[ti + 2]; date[9] = t[ti + 3]; date[10] = t[ti + 4]; /* zzz */ t = zn ? (t + zn + 1) : (unsigned char *) "LCL"; date[21] = *t++; date[22] = *t++; date[23] = *t++; if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0'; else { /* numeric time zone */ date[24] = *t++; date[25] = *t++; date[26] = '\0'; date[20] = ' '; } /* set internal date */ if (!mail_parse_date (elt,date)) { sprintf (tmp,"Unable to parse internal date: %s",(char *) date); MM_LOG (tmp,WARN); } } else { /* make date from file date */ struct tm *tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; t = NIL; /* suppress line read */ } /* header starts here */ elt->private.msg.header.offset = elt->private.special.text.size; do { /* look for message body */ if (t) s = t = mmdf_mbxline (stream,&bs,&i); else t = s; /* this line read was suppressed */ if (ISMMDF (s)) break; /* this line is part of header */ elt->private.msg.header.text.size += i; if (i) switch (*s) { /* check header lines */ case 'X': /* possible X-???: line */ if (s[1] == '-') { /* must be immediately followed by hyphen */ /* X-Status: becomes Status: in S case */ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' && s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2; /* possible X-Keywords */ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' && s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && s[8] == 'd' && s[9] == 's' && s[10] == ':') { SIZEDTEXT uf; retain = NIL; /* don't retain continuation */ s += 11; /* flush leading whitespace */ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){ while (*s == ' ') s++; /* find end of keyword */ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); /* got a keyword? */ if ((k = (u - s)) && (k < MAXUSERFLAG)) { uf.data = (unsigned char *) s; uf.size = k; for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j) if (!compare_csizedtext (stream->user_flags[j],&uf)) { elt->user_flags |= ((long) 1) << j; break; } } s = u; /* advance to next keyword */ } break; } /* possible X-IMAP */ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') && (s[5] == 'P') && ((m = (s[6] == ':')) || ((s[6] == 'b') && (s[7] == 'a') && (s[8] == 's') && (s[9] == 'e') && (s[10] == ':')))) { retain = NIL; /* don't retain continuation */ if ((nmsgs == 1) && !stream->uid_validity) { /* advance to data */ s += m ? 7 : 11; /* flush whitespace */ while (*s == ' ') s++; j = 0; /* slurp UID validity */ /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush whitespace */ while (*s == ' ') s++; /* must have valid UID validity and UID last */ if (j && isdigit (*s)) { /* pseudo-header seen if X-IMAP */ if (m) pseudoseen = LOCAL->pseudo = T; /* save UID validity */ stream->uid_validity = j; j = 0; /* slurp UID last */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* save UID last */ stream->uid_last = j; /* process keywords */ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n')); s = u,j++) { /* flush leading whitespace */ while (*s == ' ') s++; u = strpbrk (s," \n\r"); /* got a keyword? */ if ((k = (u - s)) && j < NUSERFLAGS) { if (stream->user_flags[j]) fs_give ((void **) &stream->user_flags[j]); stream->user_flags[j] = (char *) fs_get (k + 1); strncpy (stream->user_flags[j],s,k); stream->user_flags[j][k] = '\0'; } } } } break; } /* possible X-UID */ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' && s[5] == ':') { retain = NIL; /* don't retain continuation */ /* only believe if have a UID validity */ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) { s += 6; /* advance to UID value */ /* flush whitespace */ while (*s == ' ') s++; j = 0; /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush remainder of line */ while (*s != '\n') s++; /* make sure not duplicated */ if (elt->private.uid) sprintf (tmp,"Message %lu UID %lu already has UID %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,elt->private.uid); /* make sure UID doesn't go backwards */ else if (j <= prevuid) sprintf (tmp,"Message %lu UID %lu less than %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,prevuid + 1); /* or skip by mailbox's recorded last */ else if (j > stream->uid_last) sprintf (tmp,"Message %lu UID %lu greater than last %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,stream->uid_last); else { /* normal UID case */ prevuid = elt->private.uid = j; break; /* exit this cruft */ } MM_LOG (tmp,WARN); /* invalidate UID validity */ stream->uid_validity = 0; elt->private.uid = 0; } break; } } /* otherwise fall into S case */ case 'S': /* possible Status: line */ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' && s[5] == 's' && s[6] == ':') { retain = NIL; /* don't retain continuation */ s += 6; /* advance to status flags */ do switch (*s++) {/* parse flags */ case 'R': /* message read */ elt->seen = T; break; case 'O': /* message old */ if (elt->recent) { elt->recent = NIL; recent--; /* it really wasn't recent */ } break; case 'D': /* message deleted */ elt->deleted = T; break; case 'F': /* message flagged */ elt->flagged = T; break; case 'A': /* message answered */ elt->answered = T; break; case 'T': /* message is a draft */ elt->draft = T; break; default: /* some other crap */ break; } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))); break; /* all done */ } /* otherwise fall into default case */ default: /* ordinary header line */ if ((*s == 'S') || (*s == 's') || (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) { unsigned char *e,*v; /* must match what mail_filter() does */ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1); (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') && ((c > ' ') || ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))); *v++ = *u++); *v = '\0'; /* tie off */ /* matches internal header? */ if (!compare_cstring (tmp,"STATUS") || !compare_cstring (tmp,"X-STATUS") || !compare_cstring (tmp,"X-KEYWORDS") || !compare_cstring (tmp,"X-UID") || !compare_cstring (tmp,"X-IMAP") || !compare_cstring (tmp,"X-IMAPBASE")) { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus %s header in message %lu", (char *) tmp,elt->msgno); MM_LOG (err,WARN); retain = NIL; /* don't retain continuation */ break; /* different case or something */ } } /* retain or non-continuation? */ if (retain || ((*s != ' ') && (*s != '\t'))) { retain = T; /* retaining continuation now */ /* line length in LF format newline */ k = i - (((i >= 2) && (s[i - 2] == '\r')) ? 1 : 0); /* "internal" header size */ elt->private.data += k; /* message size */ elt->rfc822_size += k + 1; } else { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus continuation in msg %lu: %.80s", elt->msgno,(char *) s); if (u = strpbrk (err,"\r\n")) *u = '\0'; MM_LOG (err,WARN); break; /* different case or something */ } break; } } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); /* "internal" header sans trailing newline */ if (i) elt->private.data--; /* assign a UID if none found */ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { prevuid = elt->private.uid = ++stream->uid_last; elt->private.dirty = T; } else elt->private.dirty = elt->recent; /* note size of header, location of text */ elt->private.msg.header.text.size = (elt->private.msg.text.offset = (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - elt->private.special.text.size; /* note current position */ j = LOCAL->filesize + GETPOS (&bs); if (i) do { /* look for next message */ s = mmdf_mbxline (stream,&bs,&i); if (i) { /* got new data? */ if (ISMMDF (s)) break; else { elt->rfc822_size += i + (((i < 2) || s[i - 2] != '\r') ? 1 : 0); /* update current position */ j = LOCAL->filesize + GETPOS (&bs); } } } while (i); /* until found a header */ elt->private.msg.text.text.size = j - (elt->private.special.offset + elt->private.msg.text.offset); if (i) { /* get next header line */ /* remember first internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = mmdf_mbxline (stream,&bs,&i); } } while (i); /* until end of buffer */ if (pseudoseen) { /* flush pseudo-message if present */ /* decrement recent count */ if (mail_elt (stream,1)->recent) recent--; /* and the exists count */ mail_exists (stream,nmsgs--); mail_expunged(stream,1);/* fake an expunge of that message */ } /* need to start a new UID validity? */ if (!stream->uid_validity) { stream->uid_validity = time (0); /* in case a whiner with no life */ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) stream->uid_nosticky = T; else if (nmsgs) { /* don't bother if empty file */ LOCAL->dirty = T; /* make dirty to restart UID epoch */ /* need to rewrite msg 1 if not pseudo */ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; MM_LOG ("Assigning new unique identifiers to all messages",NIL); } } stream->nmsgs = oldnmsgs; /* whack it back down */ stream->silent = silent; /* restore old silent setting */ /* notify upper level of new mailbox sizes */ mail_exists (stream,nmsgs); mail_recent (stream,recent); /* mark dirty so O flags are set */ if (recent) LOCAL->dirty = T; } } /* no change, don't babble if never got time */ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime) MM_LOG ("New mailbox modification time but apparently no changes",WARN); /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; LOCAL->filetime = sbuf.st_mtime; return T; /* return the winnage */ } /* MMDF read line from mailbox * Accepts: mail stream * stringstruct * pointer to line size * Returns: pointer to input line */ char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) { unsigned long i,j,k,m; char *s,*t,*te,p1[CHUNK]; char *ret = ""; /* flush old buffer */ if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* if buffer needs refreshing */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) { /* find newline */ /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* difficult case if line spans buffer */ if ((i = s - bs->curpos) == bs->cursize) { memcpy (p1,bs->curpos,i); /* remember what we have so far */ /* load next buffer */ SETPOS (bs,k = GETPOS (bs) + i); /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; /* fast scan in overlap buffer */ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* huge line? */ if ((j = s - bs->curpos) == bs->cursize) { SETPOS (bs,GETPOS (bs) + j); /* look for end of line (s-l-o-w!!) */ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); SETPOS (bs,k); /* go back to where it started */ } /* got size of data, make buffer for return */ ret = LOCAL->line = (char *) fs_get (i + j + 2); memcpy (ret,p1,i); /* copy first chunk */ while (j) { /* copy remainder */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); i += k; /* account for this much read in */ j -= k; bs->curpos += k; /* increment new position */ bs->cursize -= k; /* eat that many bytes */ } if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */ ret[i++] = '\n'; /* make sure newline at end */ ret[i] = '\0'; /* makes debugging easier */ } else { /* this is easy */ ret = bs->curpos; /* string it at this position */ bs->curpos += ++i; /* increment new position */ bs->cursize -= i; /* eat that many bytes */ } *size = i; /* return that to user */ } else *size = 0; /* end of data, return empty */ /* embedded MMDF header at end of line? */ if ((*size > sizeof (MMDFHDRTXT)) && (s = ret + *size - (i = sizeof (MMDFHDRTXT) - 1)) && ISMMDF (s)) { SETPOS (bs,GETPOS (bs) - i);/* back up to start of MMDF header */ *size -= i; /* reduce length of line */ ret[*size - 1] = '\n'; /* force newline at end */ } return ret; } /* MMDF make pseudo-header * Accepts: MAIL stream * buffer to write pseudo-header * Returns: length of pseudo-header */ unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr) { int i; char *s,tmp[MAILTMPLEN]; time_t now = time (0); rfc822_fixed_date (tmp); sprintf (hdr,"%sFrom %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu", mmdfhdr,pseudo_from,ctime (&now), tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) now,mylocalhost (),stream->uid_validity, stream->uid_last); for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) sprintf (s += strlen (s)," %s",stream->user_flags[i]); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr); return strlen (hdr); } /* MMDF make status string * Accepts: MAIL stream * destination string to write * message cache entry * non-zero flag to write UID (.LT. 0 to write UID base info too) * Returns: length of string */ unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag) { char *t,stack[64]; char *s = status; unsigned long n; int pad = 50; /* This used to use sprintf(), but thanks to certain cretinous C libraries with horribly slow implementations of sprintf() I had to change it to this mess. At least it should be fast. */ /* need to write X-IMAPbase: header? */ if ((flag < 0) && !stream->uid_nosticky) { *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P'; *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' '; t = stack; n = stream->uid_validity; /* push UID validity digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID validity digits from stack */ while (t > stack) *s++ = *--t; *s++ = ' '; n = stream->uid_last; /* push UID last digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID last digits from stack */ while (t > stack) *s++ = *--t; for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n]) for (*s++ = ' '; *t; *s++ = *t++); *s++ = '\n'; pad += 30; /* increased padding if have IMAPbase */ } *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->seen) *s++ = 'R'; if (flag) *s++ = 'O'; /* only write O if have a UID */ *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->deleted) *s++ = 'D'; if (elt->flagged) *s++ = 'F'; if (elt->answered) *s++ = 'A'; if (elt->draft) *s++ = 'T'; *s++ = '\n'; if (!stream->uid_nosticky) { /* cretins with no life can't use this */ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w'; *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':'; if (n = elt->user_flags) do { *s++ = ' '; for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); } while (n); n = s - status; /* get size of stuff so far */ /* pad X-Keywords to make size constant */ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; *s++ = '\n'; if (flag) { /* want to include UID? */ t = stack; n = elt->private.uid; /* push UID digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; *s++ = ' '; /* pop UID from stack */ while (t > stack) *s++ = *--t; *s++ = '\n'; } } *s++ = '\n'; *s = '\0'; /* end of extended message status */ return s - status; /* return size of resulting string */ } /* Rewrite mailbox file * Accepts: MAIL stream, must be critical and locked * return pointer to number of expunged messages if want expunge * lock file name * Returns: T if success and mailbox unlocked, NIL if failure */ #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock) { MESSAGECACHE *elt; MMDFFILE f; char *s; time_t tp[2]; long ret,flag; unsigned long i,j; unsigned long recent = stream->recent; unsigned long size = LOCAL->pseudo ? mmdf_pseudo (stream,LOCAL->buf) : 0; if (nexp) *nexp = 0; /* initially nothing expunged */ /* calculate size of mailbox after rewrite */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) if (!(elt = mail_elt (stream,i))->deleted || !nexp) { /* add RFC822 size of this message */ size += elt->private.special.text.size + elt->private.data + mmdf_xstatus (stream,LOCAL->buf,elt,flag) + elt->private.msg.text.text.size + MMDFHDRLEN; flag = 1; /* only count X-IMAPbase once */ } /* no messages, has a life, and no pseudo */ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) { LOCAL->pseudo = T; /* so make a pseudo-message now */ size = mmdf_pseudo (stream,LOCAL->buf); } /* extend the file as necessary */ if (ret = mmdf_extend (stream,size)) { /* Set up buffered I/O file structure * curpos current position being written through buffering * filepos current position being written physically to the disk * bufpos current position being written in the buffer * protect current maximum position that can be written to the disk * before buffering is forced * The code tries to buffer so that that disk is written in multiples of * OVERBLOWBUFLEN bytes. */ f.stream = stream; /* note mail stream */ f.curpos = f.filepos = 0; /* start of file */ f.protect = stream->nmsgs ? /* initial protection pointer */ mail_elt (stream,1)->private.special.offset : 8192; f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); if (LOCAL->pseudo) /* update pseudo-header */ mmdf_write (&f,LOCAL->buf,mmdf_pseudo (stream,LOCAL->buf)); /* loop through all messages */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { elt = mail_elt (stream,i);/* get cache */ if (nexp && elt->deleted){/* expunge this message? */ /* one less recent message */ if (elt->recent) --recent; mail_expunged(stream,i);/* notify upper levels */ ++*nexp; /* count up one more expunged message */ } else { /* preserve this message */ i++; /* advance to next message */ if ((flag < 0) || /* need to rewrite message? */ elt->private.dirty || (f.curpos != elt->private.special.offset) || (elt->private.msg.header.text.size != (elt->private.data + mmdf_xstatus (stream,LOCAL->buf,elt,flag)))) { unsigned long newoffset = f.curpos; /* yes, seek to internal header */ lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); /* see if need to squeeze out a CR */ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') { LOCAL->buf[--elt->private.special.text.size - 1] = '\n'; --size; /* squeezed out a CR from PC */ } /* protection pointer moves to RFC822 header */ f.protect = elt->private.special.offset + elt->private.msg.header.offset; /* write internal header */ mmdf_write (&f,LOCAL->buf,elt->private.special.text.size); /* get RFC822 header */ s = mmdf_header (stream,elt->msgno,&j,FT_INTERNAL); /* in case this got decremented */ elt->private.msg.header.offset = elt->private.special.text.size; /* header size, sans trailing newline */ if ((j < 2) || (s[j - 2] == '\n')) j--; if (j != elt->private.data) fatal ("header size inconsistent"); /* protection pointer moves to RFC822 text */ f.protect = elt->private.special.offset + elt->private.msg.text.offset; mmdf_write (&f,s,j); /* write RFC822 header */ /* write status and UID */ mmdf_write (&f,LOCAL->buf, j = mmdf_xstatus (stream,LOCAL->buf,elt,flag)); flag = 1; /* only write X-IMAPbase once */ /* new file header size */ elt->private.msg.header.text.size = elt->private.data + j; /* did text move? */ if (f.curpos != f.protect) { /* get message text */ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL); /* this can happen if CRs were squeezed */ if (j < elt->private.msg.text.text.size) { /* so fix up counts */ size -= elt->private.msg.text.text.size - j; elt->private.msg.text.text.size = j; } /* can't happen it says here */ else if (j > elt->private.msg.text.text.size) fatal ("text size inconsistent"); /* new text offset, status/UID may change it */ elt->private.msg.text.offset = f.curpos - newoffset; /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : (f.curpos + j + MMDFHDRLEN); mmdf_write (&f,s,j);/* write text */ /* write trailing newline */ mmdf_write (&f,mmdfhdr,MMDFHDRLEN); } else { /* tie off header and status */ mmdf_write (&f,NIL,NIL); f.curpos = f.protect =/* restart everything at end of message */ f.filepos += elt->private.msg.text.text.size + MMDFHDRLEN; } /* new internal header offset */ elt->private.special.offset = newoffset; elt->private.dirty =NIL;/* message is now clean */ } else { /* no need to rewrite this message */ /* tie off previous message if needed */ mmdf_write (&f,NIL,NIL); f.curpos = f.protect =/* restart everything at end of message */ f.filepos += elt->private.special.text.size + elt->private.msg.header.text.size + elt->private.msg.text.text.size + MMDFHDRLEN; } } } mmdf_write (&f,NIL,NIL); /* tie off final message */ if (size != f.filepos) fatal ("file size inconsistent"); fs_give ((void **) &f.buf); /* free buffer */ /* make sure tied off */ ftruncate (LOCAL->fd,LOCAL->filesize = size); fsync (LOCAL->fd); /* make sure the updates take */ if (size && (flag < 0)) fatal ("lost UID base information"); LOCAL->dirty = NIL; /* no longer dirty */ /* notify upper level of new mailbox sizes */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); /* set atime to now, mtime a second earlier */ tp[1] = (tp[0] = time (0)) - 1; /* set the times, note change */ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; close (LOCAL->fd); /* close and reopen file */ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,NIL)) < 0) { sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); mmdf_abort (stream); } dotlock_unlock (lock); /* flush the lock file */ } return ret; /* return state from algorithm */ } /* Extend MMDF mailbox file * Accepts: MAIL stream * new desired size * Return: T if success, else NIL */ long mmdf_extend (MAILSTREAM *stream,unsigned long size) { unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0; if (i) { /* does the mailbox need to grow? */ if (i > LOCAL->buflen) { /* make sure have enough space */ /* this user won the lottery all right */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1); } memset (LOCAL->buf,'\0',i); /* get a block of nulls */ while (T) { /* until write successful or punt */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break; else { long e = errno; /* note error before doing ftruncate */ ftruncate (LOCAL->fd,LOCAL->filesize); if (MM_DISKERROR (stream,e,NIL)) { fsync (LOCAL->fd); /* user chose to punt */ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e)); if (!stream->silent) MM_LOG (LOCAL->buf,ERROR); return NIL; } } } } return LONGT; } /* Write data to buffered file * Accepts: buffered file pointer * file data or NIL to indicate "flush buffer" * date size (ignored for "flush buffer") * Does not return until success */ void mmdf_write (MMDFFILE *f,char *buf,unsigned long size) { unsigned long i,j,k; if (buf) { /* doing buffered write? */ i = f->bufpos - f->buf; /* yes, get size of current buffer data */ /* yes, have space in current buffer chunk? */ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) { /* yes, fill up buffer as much as we can */ memcpy (f->bufpos,buf,k = min (j,size)); f->bufpos += k; /* new buffer position */ f->curpos += k; /* new current position */ if (j -= k) return; /* all done if still have buffer free space */ buf += k; /* full, get new unwritten data pointer */ size -= k; /* new data size */ i += k; /* new buffer data size */ } /* This chunk of the buffer is full. See if can make some space by * writing to the disk, if there's enough unprotected space to do so. * Try to fill out any unaligned chunk, along with any subsequent full * chunks that will fit in unprotected space. */ /* any unprotected space we can write to? */ if (j = min (i,f->protect - f->filepos)) { /* yes, filepos not at chunk boundary? */ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j)) j -= k; /* yes, and can write out partial chunk */ else k = 0; /* no partial chunk to write */ /* if at least a chunk free, write that too */ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN); if (k) { /* write data if there is anything we can */ mmdf_phys_write (f,f->buf,k); /* slide buffer */ if (i -= k) memmove (f->buf,f->buf + k,i); f->bufpos = f->buf + i; /* new end of buffer */ } } /* Have flushed the buffer as best as possible. All done if no more * data to write. Otherwise, if the buffer is empty AND if the unwritten * data is larger than a chunk AND the unprotected space is also larger * than a chunk, then write as many chunks as we can directly from the * data. Buffer the rest, expanding the buffer as needed. */ if (size) { /* have more data that we need to buffer? */ /* can write any of it to disk instead? */ if ((f->bufpos == f->buf) && ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) { /* write as much as we can right now */ mmdf_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN)); buf += j; /* new data pointer */ size -= j; /* new data size */ f->curpos += j; /* advance current pointer */ } if (size) { /* still have data that we need to buffer? */ /* yes, need to expand the buffer? */ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) { /* note current position in buffer */ j = f->bufpos - f->buf; i += OVERFLOWBUFLEN; /* yes, grow another chunk */ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN)); /* in case buffer relocated */ f->bufpos = f->buf + j; } /* buffer remaining data */ memcpy (f->bufpos,buf,size); f->bufpos += size; /* new end of buffer */ f->curpos += size; /* advance current pointer */ } } } else { /* flush buffer to disk */ mmdf_phys_write (f,f->buf,i = f->bufpos - f->buf); f->bufpos = f->buf; /* reset buffer */ /* update positions */ f->curpos = f->protect = f->filepos; } } /* Physical disk write * Accepts: buffered file pointer * buffer address * buffer size * Does not return until success */ void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size) { MAILSTREAM *stream = f->stream; /* write data at desired position */ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) || (write (LOCAL->fd,buf,size) < 0))) { int e; char tmp[MAILTMPLEN]; sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno)); MM_LOG (tmp,ERROR); MM_DISKERROR (NIL,e,T); /* serious problem, must retry */ } f->filepos += size; /* update file position */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mtx.c000066400000000000000000001310671137544547100226750ustar00rootroot00000000000000/* * Program: MTX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "misc.h" #include "dummy.h" /* MTX I/O stream local data */ typedef struct mtx_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } MTXLOCAL; /* Convenient access to local data */ #define LOCAL ((MTXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mtx_valid (char *name); int mtx_isvalid (char *name,char *tmp); void *mtx_parameters (long function,void *value); void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mtx_list (MAILSTREAM *stream,char *ref,char *pat); void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mtx_create (MAILSTREAM *stream,char *mailbox); long mtx_delete (MAILSTREAM *stream,char *mailbox); long mtx_rename (MAILSTREAM *stream,char *old,char *newname); long mtx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mtx_open (MAILSTREAM *stream); void mtx_close (MAILSTREAM *stream,long options); void mtx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mtx_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mtx_ping (MAILSTREAM *stream); void mtx_check (MAILSTREAM *stream); void mtx_snarf (MAILSTREAM *stream); void mtx_expunge (MAILSTREAM *stream); long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); char *mtx_file (char *dst,char *name); long mtx_parse (MAILSTREAM *stream); MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno); void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag); unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* MTX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mtxdriver = { "mtx", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY|DR_LOCKING, (DRIVER *) NIL, /* next driver */ mtx_valid, /* mailbox is valid for us */ mtx_parameters, /* manipulate parameters */ mtx_scan, /* scan mailboxes */ mtx_list, /* list mailboxes */ mtx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ mtx_delete, /* delete mailbox */ mtx_rename, /* rename mailbox */ mtx_status, /* status of mailbox */ mtx_open, /* open mailbox */ mtx_close, /* close mailbox */ mtx_flags, /* fetch message "fast" attributes */ mtx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mtx_header, /* fetch message header */ mtx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mtx_flag, /* modify flags */ mtx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mtx_ping, /* ping mailbox to see if still alive */ mtx_check, /* check for new messages */ mtx_expunge, /* expunge deleted messages */ mtx_copy, /* copy messages to another mailbox */ mtx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mtxproto = {&mtxdriver}; /* MTX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mtx_valid (char *name) { char tmp[MAILTMPLEN]; return mtx_isvalid (name,tmp) ? &mtxdriver : NIL; } /* MTX mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int mtx_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = mtx_file (file,name)) && !stat (s,&sbuf)) { if (!sbuf.st_size) { /* allow empty file if INBOX */ if ((s = mailboxfile (tmp,name)) && !*s) ret = T; else errno = 0; /* empty file */ } else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) && (s[1] == '\012')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } /* in case INBOX but not mtx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MTX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mtx_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = mtx_file ((char *) value,"INBOX"); break; } return ret; } /* MTX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MTX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MTX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MTX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mtx_delete (MAILSTREAM *stream,char *mailbox) { return mtx_rename (stream,mailbox,NIL); } /* MTX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mtx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = T; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!mtx_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); MM_LOG (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); MM_LOG (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) ret = NIL; else *s = c; /* restore full name */ } /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } } else if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"INBOX.MTX"); return ret; /* return success */ } /* Mtx Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long mtx_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; /* calculate post-snarf results */ if (!status.recent && stream->inbox && (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* MTX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mtx_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mtxproto); if (stream->local) fatal ("mtx recycle stream"); user_flags (stream); /* set up user flags */ /* canonicalize the mailbox name */ if (!mtx_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (MTXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) { MM_LOG ("Unable to lock open mailbox",ERROR); return NIL; } (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* lock the file */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ /* time not set up yet */ LOCAL->lastsnarf = LOCAL->filetime = 0; LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (mtx_ping (stream) && !stream->nmsgs) MM_LOG ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* MTX mail close * Accepts: MAIL stream * close options */ void mtx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mtx_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MTX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mtx_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if (mtx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) mtx_elt (stream,i); } /* MTX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET); /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } LOCAL->buf[*length] = '\0'; /* tie off string */ /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length); return LOCAL->buf; } /* MTX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mtx_elt (stream,msgno); /* get message status */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mtx_update_status (stream,msgno,T); MM_FLAGS (stream,msgno); } /* in case previous text cached */ if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mtx_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); return T; /* success */ } /* MTX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { time_t tp[2]; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read comes after all that */ utime (stream->mailbox,tp); } } /* MTX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ mtx_update_status (stream,elt->msgno,NIL); } /* MTX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mtx_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) mtx_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (mtx_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } if (LOCAL) { /* stream must still be alive */ /* snarf if this is a read-write inbox */ if (stream->inbox && !stream->rdonly) { mtx_snarf (stream); fstat (LOCAL->fd,&sbuf);/* see if file changed now */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (mtx_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } } } return r; /* return result of the parse */ } /* MTX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mtx_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (mtx_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* MTX mail snarf messages from system inbox * Accepts: MAIL stream */ void mtx_snarf (MAILSTREAM *stream) { unsigned long i = 0; unsigned long j,r,hdrlen,txtlen; struct stat sbuf; char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN]; MESSAGECACHE *elt; MAILSTREAM *sysibx = NIL; int ld; /* give up if can't get exclusive permission */ if ((time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && strcmp (sysinbox (),stream->mailbox) && ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) { MM_CRITICAL (stream); /* go critical */ /* sizes match and anything in sysinbox? */ if (!stat (sysinbox (),&sbuf) && sbuf.st_size && !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { /* yes, go to end of file in our mailbox */ lseek (LOCAL->fd,sbuf.st_size,L_SET); /* for each message in sysibx mailbox */ while (r && (++i <= sysibx->nmsgs)) { /* snarf message from system INBOX */ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL)); txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK); /* if have a message */ if (j = hdrlen + txtlen) { /* calculate header line */ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i)); sprintf (LOCAL->buf + strlen (LOCAL->buf), ",%lu;0000000000%02o\015\012",j,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* copy message */ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) || (write (LOCAL->fd,hdr,hdrlen) < 0) || (write (LOCAL->fd,txt,txtlen) < 0)) r = 0; } fs_give ((void **) &hdr); } /* make sure all the updates take */ if (fsync (LOCAL->fd)) r = 0; if (r) { /* delete all the messages we copied */ if (r == 1) strcpy (tmp,"1"); else sprintf (tmp,"1:%lu",r); mail_flag (sysibx,tmp,"\\Deleted",ST_SET); mail_expunge (sysibx); /* now expunge all those messages */ } else { sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (LOCAL->fd,sbuf.st_size); } fstat (LOCAL->fd,&sbuf); /* yes, get current file size */ LOCAL->filetime = sbuf.st_mtime; } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ unlockfd (ld,lock); /* release exclusive parse/append permission */ LOCAL->lastsnarf = time (0);/* note time of last snarf */ } } /* MTX mail expunge mailbox * Accepts: MAIL stream */ void mtx_expunge (MAILSTREAM *stream) { time_t tp[2]; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* do nothing if stream dead */ if (!mtx_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ MM_LOG ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* The cretins who designed flock() created a window of vulnerability in * upgrading locks from shared to exclusive or downgrading from exclusive * to shared. Rather than maintain the lock at shared status at a minimum, * flock() actually *releases* the former lock. Obviously they never talked * to any database guys. Fortunately, we have the parse/append permission * lock. If we require this lock before going exclusive on the mailbox, * another process can not sneak in and steal the exclusive mailbox lock on * us, because it will block on trying to get parse/append permission first. */ /* get exclusive parse/append permission */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!mtx_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ (*bn) (BLOCK_NONE,NIL); MM_LOG("Can't expunge because mailbox is in use by another process",ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } MM_CRITICAL (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = mtx_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + elt->rfc822_size; if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ lseek (LOCAL->fd,pos,L_SET); while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); MM_LOG (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* reset atime to now */ utime (stream->mailbox,tp); MM_NOCRITICAL (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* MTX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; time_t tp[2]; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd= open (mtx_file (file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0){ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock copy mailbox",ERROR); MM_NOCRITICAL (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + elt->rfc822_size; do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = mtx_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ mtx_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure atime remains greater */ utime (stream->mailbox,tp); } } return ret; } /* MTX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; time_t tp[2]; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = user_flags (&mtxproto); /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"INBOX.MTX"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open (mtx_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } MM_CRITICAL (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf, (unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); } ret = NIL; } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Internal routines */ /* MTX mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *mtx_file (char *dst,char *name) { char tmp[MAILTMPLEN]; char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX",tmp) ? "~/INBOX" : "INBOX.MTX") : s; } /* MTX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mtx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ mtx_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ time_t tp[2]; tp[0] = time (0); tp[1] = LOCAL->filetime; utime (stream->mailbox,tp); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MTX get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; mtx_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) MM_FLAGS (stream,msgno); /* let top level know */ return elt; } /* MTX read flags from file * Accepts: MAIL stream * Returns: cache element */ void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* MTX update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { time_t tp[2]; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* write new flags */ write (LOCAL->fd,LOCAL->buf,12); if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read is later */ utime (stream->mailbox,tp); } } } /* MTX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; int q = 0; char *s,tmp[MAILTMPLEN]; MESSAGECACHE *elt = mtx_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for CRLF CRLF */ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) { /* read another buffer as necessary */ if ((--i <= 0) && /* buffer empty? */ (read (LOCAL->fd,s = tmp, i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)) return ret; /* I/O error? */ switch (q) { /* sniff at buffer */ case 0: /* first character */ q = (*s++ == '\015') ? 1 : 0; break; case 1: /* second character */ q = (*s++ == '\012') ? 2 : 0; break; case 2: /* third character */ q = (*s++ == '\015') ? 3 : 0; break; case 3: /* fourth character */ if (*s++ == '\012') { /* have the sequence? */ /* yes, note for later */ elt->private.msg.header.text.size = *size = siz; return ret; } q = 0; /* lost... */ break; } } /* header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mx.c000066400000000000000000001055641137544547100225140ustar00rootroot00000000000000/* * Program: MX mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1996 * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "mx.h" #include "misc.h" #include "dummy.h" /* MX I/O stream local data */ typedef struct mx_local { int fd; /* file descriptor of open index */ char *dir; /* spool directory name */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long cachedtexts; /* total size of all cached texts */ time_t scantime; /* last time directory scanned */ } MXLOCAL; /* Convenient access to local data */ #define LOCAL ((MXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mx_valid (char *name); int mx_isvalid (char *name,char *tmp); void *mx_parameters (long function,void *value); void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mx_list (MAILSTREAM *stream,char *ref,char *pat); void mx_lsub (MAILSTREAM *stream,char *ref,char *pat); void mx_list_work (MAILSTREAM *stream,char *dir,char *pat,long level); long mx_subscribe (MAILSTREAM *stream,char *mailbox); long mx_unsubscribe (MAILSTREAM *stream,char *mailbox); long mx_create (MAILSTREAM *stream,char *mailbox); long mx_delete (MAILSTREAM *stream,char *mailbox); long mx_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *mx_open (MAILSTREAM *stream); void mx_close (MAILSTREAM *stream,long options); void mx_fast (MAILSTREAM *stream,char *sequence,long flags); char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt); char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mx_ping (MAILSTREAM *stream); void mx_check (MAILSTREAM *stream); void mx_expunge (MAILSTREAM *stream); long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long options); long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int mx_numsort (const void *d1,const void *d2); char *mx_file (char *dst,char *name); long mx_lockindex (MAILSTREAM *stream); void mx_unlockindex (MAILSTREAM *stream); void mx_setdate (char *file,MESSAGECACHE *elt); /* MX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mxdriver = { "mx", /* driver name */ /* driver flags */ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING, (DRIVER *) NIL, /* next driver */ mx_valid, /* mailbox is valid for us */ mx_parameters, /* manipulate parameters */ mx_scan, /* scan mailboxes */ mx_list, /* find mailboxes */ mx_lsub, /* find subscribed mailboxes */ mx_subscribe, /* subscribe to mailbox */ mx_unsubscribe, /* unsubscribe from mailbox */ mx_create, /* create mailbox */ mx_delete, /* delete mailbox */ mx_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mx_open, /* open mailbox */ mx_close, /* close mailbox */ mx_fast, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mx_header, /* fetch message header only */ mx_text, /* fetch message body only */ NIL, /* fetch partial message test */ NIL, /* unique identifier */ NIL, /* message number */ mx_flag, /* modify flags */ mx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mx_ping, /* ping mailbox to see if still alive */ mx_check, /* check for new messages */ mx_expunge, /* expunge deleted messages */ mx_copy, /* copy messages to another mailbox */ mx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mxproto = {&mxdriver}; /* MX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mx_valid (char *name) { char tmp[MAILTMPLEN]; return mx_isvalid (name,tmp) ? &mxdriver : NIL; } /* MX mail test for valid mailbox * Accepts: mailbox name * temporary buffer to use * Returns: T if valid, NIL otherwise */ int mx_isvalid (char *name,char *tmp) { struct stat sbuf; errno = NIL; /* zap error */ /* validate name as directory */ return (!stat (MXINDEX (tmp,name),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)); } /* MX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mx_parameters (long function,void *value) { return NIL; } /* MX scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) MM_LOG ("Scan not valid for mx mailboxes",ERROR); } /* MX list mailboxes * Accepts: mail stream * reference * pattern to search */ void mx_list (MAILSTREAM *stream,char *ref,char *pat) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i = 0; /* get canonical form of name */ if (stream && dummy_canonicalize (test,ref,pat)) { /* found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test,i = s - test); file[i] = '\0'; /* tie off */ } else strcpy (file,test); /* use just that name then */ /* find directory name */ if (s = strrchr (file,'/')) { *s = '\0'; /* found, tie off at that point */ s = file; } /* do the work */ mx_list_work (stream,s,test,0); } } /* MX list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MX list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * search level */ void mx_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) { DIR *dp; struct direct *d; struct stat sbuf; char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN]; if (dir && *dir) { /* make mailbox and directory names */ sprintf (name,"%s/",dir); /* print name starts at directory */ mx_file (curdir,dir); /* path starts from mx_file() name */ } else { /* no directory, use mailbox home dir */ mx_file (curdir,mailboxdir (name,NIL,NIL)); name[0] = '\0'; /* dummy name */ } if (dp = opendir (curdir)) { /* open directory */ np = name + strlen (name); cp = curdir + strlen (strcat (curdir,"/")); while (d = readdir (dp)) { /* scan, ignore . and numeric names */ if ((d->d_name[0] != '.') && !mx_select (d)) { if (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)) { strcpy (cp,d->d_name);/* make directory name */ strcpy (np,d->d_name);/* make mx name of directory name */ if (dmatch (name,pat,'/') && !stat (curdir,&sbuf) && ((sbuf.st_mode &= S_IFMT) == S_IFDIR)) mx_list_work (stream,name,pat,level+1); } } else if (!strcmp (d->d_name,MXINDEXNAME+1) && pmatch_full (dir,pat,'/')) mm_list (stream,'/',dir,NIL); } closedir (dp); /* all done, flush directory */ } } /* MX mail subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long mx_subscribe (MAILSTREAM *stream,char *mailbox) { return sm_subscribe (mailbox); } /* MX mail unsubscribe to mailbox * Accepts: mail stream * mailbox to delete from subscription list * Returns: T on success, NIL on failure */ long mx_unsubscribe (MAILSTREAM *stream,char *mailbox) { return sm_unsubscribe (mailbox); } /* MX mail create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long mx_create (MAILSTREAM *stream,char *mailbox) { int fd; char *s,tmp[MAILTMPLEN],mbx[MAILTMPLEN]; /* assume error */ sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox); /* make sure valid name */ for (s = mailbox; s && *s;) { if (isdigit (*s)) s++; /* digit, check this node further... */ /* all digit node, barf */ else if (*s == '/') s = NIL; /* non-digit in node, skip to next node */ else if (s = strchr (s+1,'/')) s++; else tmp[0] = NIL; /* no more nodes, good name */ } if (tmp[0]); /* was there an error in the name? */ /* must not already exist */ else if (mx_isvalid (mailbox,tmp)) sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox); /* create directory */ else if (!dummy_create_path (stream,strcat (mx_file (mbx,mailbox),"/"), get_dir_protection (mailbox))) sprintf (tmp,"Can't create mailbox leaf %.80s: %s", mailbox,strerror (errno)); else { /* create index file */ int mask = umask (0); if (((fd = open (MXINDEX (tmp,mailbox),O_WRONLY|O_CREAT|O_EXCL, (int) mail_parameters (NIL,GET_MBXPROTECTION,mailbox)))<0) || close (fd)) sprintf (tmp,"Can't create mailbox index %.80s: %s", mailbox,strerror (errno)); else { /* success */ set_mbx_protections (mailbox,mbx); set_mbx_protections (mailbox,tmp); tmp[0] = NIL; } umask (mask); /* restore mask */ } if (!tmp[0]) return LONGT; /* success */ MM_LOG (tmp,ERROR); /* some error */ return NIL; } /* MX mail delete mailbox * mailbox name to delete * Returns: T on success, NIL on failure */ long mx_delete (MAILSTREAM *stream,char *mailbox) { DIR *dirp; struct direct *d; char *s; char tmp[MAILTMPLEN]; if (!mx_isvalid (mailbox,tmp)) sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox); /* delete index */ else if (unlink (MXINDEX (tmp,mailbox))) sprintf (tmp,"Can't delete mailbox %.80s index: %s", mailbox,strerror (errno)); else { /* get directory name */ *(s = strrchr (tmp,'/')) = '\0'; if (dirp = opendir (tmp)) { /* open directory */ *s++ = '/'; /* restore delimiter */ /* massacre messages */ while (d = readdir (dirp)) if (mx_select (d)) { strcpy (s,d->d_name); /* make path */ unlink (tmp); /* sayonara */ } closedir (dirp); /* flush directory */ } /* try to remove the directory */ if (rmdir (mx_file (tmp,mailbox))) { sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno)); mm_log (tmp,WARN); } return T; /* always success */ } MM_LOG (tmp,ERROR); /* something failed */ return NIL; } /* MX mail rename mailbox * Accepts: MX mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long mx_rename (MAILSTREAM *stream,char *old,char *newname) { char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN]; struct stat sbuf; if (!mx_isvalid (old,tmp)) sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old); /* new mailbox name must not be valid */ else if (mx_isvalid (newname,tmp)) sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists", newname); /* success if can rename the directory */ else { /* found superior to destination name? */ if (s = strrchr (mx_file (tmp1,newname),'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp1,get_dir_protection (newname))) return NIL; *s = c; /* restore full name */ } if (!rename (mx_file (tmp,old),tmp1)) { /* recreate file if renamed INBOX */ if (!compare_cstring (old,"INBOX")) mx_create (NIL,"INBOX"); return T; } sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s", old,newname,strerror (errno)); } MM_LOG (tmp,ERROR); /* something failed */ return NIL; } /* MX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mx_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mxproto); if (stream->local) fatal ("mx recycle stream"); stream->local = fs_get (sizeof (MXLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); mx_file (tmp,stream->mailbox);/* get directory name */ LOCAL->dir = cpystr (tmp); /* copy directory name for later */ /* make temporary buffer */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); LOCAL->scantime = 0; /* not scanned yet */ LOCAL->fd = -1; /* no index yet */ LOCAL->cachedtexts = 0; /* no cached texts */ stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (mx_ping (stream) && !(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",(long) NIL); stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T; /* can we create new user flags? */ return stream; /* return stream to caller */ } /* MX mail close * Accepts: MAIL stream * close options */ void mx_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mx_expunge (stream); if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); /* free local scratch buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ stream->silent = silent; /* reset silent state */ } } /* MX mail fetch fast information * Accepts: MAIL stream * sequence * option flags */ void mx_fast (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; MESSAGECACHE *elt; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) mx_fast_work (stream,elt); } /* MX mail fetch fast information * Accepts: MAIL stream * message cache element * Returns: name of message file */ char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; struct tm *tm; /* build message file name */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if (!elt->rfc822_size) { /* have size yet? */ stat (LOCAL->buf,&sbuf); /* get size of message */ /* make plausible IMAPish date string */ tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0; elt->rfc822_size = sbuf.st_size; } return LOCAL->buf; /* return file name */ } /* MX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i; int fd; MESSAGECACHE *elt; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get elt */ if (!elt->private.msg.header.text.data) { /* purge cache if too big */ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) { mail_gc (stream,GC_TEXTS);/* just can't keep that much */ LOCAL->cachedtexts = 0; } if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL)) < 0) return ""; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,elt->rfc822_size); /* tie off file */ LOCAL->buf[elt->rfc822_size] = '\0'; close (fd); /* flush message file */ /* find end of header */ if (elt->rfc822_size < 4) i = 0; else for (i = 4; (i < elt->rfc822_size) && !((LOCAL->buf[i - 4] == '\015') && (LOCAL->buf[i - 3] == '\012') && (LOCAL->buf[i - 2] == '\015') && (LOCAL->buf[i - 1] == '\012')); i++); /* copy header */ cpytxt (&elt->private.msg.header.text,LOCAL->buf,i); cpytxt (&elt->private.msg.text.text,LOCAL->buf+i,elt->rfc822_size - i); /* add to cached size */ LOCAL->cachedtexts += elt->rfc822_size; } *length = elt->private.msg.header.text.size; return (char *) elt->private.msg.header.text.data; } /* MX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL on failure */ long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno); /* snarf message if don't have it yet */ if (!elt->private.msg.text.text.data) { mx_header (stream,msgno,&i,flags); if (!elt->private.msg.text.text.data) return NIL; } /* mark as seen */ if (!(flags & FT_PEEK) && mx_lockindex (stream)) { elt->seen = T; mx_unlockindex (stream); MM_FLAGS (stream,msgno); } INIT (bs,mail_string,elt->private.msg.text.text.data, elt->private.msg.text.text.size); return T; } /* MX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { mx_unlockindex (stream); /* finished with index */ } /* MX per-message modify flags * Accepts: MAIL stream * message cache element */ void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { mx_lockindex (stream); /* lock index if not already locked */ } /* MX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long mx_ping (MAILSTREAM *stream) { MAILSTREAM *sysibx = NIL; MESSAGECACHE *elt,*selt; struct stat sbuf; char *s,tmp[MAILTMPLEN]; int fd; unsigned long i,j,r,old; long nmsgs = stream->nmsgs; long recent = stream->recent; int silent = stream->silent; if (stat (LOCAL->dir,&sbuf)) return NIL; stream->silent = T; /* don't pass up mm_exists() events yet */ if (sbuf.st_ctime != LOCAL->scantime) { struct direct **names = NIL; long nfiles = scandir (LOCAL->dir,&names,mx_select,mx_numsort); if (nfiles < 0) nfiles = 0; /* in case error */ old = stream->uid_last; /* note scanned now */ LOCAL->scantime = sbuf.st_ctime; /* scan directory */ for (i = 0; i < nfiles; ++i) { /* if newly seen, add to list */ if ((j = atoi (names[i]->d_name)) > old) { /* swell the cache */ mail_exists (stream,++nmsgs); stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j; elt->valid = T; /* note valid flags */ if (old) { /* other than the first pass? */ elt->recent = T; /* yup, mark as recent */ recent++; /* bump recent count */ } } fs_give ((void **) &names[i]); } /* free directory */ if (s = (void *) names) fs_give ((void **) &s); } stream->nmsgs = nmsgs; /* don't upset mail_uid() */ /* if INBOX, snarf from system INBOX */ if (mx_lockindex (stream) && stream->inbox) { old = stream->uid_last; /* paranoia check */ if (!strcmp (sysinbox (),stream->mailbox)) { stream->silent = silent; return NIL; } MM_CRITICAL (stream); /* go critical */ stat (sysinbox (),&sbuf); /* see if anything there */ /* can get sysinbox mailbox? */ if (sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */ /* build file name we will use */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old); /* snarf message from Berkeley mailbox */ selt = mail_elt (sysibx,i); if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL, S_IREAD|S_IWRITE)) >= 0) && (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) && (write (fd,s,j) == j) && (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) && (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) { /* swell the cache */ mail_exists (stream,++nmsgs); stream->uid_last = /* create new elt, note its file number */ (elt = mail_elt (stream,nmsgs))->private.uid = old; recent++; /* bump recent count */ /* set up initial flags and date */ elt->valid = elt->recent = T; elt->seen = selt->seen; elt->deleted = selt->deleted; elt->flagged = selt->flagged; elt->answered = selt->answered; elt->draft = selt->draft; elt->day = selt->day;elt->month = selt->month;elt->year = selt->year; elt->hours = selt->hours;elt->minutes = selt->minutes; elt->seconds = selt->seconds; elt->zhours = selt->zhours; elt->zminutes = selt->zminutes; elt->zoccident = selt->zoccident; mx_setdate (LOCAL->buf,elt); } else { /* failed to snarf */ if (fd) { /* did it ever get opened? */ close (fd); /* close descriptor */ unlink (LOCAL->buf);/* flush this file */ } stream->silent = silent; return NIL; /* note that something is badly wrong */ } sprintf (tmp,"%lu",i); /* delete it from the sysinbox */ mail_flag (sysibx,tmp,"\\Deleted",ST_SET); } stat (LOCAL->dir,&sbuf); /* update scan time */ LOCAL->scantime = sbuf.st_ctime; mail_expunge (sysibx); /* now expunge all those messages */ } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ } mx_unlockindex (stream); /* done with index */ stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */ mail_recent (stream,recent); return T; /* return that we are alive */ } /* MX mail check mailbox * Accepts: MAIL stream */ void mx_check (MAILSTREAM *stream) { if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* MX mail expunge mailbox * Accepts: MAIL stream */ void mx_expunge (MAILSTREAM *stream) { MESSAGECACHE *elt; unsigned long i = 1; unsigned long n = 0; unsigned long recent = stream->recent; if (mx_lockindex (stream)) { /* lock the index */ MM_CRITICAL (stream); /* go critical */ while (i <= stream->nmsgs) {/* for each message */ /* if deleted, need to trash it */ if ((elt = mail_elt (stream,i))->deleted) { sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if (unlink (LOCAL->buf)) {/* try to delete the message */ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i, strerror (errno)); MM_LOG (LOCAL->buf,(long) NIL); break; } /* note uncached */ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ? elt->private.msg.header.text.size : 0) + (elt->private.msg.text.text.data ? elt->private.msg.text.text.size : 0)); mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS); if(elt->recent)--recent;/* if recent, note one less recent message */ mail_expunged(stream,i);/* notify upper levels */ n++; /* count up one more expunged message */ } else i++; /* otherwise try next message */ } if (n) { /* output the news if any expunged */ sprintf (LOCAL->buf,"Expunged %lu messages",n); MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); MM_NOCRITICAL (stream); /* release critical */ mx_unlockindex (stream); /* finished with index */ } /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); } /* MX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { STRING st; MESSAGECACHE *elt; struct stat sbuf; int fd; unsigned long i,j; char *t,flags[MAILTMPLEN],date[MAILTMPLEN]; /* copy the messages */ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))<0) return NIL; fstat (fd,&sbuf); /* get size of message */ /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size); /* init flag string */ flags[0] = flags[1] = '\0'; if (j = elt->user_flags) do if (t = stream->user_flags[find_rightmost_bit (&j)]) strcat (strcat (flags," "),t); while (j); if (elt->seen) strcat (flags," \\Seen"); if (elt->deleted) strcat (flags," \\Deleted"); if (elt->flagged) strcat (flags," \\Flagged"); if (elt->answered) strcat (flags," \\Answered"); if (elt->draft) strcat (flags," \\Draft"); flags[0] = '('; /* open list */ strcat (flags,")"); /* close list */ mail_date (date,elt); /* generate internal date */ if (!mail_append_full (NIL,mailbox,flags,date,&st)) return NIL; if (options & CP_MOVE) elt->deleted = T; } return T; /* return success */ } /* MX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { MESSAGECACHE *elt,selt; MAILSTREAM *astream; int fd; char *flags,*date,*s,tmp[MAILTMPLEN]; STRING *message; long f,i,size; unsigned long uf; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = user_flags (&mxproto); /* N.B.: can't use LOCAL->buf for tmp */ /* make sure valid mailbox */ if (!mx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* lock the index */ if (mx_lockindex (astream)) do { if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } /* parse flags */ f = mail_parse_flags (astream,flags,&uf); if (date) { /* want to preserve date? */ /* yes, parse date into an elt */ if (!mail_parse_date (&selt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; break; } } mx_file (tmp,mailbox); /* make message name */ sprintf (tmp + strlen (tmp),"/%lu",++astream->uid_last); if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { sprintf (tmp,"Can't create append message: %s",strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; break; } /* copy message */ s = (char *) fs_get (size = SIZE (message)); for (i = 0; i < size; s[i++] = SNX (message)); /* write the data */ if ((write (fd,s,size) < 0) || fsync (fd)) { unlink (tmp); /* delete mailbox */ sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; } fs_give ((void **) &s); /* flush the buffer */ close (fd); /* close the file */ if (ret) { /* set the date for this message */ if (date) mx_setdate (tmp,&selt); /* swell the cache */ mail_exists (astream,++astream->nmsgs); /* copy flags */ (elt = mail_elt (astream,astream->nmsgs))->private.uid=astream->uid_last; if (f&fSEEN) elt->seen = T; if (f&fDELETED) elt->deleted = T; if (f&fFLAGGED) elt->flagged = T; if (f&fANSWERED) elt->answered = T; if (f&fDRAFT) elt->draft = T; elt->user_flags |= uf; /* get next message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); else { MM_LOG ("Message append failed: unable to lock index",ERROR); ret = NIL; } mx_unlockindex (astream); /* unlock index */ MM_NOCRITICAL (stream); /* release critical */ mail_close (astream); return ret; } /* Internal routines */ /* MX file name selection test * Accepts: candidate directory entry * Returns: T to use file name, NIL to skip it */ int mx_select (struct direct *name) { char c; char *s = name->d_name; while (c = *s++) if (!isdigit (c)) return NIL; return T; } /* MX file name comparision * Accepts: first candidate directory entry * second candidate directory entry * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2 */ int mx_numsort (const void *d1,const void *d2) { return atoi ((*(struct direct **) d1)->d_name) - atoi ((*(struct direct **) d2)->d_name); } /* MX mail build file name * Accepts: destination string * source * Returns: destination */ char *mx_file (char *dst,char *name) { char *s; if (!(mailboxfile (dst,name) && *dst)) return mailboxfile (dst,"~/INBOX"); /* tie off unnecessary trailing / */ if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0'; return dst; } /* MX read and lock index * Accepts: MAIL stream * Returns: T if success, NIL if failure */ long mx_lockindex (MAILSTREAM *stream) { unsigned long uf,sf,uid; int k = 0; unsigned long msgno = 1; struct stat sbuf; char *s,*t,*idx,tmp[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */ (LOCAL->fd = open (strcat (strcpy (tmp,LOCAL->dir),MXINDEXNAME), O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) { (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */ (*bn) (BLOCK_NONE,NIL); fstat (LOCAL->fd,&sbuf); /* get size of index */ /* slurp index */ read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size); idx[sbuf.st_size] = '\0'; /* tie off index */ /* parse index */ if (sbuf.st_size) while (s && *s) switch (*s) { case 'V': /* UID validity record */ stream->uid_validity = strtoul (s+1,&s,16); break; case 'L': /* UID last record */ stream->uid_last = strtoul (s+1,&s,16); break; case 'K': /* keyword */ /* find end of keyword */ if (s = strchr (t = ++s,'\n')) { *s++ = '\0'; /* tie off keyword */ /* copy keyword */ if ((k < NUSERFLAGS) && !stream->user_flags[k] && (strlen (t) <= MAXUSERFLAG)) stream->user_flags[k] = cpystr (t); k++; /* one more keyword */ } break; case 'M': /* message status record */ uid = strtoul (s+1,&s,16);/* get UID for this message */ if (*s == ';') { /* get user flags */ uf = strtoul (s+1,&s,16); if (*s == '.') { /* get system flags */ sf = strtoul (s+1,&s,16); while ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) < uid)) msgno++; /* find message number for this UID */ if ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) == uid)) { (elt = mail_elt (stream,msgno))->valid = T; elt->user_flags=uf; /* set user and system flags in elt */ if (sf & fSEEN) elt->seen = T; if (sf & fDELETED) elt->deleted = T; if (sf & fFLAGGED) elt->flagged = T; if (sf & fANSWERED) elt->answered = T; if (sf & fDRAFT) elt->draft = T; } break; } } default: /* bad news */ sprintf (tmp,"Error in index: %.80s",s); MM_LOG (tmp,ERROR); *s = NIL; /* ignore remainder of index */ } else { /* new index */ stream->uid_validity = time (0); user_flags (stream); /* init stream with default user flags */ } fs_give ((void **) &idx); /* flush index */ } return (LOCAL->fd >= 0) ? T : NIL; } /* MX write and unlock index * Accepts: MAIL stream */ void mx_unlockindex (MAILSTREAM *stream) { unsigned long i,j; off_t size = 0; char *s,tmp[MAILTMPLEN + 64]; MESSAGECACHE *elt; if (LOCAL->fd >= 0) { lseek (LOCAL->fd,0,L_SET); /* rewind file */ /* write header */ sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last); for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]); /* write messages */ for (i = 1; i <= stream->nmsgs; i++) { /* filled buffer? */ if (((s += strlen (s)) - tmp) > MAILTMPLEN) { write (LOCAL->fd,tmp,j = s - tmp); size += j; *(s = tmp) = '\0'; /* dump out and restart buffer */ } elt = mail_elt (stream,i); sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); } /* write tail end of buffer */ if ((s += strlen (s)) != tmp) { write (LOCAL->fd,tmp,j = s - tmp); size += j; } ftruncate (LOCAL->fd,size); flock (LOCAL->fd,LOCK_UN); /* unlock the index */ close (LOCAL->fd); /* finished with file */ LOCAL->fd = -1; /* no index now */ } } /* Set date for message * Accepts: file name * elt containing date */ void mx_setdate (char *file,MESSAGECACHE *elt) { time_t tp[2]; tp[0] = time (0); /* atime is now */ tp[1] = mail_longdate (elt); /* modification time */ utime (file,tp); /* set the times */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/mx.h000066400000000000000000000013101137544547100225010ustar00rootroot00000000000000/* * Program: MX mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1996 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define MXINDEXNAME "/.mxindex" #define MXINDEX(d,s) strcat (mx_file (d,s),MXINDEXNAME) /* Function prototypes */ int mx_select (struct direct *name); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/news.c000066400000000000000000000452501137544547100230370ustar00rootroot00000000000000/* * Program: News routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 4 September 1991 * Last Edited: 8 July 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include "misc.h" #include "newsrc.h" /* NEWS I/O stream local data */ typedef struct news_local { unsigned int dirty : 1; /* disk copy of .newsrc needs updating */ char *dir; /* spool directory name */ char *name; /* local mailbox name */ unsigned char *buf; /* scratch buffer */ unsigned long buflen; /* current size of scratch buffer */ unsigned long cachedtexts; /* total size of all cached texts */ } NEWSLOCAL; /* Convenient access to local data */ #define LOCAL ((NEWSLOCAL *) stream->local) /* Function prototypes */ DRIVER *news_valid (char *name); DRIVER *news_isvalid (char *name,char *mbx); void *news_parameters (long function,void *value); void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void news_list (MAILSTREAM *stream,char *ref,char *pat); void news_lsub (MAILSTREAM *stream,char *ref,char *pat); long news_canonicalize (char *ref,char *pat,char *pattern); long news_subscribe (MAILSTREAM *stream,char *mailbox); long news_unsubscribe (MAILSTREAM *stream,char *mailbox); long news_create (MAILSTREAM *stream,char *mailbox); long news_delete (MAILSTREAM *stream,char *mailbox); long news_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *news_open (MAILSTREAM *stream); int news_select (struct direct *name); int news_numsort (const void *d1,const void *d2); void news_close (MAILSTREAM *stream,long options); void news_fast (MAILSTREAM *stream,char *sequence,long flags); void news_flags (MAILSTREAM *stream,char *sequence,long flags); char *news_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long news_ping (MAILSTREAM *stream); void news_check (MAILSTREAM *stream); void news_expunge (MAILSTREAM *stream); long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* News routines */ /* Driver dispatch used by MAIL */ DRIVER newsdriver = { "news", /* driver name */ /* driver flags */ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL, (DRIVER *) NIL, /* next driver */ news_valid, /* mailbox is valid for us */ news_parameters, /* manipulate parameters */ news_scan, /* scan mailboxes */ news_list, /* find mailboxes */ news_lsub, /* find subscribed mailboxes */ news_subscribe, /* subscribe to mailbox */ news_unsubscribe, /* unsubscribe from mailbox */ news_create, /* create mailbox */ news_delete, /* delete mailbox */ news_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ news_open, /* open mailbox */ news_close, /* close mailbox */ news_fast, /* fetch message "fast" attributes */ news_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ news_header, /* fetch message header */ news_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ news_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ news_ping, /* ping mailbox to see if still alive */ news_check, /* check for new messages */ news_expunge, /* expunge deleted messages */ news_copy, /* copy messages to another mailbox */ news_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM newsproto = {&newsdriver}; /* News validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *news_valid (char *name) { int fd; char *s,*t,*u; struct stat sbuf; if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') && (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') && !strchr (name,'/') && !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) && ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY, NIL)) >= 0)) { fstat (fd,&sbuf); /* get size of active file */ /* slurp in active file */ read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size); s[sbuf.st_size] = '\0'; /* tie off file */ close (fd); /* flush file */ while (*t && (u = strchr (t,' '))) { *u++ = '\0'; /* tie off at end of name */ if (!strcmp (name+6,t)) { fs_give ((void **) &s); /* flush data */ return &newsdriver; } t = 1 + strchr (u,'\n'); /* next line */ } fs_give ((void **) &s); /* flush data */ } return NIL; /* return status */ } /* News manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *news_parameters (long function,void *value) { return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL; } /* News scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char tmp[MAILTMPLEN]; if (news_canonicalize (ref,pat,tmp)) mm_log ("Scan not valid for news mailboxes",ERROR); } /* News find list of newsgroups * Accepts: mail stream * reference * pattern to search */ void news_list (MAILSTREAM *stream,char *ref,char *pat) { int fd; int i; char *s,*t,*u,pattern[MAILTMPLEN],name[MAILTMPLEN]; struct stat sbuf; if (!pat || !*pat) { /* empty pattern? */ if (news_canonicalize (ref,"*",pattern)) { /* tie off name at root */ if (s = strchr (pattern,'.')) *++s = '\0'; else pattern[0] = '\0'; mm_list (stream,'.',pattern,LATT_NOSELECT); } } if (news_canonicalize (ref,pat,pattern) && !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) && ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY, NIL)) >= 0)) { fstat (fd,&sbuf); /* get file size and read data */ read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size); close (fd); /* close file */ s[sbuf.st_size] = '\0'; /* tie off string */ strcpy (name,"#news."); /* write initial prefix */ i = strlen (pattern); /* length of pattern */ if (pattern[--i] != '%') i = 0; if (t = strtok (s,"\n")) do if (u = strchr (t,' ')) { *u = '\0'; /* tie off at end of name */ strcpy (name + 6,t); /* make full form of name */ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL); else if (i && (u = strchr (name + i,'.'))) { *u = '\0'; /* tie off at delimiter, see if matches */ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,LATT_NOSELECT); } } while (t = strtok (NIL,"\n")); fs_give ((void **) &s); } } /* News find list of subscribed newsgroups * Accepts: mail stream * reference * pattern to search */ void news_lsub (MAILSTREAM *stream,char *ref,char *pat) { char pattern[MAILTMPLEN]; /* return data from newsrc */ if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern); } /* News canonicalize newsgroup name * Accepts: reference * pattern * returned single pattern * Returns: T on success, NIL on failure */ long news_canonicalize (char *ref,char *pat,char *pattern) { if (ref && *ref) { /* have a reference */ strcpy (pattern,ref); /* copy reference to pattern */ /* # overrides mailbox field in reference */ if (*pat == '#') strcpy (pattern,pat); /* pattern starts, reference ends, with . */ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.')) strcat (pattern,pat + 1); /* append, omitting one of the period */ else strcat (pattern,pat); /* anything else is just appended */ } else strcpy (pattern,pat); /* just have basic name */ return ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') && (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') && !strchr (pattern,'/')) ? T : NIL; } /* News subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long news_subscribe (MAILSTREAM *stream,char *mailbox) { return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL; } /* NEWS unsubscribe to mailbox * Accepts: mail stream * mailbox to delete from subscription list * Returns: T on success, NIL on failure */ long news_unsubscribe (MAILSTREAM *stream,char *mailbox) { return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL; } /* News create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long news_create (MAILSTREAM *stream,char *mailbox) { return NIL; /* never valid for News */ } /* News delete mailbox * mailbox name to delete * Returns: T on success, NIL on failure */ long news_delete (MAILSTREAM *stream,char *mailbox) { return NIL; /* never valid for News */ } /* News rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long news_rename (MAILSTREAM *stream,char *old,char *newname) { return NIL; /* never valid for News */ } /* News open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *news_open (MAILSTREAM *stream) { long i,nmsgs; char *s,tmp[MAILTMPLEN]; struct direct **names; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &newsproto; if (stream->local) fatal ("news recycle stream"); /* build directory name */ sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL), stream->mailbox + 6); while (s = strchr (s,'.')) *s = '/'; /* scan directory */ if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) { mail_exists (stream,nmsgs); /* notify upper level that messages exist */ stream->local = fs_get (sizeof (NEWSLOCAL)); LOCAL->dirty = NIL; /* no update to .newsrc needed yet */ LOCAL->dir = cpystr (tmp); /* copy directory name for later */ /* make temporary buffer */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); LOCAL->name = cpystr (stream->mailbox + 6); for (i = 0; i < nmsgs; ++i) { stream->uid_last = mail_elt (stream,i+1)->private.uid = atoi (names[i]->d_name); fs_give ((void **) &names[i]); } s = (void *) names; /* stupid language */ fs_give ((void **) &s); /* free directory */ LOCAL->cachedtexts = 0; /* no cached texts */ stream->sequence++; /* bump sequence number */ stream->rdonly = stream->perm_deleted = T; /* UIDs are always valid */ stream->uid_validity = 0xbeefface; /* read .newsrc entries */ mail_recent (stream,newsrc_read (LOCAL->name,stream)); /* notify if empty newsgroup */ if (!(stream->nmsgs || stream->silent)) { sprintf (tmp,"Newsgroup %s is empty",LOCAL->name); mm_log (tmp,WARN); } } else mm_log ("Unable to scan newsgroup spool directory",ERROR); return LOCAL ? stream : NIL; /* if stream is alive, return to caller */ } /* News file name selection test * Accepts: candidate directory entry * Returns: T to use file name, NIL to skip it */ int news_select (struct direct *name) { char c; char *s = name->d_name; while (c = *s++) if (!isdigit (c)) return NIL; return T; } /* News file name comparision * Accepts: first candidate directory entry * second candidate directory entry * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2 */ int news_numsort (const void *d1,const void *d2) { return atoi ((*(struct direct **) d1)->d_name) - atoi ((*(struct direct **) d2)->d_name); } /* News close * Accepts: MAIL stream * option flags */ void news_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ news_check (stream); /* dump final checkpoint */ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); /* free local scratch buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->name) fs_give ((void **) &LOCAL->name); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* News fetch fast information * Accepts: MAIL stream * sequence * option flags */ void news_fast (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i,j; /* ugly and slow */ if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) news_header (stream,i,&j,NIL); } /* News fetch flags * Accepts: MAIL stream * sequence * option flags */ void news_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if ((flags & FT_UID) ? /* validate all elts */ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T; } /* News fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *news_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { unsigned long i,hdrsize; int fd; unsigned char *t; struct stat sbuf; struct tm *tm; MESSAGECACHE *elt; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get elt */ (elt = mail_elt (stream,msgno))->valid = T; if (!elt->private.msg.header.text.data) { /* purge cache if too big */ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) { mail_gc (stream,GC_TEXTS);/* just can't keep that much */ LOCAL->cachedtexts = 0; } /* build message file name */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return ""; fstat (fd,&sbuf); /* get size of message */ /* make plausible IMAPish date string */ tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ /* find end of header */ for (i = 0,t = LOCAL->buf; *t && !(i && (*t == '\n')); i = (*t++ == '\n')); /* number of header bytes */ hdrsize = (*t ? ++t : t) - LOCAL->buf; elt->rfc822_size = /* size of entire message in CRLF form */ (elt->private.msg.header.text.size = strcrlfcpy (&elt->private.msg.header.text.data,&i,LOCAL->buf, hdrsize)) + (elt->private.msg.text.text.size = strcrlfcpy (&elt->private.msg.text.text.data,&i,t, sbuf.st_size - hdrsize)); /* add to cached size */ LOCAL->cachedtexts += elt->rfc822_size; } *length = elt->private.msg.header.text.size; return (char *) elt->private.msg.header.text.data; } /* News fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL on failure */ long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get elt */ /* snarf message if don't have it yet */ if (!elt->private.msg.text.text.data) { news_header (stream,msgno,&i,flags); if (!elt->private.msg.text.text.data) return NIL; } if (!(flags & FT_PEEK)) { /* mark as seen */ mail_elt (stream,msgno)->seen = T; mm_flags (stream,msgno); } if (!elt->private.msg.text.text.data) return NIL; INIT (bs,mail_string,elt->private.msg.text.text.data, elt->private.msg.text.text.size); return T; } /* News per-message modify flag * Accepts: MAIL stream * message cache element */ void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { if (!LOCAL->dirty) { /* only bother checking if not dirty yet */ if (elt->valid) { /* if done, see if deleted changed */ if (elt->sequence != elt->deleted) LOCAL->dirty = T; elt->sequence = T; /* leave the sequence set */ } /* note current setting of deleted flag */ else elt->sequence = elt->deleted; } } /* News ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long news_ping (MAILSTREAM *stream) { return T; /* always alive */ } /* News check mailbox * Accepts: MAIL stream */ void news_check (MAILSTREAM *stream) { /* never do if no updates */ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream); LOCAL->dirty = NIL; } /* News expunge mailbox * Accepts: MAIL stream */ void news_expunge (MAILSTREAM *stream) { if (!stream->silent) mm_log ("Expunge ignored on news",NIL); } /* News copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * option flags * Returns: T if copy successful, else NIL */ long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (pc) return (*pc) (stream,sequence,mailbox,options); mm_log ("Copy not valid for News",ERROR); return NIL; } /* News append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback function * data for callback * Returns: T if append successful, else NIL */ long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { mm_log ("Append not valid for news",ERROR); return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/nl_ami.c000066400000000000000000000045311137544547100233170ustar00rootroot00000000000000/* * Program: UNIX/VMS newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { long i = srcl * 2,j; unsigned char c,*d = src; if (*dst) { /* candidate destination provided? */ /* count NLs if doesn't fit worst-case */ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++; /* still too small, must reset destination */ if (i > *dstl) fs_give ((void **) dst); } /* make a new buffer if needed */ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1); d = *dst; /* destination string */ if (srcl) do { /* main copy loop */ if ((c = *src++) < '\016') { /* prepend CR to LF */ if (c == '\012') *d++ = '\015'; /* unlikely CR */ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) { *d++ = c; /* copy the CR */ c = *src++; /* grab the LF */ --srcl; /* adjust the count */ } } *d++ = c; /* copy character */ } while (--srcl); *d = '\0'; /* tie off destination */ return d - *dst; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { unsigned long pos = GETPOS (s); unsigned long i = SIZE (s); unsigned long j = i; while (j--) switch (SNX (s)) {/* search for newlines */ case '\015': /* unlikely carriage return */ if (j && (CHR (s) == '\012')) { SNX (s); /* eat the line feed */ j--; } break; case '\012': /* line feed? */ i++; default: /* ordinary chararacter */ break; } SETPOS (s,pos); /* restore old position */ return i; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/os_ami.c000066400000000000000000000034701137544547100233300ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Amiga version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define PINE /* to get full DIR description in */ #include "tcp_ami.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define DIR_SIZE(d) d->d_reclen /* for scandir.c */ #include "fs_ami.c" #include "ftl_ami.c" #include "nl_ami.c" #include "env_ami.c" #include "tcp_ami.c" #include "log_std.c" #include "gr_waitp.c" #include "tz_bsd.c" #include "scandir.c" #include "gethstid.c" #undef utime /* Amiga has its own wierd utime() with an incompatible struct utimbuf that * does not match with the traditional time_t [2]. */ /* Portable utime() that takes it args like real Unix systems * Accepts: file path * traditional utime() argument * Returns: utime() results */ int portable_utime (char *file,time_t timep[2]) { struct utimbuf times; times.actime = timep[0]; /* copy the portable values */ times.modtime = timep[1]; return utime (file,×); /* now call Amiga's routine */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/os_ami.h000066400000000000000000000022311137544547100233270ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Amiga version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include /* Different names, equivalent things in BSD and SysV */ #define direct dirent #define utime portable_utime int portable_utime (char *file,time_t timep[2]); #include "env_ami.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/phile.c000066400000000000000000000375331137544547100231710ustar00rootroot00000000000000/* * Program: File routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 25 August 1993 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include #include "mail.h" #include "osdep.h" #include #include #include #include "rfc822.h" #include "misc.h" #include "dummy.h" /* Types returned from phile_type() */ #define PTYPEBINARY 0 /* binary data */ #define PTYPETEXT 1 /* textual data */ #define PTYPECRTEXT 2 /* textual data with CR */ #define PTYPE8 4 /* textual 8bit data */ #define PTYPEISO2022JP 8 /* textual Japanese */ #define PTYPEISO2022KR 16 /* textual Korean */ #define PTYPEISO2022CN 32 /* textual Chinese */ /* PHILE I/O stream local data */ typedef struct phile_local { ENVELOPE *env; /* file envelope */ BODY *body; /* file body */ char tmp[MAILTMPLEN]; /* temporary buffer */ } PHILELOCAL; /* Convenient access to local data */ #define LOCAL ((PHILELOCAL *) stream->local) /* Function prototypes */ DRIVER *phile_valid (char *name); int phile_isvalid (char *name,char *tmp); void *phile_parameters (long function,void *value); void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void phile_list (MAILSTREAM *stream,char *ref,char *pat); void phile_lsub (MAILSTREAM *stream,char *ref,char *pat); long phile_create (MAILSTREAM *stream,char *mailbox); long phile_delete (MAILSTREAM *stream,char *mailbox); long phile_rename (MAILSTREAM *stream,char *old,char *newname); long phile_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *phile_open (MAILSTREAM *stream); int phile_type (unsigned char *s,unsigned long i,unsigned long *j); void phile_close (MAILSTREAM *stream,long options); ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body, long flags); char *phile_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); long phile_ping (MAILSTREAM *stream); void phile_check (MAILSTREAM *stream); void phile_expunge (MAILSTREAM *stream); long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* File routines */ /* Driver dispatch used by MAIL */ DRIVER philedriver = { "phile", /* driver name */ /* driver flags */ DR_LOCAL|DR_READONLY|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ phile_valid, /* mailbox is valid for us */ phile_parameters, /* manipulate parameters */ phile_scan, /* scan mailboxes */ phile_list, /* list mailboxes */ phile_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ phile_status, /* status of mailbox */ phile_open, /* open mailbox */ phile_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ phile_structure, /* fetch message envelopes */ phile_header, /* fetch message header only */ phile_text, /* fetch message body only */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ phile_ping, /* ping mailbox to see if still alive */ phile_check, /* check for new messages */ phile_expunge, /* expunge deleted messages */ phile_copy, /* copy messages to another mailbox */ phile_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM phileproto = {&philedriver}; /* File validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *phile_valid (char *name) { char tmp[MAILTMPLEN]; return phile_isvalid (name,tmp) ? &philedriver : NIL; } /* File test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int phile_isvalid (char *name,char *tmp) { struct stat sbuf; char *s; /* INBOX never accepted, any other name is */ return ((s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) && !(sbuf.st_mode & S_IFDIR) && /* only allow empty files if #ftp */ (sbuf.st_size || ((*name == '#') && ((name[1] == 'f') || (name[1] == 'F')) && ((name[2] == 't') || (name[2] == 'T')) && ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/')))); } /* File manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *phile_parameters (long function,void *value) { return NIL; } /* File mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* File list mailboxes * Accepts: mail stream * reference * pattern to search */ void phile_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* File list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void phile_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* File status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long phile_status (MAILSTREAM *stream,char *mbx,long flags) { char *s,tmp[MAILTMPLEN]; MAILSTATUS status; struct stat sbuf; long ret = NIL; if ((s = mailboxfile (tmp,mbx)) && *s && !stat (s,&sbuf)) { status.flags = flags; /* return status values */ status.unseen = (stream && mail_elt (stream,1)->seen) ? 0 : 1; status.messages = status.recent = status.uidnext = 1; status.uidvalidity = sbuf.st_mtime; /* pass status to main program */ mm_status (stream,mbx,&status); ret = LONGT; /* success */ } return ret; } /* File open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *phile_open (MAILSTREAM *stream) { int i,k,fd; unsigned long j,m; char *s,tmp[MAILTMPLEN]; struct passwd *pw; struct stat sbuf; struct tm *t; MESSAGECACHE *elt; SIZEDTEXT *buf; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &phileproto; if (stream->local) fatal ("phile recycle stream"); /* open associated file */ if (!mailboxfile (tmp,stream->mailbox) || !tmp[0] || stat (tmp,&sbuf) || (fd = open (tmp,O_RDONLY,NIL)) < 0) { sprintf (tmp,"Unable to open file %s",stream->mailbox); mm_log (tmp,ERROR); return NIL; } fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); stream->local = fs_get (sizeof (PHILELOCAL)); mail_exists (stream,1); /* make sure upper level knows */ mail_recent (stream,1); elt = mail_elt (stream,1); /* instantiate cache element */ elt->valid = elt->recent = T; /* mark valid flags */ stream->sequence++; /* bump sequence number */ stream->rdonly = T; /* make sure upper level knows readonly */ /* instantiate a new envelope and body */ LOCAL->env = mail_newenvelope (); LOCAL->body = mail_newbody (); t = gmtime (&sbuf.st_mtime); /* get UTC time and Julian day */ i = t->tm_hour * 60 + t->tm_min; k = t->tm_yday; t = localtime(&sbuf.st_mtime);/* get local time */ /* calculate time delta */ i = t->tm_hour * 60 + t->tm_min - i; if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60; k = abs (i); /* time from UTC either way */ elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec; elt->day = t->tm_mday; elt->month = t->tm_mon + 1; elt->year = t->tm_year - (BASEYEAR - 1900); elt->zoccident = (k == i) ? 0 : 1; elt->zhours = k/60; elt->zminutes = k % 60; sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d", days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+', elt->zhours,elt->zminutes); /* set up Date field */ LOCAL->env->date = cpystr (tmp); /* fill in From field from file owner */ LOCAL->env->from = mail_newaddr (); if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name); else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid); LOCAL->env->from->mailbox = cpystr (tmp); LOCAL->env->from->host = cpystr (mylocalhost ()); /* set subject to be mailbox name */ LOCAL->env->subject = cpystr (stream->mailbox); /* slurp the data */ (buf = &elt->private.special.text)->size = sbuf.st_size; read (fd,buf->data = (unsigned char *) fs_get (buf->size + 1),buf->size); buf->data[buf->size] = '\0'; close (fd); /* close the file */ /* analyze data type */ if (i = phile_type (buf->data,buf->size,&j)) { LOCAL->body->type = TYPETEXT; LOCAL->body->subtype = cpystr ("PLAIN"); if (!(i & PTYPECRTEXT)) { /* change Internet newline format as needed */ s = (char *) buf->data; /* make copy of UNIX-format string */ buf->data = NIL; /* zap the buffer */ buf->size = strcrlfcpy (&buf->data,&m,s,buf->size); fs_give ((void **) &s); /* flush original UNIX-format string */ } LOCAL->body->parameter = mail_newbody_parameter (); LOCAL->body->parameter->attribute = cpystr ("charset"); LOCAL->body->parameter->value = cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" : (i & PTYPEISO2022KR) ? "ISO-2022-KR" : (i & PTYPEISO2022CN) ? "ISO-2022-CN" : (i & PTYPE8) ? "X-UNKNOWN" : "US-ASCII"); LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT; LOCAL->body->size.lines = j; } else { /* binary data */ LOCAL->body->type = TYPEAPPLICATION; LOCAL->body->subtype = cpystr ("OCTET-STREAM"); LOCAL->body->parameter = mail_newbody_parameter (); LOCAL->body->parameter->attribute = cpystr ("name"); LOCAL->body->parameter->value = cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox); LOCAL->body->encoding = ENCBASE64; buf->data = rfc822_binary (s = (char *) buf->data,buf->size,&buf->size); fs_give ((void **) &s); /* flush originary binary contents */ } phile_header (stream,1,&j,NIL); LOCAL->body->size.bytes = LOCAL->body->contents.text.size = buf->size; elt->rfc822_size = j + buf->size; /* only one message ever... */ stream->uid_validity = sbuf.st_mtime; stream->uid_last = elt->private.uid = 1; return stream; /* return stream alive to caller */ } /* File determine data type * Accepts: data to examine * size of data * pointer to line count return * Returns: PTYPE mask of data type */ int phile_type (unsigned char *s,unsigned long i,unsigned long *j) { int ret = PTYPETEXT; char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; *j = 0; /* no lines */ /* check type of every character */ while (i--) switch (charvec[*s++]) { case 'A': ret |= PTYPE8; /* 8bit character */ break; case 'a': break; /* ASCII character */ case 'b': return PTYPEBINARY; /* binary byte seen, stop immediately */ case 'c': ret |= PTYPECRTEXT; /* CR indicates Internet text */ break; case 'e': /* ESC */ if (*s == '$') { /* ISO-2022 sequence? */ switch (s[1]) { case 'B': case '@': ret |= PTYPEISO2022JP; break; case ')': switch (s[2]) { case 'A': case 'E': case 'G': ret |= PTYPEISO2022CN; break; case 'C': ret |= PTYPEISO2022KR; break; } case '*': switch (s[2]) { case 'H': ret |= PTYPEISO2022CN; break; } case '+': switch (s[2]) { case 'I': case 'J': case 'K': case 'L': case 'M': ret |= PTYPEISO2022CN; break; } } } break; case 'l': /* newline */ (*j)++; break; } return ret; /* return type of data */ } /* File close * Accepts: MAIL stream * close options */ void phile_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ fs_give ((void **) &mail_elt (stream,1)->private.special.text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* File fetch structure * Accepts: MAIL stream * message # to fetch * pointer to return body * option flags * Returns: envelope of this message, body returned in body value * * Fetches the "fast" information as well */ ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body, long flags) { if (body) *body = LOCAL->body; return LOCAL->env; /* return the envelope */ } /* File fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *phile_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { rfc822_header (LOCAL->tmp,LOCAL->env,LOCAL->body); *length = strlen (LOCAL->tmp); return LOCAL->tmp; } /* File fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T, always */ long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { SIZEDTEXT *buf = &mail_elt (stream,msgno)->private.special.text; if (!(flags &FT_PEEK)) { /* mark message as seen */ mail_elt (stream,msgno)->seen = T; mm_flags (stream,msgno); } INIT (bs,mail_string,buf->data,buf->size); return T; } /* File ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ long phile_ping (MAILSTREAM *stream) { return T; } /* File check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void phile_check (MAILSTREAM *stream) { mm_log ("Check completed",NIL); } /* File expunge mailbox * Accepts: MAIL stream */ void phile_expunge (MAILSTREAM *stream) { mm_log ("Expunge ignored on readonly mailbox",NIL); } /* File copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { char tmp[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (tmp,"Can't copy - file \"%s\" is not in valid mailbox format", stream->mailbox); mm_log (tmp,ERROR); return NIL; } /* File append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback function * data for callback * Returns: T if append successful, else NIL */ long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { char tmp[MAILTMPLEN],file[MAILTMPLEN]; char *s = mailboxfile (file,mailbox); if (s && *s) sprintf (tmp,"Can't append - not in valid mailbox format: %.80s",s); else sprintf (tmp,"Can't append - invalid name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/pmatch.c000066400000000000000000000050631137544547100233350ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-dependent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return (*pat == *s) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return (*pat == *s) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/pseudo.c000066400000000000000000000020221137544547100233500ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Local sites may wish to alter this text */ char *pseudo_from = "MAILER-DAEMON"; char *pseudo_name = "Mail System Internal Data"; char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA"; char *pseudo_msg = "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values." ; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/pseudo.h000066400000000000000000000011421137544547100233570ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/scandir.c000066400000000000000000000041071137544547100235020ustar00rootroot00000000000000/* * Program: Scan directories * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for BSD scandir() call * Accepts: directory name * destination pointer of names array * selection function * comparison function * Returns: number of elements in the array or -1 if error */ int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar) { struct direct *p,*d,**names; int nitems; struct stat stb; long nlmax; DIR *dirp = opendir (dirname);/* open directory and get status poop */ if ((!dirp) || (fstat (dirp->dd_fd,&stb) < 0)) return -1; nlmax = stb.st_size / 24; /* guesstimate at number of files */ names = (struct direct **) fs_get (nlmax * sizeof (struct direct *)); nitems = 0; /* initially none found */ while (d = readdir (dirp)) { /* read directory item */ /* matches select criterion? */ if (select && !(*select) (d)) continue; /* get size of direct record for this file */ p = (struct direct *) fs_get (DIR_SIZE (d)); p->d_ino = d->d_ino; /* copy the poop */ strcpy (p->d_name,d->d_name); if (++nitems >= nlmax) { /* if out of space, try bigger guesstimate */ void *s = (void *) names; /* stupid language */ nlmax *= 2; /* double it */ fs_resize ((void **) &s,nlmax * sizeof (struct direct *)); names = (struct direct **) s; } names[nitems - 1] = p; /* store this file there */ } closedir (dirp); /* done with directory */ /* sort if necessary */ if (nitems && compar) qsort (names,nitems,sizeof (struct direct *),compar); *namelist = names; /* return directory */ return nitems; /* and size */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/ssl_none.c000066400000000000000000000045401137544547100237000ustar00rootroot00000000000000/* * Program: Dummy (no SSL) authentication/encryption module * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 7 February 2001 * Last Edited: 6 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Init server for SSL * Accepts: server name */ void ssl_server_init (char *server) { syslog (LOG_ERR,"This server does not support SSL"); exit (1); /* punt this program too */ } /* Start TLS * Accepts: /etc/services service name * Returns: cpystr'd error string if TLS failed, else NIL for success */ char *ssl_start_tls (char *server) { return cpystr ("This server does not support TLS"); } /* Get character * Returns: character or EOF */ int PBIN (void) { return getchar (); } /* Get string * Accepts: destination string pointer * number of bytes available * Returns: destination string pointer or NIL if EOF */ char *PSIN (char *s,int n) { return fgets (s,n,stdin); } /* Get record * Accepts: destination string pointer * number of bytes to read * Returns: T if success, NIL otherwise */ long PSINR (char *s,unsigned long n) { unsigned long i; while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i; return n ? NIL : LONGT; } /* Wait for input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long INWAIT (long seconds) { return server_input_wait (seconds); } /* Put character * Accepts: character * Returns: character written or EOF */ int PBOUT (int c) { return putchar (c); } /* Put string * Accepts: source string pointer * Returns: 0 or EOF if error */ int PSOUT (char *s) { return fputs (s,stdout); } /* Put record * Accepts: source sized text * Returns: 0 or EOF if error */ int PSOUTR (SIZEDTEXT *s) { unsigned char *t; unsigned long i,j; for (t = s->data,i = s->size; (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR))); t += j,i -= j); return i ? EOF : NIL; } /* Flush output * Returns: 0 or EOF if error */ int PFLUSH (void) { return fflush (stdout); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/tcp_ami.c000066400000000000000000000557071137544547100235070ustar00rootroot00000000000000/* * Program: Amiga TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 20 February 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #undef write /* don't use redefined write() */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_open = 0; /* TCP timeouts, in seconds */ static long ttmo_read = 0; static long ttmo_write = 0; static long allowreversedns = T;/* allow reverse DNS lookup */ static long tcpdebug = NIL; /* extra TCP debugging telemetry */ extern long maxposint; /* get this from write.c */ /* Local function prototypes */ int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst, unsigned long port); long tcp_abort (TCPSTREAM *stream); char *tcp_name (struct sockaddr_in *sin,long flag); char *tcp_name_valid (char *s); /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_OPENTIMEOUT: ttmo_open = (long) value; case GET_OPENTIMEOUT: ret = (void *) ttmo_open; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; case SET_ALLOWREVERSEDNS: allowreversedns = (long) value; case GET_ALLOWREVERSEDNS: ret = (void *) allowreversedns; break; case SET_TCPDEBUG: tcpdebug = (long) value; case GET_TCPDEBUG: ret = (void *) tcpdebug; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number and optional silent flag * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; int i; int sock = -1; int ctr = 0; int silent = (port & NET_SILENT) ? T : NIL; int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr; char *s; struct sockaddr_in sin; struct hostent *he; char hostname[MAILTMPLEN]; char tmp[MAILTMPLEN]; struct servent *sv = NIL; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data; port &= 0xffff; /* erase flags */ /* lookup service */ if (service && (sv = getservbyname (service,"tcp"))) port = ntohs (sin.sin_port = sv->s_port); /* copy port number in network format */ else sin.sin_port = htons (port); /* The domain literal form is used (rather than simply the dotted decimal as with other Amiga programs) because it has to be a valid "host name" in mailsystem terminology. */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (hostname,host+1); /* yes, copy number part */ hostname[(strlen (hostname))-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (hostname)) == -1) sprintf (tmp,"Bad format domain-literal: %.80s",host); else { sin.sin_family = AF_INET; /* family is always Internet */ strcpy (hostname,host); /* hostname is user's argument */ (*bn) (BLOCK_TCPOPEN,NIL); /* get an open socket for this system */ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port); (*bn) (BLOCK_NONE,NIL); } } else { /* lookup host name */ if (tcpdebug) { sprintf (tmp,"DNS resolution %.80s",host); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); if (!(he = gethostbyname (lcase (strcpy (hostname,host))))) sprintf (tmp,"No such host as %.80s",host); (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); if (he) { /* DNS resolution won? */ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG); /* copy address type */ sin.sin_family = he->h_addrtype; /* copy host name */ strcpy (hostname,he->h_name); #ifdef HOST_NOT_FOUND /* muliple addresses only on DNS systems */ for (sock = -1,i = 0; (sock < 0) && (s = he->h_addr_list[i]); i++) { if (i && !silent) mm_log (tmp,WARN); memcpy (&sin.sin_addr,s,he->h_length); (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port); (*bn) (BLOCK_NONE,NIL); } #else /* the one true address then */ memcpy (&sin.sin_addr,he->h_addr,he->h_length); (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port); (*bn) (BLOCK_NONE,NIL); #endif } } if (sock >= 0) { /* won */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); stream->port = port; /* port number */ /* init sockets */ stream->tcpsi = stream->tcpso = sock; /* stash in the snuck-in byte */ if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0]; /* copy official host name */ stream->host = cpystr (hostname); if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG); } else if (!silent) mm_log (tmp,ERROR); return stream; /* return success */ } /* Open a TCP socket * Accepts: Internet socket address block * scratch buffer * pointer to "first byte read in" storage or NIL * host name for error message * port number for error message * Returns: socket if success, else -1 with error string in scratch buffer */ int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst, unsigned long port) { int i,ti,sock,flgs; time_t now; struct protoent *pt = getprotobyname ("tcp"); fd_set fds,efds; struct timeval tmo; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr)); mm_log (tmp,NIL); /* make a socket */ if ((sock = socket (sin->sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) { sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno)); (*bn) (BLOCK_NONSENSITIVE,data); return -1; } flgs = fcntl (sock,F_GETFL,0);/* get current socket flags */ /* set non-blocking if want open timeout */ if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY); /* open connection */ while ((i = connect (sock,(struct sockaddr *) sin, sizeof (struct sockaddr_in))) < 0 && (errno == EINTR)); (*bn) (BLOCK_NONSENSITIVE,data); if (i < 0) switch (errno) { /* failed? */ case EAGAIN: /* DG brain damage */ case EINPROGRESS: /* what we expect to happen */ case EALREADY: /* or another form of it */ case EISCONN: /* restart after interrupt? */ case EADDRINUSE: /* restart after interrupt? */ break; /* well, not really, it was interrupted */ default: sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno)); close (sock); /* flush socket */ return -1; } if (ctr) { /* want open timeout */ now = time (0); /* open timeout */ ti = ttmo_open ? now + ttmo_open : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (sock,&fds); /* block for error or readable */ FD_SET (sock,&efds); do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i > 0) { /* success, make sure really connected */ fcntl (sock,F_SETFL,flgs);/* restore blocking status */ /* This used to be a zero-byte read(), but that crashes Solaris */ /* get socket status */ while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR)); } if (i <= 0) { /* timeout or error? */ i = i ? errno : ETIMEDOUT;/* determine error code */ close (sock); /* flush socket */ errno = i; /* return error code */ sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,port, strerror (errno)); return -1; } } return sock; /* return the socket */ } /* TCP/IP authenticated open * Accepts: host name * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* disabled */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s) { unsigned long n; /* make sure socket still alive */ if (stream->tcpsi < 0) return NIL; /* can transfer bytes from buffer? */ if (n = min (size,stream->ictr)) { memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */ s += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } if (size) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_TCPREAD,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpsi,&fds); FD_SET (stream->tcpsi,&efds); errno = NIL; /* block and read */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i > 0) { /* select says there's data to read? */ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) && (errno == EINTR)); if (i < 1) return tcp_abort (stream); s += i; /* point at new place to write */ size -= i; /* reduce byte count */ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } else if (i || !tmoh || !(*tmoh) (now - t,now - tl)) return tcp_abort (stream); } (*bn) (BLOCK_NONE,NIL); } *s = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->tcpsi < 0) return NIL; (*bn) (BLOCK_TCPREAD,NIL); while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */ FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */ errno = NIL; /* block and read */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i > 0) { /* got data? */ while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); if (i < 1) return tcp_abort (stream); stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG); } else if (i || !tmoh || !(*tmoh) (now - t,now - tl)) return tcp_abort (stream);/* error or timeout no-continue */ } (*bn) (BLOCK_NONE,NIL); return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->tcpso < 0) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_write ? now + ttmo_write : 0; if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */ FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */ errno = NIL; /* block and write */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpso+1,0,&fds,&efds,ti ? &tmo : 0); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i > 0) { /* OK to send data? */ while (((i = write (stream->tcpso,string,size)) < 0) &&(errno == EINTR)); if (i < 0) return tcp_abort (stream); size -= i; /* how much we sent */ string += i; if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); } else if (i || !tmoh || !(*tmoh) (now - t,now - tl)) return tcp_abort (stream);/* error or timeout no-continue */ } (*bn) (BLOCK_NONE,NIL); return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (stream); /* nuke the stream */ /* flush host names */ if (stream->host) fs_give ((void **) &stream->host); if (stream->remotehost) fs_give ((void **) &stream->remotehost); if (stream->localhost) fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->tcpsi >= 0) { /* no-op if no socket */ (*bn) (BLOCK_TCPCLOSE,NIL); close (stream->tcpsi); /* nuke the socket */ if (stream->tcpsi != stream->tcpso) close (stream->tcpso); stream->tcpsi = stream->tcpso = -1; } (*bn) (BLOCK_NONE,NIL); return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* use tcp_remotehost() if want guarantees */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { if (!stream->remotehost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); stream->remotehost = /* get socket's peer name */ (getpeername (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) || (sin.sin_family != AF_INET)) ? cpystr (stream->host) : tcp_name (&sin,NIL); } return stream->remotehost; } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { if (!stream->localhost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); stream->localhost = /* get socket's name */ ((stream->port & 0xffff000) || getsockname (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) || (sin.sin_family != AF_INET)) ? cpystr (mylocalhost ()) : tcp_name (&sin,NIL); } return stream->localhost; /* return local host name */ } /* TCP/IP get client host address (server calls only) * Returns: client host address */ static char *myClientAddr = NIL; char *tcp_clientaddr () { if (!myClientAddr) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myClientAddr = /* get stdin's peer name */ cpystr (getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ? "UNKNOWN" : ((sin.sin_family == AF_INET) ? inet_ntoa (sin.sin_addr) : "NON-IPv4")); } return myClientAddr; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ static char *myClientHost = NIL; char *tcp_clienthost () { if (!myClientHost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myClientHost = /* get stdin's peer name */ getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ? cpystr ("UNKNOWN") : ((sin.sin_family == AF_INET) ? tcp_name (&sin,T) : cpystr ("NON-IPv4")); } return myClientHost; } /* TCP/IP get server host address (server calls only) * Returns: server host address */ static char *myServerAddr = NIL; char *tcp_serveraddr () { if (!myServerAddr) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myServerAddr = /* get stdin's peer name */ cpystr (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ? "UNKNOWN" : ((sin.sin_family == AF_INET) ? inet_ntoa (sin.sin_addr) : "NON-IPv4")); } return myServerAddr; } /* TCP/IP get server host name (server calls only) * Returns: server host name */ static char *myServerHost = NIL; static long myServerPort = -1; char *tcp_serverhost () { if (!myServerHost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); /* get stdin's name */ if (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) || (sin.sin_family != AF_INET)) myServerHost = cpystr (mylocalhost ()); else { myServerHost = tcp_name (&sin,NIL); myServerPort = ntohs (sin.sin_port); } } return myServerHost; } /* TCP/IP get server port number (server calls only) * Returns: server port number */ long tcp_serverport () { if (!myServerHost) tcp_serverhost (); return myServerPort; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { char *ret,host[MAILTMPLEN]; struct hostent *he; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data; /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name; (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); if (tcpdebug) { sprintf (host,"DNS canonicalization %.80s",name); mm_log (host,TCPDEBUG); } /* note that Amiga requires lowercase! */ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? (char *) he->h_name : name; (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG); return ret; } /* TCP/IP return name from socket * Accepts: socket * verbose flag * Returns: cpystr name */ char *tcp_name (struct sockaddr_in *sin,long flag) { char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN]; sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr)); if (allowreversedns) { struct hostent *he; blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL); void *data; if (tcpdebug) { sprintf (tmp,"Reverse DNS resolution %s",adr); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); /* translate address to name */ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr, sizeof (struct in_addr), sin->sin_family)) ? (char *) he->h_name : NIL)) { /* produce verbose form if needed */ if (flag) sprintf (ret = tmp,"%s %s",t,adr); else ret = t; } (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG); } return cpystr (ret); } /* Validate name * Accepts: domain name * Returns: T if valid, NIL otherwise */ char *tcp_name_valid (char *s) { int c; char *ret,*tail; /* must be non-empty and not too long */ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) { /* must be alnum, dot, or hyphen */ while ((c = *s++) && (s <= tail) && (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.'))); if (c) ret = NIL; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/tcp_ami.h000066400000000000000000000017441137544547100235040ustar00rootroot00000000000000/* * Program: UNIX TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer */ #define BUFLEN 8192 /* TCP I/O stream */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ char *remotehost; /* remote host name */ int tcpsi; /* input socket */ int tcpso; /* output socket */ int ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/tenex.c000066400000000000000000001367221137544547100232130ustar00rootroot00000000000000/* * Program: Tenex mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. * * TEXT SIZE SEMANTICS * * Most of the text sizes are in internal (LF-only) form, except for the * msg.text size. Beware. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include "misc.h" #include "dummy.h" /* TENEX I/O stream local data */ typedef struct tenex_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* local snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } TENEXLOCAL; /* Convenient access to local data */ #define LOCAL ((TENEXLOCAL *) stream->local) /* Function prototypes */ DRIVER *tenex_valid (char *name); int tenex_isvalid (char *name,char *tmp); void *tenex_parameters (long function,void *value); void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void tenex_list (MAILSTREAM *stream,char *ref,char *pat); void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat); long tenex_create (MAILSTREAM *stream,char *mailbox); long tenex_delete (MAILSTREAM *stream,char *mailbox); long tenex_rename (MAILSTREAM *stream,char *old,char *newname); long tenex_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *tenex_open (MAILSTREAM *stream); void tenex_close (MAILSTREAM *stream,long options); void tenex_fast (MAILSTREAM *stream,char *sequence,long flags); void tenex_flags (MAILSTREAM *stream,char *sequence,long flags); char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long tenex_ping (MAILSTREAM *stream); void tenex_check (MAILSTREAM *stream); void tenex_snarf (MAILSTREAM *stream); void tenex_expunge (MAILSTREAM *stream); long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); unsigned long tenex_size (MAILSTREAM *stream,unsigned long m); char *tenex_file (char *dst,char *name); long tenex_parse (MAILSTREAM *stream); MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno); void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void tenex_update_status (MAILSTREAM *stream,unsigned long msgno, long syncflag); unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* Tenex mail routines */ /* Driver dispatch used by MAIL */ DRIVER tenexdriver = { "tenex", /* driver name */ DR_LOCAL|DR_MAIL|DR_NOSTICKY|DR_LOCKING, /* driver flags */ (DRIVER *) NIL, /* next driver */ tenex_valid, /* mailbox is valid for us */ tenex_parameters, /* manipulate parameters */ tenex_scan, /* scan mailboxes */ tenex_list, /* list mailboxes */ tenex_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ tenex_delete, /* delete mailbox */ tenex_rename, /* rename mailbox */ tenex_status, /* status of mailbox */ tenex_open, /* open mailbox */ tenex_close, /* close mailbox */ tenex_fast, /* fetch message "fast" attributes */ tenex_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ tenex_header, /* fetch message header */ tenex_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ tenex_flag, /* modify flags */ tenex_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ tenex_ping, /* ping mailbox to see if still alive */ tenex_check, /* check for new messages */ tenex_expunge, /* expunge deleted messages */ tenex_copy, /* copy messages to another mailbox */ tenex_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM tenexproto = {&tenexdriver}; /* Tenex mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *tenex_valid (char *name) { char tmp[MAILTMPLEN]; return tenex_isvalid (name,tmp) ? &tenexdriver : NIL; } /* Tenex mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int tenex_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = tenex_file (file,name)) && !stat (s,&sbuf)) { if (!sbuf.st_size) { /* allow empty file if INBOX */ if ((s = mailboxfile (tmp,name)) && !*s) ret = T; else errno = 0; /* empty file */ } else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) && (s[-1] != '\015')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } /* in case INBOX but not tenex format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* Tenex manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tenex_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = tenex_file ((char *) value,"INBOX"); break; } return ret; } /* Tenex mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* Tenex mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* Tenex mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* Tenex mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long tenex_delete (MAILSTREAM *stream,char *mailbox) { return tenex_rename (stream,mailbox,NIL); } /* Tenex mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long tenex_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = T; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); MM_LOG (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) ret = NIL; else *s = c; /* restore full name */ } /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } } else if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"mail.txt"); return ret; /* return success */ } /* Tenex Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long tenex_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; /* calculate post-snarf results */ if (!status.recent && stream->inbox && (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* Tenex mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *tenex_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&tenexproto); if (stream->local) fatal ("tenex recycle stream"); user_flags (stream); /* set up user flags */ /* canonicalize the mailbox name */ if (!tenex_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (TENEXLOCAL)); LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); LOCAL->fd = fd; /* bind the file */ /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) { MM_LOG ("Unable to lock open mailbox",ERROR); return NIL; } (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* lock the file */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ /* time not set up yet */ LOCAL->lastsnarf = LOCAL->filetime = 0; LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (tenex_ping (stream) && !stream->nmsgs) MM_LOG ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* Tenex mail close * Accepts: MAIL stream * close options */ void tenex_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) tenex_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* Tenex mail fetch fast data * Accepts: MAIL stream * sequence * option flags */ void tenex_fast (MAILSTREAM *stream,char *sequence,long flags) { STRING bs; MESSAGECACHE *elt; unsigned long i; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { if (!elt->rfc822_size) { /* have header size yet? */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); /* resize bigbuf if necessary */ if (LOCAL->buflen < elt->private.msg.full.text.size) { fs_give ((void **) &LOCAL->buf); LOCAL->buflen = elt->private.msg.full.text.size; LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1); } /* tie off string */ LOCAL->buf[elt->private.msg.full.text.size] = '\0'; /* read in the message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size); INIT (&bs,mail_string,(void *) LOCAL->buf, elt->private.msg.full.text.size); /* calculate its CRLF size */ elt->rfc822_size = strcrlflen (&bs); } tenex_elt (stream,i); /* get current flags from file */ } } /* Tenex mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to get flags */ void tenex_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) tenex_elt (stream,i); } /* TENEX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { char *s; unsigned long i; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET); if (flags & FT_INTERNAL) { if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length = i); } else { s = (char *) fs_get (i + 1);/* get readin buffer */ s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i); fs_give ((void **) &s); /* free readin buffer */ } return LOCAL->buf; } /* TENEX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T, always */ long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = tenex_elt (stream,msgno); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ tenex_update_status (stream,msgno,T); MM_FLAGS (stream,msgno); } if (flags & FT_INTERNAL) { /* if internal representation wanted */ /* find header position */ i = tenex_hdrpos (stream,msgno,&j); if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* slurp the data */ read (LOCAL->fd,LOCAL->buf,i); /* set up stringstruct for internal */ INIT (bs,mail_string,LOCAL->buf,i); } else { /* normal form, previous text cached? */ if (elt->private.uid == LOCAL->uid) i = elt->private.msg.text.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = tenex_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1); s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ i = elt->private.msg.text.text.size = strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i); fs_give ((void **) &s); /* free readin buffer */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); } return T; /* success */ } /* Tenex mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { time_t tp[2]; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read comes after all that */ utime (stream->mailbox,tp); } } /* Tenex mail per-message modify flags * Accepts: MAIL stream * message cache element */ void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ tenex_update_status (stream,elt->msgno,NIL); } /* Tenex mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long tenex_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) tenex_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (tenex_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } if (LOCAL) { /* stream must still be alive */ /* snarf if this is a read-write inbox */ if (stream->inbox && !stream->rdonly) { tenex_snarf (stream); fstat (LOCAL->fd,&sbuf);/* see if file changed now */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (tenex_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } } } return r; /* return result of the parse */ } /* Tenex mail check mailbox (reparses status too) * Accepts: MAIL stream */ void tenex_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (tenex_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* Tenex mail snarf messages from system inbox * Accepts: MAIL stream */ void tenex_snarf (MAILSTREAM *stream) { unsigned long i = 0; unsigned long j,r,hdrlen,txtlen; struct stat sbuf; char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN]; MESSAGECACHE *elt; MAILSTREAM *sysibx = NIL; int ld; /* give up if can't get exclusive permission */ if ((time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && strcmp (sysinbox (),stream->mailbox) && ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) { MM_CRITICAL (stream); /* go critical */ /* sizes match and anything in sysinbox? */ if (!stat (sysinbox (),&sbuf) && sbuf.st_size && !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { /* yes, go to end of file in our mailbox */ lseek (LOCAL->fd,sbuf.st_size,L_SET); /* for each message in sysibx mailbox */ while (r && (++i <= sysibx->nmsgs)) { /* snarf message from system INBOX */ hdr = cpystr (mail_fetchheader_full(sysibx,i,NIL,&hdrlen,FT_INTERNAL)); txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_INTERNAL|FT_PEEK); /* if have a message */ if (j = hdrlen + txtlen) { /* calculate header line */ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i)); sprintf (LOCAL->buf + strlen (LOCAL->buf), ",%lu;0000000000%02o\n",j,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* copy message */ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) || (write (LOCAL->fd,hdr,hdrlen) < 0) || (write (LOCAL->fd,txt,txtlen) < 0)) r = 0; } fs_give ((void **) &hdr); } /* make sure all the updates take */ if (fsync (LOCAL->fd)) r = 0; if (r) { /* delete all the messages we copied */ if (r == 1) strcpy (tmp,"1"); else sprintf (tmp,"1:%lu",r); mail_flag (sysibx,tmp,"\\Deleted",ST_SET); mail_expunge (sysibx); /* now expunge all those messages */ } else { sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (LOCAL->fd,sbuf.st_size); } fstat (LOCAL->fd,&sbuf); /* yes, get current file size */ LOCAL->filetime = sbuf.st_mtime; } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ unlockfd (ld,lock); /* release exclusive parse/append permission */ LOCAL->lastsnarf = time (0);/* note time of last snarf */ } } /* Tenex mail expunge mailbox * Accepts: MAIL stream */ void tenex_expunge (MAILSTREAM *stream) { time_t tp[2]; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* do nothing if stream dead */ if (!tenex_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ MM_LOG ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* The cretins who designed flock() created a window of vulnerability in * upgrading locks from shared to exclusive or downgrading from exclusive * to shared. Rather than maintain the lock at shared status at a minimum, * flock() actually *releases* the former lock. Obviously they never talked * to any database guys. Fortunately, we have the parse/append permission * lock. If we require this lock before going exclusive on the mailbox, * another process can not sneak in and steal the exclusive mailbox lock on * us, because it will block on trying to get parse/append permission first. */ /* get exclusive parse/append permission */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!tenex_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ (*bn) (BLOCK_NONE,NIL); MM_LOG ("Can't expunge because mailbox is in use by another process", ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } MM_CRITICAL (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = tenex_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + tenex_size (stream,i); if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ lseek (LOCAL->fd,pos,L_SET); while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); MM_LOG (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* reset atime to now */ utime (stream->mailbox,tp); MM_NOCRITICAL (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* Tenex mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; time_t tp[2]; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd=open(tenex_file(file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock copy mailbox",ERROR); MM_NOCRITICAL (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + tenex_size (stream,i); do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = tenex_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ tenex_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure atime remains greater */ utime (stream->mailbox,tp); } } return ret; } /* Tenex mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; time_t tp[2]; FILE *df; MESSAGECACHE elt; long f; unsigned long i,j,uf,size; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = user_flags (&tenexproto); /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"mail.txt"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open (tenex_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } MM_CRITICAL (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ i = GETPOS (message); /* remember current position */ for (j = SIZE (message), size = 0; j; --j) if (SNX (message) != '\015') ++size; SETPOS (message,i); /* restore position */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ while (size) if ((c = 0xff & SNX (message)) != '\015') { if (putc (c,df) != EOF) --size; else break; } /* get next message */ if (size || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); } ret = NIL; } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Internal routines */ /* Tenex mail return internal message size in bytes * Accepts: MAIL stream * message # * Returns: internal size of message */ unsigned long tenex_size (MAILSTREAM *stream,unsigned long m) { MESSAGECACHE *elt = mail_elt (stream,m); return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset : LOCAL->filesize) - (elt->private.special.offset + elt->private.special.text.size); } /* Tenex mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *tenex_file (char *dst,char *name) { char tmp[MAILTMPLEN]; char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/INBOX",tmp) ? "~/INBOX" : "mail.txt") : s; } /* Tenex mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long tenex_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!(s = strchr (LOCAL->buf,'\012'))) { sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 1) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ tenex_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ time_t tp[2]; tp[0] = time (0); tp[1] = LOCAL->filetime; utime (stream->mailbox,tp); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* Tenex get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; tenex_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) MM_FLAGS (stream,msgno); /* let top level know */ return elt; } /* Tenex read flags from file * Accepts: MAIL stream * Returns: cache element */ void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* Tenex update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { time_t tp[2]; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* write new flags */ write (LOCAL->fd,LOCAL->buf,12); if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read is later */ utime (stream->mailbox,tp); } } } /* Tenex locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; char c = '\0'; char *s = NIL; MESSAGECACHE *elt = tenex_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; unsigned long msiz = tenex_size (stream,msgno); /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for LF LF */ for (siz = 0; siz < msiz; siz++) { if (--i <= 0) /* read another buffer as necessary */ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN)); /* two newline sequence? */ if ((c == '\012') && (*s == '\012')) { /* yes, note for later */ elt->private.msg.header.text.size = (*size = siz + 1); return ret; /* return to caller */ } else c = *s++; /* next character */ } /* header consumes entire message */ elt->private.msg.header.text.size = *size = msiz; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/tz_bsd.c000066400000000000000000000013711137544547100233440ustar00rootroot00000000000000/* * Program: BSD-style Time Zone String * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 August 1994 * Last Edited: 7 November 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Append local timezone name * Accepts: destination string */ void rfc822_timezone (char *s,void *t) { /* append timezone from tm struct */ sprintf (s + strlen (s)," (%.50s)",((struct tm *) t)->tm_zone); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/unix.c000066400000000000000000002475321137544547100230550ustar00rootroot00000000000000/* * Program: UNIX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 10 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ #include #include #include extern int errno; /* just in case */ #include #include "mail.h" #include "osdep.h" #include #include #include "unix.h" #include "pseudo.h" #include "fdstring.h" #include "misc.h" #include "dummy.h" /* UNIX I/O stream local data */ typedef struct unix_local { unsigned int dirty : 1; /* disk copy needs updating */ unsigned int pseudo : 1; /* uses a pseudo message */ int fd; /* mailbox file descriptor */ int ld; /* lock file descriptor */ char *lname; /* lock file name */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time (for mbox driver) */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ unsigned long textlen; /* current text length */ char *line; /* returned line */ } UNIXLOCAL; /* Convenient access to local data */ #define LOCAL ((UNIXLOCAL *) stream->local) /* UNIX protected file structure */ typedef struct unix_file { MAILSTREAM *stream; /* current stream */ off_t curpos; /* current file position */ off_t protect; /* protected position */ off_t filepos; /* current last written file position */ char *buf; /* overflow buffer */ size_t buflen; /* current overflow buffer length */ char *bufpos; /* current buffer position */ } UNIXFILE; /* Function prototypes */ DRIVER *unix_valid (char *name); long unix_isvalid_fd (int fd); void *unix_parameters (long function,void *value); void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void unix_list (MAILSTREAM *stream,char *ref,char *pat); void unix_lsub (MAILSTREAM *stream,char *ref,char *pat); long unix_create (MAILSTREAM *stream,char *mailbox); long unix_delete (MAILSTREAM *stream,char *mailbox); long unix_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *unix_open (MAILSTREAM *stream); void unix_close (MAILSTREAM *stream,long options); char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long unix_ping (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_expunge (MAILSTREAM *stream); long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void unix_abort (MAILSTREAM *stream); char *unix_file (char *dst,char *name); int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op); void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock); int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op); char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr); unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag); long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock); long unix_extend (MAILSTREAM *stream,unsigned long size); void unix_write (UNIXFILE *f,char *s,unsigned long i); void unix_phys_write (UNIXFILE *f,char *buf,size_t size); /* UNIX mail routines */ /* Driver dispatch used by MAIL */ DRIVER unixdriver = { "unix", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ unix_valid, /* mailbox is valid for us */ unix_parameters, /* manipulate parameters */ unix_scan, /* scan mailboxes */ unix_list, /* list mailboxes */ unix_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ unix_create, /* create mailbox */ unix_delete, /* delete mailbox */ unix_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ unix_open, /* open mailbox */ unix_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ unix_header, /* fetch message header */ unix_text, /* fetch message text */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ unix_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ unix_ping, /* ping mailbox to see if still alive */ unix_check, /* check for new messages */ unix_expunge, /* expunge deleted messages */ unix_copy, /* copy messages to another mailbox */ unix_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM unixproto = {&unixdriver}; /* driver parameters */ static long unix_fromwidget = T; /* UNIX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *unix_valid (char *name) { int fd; DRIVER *ret = NIL; char *t,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* must be non-empty file */ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { /* OK if mailbox format good */ if (unix_isvalid_fd (fd)) ret = &unixdriver; else errno = -1; /* invalid format */ close (fd); /* close the file */ /* \Marked status? */ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) { tp[0] = sbuf.st_atime; /* yes, preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } return ret; /* return what we should */ } /* UNIX mail test for valid mailbox * Accepts: file descriptor * scratch buffer * Returns: T if valid, NIL otherwise */ long unix_isvalid_fd (int fd) { int zn; int ret = NIL; char tmp[MAILTMPLEN],*s,*t,c = '\n'; memset (tmp,'\0',MAILTMPLEN); if (read (fd,tmp,MAILTMPLEN-1) >= 0) { for (s = tmp; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');) c = *s++; if (c == '\n') VALID (s,t,ret,zn); } return ret; /* return what we should */ } /* UNIX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *unix_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = dummy_file ((char *) value,"INBOX"); break; case SET_FROMWIDGET: unix_fromwidget = (long) value; case GET_FROMWIDGET: ret = (void *) unix_fromwidget; break; } return ret; } /* UNIX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* UNIX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* UNIX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* UNIX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long unix_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN]; long ret = NIL; int i,fd; time_t ti = time (0); if (!(s = dummy_file (mbx,mailbox))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); MM_LOG (tmp,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) { /* done if made directory */ if ((s = strrchr (s,'/')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY, (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } /* in case a whiner with no life */ else if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T; else { /* initialize header */ memset (tmp,'\0',MAILTMPLEN); sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti)); rfc822_fixed_date (s = tmp + strlen (tmp)); /* write the pseudo-header */ sprintf (s += strlen (s), "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000", pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) ti); for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i)) sprintf (s += strlen (s)," %s",default_user_flag (i)); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg); if ((write (fd,tmp,strlen (tmp)) < 0) || close (fd)) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx, strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ } close (fd); /* close file, set proper protections */ } return ret ? set_mbx_protections (mailbox,mbx) : NIL; } /* UNIX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long unix_delete (MAILSTREAM *stream,char *mailbox) { return unix_rename (stream,mailbox,NIL); } /* UNIX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long unix_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = NIL; char c,*s = NIL; char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; DOTLOCK lockx; int fd,ld; long i; struct stat sbuf; MM_CRITICAL (stream); /* get the c-client lock */ if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); /* lock out other c-clients */ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0) sprintf (tmp,"Mailbox %.80s is in use by another process",old); else { if ((fd = unix_lock (file,O_RDWR,S_IREAD|S_IWRITE,&lockx,LOCK_EX)) < 0) sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno)); else { if (newname) { /* want rename? */ /* found superior to destination name? */ if (s = strrchr (s,'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) { unix_unlock (fd,NIL,&lockx); unix_unlock (ld,NIL,NIL); unlink (lock); MM_NOCRITICAL (stream); return ret; /* return success or failure */ } *s = c; /* restore full name */ } if (rename (file,tmp)) sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); else ret = T; /* set success */ } else if (unlink (file)) sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); else ret = T; /* set success */ unix_unlock (fd,NIL,&lockx); } unix_unlock (ld,NIL,NIL); /* flush the lock */ unlink (lock); } MM_NOCRITICAL (stream); /* no longer critical */ if (!ret) MM_LOG (tmp,ERROR); /* log error */ return ret; /* return success or failure */ } /* UNIX mail open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *unix_open (MAILSTREAM *stream) { long i; int fd; char tmp[MAILTMPLEN]; DOTLOCK lock; long retry; /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&unixproto); retry = stream->silent ? 1 : KODRETRY; if (stream->local) fatal ("unix recycle stream"); stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); /* canonicalize the stream mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); MM_LOG (tmp,ERROR); return NIL; } /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); stream->sequence++; /* bump sequence number */ /* make lock for read/write access */ if (!stream->rdonly) while (retry) { /* try to lock file */ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) { if (retry-- == KODRETRY) {/* no, first time through? */ if (i) { /* learned the other guy's PID? */ kill ((int) i,SIGUSR2); sprintf (tmp,"Trying to get mailbox lock from process %ld",i); MM_LOG (tmp,WARN); } else retry = 0; /* give up */ } if (!stream->silent) { /* nothing if silent stream */ if (retry) sleep (1); /* wait a second before trying again */ else MM_LOG ("Mailbox is open by another process, access is readonly", WARN); } } else { /* got the lock, nobody else can alter state */ LOCAL->ld = fd; /* note lock's fd and name */ LOCAL->lname = cpystr (tmp); /* make sure mode OK (don't use fchmod()) */ chmod (LOCAL->lname,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL)); if (stream->silent) i = 0;/* silent streams won't accept KOD */ else { /* note our PID in the lock */ sprintf (tmp,"%d",getpid ()); write (fd,tmp,(i = strlen (tmp))+1); } ftruncate (fd,i); /* make sure tied off */ fsync (fd); /* make sure it's available */ retry = 0; /* no more need to try */ } } /* parse mailbox */ stream->nmsgs = stream->recent = 0; /* will we be able to get write access? */ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) { MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); flock (LOCAL->ld,LOCK_UN); /* release the lock */ close (LOCAL->ld); /* close the lock file */ LOCAL->ld = -1; /* no more lock fd */ unlink (LOCAL->lname); /* delete it */ } /* reset UID validity */ stream->uid_validity = stream->uid_last = 0; if (stream->silent && !stream->rdonly && (LOCAL->ld < 0)) unix_abort (stream); /* abort if can't get RW silent stream */ /* parse mailbox */ else if (unix_parse (stream,&lock,LOCK_SH)) { unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ } if (!LOCAL) return NIL; /* failure if stream died */ /* make sure upper level knows readonly */ stream->rdonly = (LOCAL->ld < 0); /* notify about empty mailbox */ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL); if (!stream->rdonly) { /* flags stick if readwrite */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = T; if (!stream->uid_nosticky) {/* users with lives get permanent keywords */ stream->perm_user_flags = 0xffffffff; /* and maybe can create them too! */ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T; } } return stream; /* return stream alive to caller */ } /* UNIX mail close * Accepts: MAIL stream * close options */ void unix_close (MAILSTREAM *stream,long options) { int silent = stream->silent; stream->silent = T; /* go silent */ /* expunge if requested */ if (options & CL_EXPUNGE) unix_expunge (stream); /* else dump final checkpoint */ else if (LOCAL->dirty) unix_check (stream); stream->silent = silent; /* restore old silence state */ unix_abort (stream); /* now punt the file and local data */ } /* UNIX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ /* lines to filter from header */ static STRINGLIST *unix_hlines = NIL; char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { MESSAGECACHE *elt; unsigned char *s,*t,*tl; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get cache */ if (!unix_hlines) { /* once only code */ STRINGLIST *lines = unix_hlines = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Keywords")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-UID")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAP")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAPbase")); } /* go to header position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.header.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.header.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.header.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; } else { /* need to make a CRLF version */ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1), elt->private.msg.header.text.size); /* tie off string, and convert to CRLF */ s[elt->private.msg.header.text.size] = '\0'; *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, elt->private.msg.header.text.size); fs_give ((void **) &s); /* free readin buffer */ } *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT); return LOCAL->buf; /* return processed copy */ } /* UNIX mail fetch message text * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL if failure */ long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get cache element */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { /* mark message seen and dirty */ elt->seen = elt->private.dirty = LOCAL->dirty = T; MM_FLAGS (stream,msgno); } s = unix_text_work (stream,elt,&i,flags); INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* UNIX mail fetch message text worker routine * Accepts: MAIL stream * message cache element * pointer to returned header text length * option flags */ char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags) { FDDATA d; STRING bs; unsigned char *s,*t,*tl,tmp[CHUNK]; /* go to text position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.text.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.text.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.text.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; return LOCAL->buf; } /* have it cached already? */ if (elt->private.uid != LOCAL->uid) { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->text.size) { /* excessively conservative, but the right thing is too hard to do */ fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); } d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = elt->private.special.offset + elt->private.msg.text.offset; d.chunk = tmp; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) { case '\r': /* carriage return seen */ *s++ = SNX (&bs); /* copy it and any succeeding LF */ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); break; case '\n': *s++ = '\r'; /* insert a CR */ default: *s++ = SNX (&bs); /* copy characters */ } *s = '\0'; /* tie off buffer */ /* calculate length of cached data */ LOCAL->textlen = s - LOCAL->text.data; } *length = LOCAL->textlen; /* return from cache */ return (char *) LOCAL->text.data; } /* UNIX per-message modify flag * Accepts: MAIL stream * message cache element */ void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { /* only after finishing */ if (elt->valid) elt->private.dirty = LOCAL->dirty = T; } /* UNIX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long unix_ping (MAILSTREAM *stream) { DOTLOCK lock; struct stat sbuf; long reparse; /* big no-op if not readwrite */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) { if (stream->rdonly) { /* does he want to give up readwrite? */ /* checkpoint if we changed something */ if (LOCAL->dirty) unix_check (stream); flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ close (LOCAL->ld); /* close the readwrite lock file */ LOCAL->ld = -1; /* no more readwrite lock fd */ unlink (LOCAL->lname); /* delete the readwrite lock file */ } else { /* see if need to reparse */ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) { /* get current mailbox size */ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); else stat (stream->mailbox,&sbuf); reparse = (sbuf.st_size != LOCAL->filesize); } /* parse if mailbox changed */ if (reparse && unix_parse (stream,&lock,LOCK_SH)) { /* unlock mailbox */ unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* and stream */ MM_NOCRITICAL (stream); /* done with critical */ } } } return LOCAL ? LONGT : NIL; /* return if still alive */ } /* UNIX mail check mailbox * Accepts: MAIL stream */ void unix_check (MAILSTREAM *stream) { DOTLOCK lock; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,&lock,LOCK_EX)) { /* any unsaved changes? */ if (LOCAL->dirty && unix_rewrite (stream,NIL,&lock)) { if (!stream->silent) MM_LOG ("Checkpoint completed",NIL); } /* no checkpoint needed, just unlock */ else unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ } } /* UNIX mail expunge mailbox * Accepts: MAIL stream */ void unix_expunge (MAILSTREAM *stream) { unsigned long i; DOTLOCK lock; char *msg = NIL; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,&lock,LOCK_EX)) { /* count expunged messages if not dirty */ if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; if (!LOCAL->dirty) { /* not dirty and no expunged messages */ unix_unlock (LOCAL->fd,stream,&lock); msg = "No messages deleted, so no update needed"; } else if (unix_rewrite (stream,&i,&lock)) { if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); else msg = "Mailbox checkpointed, but no messages expunged"; } /* rewrite failed */ else unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ if (msg && !stream->silent) MM_LOG (msg,NIL); } else if (!stream->silent) MM_LOG("Expunge ignored on readonly mailbox",WARN); } /* UNIX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; int fd; char *s,file[MAILTMPLEN]; DOTLOCK lock; time_t tp[2]; unsigned long i,j; MESSAGECACHE *elt; long ret = T; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { if (pc) return (*pc) (stream,sequence,mailbox,options); unix_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } LOCAL->buf[0] = '\0'; MM_CRITICAL (stream); /* go critical */ if ((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR);/* log the error */ return NIL; /* failed */ } fstat (fd,&sbuf); /* get current file size */ /* write all requested messages to mailbox */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL; else { /* internal header succeeded */ s = unix_header (stream,i,&j,FT_INTERNAL); /* header size, sans trailing newline */ if (j && (s[j - 2] == '\n')) j--; if (write (fd,s,j) < 0) ret = NIL; else { /* message header succeeded */ j = unix_xstatus (stream,LOCAL->buf,elt,NIL); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* message status succeeded */ s = unix_text_work (stream,elt,&j,FT_INTERNAL); if ((write (fd,s,j) < 0) || (write (fd,"\n",1) < 0)) ret = NIL; } } } } if (!ret || fsync (fd)) { /* force out the update */ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); ftruncate (fd,sbuf.st_size); ret = NIL; } tp[1] = time (0); /* set mtime to now */ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ else tp[0] = /* else preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; utime (file,tp); /* set the times */ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */ MM_NOCRITICAL (stream); /* release critical */ /* log the error */ if (!ret) MM_LOG (LOCAL->buf,ERROR); /* delete if requested message */ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = elt->private.dirty = LOCAL->dirty = T; return ret; } /* UNIX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ #define BUFLEN 8*MAILTMPLEN long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN]; time_t tp[2]; FILE *sf,*df; MESSAGECACHE elt; DOTLOCK lock; STRING *message; MAILSTREAM *tstream = NIL; long ret = LONGT; if (!stream) { /* stream specified? */ stream = &unixproto; /* no, default stream to prototype */ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) fs_give ((void **) &stream->user_flags[i]); stream->kwd_create = T; /* allow new flags */ } /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { unix_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } unlink (tmp); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR); else if (!unix_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); } /* get next message */ else if (MM_APPEND (af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ MM_CRITICAL (stream); /* go critical */ if (((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ rewind (sf); for (; i && ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) && (fwrite (buf,1,j,df) == j)); i -= j); fclose (sf); /* done with scratch file */ tp[1] = time (0); /* set mtime to now */ /* make sure append wins, fsync() necessary */ if (i || (fflush (df) == EOF) || fsync (fd)) { sprintf (buf,"Message append failed: %s",strerror (errno)); MM_LOG (buf,ERROR); ftruncate (fd,sbuf.st_size); tp[0] = /* preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; ret = NIL; /* return error */ } else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ utime (file,tp); /* set the times */ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */ fclose (df); /* note that unix_unlock() released the fd */ MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */ int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg) { int ti,zn,c; unsigned long i,uf; char *x,tmp[MAILTMPLEN]; int hdrp = T; long f = mail_parse_flags (stream,flags,&uf); /* build initial header */ if ((fprintf (sf,"From %s@%s %sStatus: ", myusername (),mylocalhost (),date) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if (putc ('\n',sf) == EOF) return NIL; while (SIZE (msg)) { /* copy text to scratch file */ /* disregard CRs */ while ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') == '\r'); /* see if line needs special treatment */ if ((c == 'F') || (hdrp && ((c == 'S') || (c == 'X')))) { /* copy line to buffer */ for (i = 1,tmp[0] = c; (c != '\n') && (i < MAILTMPLEN); ) if ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') != '\r') tmp[i++] = c; /* possible "From " line? */ if ((i > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') && (tmp[4] == ' ')) { /* yes, see if need to write a widget */ if (!(ti = unix_fromwidget || (c != '\n'))) VALID (tmp,x,ti,zn); if (ti && (putc ('>',sf) == EOF)) return NIL; } /* insert X- before metadata header */ else if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && (tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && (tmp[5] == 's') && (tmp[6] == ':')) || (((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && (tmp[5] == ':')) || ((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && (tmp[4] == 'A') && (tmp[5] == 'P') && ((tmp[6] == ':') || ((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && (tmp[8] == 's') && (tmp[9] == 'e') && (tmp[10] == ':')))) || ((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && (tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && (tmp[7] == 's') && (tmp[8] == ':')) || ((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && (tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && (tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && (tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF)) return NIL; /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; /* set up to copy remainder of line */ if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); else continue; /* end of line or end of message */ } /* check for end of header */ else if (hdrp && (c == '\n')) hdrp = NIL; /* copy line, tossing out CR */ do if ((c != '\r') && (putc (c,sf) == EOF)) return NIL; while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* write trailing newline and return */ return (putc ('\n',sf) == EOF) ? NIL : T; } /* Internal routines */ /* UNIX mail abort stream * Accepts: MAIL stream */ void unix_abort (MAILSTREAM *stream) { if (LOCAL) { /* only if a file is open */ if (LOCAL->fd >= 0) close (LOCAL->fd); if (LOCAL->ld >= 0) { /* have a mailbox lock? */ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ close (LOCAL->ld); /* close the lock file */ unlink (LOCAL->lname); /* and delete it */ } if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); /* free local text buffers */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* UNIX open and lock mailbox * Accepts: file name to open/lock * file open mode * destination buffer for lock file name * type of locking operation (LOCK_SH or LOCK_EX) */ int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op) { int fd; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_FILELOCK,NIL); /* try locking the easy way */ if (dotlock_lock (file,lock,-1)) { /* got dotlock file, easy open */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } /* no dot lock file, open file now */ else if ((fd = open (file,flags,mode)) >= 0) { /* try paranoid way to make a dot lock file */ if (dotlock_lock (file,lock,fd)) { close (fd); /* get fresh fd in case of timing race */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } else flock (fd,op); /* paranoid way failed, just flock() it */ } (*bn) (BLOCK_NONE,NIL); return fd; } /* UNIX unlock and close mailbox * Accepts: file descriptor * (optional) mailbox stream to check atime/mtime * (optional) lock file name */ void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock) { if (stream) { /* need to muck with times? */ struct stat sbuf; time_t tp[2]; time_t now = time (0); fstat (fd,&sbuf); /* get file times */ if (LOCAL->ld >= 0) { /* yes, readwrite session? */ tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else if (stream->recent) { /* readonly with recent messages */ if ((sbuf.st_atime >= sbuf.st_mtime) || (sbuf.st_atime >= sbuf.st_ctime)) /* keep past mtime, whack back atime */ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; else now = 0; /* no time change needed */ } /* readonly with no recent messages */ else if ((sbuf.st_atime < sbuf.st_mtime) || (sbuf.st_atime < sbuf.st_ctime)) { tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else now = 0; /* no time change needed */ /* set the times, note change */ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; } flock (fd,LOCK_UN); /* release flock'ers */ if (!stream) close (fd); /* close the file if no stream */ dotlock_unlock (lock); /* flush the lock file if any */ } /* UNIX mail parse and lock mailbox * Accepts: MAIL stream * space to write lock file name * type of locking operation * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure */ int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op) { int zn; unsigned long i,j,k,m; unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; int ti = 0,retain = T; unsigned long nmsgs = stream->nmsgs; unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; unsigned long recent = stream->recent; unsigned long oldnmsgs = stream->nmsgs; short silent = stream->silent; short pseudoseen = NIL; struct stat sbuf; STRING bs; FDDATA d; MESSAGECACHE *elt; mail_lock (stream); /* guard against recursion or pingers */ /* toss out previous descriptor */ if (LOCAL->fd >= 0) close (LOCAL->fd); MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */ if ((LOCAL->fd = unix_lock (stream->mailbox,(LOCAL->ld >= 0) ? O_RDWR : O_RDONLY,NIL,lock,op)) < 0) { sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (tmp,ERROR); unix_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } fstat (LOCAL->fd,&sbuf); /* get status */ /* validate change in size */ if (sbuf.st_size < LOCAL->filesize) { sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); /* this is pretty bad */ unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } /* new data? */ else if (i = sbuf.st_size - LOCAL->filesize) { d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = LOCAL->filesize; /* get to that position in the file */ d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ /* skip leading whitespace for broken MTAs */ while (((c = CHR (&bs)) == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) SNX (&bs); if (SIZE (&bs)) { /* read new data */ /* remember internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = unix_mbxline (stream,&bs,&i); t = NIL,zn = 0; if (i) VALID (s,t,ti,zn); /* see if valid From line */ if (!ti) { /* someone pulled the rug from under us */ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", (char *) s); MM_LOG (tmp,ERROR); unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); /* done with critical */ MM_NOCRITICAL (stream); return NIL; } stream->silent = T; /* quell main program new message events */ do { /* found a message */ /* instantiate first new message */ mail_exists (stream,++nmsgs); (elt = mail_elt (stream,nmsgs))->valid = T; recent++; /* assume recent by default */ elt->recent = T; /* note position/size of internal header */ elt->private.special.offset = j; elt->private.msg.header.offset = elt->private.special.text.size = i; /* generate plausible IMAPish date string */ date[2] = date[6] = date[20] = '-'; date[11] = ' '; date[14] = date[17] = ':'; /* dd */ date[0] = t[ti - 2]; date[1] = t[ti - 1]; /* mmm */ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; /* hh */ date[12] = t[ti + 1]; date[13] = t[ti + 2]; /* mm */ date[15] = t[ti + 4]; date[16] = t[ti + 5]; if (t[ti += 6] == ':') {/* ss */ date[18] = t[++ti]; date[19] = t[++ti]; ti++; /* move to space */ } else date[18] = date[19] = '0'; /* yy -- advance over timezone if necessary */ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4); date[7] = t[ti + 1]; date[8] = t[ti + 2]; date[9] = t[ti + 3]; date[10] = t[ti + 4]; /* zzz */ t = zn ? (t + zn + 1) : (unsigned char *) "LCL"; date[21] = *t++; date[22] = *t++; date[23] = *t++; if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0'; else { /* numeric time zone */ date[24] = *t++; date[25] = *t++; date[26] = '\0'; date[20] = ' '; } /* set internal date */ if (!mail_parse_date (elt,date)) { sprintf (tmp,"Unable to parse internal date: %s",(char *) date); MM_LOG (tmp,WARN); } do { /* look for message body */ s = t = unix_mbxline (stream,&bs,&i); if (i) switch (*s) { /* check header lines */ case 'X': /* possible X-???: line */ if (s[1] == '-') { /* must be immediately followed by hyphen */ /* X-Status: becomes Status: in S case */ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' && s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2; /* possible X-Keywords */ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' && s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && s[8] == 'd' && s[9] == 's' && s[10] == ':') { SIZEDTEXT uf; retain = NIL; /* don't retain continuation */ s += 11; /* flush leading whitespace */ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){ while (*s == ' ') s++; /* find end of keyword */ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); /* got a keyword? */ if ((k = (u - s)) && (k < MAXUSERFLAG)) { uf.data = (unsigned char *) s; uf.size = k; for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j) if (!compare_csizedtext (stream->user_flags[j],&uf)) { elt->user_flags |= ((long) 1) << j; break; } } s = u; /* advance to next keyword */ } break; } /* possible X-IMAP */ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') && (s[5] == 'P') && ((m = (s[6] == ':')) || ((s[6] == 'b') && (s[7] == 'a') && (s[8] == 's') && (s[9] == 'e') && (s[10] == ':')))) { retain = NIL; /* don't retain continuation */ if ((nmsgs == 1) && !stream->uid_validity) { /* advance to data */ s += m ? 7 : 11; /* flush whitespace */ while (*s == ' ') s++; j = 0; /* slurp UID validity */ /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush whitespace */ while (*s == ' ') s++; /* must have valid UID validity and UID last */ if (j && isdigit (*s)) { /* pseudo-header seen if X-IMAP */ if (m) pseudoseen = LOCAL->pseudo = T; /* save UID validity */ stream->uid_validity = j; j = 0; /* slurp UID last */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* save UID last */ stream->uid_last = j; /* process keywords */ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n')); s = u,j++) { /* flush leading whitespace */ while (*s == ' ') s++; u = strpbrk (s," \n\r"); /* got a keyword? */ if ((k = (u - s)) && j < NUSERFLAGS) { if (stream->user_flags[j]) fs_give ((void **) &stream->user_flags[j]); stream->user_flags[j] = (char *) fs_get (k + 1); strncpy (stream->user_flags[j],s,k); stream->user_flags[j][k] = '\0'; } } } } break; } /* possible X-UID */ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' && s[5] == ':') { retain = NIL; /* don't retain continuation */ /* only believe if have a UID validity */ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) { s += 6; /* advance to UID value */ /* flush whitespace */ while (*s == ' ') s++; j = 0; /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush remainder of line */ while (*s != '\n') s++; /* make sure not duplicated */ if (elt->private.uid) sprintf (tmp,"Message %lu UID %lu already has UID %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,elt->private.uid); /* make sure UID doesn't go backwards */ else if (j <= prevuid) sprintf (tmp,"Message %lu UID %lu less than %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,prevuid + 1); /* or skip by mailbox's recorded last */ else if (j > stream->uid_last) sprintf (tmp,"Message %lu UID %lu greater than last %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,stream->uid_last); else { /* normal UID case */ prevuid = elt->private.uid = j; break; /* exit this cruft */ } MM_LOG (tmp,WARN); /* invalidate UID validity */ stream->uid_validity = 0; elt->private.uid = 0; } break; } } /* otherwise fall into S case */ case 'S': /* possible Status: line */ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' && s[5] == 's' && s[6] == ':') { retain = NIL; /* don't retain continuation */ s += 6; /* advance to status flags */ do switch (*s++) {/* parse flags */ case 'R': /* message read */ elt->seen = T; break; case 'O': /* message old */ if (elt->recent) { elt->recent = NIL; recent--; /* it really wasn't recent */ } break; case 'D': /* message deleted */ elt->deleted = T; break; case 'F': /* message flagged */ elt->flagged = T; break; case 'A': /* message answered */ elt->answered = T; break; case 'T': /* message is a draft */ elt->draft = T; break; default: /* some other crap */ break; } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))); break; /* all done */ } /* otherwise fall into default case */ default: /* ordinary header line */ if ((*s == 'S') || (*s == 's') || (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) { unsigned char *e,*v; /* must match what mail_filter() does */ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1); (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') && ((c > ' ') || ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))); *v++ = *u++); *v = '\0'; /* tie off */ /* matches internal header? */ if (!compare_cstring (tmp,"STATUS") || !compare_cstring (tmp,"X-STATUS") || !compare_cstring (tmp,"X-KEYWORDS") || !compare_cstring (tmp,"X-UID") || !compare_cstring (tmp,"X-IMAP") || !compare_cstring (tmp,"X-IMAPBASE")) { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus %s header in message %lu", (char *) tmp,elt->msgno); MM_LOG (err,WARN); retain = NIL; /* don't retain continuation */ break; /* different case or something */ } } /* retain or non-continuation? */ if (retain || ((*s != ' ') && (*s != '\t'))) { retain = T; /* retaining continuation now */ /* line length in LF format newline */ k = i - (((i >= 2) && (s[i - 2] == '\r')) ? 1 : 0); /* "internal" header size */ elt->private.data += k; /* message size */ elt->rfc822_size += k + 1; } else { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus continuation in msg %lu: %.80s", elt->msgno,(char *) s); if (u = strpbrk (err,"\r\n")) *u = '\0'; MM_LOG (err,WARN); break; /* different case or something */ } break; } } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); /* "internal" header sans trailing newline */ if (i) elt->private.data--; /* assign a UID if none found */ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { prevuid = elt->private.uid = ++stream->uid_last; elt->private.dirty = T; } else elt->private.dirty = elt->recent; /* note size of header, location of text */ elt->private.msg.header.text.size = (elt->private.msg.text.offset = (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - elt->private.special.text.size; k = m = 0; /* no previous line size yet */ /* note current position */ j = LOCAL->filesize + GETPOS (&bs); if (i) do { /* look for next message */ s = unix_mbxline (stream,&bs,&i); if (i) { /* got new data? */ VALID (s,t,ti,zn); /* yes, parse line */ if (!ti) { /* not a header line, add it to message */ elt->rfc822_size += k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0)); /* update current position */ j = LOCAL->filesize + GETPOS (&bs); } } } while (i && !ti); /* until found a header */ elt->private.msg.text.text.size = j - (elt->private.special.offset + elt->private.msg.text.offset); if (k == 2) { /* last line was blank? */ elt->private.msg.text.text.size -= (m ? 1 : 2); elt->rfc822_size -= 2; } } while (i); /* until end of buffer */ if (pseudoseen) { /* flush pseudo-message if present */ /* decrement recent count */ if (mail_elt (stream,1)->recent) recent--; /* and the exists count */ mail_exists (stream,nmsgs--); mail_expunged(stream,1);/* fake an expunge of that message */ } /* need to start a new UID validity? */ if (!stream->uid_validity) { stream->uid_validity = time (0); /* in case a whiner with no life */ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) stream->uid_nosticky = T; else if (nmsgs) { /* don't bother if empty file */ LOCAL->dirty = T; /* make dirty to restart UID epoch */ /* need to rewrite msg 1 if not pseudo */ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; MM_LOG ("Assigning new unique identifiers to all messages",NIL); } } stream->nmsgs = oldnmsgs; /* whack it back down */ stream->silent = silent; /* restore old silent setting */ /* notify upper level of new mailbox sizes */ mail_exists (stream,nmsgs); mail_recent (stream,recent); /* mark dirty so O flags are set */ if (recent) LOCAL->dirty = T; } } /* no change, don't babble if never got time */ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime) MM_LOG ("New mailbox modification time but apparently no changes",WARN); /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; LOCAL->filetime = sbuf.st_mtime; return T; /* return the winnage */ } /* UNIX read line from mailbox * Accepts: mail stream * stringstruct * pointer to line size * Returns: pointer to input line */ char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) { unsigned long i,j,k,m; char *s,*t,*te,p1[CHUNK]; char *ret = ""; /* flush old buffer */ if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* if buffer needs refreshing */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) { /* find newline */ /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* difficult case if line spans buffer */ if ((i = s - bs->curpos) == bs->cursize) { memcpy (p1,bs->curpos,i); /* remember what we have so far */ /* load next buffer */ SETPOS (bs,k = GETPOS (bs) + i); /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; /* fast scan in overlap buffer */ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* huge line? */ if ((j = s - bs->curpos) == bs->cursize) { SETPOS (bs,GETPOS (bs) + j); /* look for end of line (s-l-o-w!!) */ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); SETPOS (bs,k); /* go back to where it started */ } /* got size of data, make buffer for return */ ret = LOCAL->line = (char *) fs_get (i + j + 2); memcpy (ret,p1,i); /* copy first chunk */ while (j) { /* copy remainder */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); i += k; /* account for this much read in */ j -= k; bs->curpos += k; /* increment new position */ bs->cursize -= k; /* eat that many bytes */ } if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */ ret[i++] = '\n'; /* make sure newline at end */ ret[i] = '\0'; /* makes debugging easier */ } else { /* this is easy */ ret = bs->curpos; /* string it at this position */ bs->curpos += ++i; /* increment new position */ bs->cursize -= i; /* eat that many bytes */ } *size = i; /* return that to user */ } else *size = 0; /* end of data, return empty */ return ret; } /* UNIX make pseudo-header * Accepts: MAIL stream * buffer to write pseudo-header * Returns: length of pseudo-header */ unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr) { int i; char *s,tmp[MAILTMPLEN]; time_t now = time (0); rfc822_fixed_date (tmp); sprintf (hdr,"From %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu", pseudo_from,ctime (&now), tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) now,mylocalhost (),stream->uid_validity, stream->uid_last); for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) sprintf (s += strlen (s)," %s",stream->user_flags[i]); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg); return strlen (hdr); /* return header length */ } /* UNIX make status string * Accepts: MAIL stream * destination string to write * message cache entry * non-zero flag to write UID (.LT. 0 to write UID base info too) * Returns: length of string */ unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag) { char *t,stack[64]; char *s = status; unsigned long n; int pad = 50; /* This used to use sprintf(), but thanks to certain cretinous C libraries with horribly slow implementations of sprintf() I had to change it to this mess. At least it should be fast. */ /* need to write X-IMAPbase: header? */ if ((flag < 0) && !stream->uid_nosticky) { *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P'; *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' '; t = stack; n = stream->uid_validity; /* push UID validity digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID validity digits from stack */ while (t > stack) *s++ = *--t; *s++ = ' '; n = stream->uid_last; /* push UID last digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID last digits from stack */ while (t > stack) *s++ = *--t; for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n]) for (*s++ = ' '; *t; *s++ = *t++); *s++ = '\n'; pad += 30; /* increased padding if have IMAPbase */ } *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->seen) *s++ = 'R'; if (flag) *s++ = 'O'; /* only write O if have a UID */ *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->deleted) *s++ = 'D'; if (elt->flagged) *s++ = 'F'; if (elt->answered) *s++ = 'A'; if (elt->draft) *s++ = 'T'; *s++ = '\n'; if (!stream->uid_nosticky) { /* cretins with no life can't use this */ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w'; *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':'; if (n = elt->user_flags) do { *s++ = ' '; for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); } while (n); n = s - status; /* get size of stuff so far */ /* pad X-Keywords to make size constant */ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; *s++ = '\n'; if (flag) { /* want to include UID? */ t = stack; n = elt->private.uid; /* push UID digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; *s++ = ' '; /* pop UID from stack */ while (t > stack) *s++ = *--t; *s++ = '\n'; } } *s++ = '\n'; *s = '\0'; /* end of extended message status */ return s - status; /* return size of resulting string */ } /* Rewrite mailbox file * Accepts: MAIL stream, must be critical and locked * return pointer to number of expunged messages if want expunge * lock file name * Returns: T if success and mailbox unlocked, NIL if failure */ #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock) { MESSAGECACHE *elt; UNIXFILE f; char *s; time_t tp[2]; long ret,flag; unsigned long i,j; unsigned long recent = stream->recent; unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0; if (nexp) *nexp = 0; /* initially nothing expunged */ /* calculate size of mailbox after rewrite */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) if (!(elt = mail_elt (stream,i))->deleted || !nexp) { /* add RFC822 size of this message */ size += elt->private.special.text.size + elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag) + elt->private.msg.text.text.size + 1; flag = 1; /* only count X-IMAPbase once */ } /* no messages, has a life, and no pseudo */ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) { LOCAL->pseudo = T; /* so make a pseudo-message now */ size = unix_pseudo (stream,LOCAL->buf); } /* extend the file as necessary */ if (ret = unix_extend (stream,size)) { /* Set up buffered I/O file structure * curpos current position being written through buffering * filepos current position being written physically to the disk * bufpos current position being written in the buffer * protect current maximum position that can be written to the disk * before buffering is forced * The code tries to buffer so that that disk is written in multiples of * OVERBLOWBUFLEN bytes. */ f.stream = stream; /* note mail stream */ f.curpos = f.filepos = 0; /* start of file */ f.protect = stream->nmsgs ? /* initial protection pointer */ mail_elt (stream,1)->private.special.offset : 8192; f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); if (LOCAL->pseudo) /* update pseudo-header */ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf)); /* loop through all messages */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { elt = mail_elt (stream,i);/* get cache */ if (nexp && elt->deleted){/* expunge this message? */ /* one less recent message */ if (elt->recent) --recent; mail_expunged(stream,i);/* notify upper levels */ ++*nexp; /* count up one more expunged message */ } else { /* preserve this message */ i++; /* advance to next message */ if ((flag < 0) || /* need to rewrite message? */ elt->private.dirty || (f.curpos != elt->private.special.offset) || (elt->private.msg.header.text.size != (elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag)))) { unsigned long newoffset = f.curpos; /* yes, seek to internal header */ lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); /* see if need to squeeze out a CR */ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') { LOCAL->buf[--elt->private.special.text.size - 1] = '\n'; --size; /* squeezed out a CR from PC */ } /* protection pointer moves to RFC822 header */ f.protect = elt->private.special.offset + elt->private.msg.header.offset; /* write internal header */ unix_write (&f,LOCAL->buf,elt->private.special.text.size); /* get RFC822 header */ s = unix_header (stream,elt->msgno,&j,FT_INTERNAL); /* in case this got decremented */ elt->private.msg.header.offset = elt->private.special.text.size; /* header size, sans trailing newline */ if ((j < 2) || (s[j - 2] == '\n')) j--; if (j != elt->private.data) fatal ("header size inconsistent"); /* protection pointer moves to RFC822 text */ f.protect = elt->private.special.offset + elt->private.msg.text.offset; unix_write (&f,s,j); /* write RFC822 header */ /* write status and UID */ unix_write (&f,LOCAL->buf, j = unix_xstatus (stream,LOCAL->buf,elt,flag)); flag = 1; /* only write X-IMAPbase once */ /* new file header size */ elt->private.msg.header.text.size = elt->private.data + j; /* did text move? */ if (f.curpos != f.protect) { /* get message text */ s = unix_text_work (stream,elt,&j,FT_INTERNAL); /* this can happen if CRs were squeezed */ if (j < elt->private.msg.text.text.size) { /* so fix up counts */ size -= elt->private.msg.text.text.size - j; elt->private.msg.text.text.size = j; } /* can't happen it says here */ else if (j > elt->private.msg.text.text.size) fatal ("text size inconsistent"); /* new text offset, status/UID may change it */ elt->private.msg.text.offset = f.curpos - newoffset; /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : (f.curpos + j + 1); unix_write (&f,s,j);/* write text */ /* write trailing newline */ unix_write (&f,"\n",1); } else { /* tie off header and status */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\n",1); } } /* new internal header offset */ elt->private.special.offset = newoffset; elt->private.dirty =NIL;/* message is now clean */ } else { /* no need to rewrite this message */ /* tie off previous message if needed */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.special.text.size + elt->private.msg.header.text.size + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\n",1); } } } } unix_write (&f,NIL,NIL); /* tie off final message */ if (size != f.filepos) fatal ("file size inconsistent"); fs_give ((void **) &f.buf); /* free buffer */ /* make sure tied off */ ftruncate (LOCAL->fd,LOCAL->filesize = size); fsync (LOCAL->fd); /* make sure the updates take */ if (size && (flag < 0)) fatal ("lost UID base information"); LOCAL->dirty = NIL; /* no longer dirty */ /* notify upper level of new mailbox sizes */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); /* set atime to now, mtime a second earlier */ tp[1] = (tp[0] = time (0)) - 1; /* set the times, note change */ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; close (LOCAL->fd); /* close and reopen file */ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,NIL)) < 0) { sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); unix_abort (stream); } dotlock_unlock (lock); /* flush the lock file */ } return ret; /* return state from algorithm */ } /* Extend UNIX mailbox file * Accepts: MAIL stream * new desired size * Return: T if success, else NIL */ long unix_extend (MAILSTREAM *stream,unsigned long size) { unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0; if (i) { /* does the mailbox need to grow? */ if (i > LOCAL->buflen) { /* make sure have enough space */ /* this user won the lottery all right */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1); } memset (LOCAL->buf,'\0',i); /* get a block of nulls */ while (T) { /* until write successful or punt */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break; else { long e = errno; /* note error before doing ftruncate */ ftruncate (LOCAL->fd,LOCAL->filesize); if (MM_DISKERROR (stream,e,NIL)) { fsync (LOCAL->fd); /* user chose to punt */ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e)); if (!stream->silent) MM_LOG (LOCAL->buf,ERROR); return NIL; } } } } return LONGT; } /* Write data to buffered file * Accepts: buffered file pointer * file data or NIL to indicate "flush buffer" * date size (ignored for "flush buffer") * Does not return until success */ void unix_write (UNIXFILE *f,char *buf,unsigned long size) { unsigned long i,j,k; if (buf) { /* doing buffered write? */ i = f->bufpos - f->buf; /* yes, get size of current buffer data */ /* yes, have space in current buffer chunk? */ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) { /* yes, fill up buffer as much as we can */ memcpy (f->bufpos,buf,k = min (j,size)); f->bufpos += k; /* new buffer position */ f->curpos += k; /* new current position */ if (j -= k) return; /* all done if still have buffer free space */ buf += k; /* full, get new unwritten data pointer */ size -= k; /* new data size */ i += k; /* new buffer data size */ } /* This chunk of the buffer is full. See if can make some space by * writing to the disk, if there's enough unprotected space to do so. * Try to fill out any unaligned chunk, along with any subsequent full * chunks that will fit in unprotected space. */ /* any unprotected space we can write to? */ if (j = min (i,f->protect - f->filepos)) { /* yes, filepos not at chunk boundary? */ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j)) j -= k; /* yes, and can write out partial chunk */ else k = 0; /* no partial chunk to write */ /* if at least a chunk free, write that too */ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN); if (k) { /* write data if there is anything we can */ unix_phys_write (f,f->buf,k); /* slide buffer */ if (i -= k) memmove (f->buf,f->buf + k,i); f->bufpos = f->buf + i; /* new end of buffer */ } } /* Have flushed the buffer as best as possible. All done if no more * data to write. Otherwise, if the buffer is empty AND if the unwritten * data is larger than a chunk AND the unprotected space is also larger * than a chunk, then write as many chunks as we can directly from the * data. Buffer the rest, expanding the buffer as needed. */ if (size) { /* have more data that we need to buffer? */ /* can write any of it to disk instead? */ if ((f->bufpos == f->buf) && ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) { /* write as much as we can right now */ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN)); buf += j; /* new data pointer */ size -= j; /* new data size */ f->curpos += j; /* advance current pointer */ } if (size) { /* still have data that we need to buffer? */ /* yes, need to expand the buffer? */ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) { /* note current position in buffer */ j = f->bufpos - f->buf; i += OVERFLOWBUFLEN; /* yes, grow another chunk */ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN)); /* in case buffer relocated */ f->bufpos = f->buf + j; } /* buffer remaining data */ memcpy (f->bufpos,buf,size); f->bufpos += size; /* new end of buffer */ f->curpos += size; /* advance current pointer */ } } } else { /* flush buffer to disk */ unix_phys_write (f,f->buf,i = f->bufpos - f->buf); f->bufpos = f->buf; /* reset buffer */ /* update positions */ f->curpos = f->protect = f->filepos; } } /* Physical disk write * Accepts: buffered file pointer * buffer address * buffer size * Does not return until success */ void unix_phys_write (UNIXFILE *f,char *buf,size_t size) { MAILSTREAM *stream = f->stream; /* write data at desired position */ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) || (write (LOCAL->fd,buf,size) < 0))) { int e; char tmp[MAILTMPLEN]; sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno)); MM_LOG (tmp,ERROR); MM_DISKERROR (NIL,e,T); /* serious problem, must retry */ } f->filepos += size; /* update file position */ } /* mbox mail routines */ /* Function prototypes */ DRIVER *mbox_valid (char *name); long mbox_create (MAILSTREAM *stream,char *mailbox); long mbox_delete (MAILSTREAM *stream,char *mailbox); long mbox_rename (MAILSTREAM *stream,char *old,char *newname); long mbox_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mbox_open (MAILSTREAM *stream); long mbox_ping (MAILSTREAM *stream); void mbox_check (MAILSTREAM *stream); void mbox_expunge (MAILSTREAM *stream); long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* MBOX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mboxdriver = { "mbox", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ mbox_valid, /* mailbox is valid for us */ unix_parameters, /* manipulate parameters */ unix_scan, /* scan mailboxes */ unix_list, /* find mailboxes */ unix_lsub, /* find subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mbox_create, /* create mailbox */ mbox_delete, /* delete mailbox */ mbox_rename, /* rename mailbox */ mbox_status, /* status of mailbox */ mbox_open, /* open mailbox */ unix_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ unix_header, /* fetch message header */ unix_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ unix_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mbox_ping, /* ping mailbox to see if still alive */ mbox_check, /* check for new messages */ mbox_expunge, /* expunge deleted messages */ unix_copy, /* copy messages to another mailbox */ mbox_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mboxproto = {&mboxdriver}; /* MBOX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mbox_valid (char *name) { /* only INBOX, mbox must exist */ if (!compare_cstring (name,"INBOX") && (unix_valid ("mbox") || !errno) && (unix_valid (sysinbox()) || !errno || (errno == ENOENT))) return &mboxdriver; return NIL; /* can't win (yet, anyway) */ } /* MBOX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mbox_create (MAILSTREAM *stream,char *mailbox) { return unix_create (NIL,"mbox"); } /* MBOX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mbox_delete (MAILSTREAM *stream,char *mailbox) { return mbox_rename (stream,mailbox,NIL); } /* MBOX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mbox_rename (MAILSTREAM *stream,char *old,char *newname) { char tmp[MAILTMPLEN]; long ret = unix_rename (stream,"~/mbox",newname); /* recreate file if renamed INBOX */ if (ret) unix_create (NIL,"mbox"); else MM_LOG (tmp,ERROR); /* log error */ return ret; /* return success */ } /* MBOX Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long mbox_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; if (!status.recent && /* calculate post-snarf results */ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* MBOX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mbox_open (MAILSTREAM *stream) { unsigned long i = 1; unsigned long recent = 0; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &mboxproto; /* change mailbox file name */ fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr ("mbox"); /* open mailbox, snarf new mail */ if (!(unix_open (stream) && mbox_ping (stream))) return NIL; stream->inbox = T; /* mark that this is an INBOX */ /* notify upper level of mailbox sizes */ mail_exists (stream,stream->nmsgs); while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent; mail_recent (stream,recent); /* including recent messages */ return stream; } /* MBOX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ static int snarfed = 0; /* number of snarfs */ long mbox_ping (MAILSTREAM *stream) { int sfd; unsigned long size; struct stat sbuf; char *s; DOTLOCK lock,lockx; /* time to try snarf and sysinbox non-empty? */ if (LOCAL && !stream->rdonly && !stream->lock && (time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && !stat (sysinbox (),&sbuf) && sbuf.st_size) { /* yes, open and lock sysinbox */ if ((sfd = unix_lock (sysinbox (),O_RDWR,NIL,&lockx,LOCK_EX)) >= 0) { /* locked sysinbox in good format? */ if (fstat (sfd,&sbuf) || !(size = sbuf.st_size) || !unix_isvalid_fd (sfd)) { sprintf (LOCAL->buf,"Mail drop %s is not in standard Unix format", sysinbox ()); MM_LOG (LOCAL->buf,ERROR); } /* sysinbox good, parse and excl-lock mbox */ else if (unix_parse (stream,&lock,LOCK_EX)) { lseek (sfd,0,L_SET); /* read entire sysinbox into memory */ read (sfd,s = (char *) fs_get (size + 1),size); s[size] = '\0'; /* tie it off */ /* append to end of mbox */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); /* copy to mbox */ if ((write (LOCAL->fd,s,size) < 0) || fsync (LOCAL->fd)) { sprintf (LOCAL->buf,"New mail move failed: %s",strerror (errno)); MM_LOG (LOCAL->buf,WARN); /* revert mbox to previous size */ ftruncate (LOCAL->fd,LOCAL->filesize); } /* sysinbox better not have changed */ else if (fstat (sfd,&sbuf) || (size != sbuf.st_size)) { sprintf (LOCAL->buf,"Mail drop %s lock failure, old=%lu now=%lu", sysinbox (),size,(unsigned long) sbuf.st_size); MM_LOG (LOCAL->buf,ERROR); /* revert mbox to previous size */ ftruncate (LOCAL->fd,LOCAL->filesize); /* Believe it or not, a Singaporean government system actually had * symlinks from /var/mail/user to ~user/mbox. To compound this * error, they used an SVR4 system; BSD and OSF locks would have * prevented it but not SVR4 locks. */ if (!fstat (sfd,&sbuf) && (size == sbuf.st_size)) syslog (LOG_ALERT,"File %s and %s are the same file!", sysinbox (),stream->mailbox); } else { /* data copied OK */ ftruncate (sfd,0); /* truncate sysinbox to zero bytes */ if (!snarfed++) { /* have we snarfed before? */ /* syslog if server, else mm_log() */ sprintf (LOCAL->buf,"Moved %lu bytes of new mail to %s from %s", size,stream->mailbox,sysinbox ()); if (strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL), "unknown")) syslog (LOG_INFO,"%s host= %s",LOCAL->buf,tcp_clienthost ()); else MM_LOG (LOCAL->buf,WARN); } } /* done with sysinbox text */ fs_give ((void **) &s); /* all done with mbox */ unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ /* done with critical */ MM_NOCRITICAL (stream); } /* all done with sysinbox */ unix_unlock (sfd,NIL,&lockx); } LOCAL->lastsnarf = time (0);/* note time of last snarf */ } return unix_ping (stream); /* do the unix routine now */ } /* MBOX mail check mailbox * Accepts: MAIL stream */ void mbox_check (MAILSTREAM *stream) { /* do local ping, then do unix routine */ if (mbox_ping (stream)) unix_check (stream); } /* MBOX mail expunge mailbox * Accepts: MAIL stream */ void mbox_expunge (MAILSTREAM *stream) { unix_expunge (stream); /* do expunge */ mbox_ping (stream); /* do local ping */ } /* MBOX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { return unix_append (stream,"mbox",af,data); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/unix.h000066400000000000000000000206521137544547100230520ustar00rootroot00000000000000/* * Program: UNIX mail routines, Amiga version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ /* Validate line * Accepts: pointer to candidate string to validate as a From header * return pointer to end of date/time field * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') * return pointer to offset from t of time zone (if non-zero) * Returns: t,ti,zn set if valid From string, else ti is NIL */ #define VALID(s,x,ti,zn) { \ int remote = 0; \ ti = 0; \ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ (s[4] == ' ')) { \ for (x = s + 5; *x && *x != '\012'; x++); \ if (*x) { \ if (x[-1] == '\015') --x; \ if (x - s >= 41) { \ for (zn = -1; x[zn] != ' '; zn--); \ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ { \ while (x[zn-13] == ' ') zn--; \ x += zn - 12; \ remote = 1; \ } \ } \ if (x - s >= 27) { \ if (x[-5] == ' ') { \ if (x[-8] == ':') zn = 0,ti = -5; \ else if (x[-9] == ' ') ti = zn = -9; \ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ ti = zn = -11; \ } \ else if (x[-4] == ' ') { \ if (x[-9] == ' ') zn = -4,ti = -9; \ else if ( (x[-13] == ' ') && (x[-16] == ' ') \ && (x[-20] ==' ') && \ ( ((x[-22] == ' ') && (x[-23] == ',')) || \ ((x[-23] == ' ') && (x[-24] == ',')) ) ) { \ char weekday[4]={0,}, month[4]={0,}, time[11]={0,}; \ char tzone[4]={0,}; \ char realtime[80]; \ int day,year,start=-26; \ if (x[-23] == ' ') x--; \ sscanf(&x[start],"%3c, %d %s %d %s %s", \ weekday,&day,month,&year,time,tzone); \ sprintf(realtime,"%s %s %2d %s %d %s", \ weekday,month,day,time, \ ( (year < 100) ? year+1900 : year),tzone); \ if (remote) \ strcat(realtime," remote from "); \ else \ strcat(realtime,"\n"); \ strncpy(&x[start],realtime,strlen(realtime)); \ zn = -2; \ ti = -7; \ } \ } \ else if (x[-6] == ' ') { \ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ zn = -6,ti = -11; \ } \ else if (x[-9] == ' ') { \ if ( ( (x[-12] == ' ') && (x[-16] == ' ') && \ ( ((x[-18] == ' ') && (x[-19] == ',') ) || \ ((x[-19] == ' ') && (x[-20] == ',')) ) \ || \ ((x[-14] == ' ') && (x[-18] == ' ') && \ ( ((x[-20] == ' ') && (x[-21] == ',') ) || \ ((x[-21] == ' ') && (x[-22] == ',')) ) ) ) ) { \ char weekday[4]={0,}, month[4]={0,},time[11]={0,}; \ int day,year,start=-24; \ char realtime[80]; \ if (x[-12] == ' ') x++; \ if (x[-19] == ' ') x++; \ sscanf(&x[start],"%3c, %d %3c %d %s",weekday, \ &day,month,&year,time); \ sprintf(realtime,"%s %s %2d %s %d",weekday,month,day,time,\ ( (year < 100) ? year+1900 : year)); \ if (remote) \ strcat(realtime," remote from "); \ else \ strcat(realtime,"\n"); \ strncpy(&x[start],realtime,strlen(realtime)); \ ti=-5; \ zn=0; \ } \ } \ if (ti && !((x[ti - 3] == ':') && \ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ (x[ti - 11] == ' '))) ti = 0; \ } \ } \ } \ } /* You are not expected to understand this macro, but read the next page if * you are not faint of heart. * * Known formats to the VALID macro are: * From user Wed Dec 2 05:53 1992 * BSD From user Wed Dec 2 05:53:22 1992 * SysV From user Wed Dec 2 05:53 PST 1992 * rn From user Wed Dec 2 05:53:22 PST 1992 * From user Wed Dec 2 05:53 -0700 1992 * emacs From user Wed Dec 2 05:53:22 -0700 1992 * From user Wed Dec 2 05:53 1992 PST * From user Wed Dec 2 05:53:22 1992 PST * From user Wed Dec 2 05:53 1992 -0700 * Solaris From user Wed Dec 2 05:53:22 1992 -0700 * * Amiga From user Wed, 6 Dec 92 05:53:22 who did this !!! * CHANGED in place to * From user Wed Dec 2 05:53:22 1992 * * Plus all of the above with `` remote from xxx'' after it. Thank you very * much, smail and Solaris, for making my life considerably more complicated. */ /* * What? You want to understand the VALID macro anyway? Alright, since you * insist. Actually, it isn't really all that difficult, provided that you * take it step by step. * * Line 1 Initializes the return ti value to failure (0); * Lines 2-3 Validates that the 1st-5th characters are ``From ''. * Lines 4-6 Validates that there is an end of line and points x at it. * Lines 7-14 First checks to see if the line is at least 41 characters long. * If so, it scans backwards to find the rightmost space. From * that point, it scans backwards to see if the string matches * `` remote from''. If so, it sets x to point to the space at * the start of the string. * Line 15 Makes sure that there are at least 27 characters in the line. * Lines 16-21 Checks if the date/time ends with the year (there is a space * five characters back). If there is a colon three characters * further back, there is no timezone field, so zn is set to 0 * and ti is set in front of the year. Otherwise, there must * either to be a space four characters back for a three-letter * timezone, or a space six characters back followed by a + or - * for a numeric timezone; in either case, zn and ti become the * offset of the space immediately before it. * Lines 22-24 Are the failure case for line 14. If there is a space four * characters back, it is a three-letter timezone; there must be a * space for the year nine characters back. zn is the zone * offset; ti is the offset of the space. * Lines 25-28 Are the failure case for line 20. If there is a space six * characters back, it is a numeric timezone; there must be a * space eleven characters back and a + or - five characters back. * zn is the zone offset; ti is the offset of the space. * Line 29-32 If ti is valid, make sure that the string before ti is of the * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise * invalidate ti. There must be a colon three characters back * and a space six or nine characters back (depending upon * whether or not the character six characters back is a colon). * There must be a space three characters further back (in front * of the day), one seven characters back (in front of the month), * and one eleven characters back (in front of the day of week). * ti is set to be the offset of the space before the time. * * Why a macro? It gets invoked a *lot* in a tight loop. On some of the * newer pipelined machines it is faster being open-coded than it would be if * subroutines are called. * * Why does it scan backwards from the end of the line, instead of doing the * much easier forward scan? There is no deterministic way to parse the * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to * see if unquoted spaces were possible. They are, and I've encountered enough * evil mail to be totally unwilling to trust that ``it will never happen''. */ /* Build parameters */ #define KODRETRY 15 /* kiss-of-death retry in seconds */ #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define CHUNK 16384 /* read-in chunk size */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/amiga/write.c000066400000000000000000000030231137544547100232050ustar00rootroot00000000000000/* * Program: Write data, treating partial writes as an error * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* The whole purpose of this unfortunate routine is to deal with DOS and * certain cretinous versions of UNIX which decided that the "bytes actually * written" return value from write() gave them license to use that for things * that are really errors, such as disk quota exceeded, maximum file size * exceeded, disk full, etc. * * BSD won't screw us this way on the local filesystem, but who knows what * some NFS-mounted filesystem will do. */ #undef write /* Write data to file * Accepts: file descriptor * I/O vector structure * number of vectors in structure * Returns: number of bytes written if successful, -1 if failure */ long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1); long safe_write (int fd,char *buf,long nbytes) { long i,j; if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) { while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) && (errno == EINTR)); if (j < 0) return j; } return nbytes; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/000077500000000000000000000000001137544547100214205ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/bezrkdos.c000066400000000000000000000672561137544547100234270ustar00rootroot00000000000000/* * Program: Berkeley mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 June 1992 * Last Edited: 22 January 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to those Merry Marvels of Musical * Madness . . . * -> The Incomparable Leland Stanford Junior University Marching Band <- * who entertain, awaken, and outrage Stanford fans in the fact of repeated * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have * HUSKY FEVER!!!]. * */ #include #include #include #include "mail.h" #include "osdep.h" #include #include #include #include "bezrkdos.h" #include "rfc822.h" #include "dummy.h" #include "misc.h" #include "fdstring.h" /* Build parameters */ #define CHUNK 4096 /* Berkeley I/O stream local data */ typedef struct bezerk_local { int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ char *buf; /* temporary buffer */ } BEZERKLOCAL; /* Convenient access to local data */ #define LOCAL ((BEZERKLOCAL *) stream->local) /* Function prototypes */ DRIVER *bezerk_valid (char *name); long bezerk_isvalid (char *name,char *tmp); int bezerk_valid_line (char *s,char **rx,int *rzn); void *bezerk_parameters (long function,void *value); void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void bezerk_list (MAILSTREAM *stream,char *ref,char *pat); void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat); long bezerk_create (MAILSTREAM *stream,char *mailbox); long bezerk_delete (MAILSTREAM *stream,char *mailbox); long bezerk_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *bezerk_open (MAILSTREAM *stream); void bezerk_close (MAILSTREAM *stream,long options); char *bezerk_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags); long bezerk_ping (MAILSTREAM *stream); void bezerk_check (MAILSTREAM *stream); void bezerk_expunge (MAILSTREAM *stream); long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long options); long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void bezerk_gc (MAILSTREAM *stream,long gcflags); char *bezerk_file (char *dst,char *name); long bezerk_badname (char *tmp,char *s); long bezerk_parse (MAILSTREAM *stream); unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* Berkeley mail routines */ /* Driver dispatch used by MAIL */ DRIVER bezerkdriver = { "bezerk", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ bezerk_valid, /* mailbox is valid for us */ bezerk_parameters, /* manipulate parameters */ bezerk_scan, /* scan mailboxes */ bezerk_list, /* list mailboxes */ bezerk_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ bezerk_create, /* create mailbox */ bezerk_delete, /* delete mailbox */ bezerk_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ bezerk_open, /* open mailbox */ bezerk_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ bezerk_header, /* fetch message header */ bezerk_text, /* fetch message text */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ bezerk_ping, /* ping mailbox to see if still alive */ bezerk_check, /* check for new messages */ bezerk_expunge, /* expunge deleted messages */ bezerk_copy, /* copy messages to another mailbox */ bezerk_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM bezerkproto = {&bezerkdriver}; /* Berkeley mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *bezerk_valid (char *name) { char tmp[MAILTMPLEN]; return bezerk_isvalid (name,tmp) ? &bezerkdriver : (DRIVER *) NIL; } /* Berkeley mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ long bezerk_isvalid (char *name,char *tmp) { int fd; long ret = NIL; struct stat sbuf; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); errno = -1; /* in case bezerk_valid_line fails */ if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = bezerk_valid_line (tmp,NIL,NIL); close (fd); /* close the file */ } } /* in case INBOX but not bezerk format */ else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) && ((name[1] == 'N') || (name[1] == 'n')) && ((name[2] == 'B') || (name[2] == 'b')) && ((name[3] == 'O') || (name[3] == 'o')) && ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1; return ret; /* return what we should */ } /* Validate line * Accepts: pointer to candidate string to validate as a From header * return pointer to end of date/time field * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') * return pointer to offset from t of time zone (if non-zero) * Returns: t,ti,zn set if valid From string, else ti is NIL */ int bezerk_valid_line (char *s,char **rx,int *rzn) { char *x; int zn; int ti = 0; /* line must begin with "From " */ if ((*s != 'F') || (s[1] != 'r') || (s[2] != 'o') || (s[3] != 'm') || (s[4] != ' ')) return NIL; /* find end of line */ for (x = s + 5; *x && *x != '\012'; x++); if (!x) return NIL; /* end of line not found */ if (x[-1] == '\015') x--; /* ignore CR */ if ((x - s < 27)) return NIL; /* line too short */ if (x - s >= 41) { /* possible search for " remote from " */ for (zn = -1; x[zn] != ' '; zn--); if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' ')) x += zn - 12; } if (x[-5] == ' ') { /* ends with year? */ /* no timezone? */ if (x[-8] == ':') zn = 0,ti = -5; /* three letter timezone? */ else if (x[-9] == ' ') ti = zn = -9; /* numeric timezone? */ else if ((x[-11]==' ') && ((x[-10]=='+') || (x[-10]=='-'))) ti = zn = -11; } else if (x[-4] == ' ') { /* no year and three leter timezone? */ if (x[-9] == ' ') zn = -4,ti = -9; } else if (x[-6] == ' ') { /* no year and numeric timezone? */ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) zn = -6,ti = -11; } /* time must be www mmm dd hh:mm[:ss] */ if (ti && !((x[ti - 3] == ':') && (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && (x[ti - 11] == ' '))) return NIL; if (rx) *rx = x; /* set return values */ if (rzn) *rzn = zn; return ti; } /* Berkeley manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *bezerk_parameters (long function,void *value) { return NIL; } /* Berkeley mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* Berkeley mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void bezerk_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (stream,ref,pat); } /* Berkeley mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (stream,ref,pat); } /* Berkeley mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long bezerk_create (MAILSTREAM *stream,char *mailbox) { return dummy_create (stream,mailbox); } /* Berkeley mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long bezerk_delete (MAILSTREAM *stream,char *mailbox) { return dummy_delete (stream,mailbox); } /* Berkeley mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long bezerk_rename (MAILSTREAM *stream,char *old,char *newname) { return dummy_rename (stream,old,newname); } /* Berkeley mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *bezerk_open (MAILSTREAM *stream) { long i; int fd; char *s; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &bezerkproto; if (stream->local) fatal ("bezerk recycle stream"); if (!mailboxfile (tmp,stream->mailbox)) return (MAILSTREAM *) bezerk_badname (tmp,stream->mailbox); if (((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0)) { sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } stream->rdonly = T; /* this driver is readonly */ stream->local = fs_get (sizeof (BEZERKLOCAL)); /* canonicalize the stream mailbox name */ fs_give ((void **) &stream->mailbox); if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0'; stream->mailbox = cpystr (tmp); LOCAL->fd = fd; /* note the file */ LOCAL->filesize = 0; /* initialize parsed file size */ LOCAL->buf = NIL; /* initially no local buffer */ stream->sequence++; /* bump sequence number */ stream->uid_validity = time (0); /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (!bezerk_ping (stream)) return NIL; if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = NIL; stream->perm_user_flags = NIL; return stream; /* return stream to caller */ } /* Berkeley mail close * Accepts: MAIL stream * close options */ void bezerk_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; if (options & CL_EXPUNGE) bezerk_expunge (stream); close (LOCAL->fd); /* close the local file */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* Berkeley mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *bezerk_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { char tmp[MAILTMPLEN]; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,bezerk_hdrpos (stream,msgno,length),L_SET); /* is buffer big enough? */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((size_t) *length + 1); LOCAL->buf[*length] = '\0'; /* tie off string */ /* slurp the data */ read (LOCAL->fd,LOCAL->buf,(size_t) *length); return LOCAL->buf; } /* Berkeley mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { MESSAGECACHE *elt; FDDATA d; unsigned long hdrsize,hdrpos; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* if message not seen */ /* mark message as seen */ if (elt->seen && !(flags & FT_PEEK)) { elt->seen = T; mm_flags (stream,msgno); } /* get location of text data */ hdrpos = bezerk_hdrpos (stream,msgno,&hdrsize); d.fd = LOCAL->fd; /* set initial stringstruct */ d.pos = hdrpos + hdrsize; /* flush old buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNK); INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize); return T; /* success */ } /* Berkeley mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long bezerk_ping (MAILSTREAM *stream) { /* punt if stream no longer alive */ if (!(stream && LOCAL)) return NIL; /* parse mailbox, punt if parse dies */ return (bezerk_parse (stream)) ? T : NIL; } /* Berkeley mail check mailbox (reparses status too) * Accepts: MAIL stream */ void bezerk_check (MAILSTREAM *stream) { unsigned long i = 1; if (bezerk_ping (stream)) { /* ping mailbox */ /* get new message status */ while (i <= stream->nmsgs) mail_elt (stream,i++); mm_log ("Check completed",(long) NIL); } } /* Berkeley mail expunge mailbox * Accepts: MAIL stream */ void bezerk_expunge (MAILSTREAM *stream) { mm_log ("Expunge ignored on readonly mailbox",WARN); } /* Berkeley mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { char tmp[MAILTMPLEN]; struct stat sbuf; MESSAGECACHE *elt; unsigned long i,j,k; int fd; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* make sure valid mailbox */ if (!bezerk_isvalid (mailbox,tmp) && errno) { if (errno == ENOENT) mm_notify (stream,"[TRYCREATE] Must create mailbox before append", (long) NIL); else if (pc) return (*pc) (stream,sequence,mailbox,options); else if (mailboxfile (tmp,mailbox)) { sprintf (tmp,"Not a Bezerk-format mailbox: %s",mailbox); mm_log (tmp,ERROR); } else bezerk_badname (tmp,mailbox); return NIL; } /* open the destination */ if (!mailboxfile (tmp,mailbox) || (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) { sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } mm_critical (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ /* for each requested message */ for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET); /* number of bytes to copy */ j = elt->private.msg.full.offset + elt->rfc822_size; do { /* read from source position */ k = min (j,(unsigned long) MAILTMPLEN); read (LOCAL->fd,tmp,(unsigned int) k); if (write (fd,tmp,(unsigned int) k) < 0) { sprintf (tmp,"Unable to write message: %s",strerror (errno)); mm_log (tmp,ERROR); chsize (fd,sbuf.st_size); close (fd); /* punt */ mm_nocritical (stream); return NIL; } } while (j -= k); /* until done */ } close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = T; return T; } /* Berkeley mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ #define BUFLEN MAILTMPLEN long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN]; FILE *sf,*df; MESSAGECACHE elt; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &bezerkproto; /* make sure valid mailbox */ if (!bezerk_isvalid (mailbox,tmp) && errno) { if (errno == ENOENT) { if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) && ((mailbox[1] == 'N') || (mailbox[1] == 'n')) && ((mailbox[2] == 'B') || (mailbox[2] == 'b')) && ((mailbox[3] == 'O') || (mailbox[3] == 'o')) && ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) bezerk_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } } else if (mailboxfile (tmp,mailbox)) { sprintf (tmp,"Not a Bezerk-format mailbox: %.80ss",mailbox); mm_log (tmp,ERROR); } else bezerk_badname (tmp,mailbox); return NIL; } tzset (); /* initialize timezone stuff */ /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR); else if (!bezerk_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); } /* get next message */ else if ((*af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ mm_critical (stream); /* go critical */ /* open the destination */ if (!mailboxfile (tmp,mailbox) || ((fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { mm_nocritical (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ while (i) /* until written all bytes */ if ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) && (fwrite (buf,1,j,df) == j)) i -= j; fclose (sf); /* done with scratch file */ /* make sure append wins */ if (i || (fflush (df) == EOF)) { chsize (fd,sbuf.st_size); /* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ sprintf (buf,"Message append failed: %s",strerror (errno)); mm_log (buf,ERROR); ret = NIL; /* return error */ } fclose (df); mm_nocritical (stream); /* release critical */ return ret; } /* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */ int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg) { int c; unsigned long i,uf; char tmp[MAILTMPLEN]; long f = mail_parse_flags (stream,flags,&uf); /* build initial header */ if ((fprintf (sf,"From %s@%s %sStatus: ", myusername (),mylocalhost (),date) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if (putc ('\n',sf) == EOF) return NIL; while (SIZE (msg)) { /* copy text to scratch file */ /* possible delimiter if line starts with F */ if ((c = 0xff & SNX (msg)) == 'F') { /* copy line to buffer */ for (i = 1,tmp[0] = c; SIZE (msg) && (c != '\n') && (i < MAILTMPLEN);) if (((c = 0xff & SNX (msg)) != '\r') || !(SIZE (msg)) || (CHR (msg) != '\n')) tmp[i++] = c; if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') && (tmp[4] == ' ')) { /* possible "From " line? */ /* yes, see if need to write a widget */ if (((c != '\n') || bezerk_valid_line (tmp,NIL,NIL)) && (putc ('>',sf) == EOF)) return NIL; } /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; if (c == '\n') continue; /* all done if got a complete line */ } /* copy line, toss out CR from CRLF */ do if (((c == '\r') && SIZE (msg) && ((c = 0xff & SNX (msg)) != '\n') && (putc ('\r',sf) == EOF)) || (putc (c,sf) == EOF)) return NIL; while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* write trailing newline and return */ return (putc ('\n',sf) == EOF) ? NIL : T; } /* Return bad file name error message * Accepts: temporary buffer * file name * Returns: long NIL always */ long bezerk_badname (char *tmp,char *s) { sprintf (tmp,"Invalid mailbox name: %s",s); mm_log (tmp,ERROR); return (long) NIL; } /* Parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long bezerk_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt; char *s,*t,tmp[MAILTMPLEN + 1],*db,datemsg[100]; long i; int j,ti,zn; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size); mm_log (tmp,ERROR); bezerk_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ db = datemsg + strlen (strcpy (datemsg,"Unparsable date: ")); while (sbuf.st_size - curpos){/* while there is data to read */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,SEEK_SET); /* read first buffer's worth */ read (LOCAL->fd,tmp,j = (int) min (i,(long) MAILTMPLEN)); tmp[j] = '\0'; /* tie off buffer */ if (!(ti = bezerk_valid_line (tmp,&t,&zn))) { mm_log ("Mailbox format invalidated (consult an expert), aborted",ERROR); bezerk_close (stream,NIL); return NIL; } /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* note offset of message */ elt->private.msg.full.offset = (s = ((*t == '\015') ? (t + 2) : (t + 1))) - tmp; /* generate plausable IMAPish date string */ db[2] = db[6] = db[20] = '-'; db[11] = ' '; db[14] = db[17] = ':'; /* dd */ db[0] = t[ti - 2]; db[1] = t[ti - 1]; /* mmm */ db[3] = t[ti - 6]; db[4] = t[ti - 5]; db[5] = t[ti - 4]; /* hh */ db[12] = t[ti + 1]; db[13] = t[ti + 2]; /* mm */ db[15] = t[ti + 4]; db[16] = t[ti + 5]; if (t[ti += 6] == ':') { /* ss if present */ db[18] = t[++ti]; db[19] = t[++ti]; ti++; /* move to space */ } else db[18] = db[19] = '0'; /* assume 0 seconds */ /* yy -- advance over timezone if necessary */ if (++zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4); db[7] = t[ti]; db[8] = t[ti + 1]; db[9] = t[ti + 2]; db[10] = t[ti + 3]; t = zn ? (t + zn) : "LCL"; /* zzz */ db[21] = *t++; db[22] = *t++; db[23] = *t++; if ((db[21] != '+') && (db[21] != '-')) db[24] = '\0'; else { /* numeric time zone */ db[20] = ' '; db[24] = *t++; db[25] = *t++; db[26] = '\0'; } /* set internal date */ if (!mail_parse_date (elt,db)) mm_log (datemsg,WARN); curpos += s - tmp; /* advance position after header */ t = strchr (s,'\012'); /* start of next line */ /* find start of next message */ while (!(bezerk_valid_line (s,NIL,NIL))) { if (t) { /* have next line? */ t++; /* advance to new line */ curpos += t - s; /* update position and size */ elt->rfc822_size += ((t - s) + ((t[-2] == '\015') ? 0 : 1)); s = t; /* move to next line */ t = strchr (s,'\012'); } else { /* try next buffer */ j = strlen (s); /* length of unread data in buffer */ if ((i = sbuf.st_size - curpos) && (i != j)) { /* get to that position in the file */ lseek (LOCAL->fd,curpos,SEEK_SET); /* read another buffer's worth */ read (LOCAL->fd,s = tmp,j = (int) min (i,(long) MAILTMPLEN)); tmp[j] = '\0'; /* tie off buffer */ if (!(t = strchr (s,'\012'))) fatal ("Line too long in mailbox"); } else { curpos += j; /* last bit of data */ elt->rfc822_size += j; break; } } } } /* update parsed file size */ LOCAL->filesize = sbuf.st_size; stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return T; /* return the winnage */ } /* Berkeley locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { long siz; size_t i = 0; char c = '\0'; char *s; char tmp[MAILTMPLEN]; MESSAGECACHE *elt = mail_elt (stream,msgno); long pos = elt->private.special.offset + elt->private.msg.full.offset; /* is size known? */ if (!(*size = elt->private.msg.header.text.size)) { /* get to header position */ lseek (LOCAL->fd,pos,SEEK_SET); /* search message for CRLF CRLF */ for (siz = 1; siz <= elt->rfc822_size; siz++) { if (!i && /* buffer empty? */ (read (LOCAL->fd,s = tmp, i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0)) return pos; else i--; /* two newline sequence? */ if ((c == '\012') && (*s == '\012')) { /* yes, note for later */ elt->private.msg.header.text.size = (*size = siz); return pos; /* return to caller */ } else if ((c == '\012') && (*s == '\015')) { /* yes, note for later */ elt->private.msg.header.text.size = (*size = siz + 1); return pos; /* return to caller */ } else c = *s++; /* next character */ } } return pos; /* have position */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/bezrkdos.h000066400000000000000000000053131137544547100234160ustar00rootroot00000000000000/* * Program: Berkeley mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 June 1992 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to those Merry Marvels of Musical * Madness . . . * -> The Incomparable Leland Stanford Junior University Marching Band <- * who entertain, awaken, and outrage Stanford fans in the fact of repeated * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have * HUSKY FEVER!!!]. * */ /* Build parameters */ #define CHUNK 4096 /* Berkeley I/O stream local data */ typedef struct bezerk_local { int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ char *buf; /* temporary buffer */ } BEZERKLOCAL; /* Convenient access to local data */ #define LOCAL ((BEZERKLOCAL *) stream->local) /* Function prototypes */ DRIVER *bezerk_valid (char *name); long bezerk_isvalid (char *name,char *tmp); int bezerk_valid_line (char *s,char **rx,int *rzn); void *bezerk_parameters (long function,void *value); void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void bezerk_list (MAILSTREAM *stream,char *ref,char *pat); void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat); long bezerk_create (MAILSTREAM *stream,char *mailbox); long bezerk_delete (MAILSTREAM *stream,char *mailbox); long bezerk_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *bezerk_open (MAILSTREAM *stream); void bezerk_close (MAILSTREAM *stream,long options); char *bezerk_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags); long bezerk_ping (MAILSTREAM *stream); void bezerk_check (MAILSTREAM *stream); void bezerk_expunge (MAILSTREAM *stream); long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long options); long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void bezerk_gc (MAILSTREAM *stream,long gcflags); char *bezerk_file (char *dst,char *name); long bezerk_badname (char *tmp,char *s); long bezerk_parse (MAILSTREAM *stream); unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/drivers.bat000066400000000000000000000013421137544547100235660ustar00rootroot00000000000000@ECHO OFF REM Program: Driver Linkage Generator for DOS/NT REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 11 October 1989 REM Last Edited: 6 July 2004 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 1988-2004 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Erase old driver linkage IF EXIST LINKAGE.* DEL LINKAGE.* REM Now define the new list FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D EXIT 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/drivraux.bat000066400000000000000000000012701137544547100237540ustar00rootroot00000000000000@ECHO OFF REM Program: Driver Linkage Generator auxillary for DOS REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 11 October 1989 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. ECHO extern DRIVER %1driver; >> LINKAGE.H ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/dummy.h000066400000000000000000000021051137544547100227220ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/dummydos.c000066400000000000000000000467061137544547100234420ustar00rootroot00000000000000/* * Program: Dummy routines for DOS * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 May 1993 * Last Edited: 2 February 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include "mail.h" #include "osdep.h" #include #include #include "dummy.h" #include "misc.h" /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level); long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents); long dummy_subscribe (MAILSTREAM *stream,char *mailbox); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); long dummy_badname (char *tmp,char *s); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ dummy_subscribe, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* driver parameters */ static char *file_extension = NIL; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ return (name && *name && (*name != '{') && (s = mailboxfile (tmp,name)) && (!*s || !stat (s,&sbuf))) ? &dummydriver : NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_EXTENSION: if (file_extension) fs_give ((void **) &file_extension); if (*(char *) value) file_extension = cpystr ((char *) value); case GET_EXTENSION: ret = (void *) file_extension; } return ret; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ #define LISTTMPLEN 128 void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char *s,test[LISTTMPLEN],file[LISTTMPLEN]; long i = 0; if (!pat || !*pat) { /* empty pattern? */ if (dummy_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'\\')) *++s = '\0'; else test[0] = '\0'; dummy_listed (stream,'\\',test,LATT_NOINFERIORS,NIL); } } /* get canonical form of name */ else if (dummy_canonicalize (test,ref,pat)) { /* found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test,(size_t) (i = s - test)); file[i] = '\0'; /* tie off */ } else strcpy (file,test); /* use just that name then */ /* find directory name */ if (s = strrchr (file,'\\')) { *++s = '\0'; /* found, tie off at that point */ s = file; } /* silly case */ else if (file[0] == '#') s = file; /* do the work */ dummy_list_work (stream,s,test,contents,0); if (pmatch ("INBOX",test)) /* always an INBOX */ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents); } } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { dummy_scan (stream,ref,pat,NIL); } /* Dummy list subscribed mailboxes * Accepts: mail stream * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { void *sdb = NIL; char *s,*t,test[MAILTMPLEN]; int showuppers = pat[strlen (pat) - 1] == '%'; /* get canonical form of name */ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do if (*s != '{') { if (pmatch_full (s,test,'\\')) { if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS); else mm_lsub (stream,'\\',s,NIL); } else while (showuppers && (t = strrchr (s,'\\'))) { *t = '\0'; /* tie off the name */ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT); } } while (s = sm_read (&sdb)); /* until no more subscriptions */ } /* Dummy subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long dummy_subscribe (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox); sprintf (tmp,"Can't subscribe %s: not a mailbox",mailbox); mm_log (tmp,ERROR); return NIL; } /* Dummy list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * string to scan * search level */ void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level) { struct find_t f; struct stat sbuf; char *s,tmp[LISTTMPLEN],tmpx[LISTTMPLEN]; char *base = (dir && (dir[0] == '\\')) ? NIL : myhomedir (); /* build name */ if (base) sprintf (tmpx,"%s\\",base); else tmpx[0] = '\0'; if (dir) strcat (tmpx,dir); /* punt if bogus name */ if (!mailboxfile (tmp,tmpx)) return; /* make directory wildcard */ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*." : "\\*."); strcat (tmp,file_extension ? file_extension : "*"); /* do nothing if can't open directory */ if (!_dos_findfirst (tmp,_A_NORMAL|_A_SUBDIR,&f)) { /* list it if not at top-level */ if (!level && dir && pmatch_full (dir,pat,'\\')) dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents); /* scan directory */ if (tmpx[strlen (tmpx) - 1] == '\\') do if (*f.name != '.') { if (base) sprintf (tmpx,"%s\\",base); else tmpx[0] = '\0'; if (dir) sprintf (tmpx + strlen (tmpx),"%s%s",dir,f.name); else strcat (tmpx,f.name); if (mailboxfile (tmp,tmpx) && !stat (tmp,&sbuf)) { /* suppress extension */ if (file_extension && (s = strchr (f.name,'.'))) *s = '\0'; /* now make name we'd return */ if (dir) sprintf (tmp,"%s%s",dir,f.name); else strcpy (tmp,f.name); /* only interested in file type */ switch (sbuf.st_mode & S_IFMT) { case S_IFDIR: /* directory? */ if (pmatch_full (tmp,pat,'\\')) { dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents); strcat (tmp,"\\"); /* set up for dmatch call */ } /* try again with trailing / */ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\')) dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents); if (dmatch (tmp,pat,'\\') && (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) dummy_list_work (stream,tmp,pat,contents,level+1); break; case S_IFREG: /* ordinary name */ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp)) dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents); break; } } } while (!_dos_findnext (&f)); } } /* Mailbox found * Accepts: hierarchy delimiter * mailbox name * attributes * contents to search before calling mm_list() * Returns: T, always */ #define BUFSIZE MAILTMPLEN long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents) { struct stat sbuf; int fd; size_t csiz,ssiz,bsiz; char *buf,tmp[MAILTMPLEN]; if (contents) { /* want to search contents? */ /* forget it if can't select or open */ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) || !mailboxfile (tmp,name) || stat (tmp,&sbuf) || (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0)) return T; /* get buffer including slop */ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1); memset (buf,'\0',ssiz); /* no slop area the first time */ while (sbuf.st_size) { /* until end of file */ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE)); if (search ((unsigned char *) buf,bsiz+ssiz, (unsigned char *) contents,csiz)) break; memcpy (buf,buf+BUFSIZE,ssiz); sbuf.st_size -= bsiz; /* note that we read that much */ } fs_give ((void **) &buf); /* flush buffer */ close (fd); /* finished with file */ if (!sbuf.st_size) return T;/* not found */ } /* notify main program */ mm_list (stream,delimiter,name,attributes); return T; } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { char tmp[MAILTMPLEN]; return (compare_cstring (mailbox,"INBOX") && mailboxfile (tmp,mailbox)) ? dummy_create_path (stream,tmp,NIL) : dummy_badname (tmp,mailbox); } /* Dummy create path * Accepts: mail stream * path name to create * directory mode * Returns: T on success, NIL on failure */ long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN]; int fd; long ret = NIL; char *t = strrchr (path,'\\'); char *pt = (path[1] == ':') ? path + 2 : path; int wantdir = t && !t[1]; if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */ /* found superior to this name? */ if ((s = strrchr (pt,'\\')) && (s != pt)) { strncpy (tmp,path,(size_t) (s - path)); tmp[s - path] = '\0'; /* make directory name for stat */ c = *++s; /* tie off in case need to recurse */ *s = '\0'; /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,path,dirmode)) return NIL; *s = c; /* restore full name */ } if (wantdir) { /* want to create directory? */ ret = !mkdir (path); *t = '\\'; /* restore directory delimiter */ } /* create file */ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0) ret = !close (fd); /* close file */ if (!ret) { /* error? */ sprintf (tmp,"Can't create mailbox node %s: %s",path,strerror (errno)); mm_log (tmp,ERROR); } return ret; /* return status */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { struct stat sbuf; char *s,tmp[MAILTMPLEN]; if (!mailboxfile (tmp,mailbox)) return dummy_badname (tmp,mailbox); /* no trailing \ */ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0'; if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? rmdir (tmp) : unlink (tmp)) { sprintf (tmp,"Can't delete mailbox %s: %s",mailbox,strerror (errno)); mm_log (tmp,ERROR); return NIL; } return T; /* return success */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN]; /* make file name */ if (!mailboxfile (file,old)) return dummy_badname (tmp,old); /* no trailing \ allowed */ if (!(s = mailboxfile (tmp,newname)) || ((s = strrchr (s,'\\')) && !s[1])) return dummy_badname (tmp,newname); if (s) { /* found superior to destination name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (file,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create (stream,file)) return NIL; *s = c; /* restore full name */ } if (rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %s to %s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); return NIL; } return LONGT; /* return success */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; struct stat sbuf; int fd = -1; /* OP_PROTOTYPE call or silence */ if (!stream || stream->silent) return NIL; if (!mailboxfile (tmp,stream->mailbox)) sprintf (tmp,"Can't open this name: %.80s",stream->mailbox); else if (compare_cstring (stream->mailbox,"INBOX") && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) sprintf (tmp,"%s: %s",strerror (errno),stream->mailbox); else { if (fd >= 0) { /* if got a file */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); if (sbuf.st_size) sprintf (tmp,"Not a mailbox: %s",stream->mailbox); else fd = -1; /* a-OK */ } if (fd < 0) { /* no file, right? */ if (!stream->silent) { /* only if silence not requested */ /* say there are 0 messages */ mail_exists (stream,(long) 0); mail_recent (stream,(long) 0); stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } } mm_log (tmp,stream->silent ? WARN: ERROR); return NIL; /* always fails */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ long dummy_ping (MAILSTREAM *stream) { MAILSTREAM *test; /* time to do another test? */ if (time (0) >= ((time_t) (stream->gensym + 30))) { /* has mailbox format changed? */ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) && (test->dtb != stream->dtb) && (test = mail_open (NIL,stream->mailbox,NIL))) { /* preserve some resources */ test->original_mailbox = stream->original_mailbox; stream->original_mailbox = NIL; test->sparep = stream->sparep; stream->sparep = NIL; test->sequence = stream->sequence; mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */ memcpy (fs_get (sizeof (MAILSTREAM)),stream, sizeof (MAILSTREAM))); /* swap the streams */ memcpy (stream,test,sizeof (MAILSTREAM)); fs_give ((void **) &test);/* flush test now that copied */ /* make sure application knows */ mail_exists (stream,stream->recent = stream->nmsgs); } /* still hasn't changed */ else stream->gensym = time (0); } return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd = -1; int e; char tmp[MAILTMPLEN]; MAILSTREAM *ts = default_proto (T); if (compare_cstring (mailbox,"INBOX") && mailboxfile (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) { if ((e = errno) == ENOENT) /* failed, was it no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before append", (long) NIL); sprintf (tmp,"%s: %s",strerror (e),mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } if (fd >= 0) { /* found file? */ fstat (fd,&sbuf); /* get its size */ close (fd); /* toss out the fd */ if (sbuf.st_size) ts = NIL; /* non-empty file? */ } if (ts) return (*ts->dtb->append) (stream,mailbox,af,data); sprintf (tmp,"Indeterminate mailbox format: %s",mailbox); mm_log (tmp,ERROR); return NIL; } /* Return bad file name error message * Accepts: temporary buffer * file name * Returns: long NIL always */ long dummy_badname (char *tmp,char *s) { sprintf (tmp,"Invalid mailbox name: %s",s); mm_log (tmp,ERROR); return (long) NIL; } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { char dev[4]; /* initially no device */ dev[0] = dev[1] = dev[2] = dev[3] = '\0'; if (ref) switch (*ref) { /* preliminary reference check */ case '{': /* remote names not allowed */ return NIL; /* disallowed */ case '\0': /* empty reference string */ break; default: /* all other names */ if (ref[1] == ':') { /* start with device name? */ dev[0] = *ref++; dev[1] = *ref++; } break; } if (pat[1] == ':') { /* device name in pattern? */ dev[0] = *pat++; dev[1] = *pat++; ref = NIL; /* ignore reference */ } switch (*pat) { case '#': /* namespace names */ if (mailboxfile (tmp,pat)) strcpy (tmp,pat); else return NIL; /* unknown namespace */ break; case '{': /* remote names not allowed */ return NIL; case '\\': /* rooted name */ ref = NIL; /* ignore reference */ break; } /* make sure device names are rooted */ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\'; /* build name */ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat); ucase (tmp); /* force upper case */ return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/env_dos.c000066400000000000000000000201321137544547100232170ustar00rootroot00000000000000/* * Program: DOS environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 8 July 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static char *myLocalHost = NIL; /* local host name */ static char *myClientAddr = NIL;/* client host address */ static char *myClientHost = NIL;/* client host name */ static char *myServerAddr = NIL;/* server host address */ static char *myServerHost = NIL;/* server host name */ static char *myHomeDir = NIL; /* home directory name */ static char *myNewsrc = NIL; /* newsrc file name */ static long list_max_level = 5; /* maximum level of list recursion */ static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ /* home namespace */ static NAMESPACE nshome = {"",'\\',NIL,NIL}; /* namespace list */ static NAMESPACE *nslist[3] = {&nshome,NIL,NIL}; #include "write.c" /* include safe writing routines */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Dummy definitions to prevent errors */ #define server_login(user,pass,authuser,argc,argv) NIL #define authserver_login(user,authuser,argc,argv) NIL #define myusername() "" #define MD5ENABLE "\\.nosuch.." /* Get all authenticators */ #include "auths.c" /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_NAMESPACE: ret = (void *) nslist; break; case SET_HOMEDIR: myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: if (!myNewsrc) { /* set news file name if not defined */ char tmp[MAILTMPLEN]; sprintf (tmp,"%s\\NEWSRC",myhomedir ()); myNewsrc = cpystr (tmp); } ret = (void *) myNewsrc; break; case SET_LISTMAXLEVEL: list_max_level = (long) value; case GET_LISTMAXLEVEL: ret = (void *) list_max_level; break; case SET_DISABLE822TZTEXT: no822tztext = value ? T : NIL; case GET_DISABLE822TZTEXT: ret = (void *) (no822tztext ? VOIDT : NIL); break; } return ret; } /* Write current time * Accepts: destination string * optional format of day-of-week prefix * format of date and time * flag whether to append symbolic timezone */ static void do_date (char *date,char *prefix,char *fmt,int suffix) { time_t tn = time (0); struct tm *t = gmtime (&tn); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&tn); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; if (prefix) { /* want day of week? */ sprintf (date,prefix,days[t->tm_wday]); date += strlen (date); /* make next sprintf append */ } /* output the date */ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); if (suffix) { /* append timezone suffix if desired */ tzset (); /* get timezone from TZ environment stuff */ sprintf (date + strlen (date)," (%.50s)", tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]); } } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d", no822tztext ? NIL : T); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL); } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { int i; char *s; if (!myHomeDir) { /* get home directory name if not yet known */ i = strlen (myHomeDir = cpystr ((s = getenv ("HOME")) ? s : "")); if (i && ((myHomeDir[i-1] == '\\') || (myHomeDir[i-1]=='/'))) myHomeDir[i-1] = '\0'; /* tie off trailing directory delimiter */ } return myHomeDir; } /* Return mailbox file name * Accepts: destination buffer * mailbox name * Returns: file name */ char *mailboxfile (char *dst,char *name) { char *s; char *ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL); /* forbid extraneous extensions */ if ((s = strchr ((s = strrchr (name,'\\')) ? s : name,'.')) && ((ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL)) || strchr (s+1,'.'))) return NIL; /* absolute path name? */ if ((*name == '\\') || (name[1] == ':')) strcpy (dst,name); else sprintf (dst,"%s\\%s",myhomedir (),name); if (ext) sprintf (dst + strlen (dst),".%s",ext); return ucase (dst); } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { extern MAILSTREAM DEFAULTPROTO; return &DEFAULTPROTO; /* return default driver's prototype */ } /* Global data */ static unsigned rndm = 0; /* initial `random' number */ /* Return random number */ long random () { if (!rndm) srand (rndm = (unsigned) time (0L)); return (long) rand (); } /* Default mailgets routine on DOS * Accepts: readin function pointer * stream to use * number of bytes * identifier data * Returns: string read in, truncated if necessary * * This is a sample mailgets routine. It simply truncates any data larger * than 63K. On most systems, you generally don't use a mailgets * routine at all, but on DOS it's required to prevent the application from * crashing. */ static char *dos_gets_buf = NIL; char *dos_default_gets (readfn_t f,void *stream,unsigned long size, GETS_DATA *md) { readprogress_t *rp = mail_parameters (NIL,GET_READPROGRESS,NIL); char *ret,tmp[MAILTMPLEN+1]; unsigned long i,j,dsc,rdi = 0; unsigned long dos_max = 63 * 1024; if (!dos_gets_buf) /* one-time initialization */ dos_gets_buf = (char *) fs_get ((size_t) dos_max + 1); ret = (md->flags & MG_COPY) ? ((char *) fs_get ((size_t) size + 1)) : dos_gets_buf; if (size > dos_max) { sprintf (tmp,"Mailbox %s, %s %lu[%.80s], %lu octets truncated to %ld", md->stream->mailbox,(md->flags & MG_UID) ? "UID" : "#", md->msgno,md->what,size,(long) dos_max); mm_log (tmp,WARN); /* warn user */ dsc = size - dos_max; /* number of bytes to discard */ size = dos_max; /* maximum length string we can read */ } else dsc = 0; /* nothing to discard */ dos_gets_buf[size] = '\0'; /* tie off string */ if (rp) for (i = size; j = min ((long) MAILTMPLEN,(long) i); i -= j) { (*f) (stream,j,ret + rdi); (*rp) (md,rdi += j); } else (*f) (stream,size,dos_gets_buf); /* toss out everything after that */ for (i = dsc; j = min ((long) MAILTMPLEN,(long) i); i -= j) { (*f) (stream,j,tmp); if (rp) (*rp) (md,rdi += j); } return ret; } /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ void syslog (int priority,const char *message,...) { } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ void openlog (const char *ident,int logopt,int facility) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/env_dos.h000066400000000000000000000036231137544547100232320ustar00rootroot00000000000000/* * Program: DOS environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s/MAILBOX.LST",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/MAILBOX.TMP",myhomedir ()) #define L_SET SEEK_SET /* Function prototypes */ #include "env.h" char *dos_default_gets (readfn_t f,void *stream,unsigned long size, GETS_DATA *md); long safe_write (int fd,char *buf,long nbytes); long random (); #if _MSC_VER < 700 #define getpid random #endif /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ void openlog (const char *ident,int logopt,int facility); void syslog (int priority,const char *message,...); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/fdstring.c000066400000000000000000000050171137544547100234070ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "mail.h" #include "osdep.h" #include "misc.h" #include "fdstring.h" /* String driver for fd stringstructs */ static void fd_string_init (STRING *s,void *data,unsigned long size); static char fd_string_next (STRING *s); static void fd_string_setpos (STRING *s,unsigned long i); STRINGDRIVER fd_string = { fd_string_init, /* initialize string structure */ fd_string_next, /* get next byte in string structure */ fd_string_setpos /* set position in string structure */ }; /* Initialize string structure for fd stringstruct * Accepts: string structure * pointer to string * size of string */ static void fd_string_init (STRING *s,void *data,unsigned long size) { FDDATA *d = (FDDATA *) data; s->data = (void *) d->fd; /* note fd */ s->data1 = d->pos; /* note file offset */ s->size = size; /* note size */ s->curpos = s->chunk = d->chunk; s->chunksize = (unsigned long) d->chunksize; s->offset = 0; /* initial position */ /* and size of data */ s->cursize = min (s->chunksize,size); /* move to that position in the file */ lseek (d->fd,d->pos,L_SET); read (d->fd,s->chunk,(size_t) s->cursize); } /* Get next character from fd stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ static char fd_string_next (STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for fd stringstruct * Accepts: string structure * new position */ static void fd_string_setpos (STRING *s,unsigned long i) { if (i > s->size) i = s->size; /* don't permit setting beyond EOF */ s->offset = i; /* set new offset */ s->curpos = s->chunk; /* reset position */ /* set size of data */ if (s->cursize = min (s->chunksize,SIZE (s))) { /* move to that position in the file */ lseek ((int) s->data,s->data1 + s->offset,L_SET); read ((int) s->data,s->curpos,(size_t) s->cursize); } } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/fdstring.h000066400000000000000000000015131137544547100234110ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Driver-dependent data passed to init method */ typedef struct fd_data { int fd; /* file descriptor */ unsigned long pos; /* initial position */ char *chunk; /* I/O buffer chunk */ unsigned long chunksize; /* I/O buffer chunk length */ } FDDATA; extern STRINGDRIVER fd_string; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/fs_dos.c000066400000000000000000000022341137544547100230420ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { free (*block); *block = NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/ftl_dos.c000066400000000000000000000013271137544547100232210ustar00rootroot00000000000000/* * Program: DOS/VMS/TOPS-20 crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/makefile000066400000000000000000000047031137544547100231240ustar00rootroot00000000000000# Program: Portable C client makefile -- MS-DOS version # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 14 October 2003 # # The IMAP toolkit provided in this Distribution is # Copyright 2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. OS = wsk EXTRAAUTHENTICATORS= DEFAULTAUTHENTICATORS=md5 pla log EXTRADRIVERS = DRIVERS = imap nntp pop3 mtx bezerk DEFAULTDRIVER = mtx CFLAGS= -AL -nologo $(EXTRACFLAGS) CC = cl all: mtest.exe .c.obj: $(CC) -c $(CFLAGS) $*.c osdep.h: os_$(OS).h copy os_$(OS).h osdep.h drivers $(EXTRADRIVERS) $(DRIVERS) dummy mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS) ECHO #define DEFAULTPROTO $(DEFAULTDRIVER)proto >> LINKAGE.H ECHO if (!mail_parameters (NIL,GET_GETS)) >> LINKAGE.C ECHO mail_parameters (NIL,SET_GETS,(void *) dos_default_gets); >> LINKAGE.C mtest.obj: mail.h smtp.h misc.h osdep.h mtest.c mail.obj: mail.h misc.h osdep.h mail.c misc.obj: mail.h misc.h misc.c fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c rfc822.obj: mail.h rfc822.h misc.h rfc822.c smanager.obj: mail.h misc.h smanager.c utf8.obj: mail.h misc.h osdep.h utf8.h imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c pop3.obj: mail.h pop3.h rfc822.h misc.h osdep.h pop3.c smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c os_$(OS).obj: mail.h osdep.h env_dos.h fs.h ftl.h nl.h tcp.h \ os_$(OS).c fs_dos.c ftl_dos.c nl_dos.c env_dos.c pmatch.c write.c mtxdos.obj: mail.h misc.h osdep.h mtxdos.c bezrkdos.obj: mail.h misc.h osdep.h bezrkdos.c dummydos.obj: mail.h dummy.h misc.h osdep.h dummydos.c cclient.lib: mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_$(OS).obj \ mtxdos.obj bezrkdos.obj dummydos.obj erase cclient.lib lib cclient +mail+misc+fdstring+flstring+netmsg+newsrc+rfc822+smanager+imap4r1+nntp+pop3+smtp+os_$(OS)+mtxdos+bezrkdos+dummydos; mtest.exe: cclient.lib mtest.obj mtest$(OS) tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mkautaux.bat000066400000000000000000000013611137544547100237500ustar00rootroot00000000000000@ECHO OFF REM Program: Authenticator Linkage Generator auxillary for DOS REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 7 December 1995 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C ECHO #include "auth_%1.c" >> AUTHS.C tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mkauths.bat000066400000000000000000000013451137544547100235670ustar00rootroot00000000000000@ECHO OFF REM Program: Authenticator Linkage Generator for DOS REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 6 December 1995 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Erase old authenticators list IF EXIST AUTHS.C DEL AUTHS.C REM Now define the new list FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D EXIT 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtest.def000066400000000000000000000004241137544547100232340ustar00rootroot00000000000000; ; MTEST.DEF - def file for c-client test application mtest ; NAME WINDOWSAPI DESCRIPTION 'C-Client Test Application' EXETYPE WINDOWS PROTMODE CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE HEAPSIZE 1024 STACKSIZE 32767 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtestdbw.bat000066400000000000000000000012261137544547100237420ustar00rootroot00000000000000@ECHO OFF REM Program: Portable C client makefile -- MS-DOS B&W link REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 26 June 1994 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib llbwtcp.lib llibce.lib tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtestdnf.bat000066400000000000000000000012151137544547100237330ustar00rootroot00000000000000@ECHO OFF REM Program: Portable C client makefile -- MS-DOS PC-NFS link REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 26 June 1994 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib ltklib.lib tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtestdnv.bat000066400000000000000000000012171137544547100237550ustar00rootroot00000000000000@ECHO OFF REM Program: Portable C client makefile -- MS-DOS Novell link REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 26 June 1994 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib llibsock.lib tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtestdpc.bat000066400000000000000000000012721137544547100237350ustar00rootroot00000000000000@ECHO OFF REM Program: Portable C client makefile -- MS-DOS PC/TCP link REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 26 June 1994 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib lsocket.lib lnetlib.lib lpc.lib lconfig.lib llibce.lib; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtestdwa.bat000066400000000000000000000012211137544547100237340ustar00rootroot00000000000000@ECHO OFF REM Program: Portable C client makefile -- MS-DOS Waterloo link REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 26 June 1994 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib wattcplg.lib tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtestwsk.bat000066400000000000000000000012461137544547100237740ustar00rootroot00000000000000@ECHO OFF REM Program: Portable C client makefile -- MS-DOS Winsock link REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 26 June 1994 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. link /NOD:llibce mtest.obj,mtest.exe,,cclient.lib winsock.lib llibcewq.lib libw.lib, mtest tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/mtxdos.c000066400000000000000000000632371137544547100231150ustar00rootroot00000000000000/* * Program: MTX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 June 1992 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include "mail.h" #include "osdep.h" #include #include #include #include "rfc822.h" #include "dummy.h" #include "misc.h" #include "fdstring.h" /* Build parameters */ #define CHUNK 4096 /* MTX I/O stream local data */ typedef struct mtx_local { int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ unsigned char *buf; /* temporary buffer */ } MTXLOCAL; /* Drive-dependent data passed to init method */ typedef struct mtx_data { int fd; /* file data */ unsigned long pos; /* initial position */ } MTXDATA; /* Convenient access to local data */ #define LOCAL ((MTXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mtx_valid (char *name); long mtx_isvalid (char *name,char *tmp); void *mtx_parameters (long function,void *value); void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mtx_list (MAILSTREAM *stream,char *ref,char *pat); void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mtx_create (MAILSTREAM *stream,char *mailbox); long mtx_delete (MAILSTREAM *stream,char *mailbox); long mtx_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *mtx_open (MAILSTREAM *stream); void mtx_close (MAILSTREAM *stream,long options); char *mtx_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mtx_ping (MAILSTREAM *stream); void mtx_check (MAILSTREAM *stream); void mtx_expunge (MAILSTREAM *stream); long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); char *mtx_file (char *dst,char *name); long mtx_badname (char *tmp,char *s); long mtx_parse (MAILSTREAM *stream); void mtx_update_status (MAILSTREAM *stream,unsigned long msgno); unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* MTX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mtxdriver = { "mtx", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ mtx_valid, /* mailbox is valid for us */ mtx_parameters, /* manipulate parameters */ mtx_scan, /* scan mailboxes */ mtx_list, /* list mailboxes */ mtx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mtx_create, /* create mailbox */ mtx_delete, /* delete mailbox */ mtx_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mtx_open, /* open mailbox */ mtx_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mtx_header, /* fetch message header */ mtx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ mtx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mtx_ping, /* ping mailbox to see if still alive */ mtx_check, /* check for new messages */ mtx_expunge, /* expunge deleted messages */ mtx_copy, /* copy messages to another mailbox */ mtx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mtxproto = {&mtxdriver}; /* MTX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mtx_valid (char *name) { char tmp[MAILTMPLEN]; return mtx_isvalid (name,tmp) ? &mtxdriver : (DRIVER *) NIL; } /* MTX mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ long mtx_isvalid (char *name,char *tmp) { int fd; long ret = NIL; char *s; struct stat sbuf; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) && (s[1] == '\012')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ } } /* in case INBOX but not mtx format */ else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) && ((name[1] == 'N') || (name[1] == 'n')) && ((name[2] == 'B') || (name[2] == 'b')) && ((name[3] == 'O') || (name[3] == 'o')) && ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1; return ret; /* return what we should */ } /* MTX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mtx_parameters (long function,void *value) { return NIL; } /* MTX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MTX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (stream,ref,pat); } /* MTX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (stream,ref,pat); } /* MTX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mtx_create (MAILSTREAM *stream,char *mailbox) { return dummy_create (stream,mailbox); } /* MTX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mtx_delete (MAILSTREAM *stream,char *mailbox) { return dummy_delete (stream,mailbox); } /* MTX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mtx_rename (MAILSTREAM *stream,char *old,char *newname) { return dummy_rename (stream,old,newname); } /* MTX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mtx_open (MAILSTREAM *stream) { long i; int fd; char *s; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &mtxproto; if (stream->local) fatal ("mtx recycle stream"); if (!mailboxfile (tmp,stream->mailbox)) return (MAILSTREAM *) mtx_badname (tmp,stream->mailbox); /* open, possibly creating INBOX */ if (((fd = open (tmp,O_BINARY|(stream->rdonly ? O_RDONLY:O_RDWR),NIL)) < 0)&& (compare_cstring (stream->mailbox,"INBOX") || ((fd = open (tmp,O_BINARY|O_RDWR|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))<0))){ sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } stream->local = fs_get (sizeof (MTXLOCAL)); /* canonicalize the stream mailbox name */ fs_give ((void **) &stream->mailbox); if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0'; stream->mailbox = cpystr (tmp); LOCAL->fd = fd; /* note the file */ LOCAL->filesize = 0; /* initialize parsed file size */ LOCAL->buf = NIL; /* initially no local buffer */ stream->sequence++; /* bump sequence number */ stream->uid_validity = time (0); /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (!mtx_ping (stream)) return NIL; if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* MTX mail close * Accepts: MAIL stream * close options */ void mtx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; if (options & CL_EXPUNGE) mtx_expunge (stream); close (LOCAL->fd); /* close the local file */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MTX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET); /* is buffer big enough? */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((size_t) *length + 1); LOCAL->buf[*length] = '\0'; /* tie off string */ /* slurp the data */ read (LOCAL->fd,LOCAL->buf,(size_t) *length); return LOCAL->buf; } /* MTX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { MESSAGECACHE *elt; FDDATA d; unsigned long hdrsize,hdrpos; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* if message not seen */ if (elt->seen && !(flags & FT_PEEK)) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mtx_update_status (stream,msgno); mm_flags (stream,msgno); } /* get location of text data */ hdrpos = mtx_hdrpos (stream,msgno,&hdrsize); d.fd = LOCAL->fd; /* set initial stringstruct */ d.pos = hdrpos + hdrsize; /* flush old buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNK); INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize); return T; /* success */ } /* MTX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { /* recalculate status */ mtx_update_status (stream,elt->msgno); } /* MTX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mtx_ping (MAILSTREAM *stream) { /* punt if stream no longer alive */ if (!(stream && LOCAL)) return NIL; /* parse mailbox, punt if parse dies */ return (mtx_parse (stream)) ? T : NIL; } /* MTX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mtx_check (MAILSTREAM *stream) { unsigned long i = 1; if (mtx_ping (stream)) { /* ping mailbox */ /* get new message status */ while (i <= stream->nmsgs) mail_elt (stream,i++); mm_log ("Check completed",(long) NIL); } } /* MTX mail expunge mailbox * Accepts: MAIL stream */ void mtx_expunge (MAILSTREAM *stream) { unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; MESSAGECACHE *elt; char tmp[MAILTMPLEN]; /* do nothing if stream dead */ if (!mtx_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ mm_log ("Expunge ignored on readonly mailbox",WARN); return; } mm_critical (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged */ while (i <= stream->nmsgs) { /* for each message */ elt = mail_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + elt->rfc822_size; if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more deleted message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,(unsigned long) MAILTMPLEN); lseek (LOCAL->fd,j,SEEK_SET); read (LOCAL->fd,tmp,(size_t) m); /* write to destination position */ lseek (LOCAL->fd,j - delta,SEEK_SET); write (LOCAL->fd,tmp,(size_t) m); j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ elt->private.special.offset -= delta; } } if (n) { /* truncate file after last message */ chsize (LOCAL->fd,LOCAL->filesize -= delta); sprintf (tmp,"Expunged %ld messages",n); mm_log (tmp,(long) NIL); /* output the news */ } else mm_log ("No messages deleted, so no update needed",(long) NIL); mm_nocritical (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); } /* MTX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { char tmp[MAILTMPLEN]; struct stat sbuf; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; if (!mtx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before append", (long) NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: /* name is bogus */ if (pc) return (*pc) (stream,sequence,mailbox,options); return mtx_badname (tmp,mailbox); default: /* file exists, but not valid format */ if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (tmp,"Not a MTX-format mailbox: %s",mailbox); mm_log (tmp,ERROR); return NIL; } /* open the destination */ if (!mailboxfile (tmp,mailbox) || (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) { sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } mm_critical (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET); /* number of bytes to copy */ k = elt->private.special.text.size + elt->rfc822_size; do { /* read from source position */ j = min (k,(long) MAILTMPLEN); read (LOCAL->fd,tmp,(size_t) j); if (write (fd,tmp,(size_t) j) < 0) { sprintf (tmp,"Unable to write message: %s",strerror (errno)); mm_log (tmp,ERROR); chsize (fd,sbuf.st_size); j = k; ret = NIL; /* note error */ break; } } while (k -= j); /* until done */ } close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ mtx_update_status (stream,i); } return ret; } /* MTX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN]; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &mtxproto; /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) && ((mailbox[1] == 'N') || (mailbox[1] == 'n')) && ((mailbox[2] == 'B') || (mailbox[2] == 'b')) && ((mailbox[3] == 'O') || (mailbox[3] == 'o')) && ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) dummy_create (NIL,"INBOX.MTX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: return mtx_badname (tmp,mailbox); default: sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (!mailboxfile (file,mailbox) || ((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } mm_critical (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf, (unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* revert file if failure */ if (!ret || (fflush (df) == EOF)) { chsize (fd,sbuf.st_size); /* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); } ret = NIL; } fclose (df); /* close the file */ mm_nocritical (stream); /* release critical */ return ret; } /* Return bad file name error message * Accepts: temporary buffer * file name * Returns: long NIL always */ long mtx_badname (char *tmp,char *s) { sprintf (tmp,"Invalid mailbox name: %s",s); mm_log (tmp,ERROR); return (long) NIL; } /* Parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mtx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char *s,*t,*x,lbuf[65]; char tmp[MAILTMPLEN]; long i; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } /* while there is stuff to parse */ while (i = sbuf.st_size - curpos) { /* get to that position in the file */ lseek (LOCAL->fd,curpos,SEEK_SET); if ((i = read (LOCAL->fd,lbuf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s", curpos,sbuf.st_size,i ? strerror (errno) : "no data read"); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } lbuf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (lbuf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find end of line at %ld in %ld bytes, text: %s", curpos,i,(char *) lbuf); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - lbuf; /* note start of text offset */ if (!((s = strchr (lbuf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos, (char *) lbuf); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ /* intantiate an elt for this message */ (elt = mail_elt (stream,++nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* as well as offset from header of message */ elt->private.special.text.size = i; /* header size not known yet */ elt->private.msg.header.text.size = 0; /* parse the header components */ if (!(mail_parse_date (elt,lbuf) && (elt->rfc822_size = strtol (x = s,(char **) &s,10)) && (!(s && *s))&& isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) { sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) lbuf,(char *) x,(char *) t); mtx_close (stream,NIL); return NIL; } /* update current position to next header */ curpos += i + elt->rfc822_size; /* calculate system flags */ if ((i = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (i & fDELETED) elt->deleted = T; if (i & fFLAGGED) elt->flagged = T; if (i & fANSWERED) elt->answered = T; if (i & fDRAFT) elt->draft = T; if (curpos > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,curpos,sbuf.st_size); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } } /* update parsed file size */ LOCAL->filesize = sbuf.st_size; mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return T; /* return the winnage */ } /* Update status string * Accepts: MAIL stream * message number */ void mtx_update_status (MAILSTREAM *stream,unsigned long msgno) { char tmp[MAILTMPLEN]; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* not if readonly you don't */ if (stream->rdonly || !elt->valid) return; j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << 29 - find_rightmost_bit (&j); sprintf (tmp,"%010lo%02o",k, /* print new flag string */ fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft)); /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,SEEK_SET); write (LOCAL->fd,tmp,12); /* write new flags */ } /* MTX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; size_t i = 0; int q = 0; char *s,tmp[MAILTMPLEN]; MESSAGECACHE *elt = mail_elt (stream,msgno); long pos = elt->private.special.offset + elt->private.special.text.size; /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { /* get to header position */ lseek (LOCAL->fd,pos,SEEK_SET); /* search message for CRLF CRLF */ for (siz = 1; siz <= elt->rfc822_size; siz++) { if (!i && /* buffer empty? */ (read (LOCAL->fd,s = tmp, i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0)) return pos; else i--; switch (q) { /* sniff at buffer */ case 0: /* first character */ q = (*s++ == '\015') ? 1 : 0; break; case 1: /* second character */ q = (*s++ == '\012') ? 2 : 0; break; case 2: /* third character */ q = (*s++ == '\015') ? 3 : 0; break; case 3: /* fourth character */ if (*s++ == '\012') { /* have the sequence? */ /* yes, note for later */ elt->private.msg.header.text.size = (*size = siz); return pos; /* return to caller */ } q = 0; /* lost... */ break; } } } return pos; /* have position */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/nl_dos.c000066400000000000000000000027511137544547100230470ustar00rootroot00000000000000/* * Program: Windows/TOPS-20 newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { /* flush destination buffer if too small */ if (*dst && (srcl > *dstl)) fs_give ((void **) dst); if (!*dst) { /* make a new buffer if needed */ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1); if (dstl) *dstl = srcl; /* return new buffer length to main program */ } /* copy strings */ if (srcl) memcpy (*dst,src,(size_t) srcl); *(*dst + srcl) = '\0'; /* tie off destination */ return srcl; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { return SIZE (s); /* no-brainer on DOS! */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dbw.c000066400000000000000000000037241137544547100230470ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MS-DOS (B&W) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Private function prototypes */ #include "tcp_dos.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include "misc.h" #include "stdlib.h" #include "bwtcp.h" #include "fs_dos.c" #include "ftl_dos.c" #include "nl_dos.c" #include "env_dos.c" #undef write #define read soread #define write sowrite #define close soclose #include "tcp_dos.c" /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { char *s; if (!myLocalHost) { /* known yet? */ /* get local host name from DISPLAY env var */ if (!((s = getenv ("DISPLAY")) || (s = getenv ("display")))) { mm_log ("Environment variable 'DISPLAY' is not set", ERROR); s = "random-pc"; } myLocalHost = cpystr (s); } return myLocalHost; } /* Look up host address * Accepts: pointer to pointer to host name * socket address block * Returns: non-zero with host address in socket, official host name in host; * else NIL */ long lookuphost (char **host,struct sockaddr_in *sin) { char *s = *host; /* in case of error */ sin->sin_addr.s_addr = rhost (host); if (sin->sin_addr.s_addr == -1) { *host = s; /* error, restore old host name */ return NIL; } *host = cpystr (*host); /* make permanent copy of name */ return T; /* success */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dbw.h000066400000000000000000000014611137544547100230500ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- DOS (B&W/Novell) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define INADEQUATE_MEMORY #include #include #include #include #include #define gethostid clock #include "env_dos.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dnf.c000066400000000000000000000042271137544547100230410ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MS-DOS (PC-NFS) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Private function prototypes */ #include "tcp_dos.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" #include "fs_dos.c" #include "ftl_dos.c" #include "nl_dos.c" #include "env_dos.c" #undef write #include "tcp_dos.c" /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { /* known yet? */ char *s,tmp[MAILTMPLEN]; unsigned long myip; /* see if known host name */ if (!gethostname (tmp,MAILTMPLEN-1)) s = tmp; /* no, try host address */ else if (get_myipaddr ((char *) &myip)) sprintf (s = tmp,"[%s]",inet_ntoa (myip)); else s = "random-pc"; /* say what? */ myLocalHost = cpystr (s); /* record for subsequent use */ } return myLocalHost; } /* Look up host address * Accepts: pointer to pointer to host name * socket address block * Returns: non-zero with host address in socket, official host name in host; * else NIL */ long lookuphost (char **host,struct sockaddr_in *sin) { long ret = -1; char tmp[MAILTMPLEN]; struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host))); if (!hn) return NIL; /* got a host name? */ *host = cpystr (hn->h_name); /* set official name */ /* copy host addresses */ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length); return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dnf.h000066400000000000000000000015451137544547100230460ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- DOS (PC-NFS) version * * Author: Mike Seibel from Novell version by Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MikeS@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define INADEQUATE_MEMORY #include #include #include #include #include #include #define gethostid clock #include "env_dos.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dnv.c000066400000000000000000000041771137544547100230650ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MS-DOS (Novell) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Private function prototypes */ #include "tcp_dos.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include "misc.h" #include "fs_dos.c" #include "ftl_dos.c" #include "nl_dos.c" #include "env_dos.c" #undef write #define read soread #define write sowrite #define close soclose #include "tcp_dos.c" /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { /* known yet? */ char *s,tmp[MAILTMPLEN]; struct hostent *he; struct in_addr in; /* could we get local id? */ if ((in.s_addr = getmyipaddr ()) != -1) { if (he = gethostbyaddr ((char *) &in.s_addr,4,PF_INET)) s = he->h_name; else sprintf (s = tmp,"[%s]",inet_ntoa (in)); } else s = "random-pc"; /* say what? */ myLocalHost = cpystr (s); /* record for subsequent use */ } return myLocalHost; } /* Look up host address * Accepts: pointer to pointer to host name * socket address block * Returns: non-zero with host address in socket, official host name in host; * else NIL */ long lookuphost (char **host,struct sockaddr_in *sin) { char *s = *host; /* in case of error */ sin->sin_addr.s_addr = rhost (host); if (sin->sin_addr.s_addr == -1) { *host = s; /* error, restore old host name */ return NIL; } *host = cpystr (*host); /* make permanent copy of name */ return T; /* success */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dnv.h000066400000000000000000000014611137544547100230630ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- DOS (B&W/Novell) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define INADEQUATE_MEMORY #include #include #include #include #include #define gethostid clock #include "env_dos.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dpc.c000066400000000000000000000045321137544547100230370ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MS-DOS (PC/TCP) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Private function prototypes */ #include "tcp_dos.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include <4bsddefs.h> #include #include #include #include #include #include "misc.h" #include "fs_dos.c" #include "ftl_dos.c" #include "nl_dos.c" #include "env_dos.c" #undef write #include "tcp_dos.c" /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { /* known yet */ char *s,tmp[MAILTMPLEN]; long myip; /* see if known host name */ if (!gethostname (tmp,MAILTMPLEN-1)) s = tmp; /* no, try IP address */ else if (myip = gethostid ()) { struct in_addr in; in.s_addr = myip; sprintf (s = tmp,"[%s]",inet_ntoa (in)); } /* older kernel, look harder. */ else if (getconf ("ifcust","ip-address",tmp+1,MAILTMPLEN-2)) { *(s = tmp) = '['; /* wrap the brackets around it */ strcat (tmp,"]"); } else s = "random-pc"; /* say what? */ myLocalHost = cpystr (s); /* record for subsequent use */ } return myLocalHost; } /* Look up host address * Accepts: pointer to pointer to host name * socket address block * Returns: non-zero with host address in socket, official host name in host; * else NIL */ long lookuphost (char **host,struct sockaddr_in *sin) { long ret = -1; char tmp[MAILTMPLEN]; struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host))); if (!hn) return NIL; /* got a host name? */ *host = cpystr (hn->h_name); /* set official name */ /* copy host addresses */ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length); return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dpc.h000066400000000000000000000014241137544547100230410ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- DOS (PC/TCP) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define INADEQUATE_MEMORY #include #include #include #include #include #include "env_dos.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dwa.c000066400000000000000000000030231137544547100230360ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- DOS (Waterloo) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include /* must be before TCPSTREAM definition */ #include "tcp_dwa.h" /* must be before osdep include our tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include "misc.h" /* Undo compatibility definition */ #undef tcp_open #include "fs_dos.c" #include "ftl_dos.c" #include "nl_dos.c" #include "env_dos.c" #include "tcp_dwa.c" /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { char *s,hname[32],tmp[MAILTMPLEN]; long myip; if (!sock_initted++) sock_init(); tcp_cbrk (0x01); /* turn off ctrl-break catching */ /* * haven't discovered a way to find out the local host's * name with wattcp yet. */ if (myip = gethostid ()) sprintf (s = tmp,"[%s]",inet_ntoa (hname,myip)); else s = "random-pc"; myLocalHost = cpystr (s); } return myLocalHost; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_dwa.h000066400000000000000000000014611137544547100230470ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- DOS (Waterloo) version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define INADEQUATE_MEMORY #include #include #include #include #include #define tcp_open TCP_open #include "env_dos.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_wsk.c000066400000000000000000000017271137544547100231000ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Winsock version * * Author: Mike Seibel from Unix version by Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MikeS@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_wsk.h" /* must be before osdep includes tcp.h */ #undef ERROR /* quell conflicting def warning */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include "misc.h" #include "fs_dos.c" #include "ftl_dos.c" #include "nl_dos.c" #include "env_dos.c" #include "tcp_wsk.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/os_wsk.h000066400000000000000000000016211137544547100230760ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- 16-bit Winsock version * * Author: Mike Seibel from Novell version by Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MikeS@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define INADEQUATE_MEMORY #include #include #include #include #include #define gethostid clock #define WSA_VERSION ((1 << 8) | 1) #include "env_dos.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #undef noErr #undef MAC tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/pmatch.c000066400000000000000000000053271137544547100230470ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-independent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/tcp_dos.c000066400000000000000000000242541137544547100232260ustar00rootroot00000000000000/* * Program: MS-DOS TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_read = 0; /* TCP timeouts, in seconds */ static long ttmo_write = 0; /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; struct sockaddr_in sin; int sock; char *s,tmp[MAILTMPLEN]; port &= 0xffff; /* erase flags */ /* The domain literal form is used (rather than simply the dotted decimal as with other Unix programs) because it has to be a valid "host name" in mailsystem terminology. */ sin.sin_family = AF_INET; /* family is always Internet */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (tmp,host+1); /* yes, copy number part */ tmp[strlen (tmp)-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } /* look up host name */ else if (!lookuphost (&host,&sin)) { sprintf (tmp,"Host not found: %s",host); mm_log (tmp,ERROR); return NIL; } /* copy port number in network format */ if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open"); /* get a TCP stream */ if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) { sprintf (tmp,"Unable to create TCP socket (%d)",errno); mm_log (tmp,ERROR); fs_give ((void **) &host); return NIL; } /* open connection */ if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) { switch (errno) { /* analyze error */ case ECONNREFUSED: s = "Refused"; break; case ENOBUFS: s = "Insufficient system resources"; break; case ETIMEDOUT: s = "Timed out"; break; default: s = "Unknown error"; break; } sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno); mm_log (tmp,ERROR); close (sock); fs_give ((void **) &host); return NIL; } /* create TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); stream->host = host; /* official host name */ stream->localhost = cpystr (mylocalhost ()); stream->port = port; /* port number */ stream->tcps = sock; /* init socket */ stream->ictr = 0; /* init input counter */ return stream; /* return success */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* always NIL on DOS */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) return NIL; /* special case of newline broken by buffer */ if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; char *bufptr = buffer; while (size > 0) { /* until request satisfied */ if (!tcp_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (bufptr,stream->iptr,n); bufptr += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } bufptr[0] = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); if (stream->tcps < 0) return NIL; while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_read; /* read timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcps,&fds);/* set bit in selection vector */ FD_SET(stream->tcps,&efds);/* set bit in error selection vector */ errno = NIL; /* block and read */ while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); if (i < 1) return tcp_abort (stream); stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ } return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; fd_set fds; struct timeval tmo; time_t t = time (0); if (stream->tcps < 0) return NIL; while (size > 0) { /* until request satisfied */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_write; /* write timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_SET (stream->tcps,&fds);/* set bit in selection vector */ errno = NIL; /* block and write */ while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR)); if (i < 0) return tcp_abort (stream); size -= i; /* how much we sent */ string += i; } return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (stream); /* nuke the socket */ /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { if (stream->tcps >= 0) close (stream->tcps); stream->tcps = -1; return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* all we can do for now */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { return name; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/tcp_dos.h000066400000000000000000000021441137544547100232250ustar00rootroot00000000000000/* * Program: MS-DOS TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer -- must be large enough to prevent overflow */ #define BUFLEN 8192 /* TCP I/O stream (must be before osdep.h is included) */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ int tcps; /* tcp socket */ long ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; /* Local function prototypes */ long lookuphost (char **host,struct sockaddr_in *sin); long tcp_abort (TCPSTREAM *stream); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/tcp_dwa.c000066400000000000000000000173731137544547100232200ustar00rootroot00000000000000/* * Program: Waterloo DOS TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Global data */ short sock_initted = 0; /* global so others using net can see it */ /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { return NIL; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *TCP_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; tcp_Socket *sock; char *s,tmp[MAILTMPLEN]; unsigned long adr,i,j,k,l; port &= 0xffff; /* erase flags */ /* initialize if first time here */ if (!sock_initted++) sock_init(); /* The domain literal form is used (rather than simply the dotted decimal as with other Unix programs) because it has to be a valid "host name" in mailsystem terminology. */ /* look like domain literal? */ if (host[0] == '[' && host[strlen (host)-1] == ']') { if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' && ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' && ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' && ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) adr = (i << 24) + (j << 16) + (k << 8) + l; else { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else { /* lookup host name */ if (!(adr = resolve (host))) { sprintf (tmp,"Host not found: %s",host); mm_log (tmp,ERROR); return NIL; } } /* OK to instantiate socket now */ sock = (tcp_Socket *) fs_get (sizeof (tcp_Socket)); /* open connection */ if (!tcp_open (sock,(word) 0,adr,(word) port,NULL)) { sprintf (tmp,"Can't connect to %.80s,%ld",host,port); mm_log (tmp,ERROR); fs_give ((void **) &sock); return NIL; } /* create TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); stream->host = cpystr (host); /* official host name */ stream->localhost = cpystr (mylocalhost ()); stream->port = port; /* port number */ stream->tcps = sock; /* init socket */ stream->ictr = 0; /* init input counter */ return stream; /* return success */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* always NIL on DOS */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) return NIL; /* special case of newline broken by buffer */ if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; char *bufptr = buffer; while (size > 0) { /* until request satisfied */ if (!tcp_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (bufptr,stream->iptr,(size_t) n); bufptr += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } bufptr[0] = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int status; if (!stream->tcps) return NIL;/* no-no nuked socket */ while (stream->ictr < 1) { /* if buffer empty, block for input and read */ if (!_ip_delay1 (stream->tcps,600,NULL,&status)) stream->ictr = sock_fastread (stream->tcps, stream->iptr = stream->ibuf,BUFLEN); else if (status == 1) { /* nuke the socket if closed */ sock_close (stream->tcps); fs_give ((void **) &stream->tcps); return NIL; } } return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { /* output the cruft */ sock_puts (stream->tcps,string); return T; /* all done */ } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { sock_write (stream->tcps,string,(int) size); return T; } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { if (stream->tcps){ /* nuke the socket */ sock_close (stream->tcps); _ip_delay2 (stream->tcps,0,NULL,NULL); } fs_give ((void **) &stream->tcps); /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { return name; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/tcp_dwa.h000066400000000000000000000017631137544547100232210ustar00rootroot00000000000000/* * Program: Waterloo DOS TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer -- must be large enough to prevent overflow */ #define BUFLEN 8192 /* TCP I/O stream (must be before osdep.h is included) */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ tcp_Socket *tcps; /* tcp socket */ long ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/tcp_wsk.c000066400000000000000000000537561137544547100232560ustar00rootroot00000000000000/* * Program: Winsock TCP/IP routines * * Author: Mark Crispin from Mike Seibel's Winsock code * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 3 September 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define TCPMAXSEND 32768 /* Private functions */ int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst, unsigned long port); long tcp_abort (SOCKET *sock); char *tcp_name (struct sockaddr_in *sin,long flag); char *tcp_name_valid (char *s); /* Private data */ int wsa_initted = 0; /* init ? */ static int wsa_sock_open = 0; /* keep track of open sockets */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_read = 0; /* TCP timeouts, in seconds */ static long ttmo_write = 0; static long allowreversedns = T;/* allow reverse DNS lookup */ static long tcpdebug = NIL; /* extra TCP debugging telemetry */ /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; case SET_ALLOWREVERSEDNS: allowreversedns = (long) value; case GET_ALLOWREVERSEDNS: ret = (void *) allowreversedns; break; case SET_TCPDEBUG: tcpdebug = (long) value; case GET_TCPDEBUG: ret = (void *) tcpdebug; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number and optional silent flag * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; int i; SOCKET sock = INVALID_SOCKET; int silent = (port & NET_SILENT) ? T : NIL; char *s; struct sockaddr_in sin; struct hostent *he; char hostname[MAILTMPLEN]; char tmp[MAILTMPLEN]; struct servent *sv = NIL; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (i = (int) WSAStartup (WSA_VERSION,&wsock)) { wsa_initted = 0; /* in case we try again */ sprintf (tmp,"Unable to start Windows Sockets (%d)",i); mm_log (tmp,ERROR); return NIL; } } port &= 0xffff; /* erase flags */ /* lookup service */ if (service && (sv = getservbyname (service,"tcp"))) port = ntohs (sin.sin_port = sv->s_port); /* copy port number in network format */ else sin.sin_port = htons ((u_short) port); /* The domain literal form is used (rather than simply the dotted decimal as with other Windows programs) because it has to be a valid "host name" in mailsystem terminology. */ sin.sin_family = AF_INET; /* family is always Internet */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (tmp,host+1); /* yes, copy number part */ tmp[strlen (tmp)-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (tmp)) == INADDR_NONE) { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } else { sin.sin_family = AF_INET; /* family is always Internet */ strcpy (hostname,host); (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (&sin,tmp,hostname,port); (*bn) (BLOCK_NONE,NIL); } } else { /* lookup host name */ if (tcpdebug) { sprintf (tmp,"DNS resolution %.80s",host); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */ if (!(he = gethostbyname (lcase (strcpy (tmp,host))))) sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError(),host); (*bn) (BLOCK_NONE,NIL); if (he) { /* DNS resolution won? */ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG); /* copy address type */ sin.sin_family = he->h_addrtype; /* copy host name */ strcpy (hostname,he->h_name); wsa_sock_open++; /* prevent tcp_abort() from freeing in loop */ for (i = 0; (sock == INVALID_SOCKET) && (s = he->h_addr_list[i]); i++) { if (i && !silent) mm_log (tmp,WARN); memcpy (&sin.sin_addr,s,he->h_length); (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (&sin,tmp,hostname,port); (*bn) (BLOCK_NONE,NIL); } wsa_sock_open--; /* undo protection */ } } if (sock == INVALID_SOCKET) { /* error? */ if (!silent) mm_log (tmp,ERROR); tcp_abort (&sock); /* do possible cleanup action */ } else { /* got a socket, create TCP/IP stream */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); stream->port = port; /* port number */ /* init socket */ stream->tcpsi = stream->tcpso = sock; stream->ictr = 0; /* init input counter */ /* copy official host name */ stream->host = cpystr (hostname); if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG); } return stream; /* return success */ } /* Open a TCP socket * Accepts: Internet socket address block * scratch buffer * host name for error message * port number for error message * Returns: socket if success, else -1 with error string in scratch buffer */ int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst, unsigned long port) { int sock; char *s; sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr)); mm_log (tmp,NIL); /* get a TCP stream */ if ((sock = socket (sin->sin_family,SOCK_STREAM,0)) == INVALID_SOCKET) { sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError()); return -1; } wsa_sock_open++; /* count this socket as open */ /* open connection */ if (connect (sock,(struct sockaddr *) sin,sizeof (struct sockaddr_in)) == SOCKET_ERROR) { switch (WSAGetLastError ()){/* analyze error */ case WSAECONNREFUSED: s = "Refused"; break; case WSAENOBUFS: s = "Insufficient system resources"; break; case WSAETIMEDOUT: s = "Timed out"; break; case WSAEHOSTUNREACH: s = "Host unreachable"; break; default: s = "Unknown error"; break; } sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s, WSAGetLastError ()); tcp_abort (&sock); /* flush socket */ sock = INVALID_SOCKET; } return sock; /* return the socket */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* always NIL on Windows */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s) { unsigned long n; /* make sure socket still alive */ if (stream->tcpsi == INVALID_SOCKET) return NIL; /* can transfer bytes from buffer? */ if (n = min (size,stream->ictr)) { memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */ s += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } if (size) { int i; fd_set fds; struct timeval tmo; time_t tc,t = time (0); blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_TCPREAD,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG); FD_ZERO (&fds); /* initialize selection vector */ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */ tmo.tv_sec = ttmo_read; tmo.tv_usec = 0; /* block and read */ switch ((stream->tcpsi == stream->tcpso) ? select (stream->tcpsi+1,&fds,0,0, ttmo_read ? &tmo : (struct timeval *) 0) : 1) { case SOCKET_ERROR: /* error */ if (WSAGetLastError () != WSAEINTR) return tcp_abort (&stream->tcpsi); break; case 0: /* timeout */ tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) break; return tcp_abort (&stream->tcpsi); default: if (stream->tcpsi == stream->tcpso) while (((i = recv (stream->tcpsi,s,(int) min (maxposint,size),0)) == SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR)); else while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) && (errno == EINTR)); switch (i) { case SOCKET_ERROR: /* error */ case 0: /* no data read */ return tcp_abort (&stream->tcpsi); default: s += i; /* point at new place to write */ size -= i; /* reduce byte count */ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } } } (*bn) (BLOCK_NONE,NIL); } *s = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { struct timeval tmo; int i; fd_set fds; time_t tc,t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); FD_ZERO (&fds); /* initialize selection vector */ if (stream->tcpsi == INVALID_SOCKET) return NIL; (*bn) (BLOCK_TCPREAD,NIL); tmo.tv_sec = ttmo_read; tmo.tv_usec = 0; while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG); FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */ /* block and read */ switch ((stream->tcpsi == stream->tcpso) ? select (stream->tcpsi+1,&fds,0,0, ttmo_read ? &tmo : (struct timeval *) 0) : 1) { case SOCKET_ERROR: /* error */ if (WSAGetLastError () != WSAEINTR) return tcp_abort (&stream->tcpsi); break; case 0: /* timeout */ tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) break; return tcp_abort (&stream->tcpsi); default: if (stream->tcpsi == stream->tcpso) while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) == SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR)); else while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); switch (i) { case SOCKET_ERROR: /* error */ case 0: /* no data read */ return tcp_abort (&stream->tcpsi); default: stream->ictr = i; /* set new byte count */ /* point at TCP buffer */ stream->iptr = stream->ibuf; if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG); } } } (*bn) (BLOCK_NONE,NIL); return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; struct timeval tmo; fd_set fds; time_t tc,t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); tmo.tv_sec = ttmo_write; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ if (stream->tcpso == INVALID_SOCKET) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG); FD_SET (stream->tcpso,&fds);/* set bit in selection vector */ /* block and write */ switch ((stream->tcpsi == stream->tcpso) ? select (stream->tcpso+1,NULL,&fds,NULL, tmo.tv_sec ? &tmo : (struct timeval *) 0) : 1) { case SOCKET_ERROR: /* error */ if (WSAGetLastError () != WSAEINTR) return tcp_abort (&stream->tcpsi); break; case 0: /* timeout */ tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) break; return tcp_abort (&stream->tcpsi); default: if (stream->tcpsi == stream->tcpso) while (((i = send (stream->tcpso,string, (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR)); else while (((i = write (stream->tcpso,string, min (size,TCPMAXSEND))) < 0) && (errno == EINTR)); if (i == SOCKET_ERROR) return tcp_abort (&stream->tcpsi); size -= i; /* count this size */ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); string += i; } } (*bn) (BLOCK_NONE,NIL); return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (&stream->tcpsi); /* nuke the socket */ /* flush host names */ if (stream->host) fs_give ((void **) &stream->host); if (stream->remotehost) fs_give ((void **) &stream->remotehost); if (stream->localhost) fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: WinSock socket * Returns: NIL, always */ long tcp_abort (SOCKET *sock) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* something to close? */ if (sock && (*sock != INVALID_SOCKET)) { (*bn) (BLOCK_TCPCLOSE,NIL); closesocket (*sock); /* WinSock socket close */ *sock = INVALID_SOCKET; (*bn) (BLOCK_NONE,NIL); wsa_sock_open--; /* drop this socket */ } /* no more open streams? */ if (wsa_initted && !wsa_sock_open) { mm_log ("Winsock cleanup",NIL); wsa_initted = 0; /* no more sockets, so... */ WSACleanup (); /* free up resources until needed */ } return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* use tcp_remotehost() if want guarantees */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { if (!stream->remotehost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); stream->remotehost = /* get socket's peer name */ ((getpeername (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr (stream->host) : tcp_name (&sin,NIL); } return stream->remotehost; } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { if (!stream->localhost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); stream->localhost = /* get socket's name */ ((stream->port & 0xffff000) || ((getsockname (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0))) ? cpystr (mylocalhost ()) : tcp_name (&sin,NIL); } return stream->localhost; /* return local host name */ } /* TCP/IP get client host address (server calls only) * Returns: client host address */ char *tcp_clientaddr () { if (!myClientAddr) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myClientAddr = /* get stdin's peer name */ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr)); } return myClientAddr; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { if (!myClientHost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myClientHost = /* get stdin's peer name */ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (&sin,T); } return myClientHost; } /* TCP/IP get server host address (server calls only) * Returns: server host address */ char *tcp_serveraddr () { if (!myServerAddr) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myServerAddr = /* get stdin's peer name */ ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr)); } return myServerAddr; } /* TCP/IP get server host name (server calls only) * Returns: server host name */ static long myServerPort = -1; char *tcp_serverhost () { if (!myServerHost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (WSAStartup (WSA_VERSION,&wsock)) { wsa_initted = 0; return "random-pc"; /* try again later? */ } } /* get stdin's name */ if ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) myServerHost = cpystr (mylocalhost ()); else { myServerHost = tcp_name (&sin,NIL); myServerPort = ntohs (sin.sin_port); } } return myServerHost; } /* TCP/IP get server port number (server calls only) * Returns: server port number */ long tcp_serverport () { if (!myServerHost) tcp_serverhost (); return myServerPort; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { char *ret,host[MAILTMPLEN]; struct hostent *he; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name; (*bn) (BLOCK_DNSLOOKUP,NIL); if (tcpdebug) { sprintf (host,"DNS canonicalization %.80s",name); mm_log (host,TCPDEBUG); } /* note that NT requires lowercase! */ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name; (*bn) (BLOCK_NONE,NIL); if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG); return ret; } /* TCP/IP return name from socket * Accepts: socket * verbose flag * Returns: cpystr name */ char *tcp_name (struct sockaddr_in *sin,long flag) { char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN]; sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr)); if (allowreversedns) { struct hostent *he; blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL); void *data; if (tcpdebug) { sprintf (tmp,"Reverse DNS resolution %s",adr); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); /* translate address to name */ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr, sizeof (struct in_addr), sin->sin_family)) ? (char *) he->h_name : NIL)) { /* produce verbose form if needed */ if (flag) sprintf (ret = tmp,"%s %s",t,adr); else ret = t; } (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG); } return cpystr (ret); } /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { char tmp[MAILTMPLEN]; if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (WSAStartup (WSA_VERSION,&wsock)) { wsa_initted = 0; return "random-pc"; /* try again later? */ } } myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ? "random-pc" : tcp_canonical (tmp)); } return myLocalHost; } /* Validate name * Accepts: domain name * Returns: T if valid, NIL otherwise */ char *tcp_name_valid (char *s) { int c; char *ret,*tail; /* must be non-empty and not too long */ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) { /* must be alnum, dot, or hyphen */ while ((c = *s++) && (s <= tail) && (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.'))); if (c) ret = NIL; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/tcp_wsk.h000066400000000000000000000022361137544547100232460ustar00rootroot00000000000000/* * Program: Winsock TCP/IP routines * * Author: Mike Seibel from Unix version by Mark Crispin * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer -- must be large enough to prevent overflow */ #define BUFLEN 16384 /* 32768 causes stdin read() to barf */ #include #define _INC_WINDOWS #include /* TCP I/O stream (must be before osdep.h is included) */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ char *remotehost; /* remote host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ SOCKET tcpsi; /* tcp socket */ SOCKET tcpso; /* tcp socket */ long ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/dos/write.c000066400000000000000000000030231137544547100227140ustar00rootroot00000000000000/* * Program: Write data, treating partial writes as an error * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* The whole purpose of this unfortunate routine is to deal with DOS and * certain cretinous versions of UNIX which decided that the "bytes actually * written" return value from write() gave them license to use that for things * that are really errors, such as disk quota exceeded, maximum file size * exceeded, disk full, etc. * * BSD won't screw us this way on the local filesystem, but who knows what * some NFS-mounted filesystem will do. */ #undef write /* Write data to file * Accepts: file descriptor * I/O vector structure * number of vectors in structure * Returns: number of bytes written if successful, -1 if failure */ long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1); long safe_write (int fd,char *buf,long nbytes) { long i,j; if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) { while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) && (errno == EINTR)); if (j < 0) return j; } return nbytes; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/000077500000000000000000000000001137544547100213735ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/dummy.h000066400000000000000000000021051137544547100226750ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/dummymac.c000066400000000000000000000160651137544547100233630ustar00rootroot00000000000000/* * Program: Dummy routines for Mac * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 May 1993 * Last Edited: 5 March 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include "mail.h" #include "osdep.h" #include "dummy.h" #include "misc.h" /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char tmp[MAILTMPLEN]; /* must be valid local mailbox */ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ? &dummydriver : NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { return NIL; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { /* return silently */ } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * driver type to use * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { return NIL; /* always fails */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; /* OP_PROTOTYPE call or silence */ if (!stream || stream->silent) return NIL; if (compare_cstring (stream->mailbox,"INBOX")) { sprintf (tmp,"Not a mailbox: %s",stream->mailbox); mm_log (tmp,ERROR); return NIL; /* always fails */ } if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ long dummy_ping (MAILSTREAM *stream) { return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { char tmp[MAILTMPLEN]; sprintf (tmp,"Can't append to %s",mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { if (*pat == '{' || (ref && (*ref == '{'))) return NIL; /* write name with reference */ if (ref && *ref) sprintf (tmp,"%s%s",ref,pat); else strcpy (tmp,pat); /* ignore reference, only need mailbox name */ return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/env_mac.c000066400000000000000000000131741137544547100231550ustar00rootroot00000000000000/* * Program: Mac environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 January 1992 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static char *myHomeDir = NIL; /* home directory name */ static char *myLocalHost = NIL; /* local host name */ static char *myNewsrc = NIL; /* newsrc file name */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; char tmp[MAILTMPLEN]; switch ((int) function) { case SET_HOMEDIR: if (myHomeDir) fs_give ((void **) &myHomeDir); myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: /* set home directory if not defined */ if (!myHomeDir) myHomeDir = cpystr (""); ret = (void *) myHomeDir; break; case SET_LOCALHOST: myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost ? myLocalHost : "random-mac"; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: if (!myNewsrc) { /* set news file name if not defined */ sprintf (tmp,"%s:News State",myhomedir ()); myNewsrc = cpystr (tmp); } ret = (void *) myNewsrc; break; } return ret; } /* Write current time * Accepts: destination string * format of date and time * * This depends upon the ReadLocation() call in System 7 and the * user properly setting his location/timezone in the Map control * panel. * Nothing is done about the gmtFlags.dlsDelta byte yet, since I * don't know how it's supposed to work. */ static void do_date (char *date,char *fmt) { long tz,tzm; time_t ti = time (0); struct tm *t = localtime (&ti); MachineLocation loc; ReadLocation (&loc); /* get location/timezone poop */ /* get sign-extended time zone */ tz = (loc.gmtFlags.gmtDelta & 0x00ffffff) | ((loc.gmtFlags.gmtDelta & 0x00800000) ? 0xff000000 : 0); tz /= 60; /* get timezone in minutes */ tzm = tz % 60; /* get minutes from the hour */ /* output time */ strftime (date,MAILTMPLEN,fmt,t); /* now output time zone */ sprintf (date += strlen (date),"%+03ld%02ld",tz/60,tzm >= 0 ? tzm : -tzm); } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%a, %d %b %Y %H:%M:%S "); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,"%2d-%b-%Y %H:%M:%S "); } /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { return (char *) mail_parameters (NIL,GET_LOCALHOST,NIL); } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { return (char *) mail_parameters (NIL,GET_HOMEDIR,NIL); } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { extern MAILSTREAM dummyproto; return &dummyproto; /* return default driver's prototype */ } /* Block until event satisfied * Called as: while (wait_condition && wait ()); * Returns T if OK, NIL if user wants to abort * * Allows user to run a desk accessory, select a different window, or go * to another application while waiting for the event to finish. COMMAND/. * will abort the wait. * Assumes the Apple menu has the apple character as its first character, * and that the main program has disabled all other menus. */ long wait () { EventRecord event; WindowPtr window; MenuInfo **m; long r; Str255 tmp; /* wait for an event */ WaitNextEvent (everyEvent,&event,(long) 6,NIL); switch (event.what) { /* got one -- what is it? */ case mouseDown: /* mouse clicked */ switch (FindWindow (event.where,&window)) { case inMenuBar: /* menu bar item? */ /* yes, interesting event? */ if (r = MenuSelect (event.where)) { /* round-about test for Apple menu */ if ((*(m = GetMHandle (HiWord (r))))->menuData[1] == appleMark) { /* get desk accessory name */ GetItem (m,LoWord (r),tmp); OpenDeskAcc (tmp); /* fire it up */ SetPort (window); /* put us back at our window */ } else SysBeep (60); /* the fool forgot to disable it! */ } HiliteMenu (0); /* unhighlight it */ break; case inContent: /* some window was selected */ if (window != FrontWindow ()) SelectWindow (window); break; default: /* ignore all others */ break; } break; case keyDown: /* key hit - if COMMAND/. then punt */ if ((event.modifiers & cmdKey) && (event.message & charCodeMask) == '.') return NIL; break; default: /* ignore all others */ break; } return T; /* try wait test again */ } /* Return random number */ long random () { return (long) rand () << 16 + rand (); } /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ void syslog (int priority,const char *message,...) { } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ void openlog (const char *ident,int logopt,int facility) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/env_mac.h000066400000000000000000000032761137544547100231640ustar00rootroot00000000000000/* * Program: Macintosh environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 25 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s:Mailbox List",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s:Mailbox List Temp",myhomedir ()) /* Function prototypes */ #include "env.h" /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ void openlog (const char *ident,int logopt,int facility); void syslog (int priority,const char *message,...); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/fs_mac.c000066400000000000000000000022341137544547100227700ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { free (*block); *block = NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/ftl_mac.c000066400000000000000000000014171137544547100231470ustar00rootroot00000000000000/* * Program: Mac crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 January 1992 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ /* nuke the resolver */ if (resolveropen) CloseResolver (); abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/linkage.c000066400000000000000000000017001137544547100231470ustar00rootroot00000000000000/* * Program: Default driver linkage * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 7 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ mail_link (&imapdriver); /* link in the imap driver */ mail_link (&nntpdriver); /* link in the nntp driver */ mail_link (&pop3driver); /* link in the pop3 driver */ mail_link (&dummydriver); /* link in the dummy driver */ auth_link (&auth_md5); /* link in the md5 authenticator */ auth_link (&auth_pla); /* link in the plain authenticator */ auth_link (&auth_log); /* link in the log authenticator */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/linkage.h000066400000000000000000000013401137544547100231540ustar00rootroot00000000000000/* * Program: Default driver linkage * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 7 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ extern DRIVER imapdriver; extern DRIVER nntpdriver; extern DRIVER pop3driver; extern DRIVER dummydriver; extern AUTHENTICATOR auth_log; extern AUTHENTICATOR auth_md5; extern AUTHENTICATOR auth_pla; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/mtest.sit.hqx000066400000000000000000000255161137544547100240600ustar00rootroot00000000000000(This file must be converted with BinHex 4.0) :#@edCA0d,R0TG!"6594%8dP8)3#3""l`!!!"4[4p8dP8)3!#!!!Hm(*-BA8#bJ# 3!aB"#Jd!$'edCA0d,VNZFR0bB`#3%j@,!*!8"h-!N!6rN!4bFh*M8P0&4!%!TFk UMDPEIVJ!!"f3!!#3"JEY!*!%`hJ!N!KIY3c!q+l(4rK4`['9YjP(D"GY*FH1(-X cXLBR0(,Fbb2([#i[,dqCK"rKGS3@iEFb3JQeji4&VeZ[1Tp``PGTPS4GCi46F[` )*dr*J*-T2h+-c2N46VMfC-$*Vi46-Z(N'*P4`L5F(#G2#5GiGT&MK"*'9LD!erG CjLAcHUq9(@&*RN@fj0Ne))``F[c)XC9`B,`kD)!("P!!B+%+X!"Ij9MdF'6TDbk eDS@rkBHYZjA")jZhqKjc,5mCd(FTrHqIGj@qeG-@lI&rjBp%)kdK&fVILa2,2T2 )Nrd4Q1G"rm8Y@pe%8-SkIQSGk2H@qIe3dIJcJrAH1R9c&i2J#HpTbZ[82H$e3hN Gr+r1$kAiHS*IpmchqmF6`GH$qi)l!%)YfjZ9XS-II05ZY0Hd[$N205VQN@SCP1m )Y64Y8k#VBqraRH@Q)09I"'0XA!G`I`Z`"YrDNrJL[SBi1UackpVJ'9h60qKDB,' ZI9HZ+l((G5@JIUk!US1Z-MDXaLiE+K4V$)T"`eF&6AH$4eI`"F3!(Y2Kj)ABCH6 #b6&ffHN-mSr[[a($&e6dMmla&Iiep%rHVrQhHi#(0JaJe+#i-PJ&%!J"P1Cl!ep YQ2T$jSEJi2Z0c+hDh!RbcmB"'fT`%@-'kLB+!r6'c4USMFh0fb68Y(llUk)VPLr Ed,hR3+5c[D2(piA[kG@V9rTHlZTmZbd5lH`ji1X1qelC&HhSl'V[kHkL2X*kMAQ aAlcJMXYmY&+R(%SV"d9HaYKaMM2'05VCQ-Q6Z-8I6HNNK$jrP@'C#fhdRG$`*Iq %&@Cib6p[RSd[23pm09LaTDQaQHE,TKFhEZ)8(AZN&j&$@cB%&dm`1'D23KiA`i4 k2MCcJkB$!`hclN6Fi$P,$&'1lZ1NTbALF5'11FV9NaR+!fJ,m`$QQX5KhSi!*8i 0H"P%&KpL"M!d&Sm2'A1eSI*J(ND+Nh2H4CbM[(`bCJHJ,N8-+K58K!`YM1)PBC( MF0C+`KM%5qDK'9kBG#fF$!KI)Lr"'SQFp+A[c%U3!1a*+dC(4bhbb%J@6`TE[35 MM(Nm(X4XR"M-a)90**KqZ"l(V4#iN!!lHpDXM)NM`EB,Cr%6K!Nq4h*KmA)pB*I )`FD3!b4&J$+3!2'`Cjcj!bJL9'E`T&l56UjPreMAX3FQ,GJZd#hK9K%451'Ll4J [Q!K6EaUrTfIlcI)Grdj,Q,L8F($6rf6,&2hjQq2Ql%alc$FQUj8Y-F8G'4R*miZ 6r#N"Nhme6(qhkM0Gm4bIGkdBkR++CrL5,4D&DElPi!UIMbP,)&-ICUi63Q#+l`6 Mr0435I-cJc8P-0hc+6l8CL,C!fPlFU@j0YHYP5HlVH6iFMqck@,jbDiKPVai*iE U$Ik%rBbr0$p6)-ka&c4LTeHf'r90kr2kc1PIL8)!1m$[-EHZbBe6E*MC$6#jJ*S D5ADDE#fUXfa6S4$,$R1GEqqVFra(qSj!BB$kcE6AAIeme%lY-+NSX$"!#a3%h`& Q*k"SP1XGG*8[q[)kriiqr[N`q606mMGrMr6Ym@A(IIpC!`re#`28larr"3,URqc [!JVpml$rlr*6*KrShqr[f3f'4hk$ZAhbP(CPb2rrCL3-m3pHa&&p!ZrP1,mSX(" (RI3Qr550fhJiM2F2G!"KiYhmHX("5F#`F3Cii$"NqG''q!Q&BH'NM`)*!fm-NMJ G#iK#50`+6KKLT8)))B%,06GHI$PV%q'LL"-iAR[``!-0+5$5!(j#))h,X24N"1M Z3b$mh!-#ETlA5jqQAj-I-'p$,*iGIjfpb3C00LpePpdq'U&fJp!9LaP)F'!L&(# B-5QFhja-i,Ii3Z#f[f4jF[adr42@M*(aS%(r(cJ$V%XHH6)M*j%XNB0M090iPKm LA!c@+El%Er(aKNG'k+Uq'6Iie,4jIhRpArhcH[pGRlRkjh#kDmVcVp8l0@+3!*- C-3dda3h982@pIFIkIZcpjA`I1q0HjqjcpETeZPLFAMG*`Fd9K!Ihi0Vp`bA5LG* hUZq8VJ`VcUpGcBK0cMcR"h&$E,bKHRr0IYFEPf+ABZGi0Kklp*kV`G9iZ[%FBMH rb60J'$JdYQJ98C5$i*'hHLqX$cj("j*GmUV[S,cU1p,C'LAqPjfYZeGLrMhLP&p !I"@iP2Q)8riXB-@A8,d'p(lS*8`T3Z`S(+*+ZVBKKRHdK,Ph`N#[r#[[UN(HZi, RmL$f+@NM9SlBFD(YENEXX0#SAS2B+U&4(82X+D&4I3Da#U&48BGBJp#BAipB3'K 82B2B3PQ$$a&l6QJ8DBJY%4T&Rb$Q&4UHaBLY&4UP5a&E)65mYBK9#BhL5X4@#Sf 5"BMjK)Dk#,(5*lDrdp8@m8ADSYhl)UeYr`%0!!GYG'9cG#kj3)#D#J&2H#J!&c6 +!*!$J!#3"!&EEhJT,!#3%4B!N!MrN!438Np+5d&)6!%!UKPEcE$CcD)!!(*+!*! '&`d!N!3HUJ#3#1[c$J#9qCRFXlGVCEICCSIR+lY0&eVRAMZqXX[00PXS2r@Df1A KkHA*"VNBTjER*iZH[pFm&Z8ZE"qcfblm[',l@,%Y@BlrBZI+'dRENNhB`TG6`U+ %rm+qcS9IXHh##19fLpl##EGFH-DfXHfjAGJq`Xl0#&Z"UN(Pee&qXHelQ@6(IQ( lb#E,Nrd4YNmb@CI$RfG![9r(Q-!B#jFc&X+DS8F3ArE,60Kl5)j0,',#3EXF'lA Ff"lcGF6XpqJ3VDZ[VCVRG&G6"[,RPeil*ApGbRl6q$rjPKb*9Y(1D'@k1jk56ib Eb(5E,A,-M0L$VA*XbY0-Yr-@1CBlLqRfhkE85@)CT[d855D6KeK',QTi*XDBC34 L5e$cI81&5RrqMB2Q!HmIb'5XY(jK24d42+cfld3SK%*fYiD6QclP6#EAG(r)aeP A9eeCik`c3H*Pl-%"%f1(rd'SBd9bYr%[Ke#EaC*,Y$c-`"ZCB(aPl&BS5Mh,BLD N0[#0c'FQ[EfTSphSF9G+mm35REeC$@ABQb1@KQ[EQd)a(q9fL4i("NpNfN2KQ-m SrQ-S(2)pT,1(,a$YBE%N(&VZLmCm8CdpQL8R8qpEN@Q2GN5E4pEl1X,aD,-2+*' J,iSDk,LB&@mhVGUh%i%T`9"c@lc&*aE90$@hqTTDI0'15bkj3XE)'FC`$f"%"*l 1P"DI2aMbLE&Sh#I+hk8AU)Im6@dGbUP#l9$8Yd)"kNG#BZUa&")[D50+5NM)&9b (+Q,e$K1L)[VIr25C($[MAIaX"HGhq)+1&j!!e[2jJj+Bi9hm,'b0jr&KUjKP@[a prP3lUX&jI')FUX(jqP([8`qIabF--RE1iir'PSMXGrLk$$B+fa(Rm6-0[2G([SX r"YZFmrJ'bKlZfl[ie([83qIV4le(06c22e6lh(IaUAI([SY2[8XIeB'afAb9Ir$ Df*FU$RcS#1"L*RJ,Ea'm-fRG)RJrZ&A`AVj,m,SFJVIm#X%lcb9ikpb#ep-PH+p #E1P2"'r!,RL[ALpi`bm+hXK8V%Z`hSce%F(lBEhJM4F*hQXl"Hp(eQ,G*(MAbRj Q$[XD'T2@ZpNUa&HaZ`@Ri-$UI+HX[rmZrkFkaRE&f&5@+e`QI%6A4AYGekUNX", a$f$r%H%$&&q992!JbRG&K)XDRZXL%808*$F5I@jPT8B8j'B8aRlIb[FAeAq55dH 1L1MQrZMBlqI#N6heG3RbIm0h"PIj6mS9SQrVVEIZf)'ii2IIHL[fZKNcr(lX)9e Qc0LaJqPIj*p+"@Tqh$jQE2+q8I`V51f,88$DMjNaBmC[idIcNIFrX['[3-eISDr ,(liA2GB#PGG%M!Y$j0I99(,S4!lH0"+9j69jk$RX1"[J-k&q&K)M!KJPmk2bp!& 6A(i)A$f%aJ9#mk**M)h2!k&-a+NY%$!d(f"'9ic!cqF$K"F86-V-6b31L6b5G`0 1M&'($4C$r*(IEi'!bf%jZAp2)MJRAj%!m+V!I53X6@!NNPIi-M$VJ*EN'9#9N!" a&YKjY(!C*kkY+8YVm&1V[[Q*Em&G4"8%*T5f,[#88kD'H-`rfpl-l%cSlTfdA9f "JHN(J%eSm*4@)PpM4k`P',DM)A3i[eTGJ6&"`DPF8NCpe"&Fi@Z-%4"P0#'KVZJ [*D-5cj9JHF(B(,XfiL-JBI-6ph@T+dUX9+M'@9P0&@T[#VEaI(3iraee4Fp0i$M ,UZ%S8Bqf"80A0`@dM*j99r5IR0'bfSC5UT%ah0(LLr"D$Z!BeBaU'aTVU*EKMXE fTQDHHdChlq40kSUZ9+[Z+8@0""1kSbfiP#2TZlpQQDfZk&!&D9'*Qh46eMA0V8e 4Z8%STi[9&If[j942(BHFSX&33-Rq5%lCFLGG'%BMSCk#N4UTJi!'FlG!4h'FNPU T&$KMB#kf"'2"F+LT$GjI0$b-H3($G!YKeP3ZTYbbDi+G[TDDF![[Um`"V"&D(HU G[+-pdDB)cfQ`AL29cPmJ9B,h"%-m&1`FlT@F&1Fi2G6j(E%QQ8Nbd5%AUL[i8-( a90C32aYL`ACH(TD"2Rj'AF'05MqA53XEUIGmSH9U3qZ"%&GA'!MMC-i"%Nd8C3+ *ea'RPkXVE!)&TEc"6VT5lqI&(N)CUk&iURNZrKLR,d,jQ,S#3d'4UZ9F3M,(%FV (eC9GU+*i5ZTi,V&QcP[%Ek[9&GfJ-QCP3`PRc'!(&4QFM[2Ve*@09rUKYEkmK,M I&28hchBiC+i'`RTe"FjV+U%9HSK`lP&ApKk`"H%%+aYUUF2-`Bj`iqcCPhd3LN, 1kAje"C+FIA"HE5A2rZT`F(CM&%M03dM[I4dTVL&p3ehC*"A*8pR!1c`@l'LFj5M NkEqTJp$a'"p6ZV5kJEM'l'p6q*CA,Zf!&T,VPY)iT)9HdcLY3aT(IdlMC*l91)C c'SI8d"Q0SkQK8aT(8d1R0)ljV-E*rSh'XIa+iea`9Z-S@ZL8aY'dd%Q0SfQKNaT Re$Q0-rUdaY'8d!Q0Ne*#ac@1TS41D"a0#Ch31"HHd$LTbPMIi(A9bcQQ#EB&8Yl )!#XVHU$IbG$d`+Y1aS"Zd*h9$4RRG-22[C'MZL&GXcGbh(&BhDmZMP6LeqTL3!Z 3!,Vi$jq8e-8C,D#ULhHdJ+BZ6QN"FK[He!*(0&$+EAM94dkhicBF8D$pEJ-T8*C 5S1Q2e-9E@Z!"ZL1#F*R4@q-TDk#qP#q[J"K)kc#&J8b@U#Yh(T@-+R"h"VJc'!$ *DASST808jr%G6d*c(NrT%10C(8*+i)`1k9F#*cd*c@NiT5kbckN,bePeFF&[e-@ )AkN,43fF8KHD'MMT5@KUi+4U'(e@0B`jTaSd2A"#0D6d`((9S1Q"%kT"d`-R9)2 e,GA`5krK4R90H3d004jd,$UX28B*bA-BIkkZQ-03F#4*aJQ&9*b"MU8C$*NYbQT ULE0UI1hKk,9DjLPPmpjMbNE12-AUkJ6'+kcqpJ6'YQ$+$aQS@ATZYc@P3c3rC%L (r,IEQY)KQJ*kABHNU[JH051TE"%j+UD3!1mDA*QLCE4'AE8j0#"j1,H'I,(f$TR G"QG*hRYLPQ63&Cjdc"@QUC)hAH(GfKcDXY)&0930FdZm[4fha2bA-j`f@JNpVDk D%P*jhFKjr98(HI`aII2fr1Uc#2q-&rQ-E(I[HlDS+e!8$X09@*c$fS,Y`4Ke#8I Ulf&00mJp,+Jp$*ErT@iB8#+N'hkQ4)5[k"mS9eIS"Xk+eVdecK+`M$!'9)Mf+JQ hYiG$(T9*G32-C$M0T,SlD`pZ89GYbZ3(5TiQ18m!03haSqNSRfY91+SPIXR0U5Q 6YlNj@mf*lLqMR*b45*[2dp4fp@Yk3R(dDKX@8%@cX)m&fhK@`PHZkhT%AD%LE(+ 218Y,FEHFN!$ME'Q*qMSk&VFe8G2)+KhG1%eGY5Q6[@J4Z9lBZf5pqEU[A&V@!08 R'%Tp(Ea83afESpCerS*+F*f3!$dr(QbqZLADG-fV26#+L30B)c8XMkq6#j!!`6i BVH*95KlUQC'9S9LEHPrD-(Z3!0I-mFS@P[(jKV,P[K"RaHK3GA09T0+bKF4K@D@ qjF(AFaUVpN&jC6ARM2*JQi`c@0YaDNB0C498Uk`'Ak!kh06bDNBADVPA1MP5DE# T,4`BcXQU)LfUj'bBY5JBDJPI-maBip8Z"'m5BjM"Ql&SQ&1'EiLab(e4r'*T!9@ daKH+$fG2p`$)c&DfQ1G%69S'0ZBj(I1JbqVVZ3GG&Sf'Sj688EHD2'LjRq4+'FY 4+CRC"R$)JjElU84@k3h0d@#%FMqDd@5Y6V@eR&NpiA#EaTH$[5F1e65EDRS-kb+ e!Z@9Lk('K+cbB'G08ka9eA[2U5Xi@0&Yi&,jaS,QL+Tr"cfA+FFm&rZ3!1FbpCc RmVkcRN[H1FrPMmjj,[RR2*GTjcbALpr`A!Ca,MRVG8ar`qXBp$$HIml$X*rf-!T 1HaL&Tcf-5dpl')l6dfdc6Xc1lPCA0[2Rr8$cE!1GFjQ+9+i`Lepe,BiLcG+3!$c 92#G8qdK1+IllJ)SN9FXjK63JM[4[kSUEPBjaVj*6bVhjS-U8cJ8HkL*M8cc@fN% ia*3TR$QUHk[JQ!QR%3VYpI'A)`l1pDRjPlVDZKR8AC&`C)CfadP+"j!!krZD$QJ HdJ%d!hK'"kLhMlbM!l6E4dlT!2*pcmcjTLD#hjRce5D#6bQ'IZIhT',)1Z[5QRr MdQUZc5qFS04%i0Y1N!!b$haUcPHE"cijjjY@jrD4JFN,ZRhNcCRmkp8e0Imb`1R UY%JkJ[QAe&4,rrc,bDQ@LHG[5,P[VHBk(kfEiMUREjK9'H!ZcA0qaF91KcHhR&j P9L@P4XJl2U0'IMkYm[EG*cHQ[)k"1HDd$kSK6Hq%@qTZ!XhV',UE)$h"V1raQ`6 )D98bT,FH8-F&fjXL-k1A+[HTT'R3&cbMY,bZa!Te6EN54eRpIqj6qBA#+(a$B@b kmKXdUc'1pqCiTp4!rVUHliGR2&681hq`KVK@4[e"U962kp`5LYV[HFYMj6a!DSC ij9rP'9A'Ce5*@SlRqcTbPSVXk,m8rjAkMQrm6UUq-NFa2A%8CQ+2ZcSI5qQZS6X AdR2l1[pjBfBUCdee$GqiFP`hV$QL'`CQ9G)6h,apG+SfrI$0fkN*0-f[16@"4MF 3r1m%fMVY[L5icNlFfm$-(AK4$hLChK'3!+EJh[idd'qB`$I%#2,M6%akN!"U#)p ,qVqSJrqI(QlqArf8"brKC8[r3[T,rV)Hf6b-Jjj%IZ`a#hm3'K2Um0BX["Z+ap$ 2Zr*CM(8*&HaQi4RX+i@2#jm4RXC,X&B,EKjG,r`9R4"fim`DS3+ReJZI%'l$UGh #-cLcFIM-TiHaEdlPTV`c5hk9dfmr[*XU$F&(6`f,+$&b4N3QcX9E+,[hCEiSl`d kC6p1f6Z8IDZbAk[X0bRlEmVlV&*j6hbLj(GfRl83'pkeaclq0#PATlRUVaq4HPJ VeXlT4ELUF,(8Bj3UqT)2jUh(lZYjeq&NPp56FEI8Bc)JpM,@ql(QZ3,ZcQ65(@C kpb*h*a0XFj+ET*l-Uk5[@CRBcYUNQlVhimaY!401ZHQ8&*pr`(a9K%NpqJF$KUU E!VT%T43ACjXlr51Q&bfTPFUN(N1[D2%DmSV&5jL)r0ejA9*mHP'Tdq2%fE[&DPY RPNR8*qbSdM+F,mblERS4&(8$6Yq#q-5mp8#V,kY'I+eB,HS$&UP(pe8pNCl&[er U-mk8&LDIG"l)bV(94X5%25,X(,9N91p@CamE(FP-f"GX3%9ehAmTA5A&q4[r@%@ Im5Gj@m`Va8jcjprbPfi'6$McI0iGE&c"E(1AAlGiJa4I[-(p[H5"+`VBE+Uaehc PKJ*6paI&bBRL3L1kjr&!@dAIiH[ceSSjJHGXa9@ImYX6da+9diY+H+dbR`j8%lC D-IDN9V%H`aT8F+@iiV!l%F56bSaqID)P-3eRRVH&fF`rA9G3b@C9ljkFp'bSQLf m2k$V6NVab-MZKaG[%0'EPMVTS(p%SBJDe!If*#i'ETIpXe)FrB-B'd&p`k-6dBP cM$Pi)*G$l04p+@,CHG2Q,ET0IK[U[MIa2V(1jVCC[@ER(QD+#GGCGfkIrh"brde VGklckk5iM5AXK#-+LDQfZYUANP[&@YFGE25P3I$4Z-4BejIG3@B4DlYIGPK4J4H R,RjXfdbVc4Uk)Q#VG`C'BmeEk-69h&p&K$&8a*'I41eeKakYef)lqQ1pMfj+(UD kk6BkTM&,94!c+k,(Y5bRhZA[M$4h2icp!F)$`Xfph@b%%VZ6BKCAmP"JDAXV-24 dVZ6(9Yhpmrdl,%ecLr`2iT6"[)aGQ$cXZ'LadlELd-lH6ffXkXqpYIF6F[C+I+B D4bbImP2MkPl*Ear2l`ANPT%mj,MS3j4EV2G6[F$B@(@C-hPijVh5Kj%91N4iTZc (9[EBYfGD!`jdbG6!L(UR2eI19pMMY5U4(Zm)*E,G#fi@RNA''p'0P-(M9$&aa8m 2Lh0qHVMU$V"@[L1Ij3GX8r1Cp8TAb3pCcU0lQ2ka+af(jKDjaSrH`bjh(1VH0pr ThiXH,qEm8qarN!$Rd)-FHU,*'10j6,JrRehZcjf&E0`2"#bl2REA(8c[dp[biF+ QN!"h!IF[a,T)cXDZjpF9ER[q4ZEUBSAEN8pGP$QXK9CGmDVNppDK0m5CSlbMEG1 BIZ0DacDUKQ-lVm4$bEhH@@*1BS&BI"64%T31-Ph!1K1Sk)&4lNJ1)e6TS'dhF2H *FjKC,2EVQJ`5[E#4&4BPl[YmZFe8@(6(ClpFIYIDJ!&m2pdV`R[3#`8qLYUmSa( 9+9'V9irGT*+qj(EE@LQHZ%q+lcq!$Pr"G+DY,%2k-`E@!mFAPG5@PXePh3QaX3k m,jUNH2k+[SffkNr[qZlU*lBpZS[(9f`ra)TY-ke@a0EdhXc2q*8cTSdpIF[i#Bp b)UHhTmr$6lL8%hS$[9BQKaJQq6Q0lp$ab5eH2AD2%qZJXaSGeTR@cBXA2CcXUh) &$N[la%2+Q9[kcr3TCe`Kmdh1GX21BZFqTL[-SE13!+i&6&rJC(TE!6-90%,EIDm )0Q1Hi(CprB&mGV(J,R[S'lY+h5kADqZfR@ZUGl-Lff4NrJVUT9"jl$-i[rkYmbY &i56#p*2R,DN-UVU@6DaD[Fa5fP@k'LG&l1+P8+jX($m4aeNc2fR!UEJG,mj-*Zr UDMEB)0fPq1BEPPUkYVQ,(Fa9l#k@iRIGX(5#RFISP,[L#ZDZq15f3U1l`Q'8iY3 4ckjpGKe3$R4YfhU$)f2l9RZ'Zq+4VHk+hKYmTSeVPZSTjiCLlh-ICD3)`4@'Z8E `a'451AR&L'GhIc&[XTLGRe&Im9*&IF8FShIF!-VDqJV$GAMQhq%&'`TBpjF+*d2 pCIUI)Tljjl,E+[`6aBa%-E,iR$J'D%X3fCMI)-j,6)X)k"IplC0%%mkjmYdDaLa aRTMKY3jNDDZ[%)Y&G-1U``8QU,cU*lBQl0fIRrqYj*1hIMf[QZFbVkS-UR-+H"" pjLMUhPI5Gc!(RE3CXCF4ZaHa,b$f)m4Z41`@a2iGX5rdDqBF-Cq0#jLaQaM)PIT B$$J4i1`(6Jca&X5p5Mb+Z!I9#+2A-4H!Phl1BFZK6)X'P1Pd)2UT'YLl+([Xje" &X(G3"E#rQ$,#hLBf5[&#iIEbUHk)jIC*K8+LS[#3!(52q$"f"m9[bQGYVFb8Q&` SL*@*#SPH*-BrdXA*r3'p@1bk+AAU$prJ+hU&X5pX[UJ04bEU2XP'ZCd0j"eZ*#m %qlqCaqq1CTX@9GH5ZhFh`M30XVe"RY0mK03(jKUH*(m%QFdK*`6lFR*!X+p[Z,+ 'iPH95189,&0B*NpR#(H9ZJKIZ,r848kam%eb)A$qm9,A!Z$S*TAMdQVBIhjH,+V Cf"I`*%GkTjmP&c&i6"6,f)mBhYe(XFaka1!98Xc`AF4!9K6,rL&HEiKhr9'XaBT c5)ZK9LdVF@iPjNB`#p&b(@+GF!U3!(2,HX3LQ2j!R9YZ4Lb'qBkPL(d'X6E8'Vh 5mJA%FKKHG*M,@Zj%E)HFAmL!'0L'amb)i9Q$2$B#X30+E$4Lqr*I3PE--!@a2EB rTZG''UC4RA1R`,YMKKUFHkCM*Hi9CKB"XDFQ2B@EFTRK`iKpQePi[Dp(,5Nb&l& 1a(*B&krhpi'a5ijPASAB6UEMHBp$V"$+Rf)6%)0DjV&*r!@-Q6aQ3mc%$$b@KaM F(Kkl'$',NSGG',YRVj+(!qHQ+RR-3LaAb@-1BK192+j!6&6bF!(lN!"F'`YHeEK RIfEle8fYEB2[A-fZ#erMLpD9d*0B"pl(UMkP0I8+9U()@"I&&&Md@Mh0K'A94F1 aF$0Z,XqLDlNaF1$Vb#S4Ui0,Si#Ji5H4$`Vm2`!5FJ!!!3#3!`%8!*!$&!#3!c) U9*!(+LVX!!!U+P53#Z`!!#T8N!-*EA4PFh3ZFfPd!J#3!e0*9%46593K!3!!T!) -!*!$!3!!8dP84&0*9#%"!!#N!J`!N"+`fEpN!!!Hm!!!!8C892q3"953!q`!N!4 8N!Er9*!&l!!!+P53"Iq3!e53!q`!N!-U9*!+l1`!N!-Ul&53"qcX!*!&l!$X9*! $l*!$!*!+l*!$!*!,!3#3!rq3#J#3#"!!+!!@!48#!3#3"J-!N!-"!*!$!43!N!- 8!*!$-J&20H`KbJ#3!a`!-J!!8f9dC`#3!`S!!2rr!*!%!8pi(2'-: tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/nl_mac.c000066400000000000000000000036341137544547100227760ustar00rootroot00000000000000/* * Program: Mac newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 January 1992 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { long i,j; unsigned char c,*d = src; if (*dst) { /* destination provided? */ if ((i = srcl * 2) > *dstl) /* calculate worst-case situation */ for (i = j = srcl; j; --j) if (*d++ == '\015') i++; /* flush destination buffer if too small */ if (i > *dstl) fs_give ((void **) dst); } /* make a new buffer if needed */ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1); d = *dst; /* destination string */ if (srcl) do { /* copy string */ c = *d++ = *src++; /* copy character */ /* append line feed to bare CR */ if ((c == '\015') && (*src != '\012')) *d++ = '\012'; } while (--srcl); *d = '\0'; /* tie off destination */ return d - *dst; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { unsigned long pos = GETPOS (s); unsigned long i = SIZE (s); unsigned long j = i; while (j--) if ((SNX (s) == '\015') && ((CHR (s) != '\012') || !j)) i++; SETPOS (s,pos); /* restore old position */ return i; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/os_mac.c000066400000000000000000000036401137544547100230030ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Macintosh version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 January 1992 * Last Edited: 7 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* This is a totally new operating-system dependent module for the Macintosh, * written using THINK C on my Mac PowerBook-100 in my free time. * Unlike earlier efforts, this version requires no external TCP library. It * also takes advantage of the Map panel in System 7 for the timezone. */ /* PPC cretins broke the MachineLocation struct */ #define gmtFlags u #include #include #include #include #define tcp_port MacTCP_port #include #include #include #include #include #include #include #include #include #include #include #include #undef tcp_port #include "tcp_mac.h" /* must be before osdep.h */ #include "mail.h" #include "osdep.h" #include "misc.h" static short TCPdriver = 0; /* MacTCP's reference number */ short resolveropen = 0; /* TCP's resolver open */ #include "env_mac.c" #include "fs_mac.c" #include "ftl_mac.c" #include "nl_mac.c" #include "tcp_mac.c" #define open(a,b,c) open (a,b) #define server_login(user,pass,authuser,argc,argv) NIL #define authserver_login(user,authuser,argc,argv) NIL #define myusername() "" /* dummy definition to prevent build errors */ #define MD5ENABLE "" #include "auth_md5.c" #include "auth_pla.c" #include "auth_log.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/os_mac.h000066400000000000000000000027061137544547100230120ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Macintosh version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 January 1992 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* This is a totally new operating-system dependent module for the Macintosh, * written using THINK C on my Mac PowerBook-100 in my free time. * Unlike earlier efforts, this version requires no external TCP library. It * also takes advantage of the Map panel in System 7 for the timezone. */ #include #include #include #include #ifndef noErr #include #include #include #include #include #include #include #include #include #endif #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END extern short resolveropen; /* make this global so caller can sniff */ #include "env_mac.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #define TCPDRIVER "\p.IPP" #define gethostid clock long wait (void); long random (void); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/osdep.h000066400000000000000000000011201137544547100226500ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Macintosh version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "os_mac.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/pmatch.c000066400000000000000000000053271137544547100230220ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-independent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/tcp_mac.c000066400000000000000000000360631137544547100231550ustar00rootroot00000000000000/* * Program: Macintosh TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 January 1992 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* This is a totally new operating-system dependent module for the Macintosh, * written using THINK C on my Mac PowerBook-100 in my free time. * Unlike earlier efforts, this version requires no external TCP library. It * also takes advantage of the Map panel in System 7 for the timezone. */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_open = 75; /* TCP timeouts, in seconds */ static long ttmo_read = 0; static long ttmo_write = 0; static long ttmo_close = 0; /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_OPENTIMEOUT: ttmo_open = (long) value; case GET_OPENTIMEOUT: ret = (void *) ttmo_open; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; case SET_CLOSETIMEOUT: ttmo_close = (long) value; case GET_CLOSETIMEOUT: ret = (void *) ttmo_close; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream; struct hostInfo hst; struct TCPCreatePB *createpb; struct TCPOpenPB *openpb; char *s; unsigned long i,j,k,l; char tmp[MAILTMPLEN]; port &= 0xffff; /* erase flags */ /* init MacTCP */ if (!TCPdriver && OpenDriver (TCPDRIVER,&TCPdriver)) { mm_log ("Can't init MacTCP",ERROR); return NIL; } if (!resolveropen && OpenResolver (NIL)) { mm_log ("Can't init domain resolver",ERROR); return NIL; } resolveropen = T; /* note resolver open now */ /* domain literal? */ if (host[0] == '[' && host[strlen (host)-1] == ']') { if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' && ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' && ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' && ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) { hst.addr[0] = (i << 24) + (j << 16) + (k << 8) + l; hst.addr[1] = 0; /* only one address to try! */ sprintf (hst.cname,"[%ld.%ld.%ld.%ld]",i,j,k,l); } else { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else { /* look up host name */ if (!tcp_dns_upp) tcp_dns_upp = NewResultProc (tcp_dns_result); if (StrToAddr (host,&hst,tcp_dns_upp,NIL)) { while (hst.rtnCode == cacheFault && wait ()); /* kludge around MacTCP bug */ if (hst.rtnCode == outOfMemory) { mm_log ("Re-initializing domain resolver",WARN); CloseResolver (); /* bop it on the head and try again */ OpenResolver (NIL); /* note this will leak 12K */ StrToAddr (host,&hst,tcp_dns_upp,NIL); while (hst.rtnCode == cacheFault && wait ()); } if (hst.rtnCode) { /* still have error status? */ switch (hst.rtnCode) { /* analyze return */ case nameSyntaxErr: s = "Syntax error in name"; break; case noResultProc: s = "No result procedure"; break; case noNameServer: s = "No name server found"; break; case authNameErr: s = "Host does not exist"; break; case noAnsErr: s = "No name servers responding"; break; case dnrErr: s = "Name server returned an error"; break; case outOfMemory: s = "Not enough memory to resolve name"; break; case notOpenErr: s = "Driver not open"; break; default: s = NIL; break; } if (s) sprintf (tmp,"%s: %.80s",s,host); else sprintf (tmp,"Unknown resolver error (%ld): %.80s", hst.rtnCode,host); mm_log (tmp,ERROR); return NIL; } } } /* create local TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); stream->ictr = 0; /* initialize input */ stream->pb.ioCRefNum = TCPdriver; createpb = &stream->pb.csParam.create; openpb = &stream->pb.csParam.open; stream->pb.csCode = TCPCreate;/* create a TCP stream */ /* set up buffer for TCP */ createpb->rcvBuffLen = 4*BUFLEN; createpb->rcvBuff = fs_get (createpb->rcvBuffLen); createpb->notifyProc = NIL; /* no special notify procedure */ createpb->userDataPtr = NIL; if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("Can't create TCP stream"); /* open TCP connection */ stream->pb.csCode = TCPActiveOpen; openpb->ulpTimeoutValue = (int) ttmo_open; openpb->ulpTimeoutAction = T; openpb->validityFlags = timeoutValue|timeoutAction; /* remote host (should try all) */ openpb->remoteHost = hst.addr[0]; openpb->remotePort = port; /* caller specified remote port */ openpb->localPort = 0; /* generate a local port */ openpb->tosFlags = 0; /* no special TOS */ openpb->precedence = 0; /* no special precedence */ openpb->dontFrag = 0; /* allow fragmentation */ openpb->timeToLive = 255; /* standards say 60, UNIX uses 255 */ openpb->security = 0; /* no special security */ openpb->optionCnt = 0; /* no IP options */ openpb->options[0] = 0; openpb->userDataPtr = NIL; /* no special data pointer */ PBControlAsync ((ParmBlkPtr) &stream->pb); while (stream->pb.ioResult == inProgress && wait ()); if (stream->pb.ioResult) { /* got back error status? */ sprintf (tmp,"Can't connect to %.80s,%ld",hst.cname,port); mm_log (tmp,ERROR); /* nuke the buffer */ stream->pb.csCode = TCPRelease; createpb->userDataPtr = NIL; if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage"); /* free its buffer */ fs_give ((void **) &createpb->rcvBuff); fs_give ((void **) &stream);/* and the local stream */ return NIL; } /* copy host names for later use */ stream->host = cpystr (hst.cname); /* tie off trailing dot */ stream->host[strlen (stream->host) - 1] = '\0'; /* the open gave us our address */ i = (openpb->localHost >> 24) & 0xff; j = (openpb->localHost >> 16) & 0xff; k = (openpb->localHost >> 8) & 0xff; l = openpb->localHost & 0xff; sprintf (tmp,"[%ld.%ld.%ld.%ld]",i,j,k,l); stream->localhost = cpystr (tmp); if (!myLocalHost) myLocalHost = cpystr (tmp); stream->port = port; /* copy port number */ return stream; } /* Called when have return from DNS * Accepts: host info pointer * user data pointer */ ResultUPP tcp_dns_upp = NIL; pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr) { /* dummy routine */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* no authenticated opens on Mac */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) return NIL; /* special case of newline broken by buffer */ if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; char *bufptr = buffer; while (size > 0) { /* until request satisfied */ if (!tcp_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (bufptr,stream->iptr,n); bufptr += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } bufptr[0] = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { time_t t = time (0); struct TCPReceivePB *receivepb = &stream->pb.csParam.receive; struct TCPAbortPB *abortpb = &stream->pb.csParam.abort; while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); stream->pb.csCode = TCPRcv; /* receive TCP data */ receivepb->commandTimeoutValue = (int) ttmo_read; receivepb->rcvBuff = stream->ibuf; receivepb->rcvBuffLen = BUFLEN; receivepb->secondTimeStamp = 0; receivepb->userDataPtr = NIL; PBControlAsync ((ParmBlkPtr) &stream->pb); while (stream->pb.ioResult == inProgress && wait ()); if (stream->pb.ioResult) { /* punt if got an error */ time_t tc = time (0); if ((stream->pb.ioResult == commandTimeout) && tmoh && ((*tmoh) (tc - t,tc - tl))) continue; /* nuke connection */ stream->pb.csCode = TCPAbort; abortpb->userDataPtr = NIL; PBControlSync ((ParmBlkPtr) &stream->pb); return NIL; } stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = receivepb->rcvBuffLen; } return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { struct TCPSendPB *sendpb = &stream->pb.csParam.send; struct TCPAbortPB *abortpb = &stream->pb.csParam.abort; struct { unsigned short length; Ptr buffer; unsigned short trailer; } wds; while (wds.length = (size > (unsigned long) 32768) ? 32768 : size) { wds.buffer = string; /* buffer */ wds.trailer = 0; /* tie off buffer */ size -= wds.length; /* this many words will be output */ string += wds.length; stream->pb.csCode = TCPSend;/* send TCP data */ sendpb->ulpTimeoutValue = (int) ttmo_write; sendpb->ulpTimeoutAction = 0; sendpb->validityFlags = timeoutValue|timeoutAction; sendpb->pushFlag = T; /* send the data now */ sendpb->urgentFlag = NIL; /* non-urgent data */ sendpb->wdsPtr = (Ptr) &wds; sendpb->userDataPtr = NIL; PBControlAsync ((ParmBlkPtr) &stream->pb); while (stream->pb.ioResult == inProgress && wait ()); if (stream->pb.ioResult) { /* punt if got an error */ /* nuke connection */ stream->pb.csCode =TCPAbort; abortpb->userDataPtr = NIL; PBControlSync ((ParmBlkPtr) &stream->pb); return NIL; } } return T; /* success */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { struct TCPClosePB *closepb = &stream->pb.csParam.close; struct TCPCreatePB *createpb = &stream->pb.csParam.create; stream->pb.csCode = TCPClose; /* close TCP stream */ closepb->ulpTimeoutValue = (int) ttmo_close; closepb->ulpTimeoutAction = 0; closepb->validityFlags = timeoutValue|timeoutAction; closepb->userDataPtr = NIL; PBControlAsync ((ParmBlkPtr) &stream->pb); while (stream->pb.ioResult == inProgress && wait ()); stream->pb.csCode =TCPRelease;/* flush the buffers */ createpb->userDataPtr = NIL; if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage"); /* free its buffer */ fs_give ((void **) &createpb->rcvBuff); /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP return host for this stream * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return remote host for this stream * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP return local host for this stream * Accepts: TCP/IP stream * Returns: local host name for this stream */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { int i; struct hostInfo hst; /* look like domain literal? */ if (name[0] == '[' && name[i = (strlen (name))-1] == ']') return name; if (StrToAddr (name,&hst,tcp_dns_upp,NIL)) { while (hst.rtnCode == cacheFault && wait ()); /* kludge around MacTCP bug */ if (hst.rtnCode == outOfMemory) { mm_log ("Re-initializing domain resolver",WARN); CloseResolver (); /* bop it on the head and try again */ OpenResolver (NIL); /* note this will leak 12K */ StrToAddr (name,&hst,tcp_dns_upp,NIL); while (hst.rtnCode == cacheFault && wait ()); } /* still have error status? */ if (hst.rtnCode) return name; } return hst.cname; /* success */ } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/mac/tcp_mac.h000066400000000000000000000020721137544547100231530ustar00rootroot00000000000000/* * Program: Macintosh TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 January 1992 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer */ #define BUFLEN (size_t) 8192 /* TCP input buffer */ /* TCP I/O stream */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ struct TCPiopb pb; /* MacTCP parameter block */ long ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; extern ResultUPP tcp_dns_upp; pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/000077500000000000000000000000001137544547100212545ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/drivers.bat000066400000000000000000000013421137544547100234220ustar00rootroot00000000000000@ECHO OFF REM Program: Driver Linkage Generator for DOS/NT REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 11 October 1989 REM Last Edited: 6 July 2004 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 1988-2004 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Erase old driver linkage IF EXIST LINKAGE.* DEL LINKAGE.* REM Now define the new list FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D EXIT 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/drivraux.bat000066400000000000000000000015631137544547100236150ustar00rootroot00000000000000@ECHO OFF REM Program: Driver Linkage Generator auxillary for NT/Win9x REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 11 October 1989 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. ECHO extern DRIVER %1driver; >> LINKAGE.H REM Note the introduction of the caret to quote the ampersand in NT if "%OS%" == "Windows_NT" ECHO mail_link (^&%1driver); /* link in the %1 driver */ >> LINKAGE.C if "%OS%" == "" ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/dummy.h000066400000000000000000000021051137544547100225560ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/dummynt.c000066400000000000000000000500521137544547100231170ustar00rootroot00000000000000/* * Program: Dummy routines for NT * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 May 1993 * Last Edited: 26 August 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include "mail.h" #include "osdep.h" #include #include #include "dummy.h" #include "misc.h" /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level); long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents); long dummy_subscribe (MAILSTREAM *stream,char *mailbox); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ dummy_subscribe, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char *s,*t,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { /* indeterminate INBOX */ if (!*s) return &dummydriver; /* remove trailing \ */ if ((t = strrchr (s,'\\')) && !t[1]) *t = '\0'; if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { case S_IFREG: /* file */ case S_IFDIR: /* future use */ return &dummydriver; } } return NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { return NIL; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i = 0; if (!pat || !*pat) { /* empty pattern? */ if (dummy_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'\\')) *++s = '\0'; else test[0] = '\0'; dummy_listed (stream,'\\',test,LATT_NOSELECT,NIL); } } /* get canonical form of name */ else if (dummy_canonicalize (test,ref,pat)) { /* found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test,(size_t) (i = s - test)); file[i] = '\0'; /* tie off */ } else strcpy (file,test); /* use just that name then */ /* find directory name */ if (s = strrchr (file,'\\')) { *++s = '\0'; /* found, tie off at that point */ s = file; } /* silly case */ else if (file[0] == '#') s = file; /* do the work */ dummy_list_work (stream,s,test,contents,0); if (pmatch ("INBOX",test)) /* always an INBOX */ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents); } } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { dummy_scan (stream,ref,pat,NIL); } /* Dummy list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { void *sdb = NIL; char *s,*t,test[MAILTMPLEN]; int showuppers = pat[strlen (pat) - 1] == '%'; /* get canonical form of name */ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do if (*s != '{') { if (pmatch_full (s,test,'\\')) { if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS); else mm_lsub (stream,'\\',s,NIL); } else while (showuppers && (t = strrchr (s,'\\'))) { *t = '\0'; /* tie off the name */ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT); } } while (s = sm_read (&sdb)); /* until no more subscriptions */ } /* Dummy subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long dummy_subscribe (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox); sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox); mm_log (tmp,ERROR); return NIL; } /* Dummy list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * string to scan * search level */ void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level) { struct _finddata_t f; struct stat sbuf; long fhandle; char tmp[MAILTMPLEN]; /* punt if bogus name */ if (!mailboxdir (tmp,dir,NIL)) return; /* make directory wildcard */ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*"); /* do nothing if can't open directory */ if ((fhandle = _findfirst (tmp,&f)) >= 0) { /* list it if not at top-level */ if (!level && dir && pmatch_full (dir,pat,'\\')) dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents); /* scan directory */ if (!dir || dir[strlen (dir) -1] == '\\') do if (((f.name[0] != '.') || (f.name[1] && ((f.name[1] != '.') || f.name[2]))) && (strlen (f.name) <= NETMAXMBX)) { /* see if name is useful */ if (dir) sprintf (tmp,"%s%s",dir,f.name); else strcpy (tmp,f.name); /* make sure useful and can get info */ if ((pmatch_full (tmp,pat,'\\') || pmatch_full (strcat (tmp,"\\"),pat,'\\') || dmatch (tmp,pat,'\\')) && mailboxdir (tmp,dir,f.name) && tmp[0] && !stat (tmp,&sbuf)) { /* now make name we'd return */ if (dir) sprintf (tmp,"%s%s",dir,f.name); else strcpy (tmp,f.name); /* only interested in file type */ switch (sbuf.st_mode & S_IFMT) { case S_IFDIR: /* directory? */ if (pmatch_full (tmp,pat,'\\')) { if (!dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))break; strcat (tmp,"\\");/* set up for dmatch call */ } /* try again with trailing \ */ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\') && !dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents)) break; if (dmatch (tmp,pat,'\\') && (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) dummy_list_work (stream,tmp,pat,contents,level+1); break; case S_IFREG: /* ordinary name */ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp)) dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents); break; } } } while (!_findnext (fhandle,&f)); _findclose(fhandle); } } /* Mailbox found * Accepts: hierarchy delimiter * mailbox name * attributes * contents to search before calling mm_list() * Returns: T, always */ #define BUFSIZE 4*MAILTMPLEN long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents) { struct stat sbuf; int fd; long csiz,ssiz,bsiz; char *s,*buf,tmp[MAILTMPLEN]; if (contents) { /* want to search contents? */ /* forget it if can't select or open */ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) || !(s = dummy_file (tmp,name)) || stat (s,&sbuf) || (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0)) return T; /* get buffer including slop */ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1); memset (buf,'\0',ssiz); /* no slop area the first time */ while (sbuf.st_size) { /* until end of file */ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE)); if (search ((unsigned char *) buf,bsiz+ssiz, (unsigned char *) contents,csiz)) break; memcpy (buf,buf+BUFSIZE,ssiz); sbuf.st_size -= bsiz; /* note that we read that much */ } fs_give ((void **) &buf); /* flush buffer */ close (fd); /* finished with file */ if (!sbuf.st_size) return T;/* not found */ } /* notify main program */ mm_list (stream,delimiter,name,attributes); return T; } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { char tmp[MAILTMPLEN]; if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox)) return dummy_create_path (stream,tmp,NIL); sprintf (tmp,"Can't create %.80s: invalid name",mailbox); mm_log (tmp,ERROR); return NIL; } /* Dummy create path * Accepts: mail stream * path name to create * directory mode * Returns: T on success, NIL on failure */ long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN]; int fd; long ret = NIL; char *t = strrchr (path,'\\'); char *pt = (path[1] == ':') ? path + 2 : path; int wantdir = t && !t[1]; if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */ /* found superior to this name? */ if ((s = strrchr (pt,'\\')) && (s != pt)) { strncpy (tmp,path,(size_t) (s - path)); tmp[s - path] = '\0'; /* make directory name for stat */ c = *++s; /* tie off in case need to recurse */ *s = '\0'; /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,path,dirmode)) return NIL; *s = c; /* restore full name */ } if (wantdir) { /* want to create directory? */ ret = !mkdir (path); *t = '\\'; /* restore directory delimiter */ } /* create file */ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0) ret = !close (fd); /* close file */ if (!ret) { /* error? */ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path, strerror (errno)); mm_log (tmp,ERROR); } return ret; /* return status */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { struct stat sbuf; char *s,tmp[MAILTMPLEN]; if (!(s = dummy_file (tmp,mailbox))) { sprintf (tmp,"Can't delete - invalid name: %.80s",s); mm_log (tmp,ERROR); } /* no trailing \ */ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0'; if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? rmdir (tmp) : unlink (tmp)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno)); mm_log (tmp,ERROR); return NIL; } return T; /* return success */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; long ret = NIL; /* no trailing \ allowed */ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || ((s = strrchr (s,'\\')) && !s[1])) { sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); mm_log (mbx,ERROR); return NIL; } /* found superior to destination name? */ if (s && (s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,mbx)) return NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } /* rename of non-ex INBOX creates dest */ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, strerror (errno)); mm_log (tmp,ERROR); return NIL; } return LONGT; /* return success */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { int fd; char err[MAILTMPLEN],tmp[MAILTMPLEN]; struct stat sbuf; /* OP_PROTOTYPE call */ if (!stream) return &dummyproto; err[0] = '\0'; /* no error message yet */ /* can we open the file? */ if (!dummy_file (tmp,stream->mailbox)) sprintf (err,"Can't open this name: %.80s",stream->mailbox); else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { /* no, error unless INBOX */ if (compare_cstring (stream->mailbox,"INBOX")) sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox); } else { /* file had better be empty then */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); if (sbuf.st_size) /* bogus format if non-empty */ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format", stream->mailbox,tmp); } if (err[0]) { /* if an error happened */ mm_log (err,stream->silent ? WARN : ERROR); return NIL; } else if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); /* and certainly no recent ones! */ stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long dummy_ping (MAILSTREAM *stream) { MAILSTREAM *test; /* time to do another test? */ if (time (0) >= ((time_t) (stream->gensym + 30))) { /* has mailbox format changed? */ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) && (test->dtb != stream->dtb) && (test = mail_open (NIL,stream->mailbox,NIL))) { /* preserve some resources */ test->original_mailbox = stream->original_mailbox; stream->original_mailbox = NIL; test->sparep = stream->sparep; stream->sparep = NIL; test->sequence = stream->sequence; mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */ memcpy (fs_get (sizeof (MAILSTREAM)),stream, sizeof (MAILSTREAM))); /* swap the streams */ memcpy (stream,test,sizeof (MAILSTREAM)); fs_give ((void **) &test);/* flush test now that copied */ /* make sure application knows */ mail_exists (stream,stream->recent = stream->nmsgs); } /* still hasn't changed */ else stream->gensym = time (0); } return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd = -1; int e; char tmp[MAILTMPLEN]; MAILSTREAM *ts = default_proto (T); if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) { if ((e = errno) == ENOENT) /* failed, was it no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before append", (long) NIL); sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } if (fd >= 0) { /* found file? */ fstat (fd,&sbuf); /* get its size */ close (fd); /* toss out the fd */ if (sbuf.st_size) ts = NIL; /* non-empty file? */ } if (ts) return (*ts->dtb->append) (stream,mailbox,af,data); sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* Dummy mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *dummy_file (char *dst,char *name) { char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? strcpy (dst,sysinbox ()) : s; } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { char dev[4]; /* initially no device */ dev[0] = dev[1] = dev[2] = dev[3] = '\0'; if (ref) switch (*ref) { /* preliminary reference check */ case '{': /* remote names not allowed */ return NIL; /* disallowed */ case '\0': /* empty reference string */ break; default: /* all other names */ if (ref[1] == ':') { /* start with device name? */ dev[0] = *ref++; dev[1] = *ref++; } break; } if (pat[1] == ':') { /* device name in pattern? */ dev[0] = *pat++; dev[1] = *pat++; ref = NIL; /* ignore reference */ } switch (*pat) { case '#': /* namespace names */ if (mailboxfile (tmp,pat)) strcpy (tmp,pat); else return NIL; /* unknown namespace */ break; case '{': /* remote names not allowed */ return NIL; case '\\': /* rooted name */ ref = NIL; /* ignore reference */ break; } /* make sure device names are rooted */ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\'; /* build name */ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat); ucase (tmp); /* force upper case */ return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/env_nt.c000066400000000000000000000522401137544547100227140ustar00rootroot00000000000000/* * Program: NT environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 18 January 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static char *myUserName = NIL; /* user name */ static char *myLocalHost = NIL; /* local host name */ static char *myClientAddr = NIL;/* client host address */ static char *myClientHost = NIL;/* client host name */ static char *myServerAddr = NIL;/* server host address */ static char *myServerHost = NIL;/* server host name */ static char *myHomeDir = NIL; /* home directory name */ static char *myNewsrc = NIL; /* newsrc file name */ static char *sysInbox = NIL; /* system inbox name */ static long list_max_level = 5; /* maximum level of list recursion */ static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ /* home namespace */ static NAMESPACE nshome = {"",'\\',NIL,NIL}; /* UNIX other user namespace */ static NAMESPACE nsother = {"#user.",'\\',NIL,NIL}; /* namespace list */ static NAMESPACE *nslist[3] = {&nshome,&nsother,NIL}; static long alarm_countdown = 0;/* alarm count down */ static void (*alarm_rang) (); /* alarm interrupt function */ static unsigned int rndm = 0; /* initial `random' number */ static int server_nli = 0; /* server and not logged in */ static int logtry = 3; /* number of login tries */ /* block notification */ static blocknotify_t mailblocknotify = mm_blocknotify; /* callback to get username */ static userprompt_t mailusername = NIL; static long is_nt = -1; /* T if NT, NIL if not NT, -1 unknown */ static HINSTANCE netapi = NIL; typedef NET_API_STATUS (CALLBACK *GETINFO) (LPCWSTR,LPCWSTR,DWORD,LPBYTE *); static GETINFO getinfo = NIL; #include "write.c" /* include safe writing routines */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Get all authenticators */ #include "auths.c" /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_NAMESPACE: ret = (void *) nslist; break; case SET_USERPROMPT : mailusername = (userprompt_t) value; case GET_USERPROMPT : ret = (void *) mailusername; break; case SET_HOMEDIR: if (myHomeDir) fs_give ((void **) &myHomeDir); myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: if (myLocalHost) fs_give ((void **) &myLocalHost); ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: if (!myNewsrc) { /* set news file name if not defined */ char tmp[MAILTMPLEN]; sprintf (tmp,"%s\\NEWSRC",myhomedir ()); myNewsrc = cpystr (tmp); } ret = (void *) myNewsrc; break; case SET_SYSINBOX: if (sysInbox) fs_give ((void **) &sysInbox); sysInbox = cpystr ((char *) value); case GET_SYSINBOX: ret = (void *) sysInbox; break; case SET_LISTMAXLEVEL: list_max_level = (long) value; case GET_LISTMAXLEVEL: ret = (void *) list_max_level; break; case SET_DISABLE822TZTEXT: no822tztext = value ? T : NIL; case GET_DISABLE822TZTEXT: ret = (void *) (no822tztext ? VOIDT : NIL); break; case SET_BLOCKNOTIFY: mailblocknotify = (blocknotify_t) value; case GET_BLOCKNOTIFY: ret = (void *) mailblocknotify; break; } return ret; } /* Write current time * Accepts: destination string * optional format of day-of-week prefix * format of date and time * flag whether to append symbolic timezone */ static void do_date (char *date,char *prefix,char *fmt,int suffix) { time_t tn = time (0); struct tm *t = gmtime (&tn); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&tn); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; if (prefix) { /* want day of week? */ sprintf (date,prefix,days[t->tm_wday]); date += strlen (date); /* make next sprintf append */ } /* output the date */ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); if (suffix) { /* append timezone suffix if desired */ char *tz; tzset (); /* get timezone from TZ environment stuff */ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]; if (tz && tz[0]) { char *s; for (s = tz; *s; s++) if (*s & 0x80) return; sprintf (date + strlen (date)," (%.50s)",tz); } } } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d", no822tztext ? NIL : T); } /* Write current time in fixed-width RFC 822 format * Accepts: destination string */ void rfc822_fixed_date (char *date) { do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL); } /* Return random number */ long random (void) { if (!rndm) srand (rndm = (unsigned) time (0L)); return (long) rand (); } /* Set alarm timer * Accepts: new value * Returns: old alarm value */ long alarm (long seconds) { long ret = alarm_countdown; alarm_countdown = seconds; return ret; } /* The clock ticked */ void CALLBACK clock_ticked (UINT IDEvent,UINT uReserved,DWORD dwUser, DWORD dwReserved1,DWORD dwReserved2) { if (alarm_rang && !--alarm_countdown) (*alarm_rang) (); } /* Initialize server * Accepts: server name for syslog or NIL * /etc/services service name or NIL * alternate /etc/services service name or NIL * clock interrupt handler * kiss-of-death interrupt handler * hangup interrupt handler * termination interrupt handler */ void server_init (char *server,char *service,char *sslservice, void *clkint,void *kodint,void *hupint,void *trmint) { if (!check_nt ()) { if (!auth_md5.server) fatal ("Can't run on Windows without MD5 database"); server_nli = T; /* Windows server not logged in */ } /* only do this if for init call */ if (server && service && sslservice) { long port; struct servent *sv; /* set server name in syslog */ openlog (server,LOG_PID,LOG_MAIL); fclose (stderr); /* possibly save a process ID */ /* Use SSL if SSL service, or if server starts with "s" and not service */ if (((port = tcp_serverport ()) >= 0)) { if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port))) syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ()); else if ((sv = getservbyname (sslservice,"tcp")) && (port == ntohs (sv->s_port))) { syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice, tcp_clientaddr ()); ssl_server_init (server); } else { /* not service or SSL service port */ syslog (LOG_DEBUG,"port %ld service init from %s",port, tcp_clientaddr ()); if (*server == 's') ssl_server_init (server); } } /* make sure stdout does binary */ setmode (fileno (stdin),O_BINARY); setmode (fileno (stdout),O_BINARY); setmode (fileno (stderr),O_BINARY); } alarm_rang = clkint; /* note the clock interrupt */ timeBeginPeriod (1000); /* set the timer interval */ timeSetEvent (1000,1000,clock_ticked,NIL,TIME_PERIODIC); } /* Wait for stdin input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long server_input_wait (long seconds) { fd_set rfd,efd; struct timeval tmo; FD_ZERO (&rfd); FD_ZERO (&efd); FD_SET (0,&rfd); FD_SET (0,&efd); tmo.tv_sec = seconds; tmo.tv_usec = 0; return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL; } /* Server log in * Accepts: user name string * password string * authenticating user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ static int gotprivs = NIL; /* once-only flag to grab privileges */ long server_login (char *user,char *pass,char *authuser,int argc,char *argv[]) { HANDLE hdl; LUID tcbpriv; TOKEN_PRIVILEGES tkp; char *s; /* need to get privileges? */ if (!gotprivs++ && check_nt ()) { /* yes, note client host if specified */ if (argc == 2) myClientHost = argv[1]; /* get process token and TCB priv value */ if (!(OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hdl) && LookupPrivilegeValue ((LPSTR) NIL,SE_TCB_NAME,&tcbpriv))) return NIL; tkp.PrivilegeCount = 1; /* want to enable this privilege */ tkp.Privileges[0].Luid = tcbpriv; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; /* enable it */ AdjustTokenPrivileges (hdl,NIL,&tkp,sizeof (TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NIL,(PDWORD) NIL); /* make sure it won */ if (GetLastError() != ERROR_SUCCESS) return NIL; } /* cretins still haven't given up */ if ((strlen (user) >= MAILTMPLEN) || (authuser && (strlen (authuser) >= MAILTMPLEN))) syslog (LOG_ALERT,"SYSTEM BREAK-IN ATTEMPT, host=%.80s",tcp_clienthost ()); else if (logtry > 0) { /* still have available logins? */ /* authentication user not supported */ if (authuser && *authuser && compare_cstring (authuser,user)) mm_log ("Authentication id must match authorization id",ERROR); if (check_nt ()) { /* NT: authserver_login() call not supported */ if (!pass) mm_log ("Unsupported authentication mechanism",ERROR); else if (( /* try to login and impersonate the guy */ #ifdef LOGIN32_LOGON_NETWORK LogonUser (user,".",pass,LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,&hdl) || #endif LogonUser (user,".",pass,LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,&hdl) || LogonUser (user,".",pass,LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT,&hdl) || LogonUser (user,".",pass,LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT,&hdl)) && ImpersonateLoggedOnUser (hdl)) return env_init (user,NIL); } else { /* Win9x: done if from authserver_login() */ if (!pass) server_nli = NIL; /* otherwise check MD5 database */ else if (s = auth_md5_pwd (user)) { /* change NLI state based on pwd match */ server_nli = strcmp (s,pass); memset (s,0,strlen (s));/* erase sensitive information */ fs_give ((void **) &s); /* flush erased password */ } /* success if no longer NLI */ if (!server_nli) return env_init (user,NIL); } } s = (logtry-- > 0) ? "Login failure" : "Excessive login attempts"; /* note the failure in the syslog */ syslog (LOG_INFO,"%s user=%.80s host=%.80s",s,user,tcp_clienthost ()); sleep (3); /* slow down possible cracker */ return NIL; } /* Authenticated server log in * Accepts: user name string * authentication user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ long authserver_login (char *user,char *authuser,int argc,char *argv[]) { return server_login (user,NIL,authuser,argc,argv); } /* Log in as anonymous daemon * Accepts: argument count * argument vector * Returns: T if successful, NIL if error */ long anonymous_login (int argc,char *argv[]) { return server_login ("Guest",NIL,NIL,argc,argv); } /* Initialize environment * Accepts: user name * home directory, or NIL to use default * Returns: T, always */ long env_init (char *user,char *home) { if (myUserName) fatal ("env_init called twice!"); myUserName = cpystr (user); /* remember user name */ if (!myHomeDir) /* only if home directory not set up yet */ myHomeDir = (home && *home) ? cpystr (home) : win_homedir (user); return T; } /* Check if NT * Returns: T if NT, NIL if Win9x */ int check_nt (void) { if (is_nt < 0) { /* not yet set up? */ OSVERSIONINFO ver; ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); GetVersionEx (&ver); is_nt = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) ? T : NIL; } return is_nt; } /* Return Windows home directory * Accepts: user name * Returns: home directory */ char *win_homedir (char *user) { char *s,*t,tmp[MAILTMPLEN]; PUSER_INFO_1 ui; /* Win9x default */ if (!check_nt ()) sprintf (tmp,"%s\\My Documents",defaultDrive ()); /* get from user info on NT */ else if ((netapi || (netapi = LoadLibrary ("netapi32.dll"))) && (getinfo || (getinfo = (GETINFO) GetProcAddress (netapi,"NetUserGetInfo"))) && MultiByteToWideChar (CP_ACP,0,user,strlen (user) + 1, (WCHAR *) tmp,MAILTMPLEN) && !(*getinfo) (NIL,(LPWSTR) &tmp,1,(LPBYTE *) &ui) && WideCharToMultiByte (CP_ACP,0,ui->usri1_home_dir,-1, tmp,MAILTMPLEN,NIL,NIL) && tmp[0]) { /* make sure doesn't end with delimiter */ if ((*(s = tmp + strlen (tmp) - 1) == '\\') || (*s == '/')) *s = '\0'; } /* no home dir, found Win2K user profile? */ else if ((s = getenv ("USERPROFILE")) && (t = strrchr (s,'\\'))) { strncpy (tmp,s,t-s); /* copy up to user name */ sprintf (tmp+(t-s),"\\%.100s\\My Documents",user); } /* last resort NT default */ else sprintf (tmp,"%s\\users\\default",defaultDrive ()); return cpystr (tmp); } /* Return default drive * Returns: default drive */ static char *defaultDrive (void) { char *s = getenv ("SystemDrive"); return (s && *s) ? s : "C:"; } /* Return my user name * Accepts: pointer to optional flags * Returns: my user name */ char *myusername_full (unsigned long *flags) { UCHAR usr[MAILTMPLEN]; DWORD len = MAILTMPLEN; char *user,*path,*d,*p,pth[MAILTMPLEN]; char *ret = "SYSTEM"; /* get user name if don't have it yet */ if (!myUserName && !server_nli && /* use callback, else logon name */ ((mailusername && (user = (char *) (*mailusername) ())) || (GetUserName (usr,&len) && _stricmp (user = (char *) usr,"SYSTEM")))) { /* try HOMEPATH, then HOME */ if (p = getenv ("HOMEPATH")) sprintf (path = pth,"%s%s", (d = getenv ("HOMEDRIVE")) ? d : defaultDrive (),p); else if (!(path = getenv ("HOME"))) sprintf (path = pth,"%s\\My Documents",defaultDrive ()); /* make sure doesn't end with delimiter */ if ((*(p = path + strlen (path) -1) == '\\') || (*p == '/')) *p = '\0'; env_init (user,path); /* initialize environment */ } if (myUserName) { /* logged in? */ if (flags) /* Guest is an anonymous user */ *flags = _stricmp (myUserName,"Guest") ? MU_LOGGEDIN : MU_ANONYMOUS; ret = myUserName; /* return user name */ } else if (flags) *flags = MU_NOTLOGGEDIN; return ret; } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { if (!myHomeDir) myusername ();/* initialize if first time */ return myHomeDir ? myHomeDir : ""; } /* Return system standard INBOX * Accepts: buffer string */ char *sysinbox () { char tmp[MAILTMPLEN]; if (!sysInbox) { /* initialize if first time */ if (check_nt ()) sprintf (tmp,MAILFILE,myUserName); else sprintf (tmp,"%s\\INBOX",myhomedir ()); sysInbox = cpystr (tmp); /* system inbox is from mail spool */ } return sysInbox; } /* Return mailbox directory name * Accepts: destination buffer * directory prefix * name in directory * Returns: file name or NIL if error */ char *mailboxdir (char *dst,char *dir,char *name) { char tmp[MAILTMPLEN]; if (dir || name) { /* if either argument provided */ if (dir) { if (strlen (dir) > NETMAXMBX) return NIL; strcpy (tmp,dir); /* write directory prefix */ } else tmp[0] = '\0'; /* otherwise null string */ if (name) { if (strlen (name) > NETMAXMBX) return NIL; strcat (tmp,name); /* write name in directory */ } /* validate name, return its name */ if (!mailboxfile (dst,tmp)) return NIL; } else strcpy (dst,myhomedir());/* no arguments, wants home directory */ return dst; /* return the name */ } /* Return mailbox file name * Accepts: destination buffer * mailbox name * Returns: file name or empty string for driver-selected INBOX or NIL if error */ char *mailboxfile (char *dst,char *name) { char *dir = myhomedir (); *dst = '\0'; /* default to empty string */ /* check for INBOX */ if (!compare_cstring (name,"INBOX")) return dst; /* reject names with / */ if (strchr (name,'/')) return NIL; else if (*name == '#') { /* namespace names */ if (((name[1] == 'u') || (name[1] == 'U')) && ((name[2] == 's') || (name[2] == 'S')) && ((name[3] == 'e') || (name[3] == 'E')) && ((name[4] == 'r') || (name[4] == 'R')) && (name[5] == '.')) { /* copy user name */ for (dir = dst,name += 6; *name && (*name != '\\'); *dir++ = *name++); *dir++ = '\0'; /* tie off user name */ if (!(dir = win_homedir (dst))) return NIL; /* build resulting name */ sprintf (dst,"%s\\%s",dir,name); fs_give ((void **) &dir); /* clean up other user's home dir */ return dst; } else return NIL; /* unknown namespace name */ } /* absolute path name? */ else if ((*name == '\\') || (name[1] == ':')) return strcpy (dst,name); /* build resulting name */ sprintf (dst,"%s\\%s",dir,name); return dst; /* return it */ } /* Lock file name * Accepts: return buffer for file name * file name * locking to be placed on file if non-NIL * Returns: file descriptor of lock or -1 if error */ int lockname (char *lock,char *fname,int op) { int ld; char c,*s; /* Win2K and Win98 have TEMP under windir */ if (!((s = lockdir (lock,getenv ("windir"),"TEMP")) || /* NT4, NT3.x and Win95 use one of these */ (s = lockdir (lock,getenv ("TEMP"),NIL)) || (s = lockdir (lock,getenv ("TMP"),NIL)) || (s = lockdir (lock,getenv ("TMPDIR"),NIL)) || /* try one of these */ (s = lockdir (lock,defaultDrive (),"WINNT\\TEMP")) || (s = lockdir (lock,defaultDrive (),"WINDOWS\\TEMP")) || /* C:\TEMP is last resort */ (s = lockdir (lock,defaultDrive (),"TEMP")))) { mm_log ("Unable to find temporary directory",ERROR); return -1; } /* generate file name */ while (c = *fname++) switch (c) { case '/': case '\\': case ':': *s++ = '!'; /* convert bad chars to ! */ break; default: *s++ = c; break; } *s++ = c; /* tie off name */ /* get the lock */ if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op) flock (ld,op); /* apply locking function */ return ld; /* return locking file descriptor */ } /* Build lock directory, check to see if it exists * Accepts: return buffer for lock directory * first part of possible name * optional second part * Returns: pointer to end of buffer if buffer has a good name, else NIL */ char *lockdir (char *lock,char *first,char *last) { struct stat sbuf; char c,*s; if (first && *first) { /* first part must be non-NIL */ /* copy first part */ for (s = lock; c = *first++; *s++ = (c == '/') ? '\\' : c); if (last && *last) { /* copy last part if specified */ /* write trailing \ in case not in first */ if (s[-1] != '\\') *s++ = '\\'; while (c = *last++) *s++ = (c == '/') ? '\\' : c; } if (s[-1] == '\\') --s; /* delete trailing \ if any */ *s = s[1] = '\0'; /* tie off name at this point */ if (!stat (lock,&sbuf)) { /* does the name exist? */ *s++ = '\\'; /* yes, reinstall trailing \ */ return s; /* return the name */ } } return NIL; /* failed */ } /* Unlock file descriptor * Accepts: file descriptor * lock file name from lockfd() */ void unlockfd (int fd,char *lock) { flock (fd,LOCK_UN); /* unlock it */ close (fd); /* close it */ } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { extern MAILSTREAM CREATEPROTO,APPENDPROTO; return type ? &APPENDPROTO : &CREATEPROTO; } /* Default block notify routine * Accepts: reason for calling * data * Returns: data */ void *mm_blocknotify (int reason,void *data) { void *ret = data; switch (reason) { case BLOCK_SENSITIVE: /* entering sensitive code */ ret = (void *) alarm (0); break; case BLOCK_NONSENSITIVE: /* exiting sensitive code */ if ((unsigned int) data) alarm ((unsigned int) data); break; default: /* ignore all other reasons */ break; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/env_nt.h000066400000000000000000000032651137544547100227240ustar00rootroot00000000000000/* * Program: NT environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 29 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\MAILBOX.LST",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\MAILBOX.TMP",myhomedir ()) /* Note: the \CRAM-MD5.PWD file only works on DOS-based Windows (Win9x/Me) * and not on NT-based Windows (WinNT/2K/XP). If installed on NT-based * Windows, servers will advertise CRAM-MD5 authentication but it will * never succeed. */ #define MD5ENABLE "\\cram-md5.pwd" #define L_SET SEEK_SET /* Function prototypes */ #include "env.h" void rfc822_fixed_date (char *date); long env_init (char *user,char *home); int check_nt (void); char *win_homedir (char *user); static char *defaultDrive (void); char *myusername_full (unsigned long *flags); #define MU_LOGGEDIN 0 #define MU_NOTLOGGEDIN 1 #define MU_ANONYMOUS 2 #define myusername() \ myusername_full (NIL) char *sysinbox (); char *mailboxdir (char *dst,char *dir,char *name); int lockname (char *lock,char *fname,int op); char *lockdir (char *lock,char *first,char *last); void unlockfd (int fd,char *lock); long safe_write (int fd,char *buf,long nbytes); void *mm_blocknotify (int reason,void *data); long random (); #if _MSC_VER < 700 #define getpid random #endif tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/fdstring.c000066400000000000000000000050171137544547100232430ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "mail.h" #include "osdep.h" #include "misc.h" #include "fdstring.h" /* String driver for fd stringstructs */ static void fd_string_init (STRING *s,void *data,unsigned long size); static char fd_string_next (STRING *s); static void fd_string_setpos (STRING *s,unsigned long i); STRINGDRIVER fd_string = { fd_string_init, /* initialize string structure */ fd_string_next, /* get next byte in string structure */ fd_string_setpos /* set position in string structure */ }; /* Initialize string structure for fd stringstruct * Accepts: string structure * pointer to string * size of string */ static void fd_string_init (STRING *s,void *data,unsigned long size) { FDDATA *d = (FDDATA *) data; s->data = (void *) d->fd; /* note fd */ s->data1 = d->pos; /* note file offset */ s->size = size; /* note size */ s->curpos = s->chunk = d->chunk; s->chunksize = (unsigned long) d->chunksize; s->offset = 0; /* initial position */ /* and size of data */ s->cursize = min (s->chunksize,size); /* move to that position in the file */ lseek (d->fd,d->pos,L_SET); read (d->fd,s->chunk,(size_t) s->cursize); } /* Get next character from fd stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ static char fd_string_next (STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for fd stringstruct * Accepts: string structure * new position */ static void fd_string_setpos (STRING *s,unsigned long i) { if (i > s->size) i = s->size; /* don't permit setting beyond EOF */ s->offset = i; /* set new offset */ s->curpos = s->chunk; /* reset position */ /* set size of data */ if (s->cursize = min (s->chunksize,SIZE (s))) { /* move to that position in the file */ lseek ((int) s->data,s->data1 + s->offset,L_SET); read ((int) s->data,s->curpos,(size_t) s->cursize); } } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/fdstring.h000066400000000000000000000015131137544547100232450ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Driver-dependent data passed to init method */ typedef struct fd_data { int fd; /* file descriptor */ unsigned long pos; /* initial position */ char *chunk; /* I/O buffer chunk */ unsigned long chunksize; /* I/O buffer chunk length */ } FDDATA; extern STRINGDRIVER fd_string; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/fs_nt.c000066400000000000000000000022341137544547100225320ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { free (*block); *block = NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/ftl_nt.c000066400000000000000000000013271137544547100227110ustar00rootroot00000000000000/* * Program: DOS/VMS/TOPS-20 crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/ip4_nt.c000066400000000000000000000116341137544547100226220ustar00rootroot00000000000000/* * Program: UNIX IPv4 routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 18 December 2003 * Last Edited: 26 May 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SADRLEN sizeof (struct sockaddr) #define SADR4(sadr) ((struct sockaddr_in *) sadr) #define SADR4LEN sizeof (struct sockaddr_in) #define SADR4ADR(sadr) SADR4 (sadr)->sin_addr #define ADR4LEN sizeof (struct in_addr) #define SADR4PORT(sadr) SADR4 (sadr)->sin_port /* IP abstraction layer */ char *ip_sockaddrtostring (struct sockaddr *sadr); long ip_sockaddrtoport (struct sockaddr *sadr); void *ip_stringtoaddr (char *text,size_t *len,int *family); struct sockaddr *ip_newsockaddr (size_t *len); struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len); char *ip_sockaddrtoname (struct sockaddr *sadr); void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next); /* Return IP address string from socket address * Accepts: socket address * Returns: IP address as name string */ char *ip_sockaddrtostring (struct sockaddr *sadr) { return (sadr->sa_family == PF_INET) ? inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4"; } /* Return port from socket address * Accepts: socket address * Returns: port number or -1 if can't determine it */ long ip_sockaddrtoport (struct sockaddr *sadr) { return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1; } /* Return IP address from string * Accepts: name string * pointer to returned length * pointer to returned address family * Returns: address if valid, length and family updated, or NIL */ void *ip_stringtoaddr (char *text,size_t *len,int *family) { unsigned long adr; struct in_addr *ret; /* get address */ if ((adr = inet_addr (text)) == -1) ret = NIL; else { /* make in_addr */ ret = (struct in_addr *) fs_get (*len = ADR4LEN); *family = AF_INET; /* IPv4 */ ret->s_addr = adr; /* set address */ } return (void *) ret; } /* Create a maximum-size socket address * Accepts: pointer to return maximum socket address length * Returns: new, empty socket address of maximum size */ struct sockaddr *ip_newsockaddr (size_t *len) { return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN); } /* Stuff a socket address * Accepts: address family * IPv4 address * length of address (always 4 in IPv4) * port number * pointer to return socket address length * Returns: socket address or NIL if error */ struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len) { struct sockaddr *sadr = ip_newsockaddr (len); switch (family) { /* build socket address based upon family */ case AF_INET: /* IPv4 */ sadr->sa_family = PF_INET; /* copy host address */ memcpy (&SADR4ADR (sadr),adr,adrlen); /* copy port number in network format */ SADR4PORT (sadr) = htons (port); *len = SADR4LEN; break; default: /* non-IP?? */ sadr->sa_family = PF_UNSPEC; break; } return sadr; } /* Return name from socket address * Accepts: socket address * Returns: canonical name for that address or NIL if none */ char *ip_sockaddrtoname (struct sockaddr *sadr) { struct hostent *he; return ((sadr->sa_family == PF_INET) && (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ? (char *) he->h_name : NIL; } /* Return address from name * Accepts: name or NIL to return next address * pointer to previous/returned length * pointer to previous/returned address family * pointer to previous/returned canonical name * pointer to previous/return state for next-address calls * Returns: address with length/family/canonical updated if needed, or NIL */ void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next) { char **adl,tmp[MAILTMPLEN]; struct hostent *he; if (name) { /* first lookup? */ /* yes, do case-independent lookup */ if ((strlen (name) < MAILTMPLEN) && (he = gethostbyname (lcase (strcpy (tmp,name))))) { adl = he->h_addr_list; if (len) *len = he->h_length; if (family) *family = he->h_addrtype; if (canonical) *canonical = (char *) he->h_name; if (next) *next = (void *) adl; } else { /* error */ adl = NIL; if (len) *len = 0; if (family) *family = 0; if (canonical) *canonical = NIL; if (next) *next = NIL; } } /* return next in series */ else if (next && (adl = (char **) *next)) *next = ++adl; else adl = NIL; /* failure */ return adl ? (void *) *adl : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/ip6_nt.c000066400000000000000000000205041137544547100226200ustar00rootroot00000000000000/* * Program: UNIX IPv6 routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 18 December 2003 * Last Edited: 12 October 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* * There is some amazingly bad design in IPv6 sockets. * * Supposedly, the new getnameinfo() and getaddrinfo() functions create an * abstraction that is not dependent upon IPv4 or IPv6. However, the * definition of getnameinfo() requires that the caller pass the length of * the sockaddr instead of deriving it from sa_family. The man page says * that there's an sa_len member in the sockaddr, but actually there isn't. * This means that any caller to getnameinfo() and getaddrinfo() has to know * the size for the protocol family used by that sockaddr. * * The new sockaddr_in6 is bigger than the generic sockaddr (which is what * connect(), accept(), bind(), getpeername(), getsockname(), etc. expect). * Rather than increase the size of sockaddr, there's a new sockaddr_storage * which is only usable for allocating space. */ #define SADRLEN sizeof (struct sockaddr_storage) #define SADR4(sadr) ((struct sockaddr_in *) sadr) #define SADR4LEN sizeof (struct sockaddr_in) #define SADR4ADR(sadr) SADR4 (sadr)->sin_addr #define ADR4LEN sizeof (struct in_addr) #define SADR4PORT(sadr) SADR4 (sadr)->sin_port #define SADR6(sadr) ((struct sockaddr_in6 *) sadr) #define SADR6LEN sizeof (struct sockaddr_in6) #define SADR6ADR(sadr) SADR6 (sadr)->sin6_addr #define ADR6LEN sizeof (struct in6_addr) #define SADR6PORT(sadr) SADR6 (sadr)->sin6_port /* IP abstraction layer */ char *ip_sockaddrtostring (struct sockaddr *sadr); long ip_sockaddrtoport (struct sockaddr *sadr); void *ip_stringtoaddr (char *text,size_t *len,int *family); struct sockaddr *ip_newsockaddr (size_t *len); struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len); char *ip_sockaddrtoname (struct sockaddr *sadr); void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next); /* Return IP address string from socket address * Accepts: socket address * Returns: IP address as name string */ char *ip_sockaddrtostring (struct sockaddr *sadr) { static char tmp[NI_MAXHOST]; switch (sadr->sa_family) { case PF_INET: /* IPv4 */ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST)) return tmp; break; case PF_INET6: /* IPv6 */ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST)) return tmp; break; } return "NON-IP"; } /* Return port from socket address * Accepts: socket address * Returns: port number or -1 if can't determine it */ long ip_sockaddrtoport (struct sockaddr *sadr) { switch (sadr->sa_family) { case PF_INET: return ntohs (SADR4PORT (sadr)); case PF_INET6: return ntohs (SADR6PORT (sadr)); } return -1; } /* Return IP address from string * Accepts: name string * pointer to returned length * pointer to returned address family * Returns: address if valid, length and family updated, or NIL */ void *ip_stringtoaddr (char *text,size_t *len,int *family) { char tmp[MAILTMPLEN]; static struct addrinfo *hints; struct addrinfo *ai; void *adr = NIL; if (!hints) { /* hints set up yet? */ hints = (struct addrinfo *) /* one-time setup */ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo)); hints->ai_family = AF_UNSPEC;/* allow any address family */ hints->ai_socktype = SOCK_STREAM; /* numeric name only */ hints->ai_flags = AI_NUMERICHOST; } /* case-independent lookup */ if (text && (strlen (text) < MAILTMPLEN) && (!getaddrinfo (lcase (strcpy (tmp,text)),NIL,hints,&ai))) { switch (*family = ai->ai_family) { case AF_INET: /* IPv4 */ adr = fs_get (*len = ADR4LEN); memcpy (adr,(void *) &SADR4ADR (ai->ai_addr),*len); break; case AF_INET6: /* IPv6 */ adr = fs_get (*len = ADR6LEN); memcpy (adr,(void *) &SADR6ADR (ai->ai_addr),*len); break; } freeaddrinfo (ai); /* free addrinfo */ } return adr; } /* Create a maximum-size socket address * Accepts: pointer to return maximum socket address length * Returns: new, empty socket address of maximum size */ struct sockaddr *ip_newsockaddr (size_t *len) { return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN); } /* Stuff a socket address * Accepts: address family * IPv4 address * length of address * port number * pointer to return socket address length * Returns: socket address */ struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len) { struct sockaddr *sadr = ip_newsockaddr (len); switch (family) { /* build socket address based upon family */ case AF_INET: /* IPv4 */ sadr->sa_family = PF_INET; /* copy host address */ memcpy (&SADR4ADR (sadr),adr,adrlen); /* copy port number in network format */ SADR4PORT (sadr) = htons (port); *len = SADR4LEN; break; case AF_INET6: /* IPv6 */ sadr->sa_family = PF_INET6; /* copy host address */ memcpy (&SADR6ADR (sadr),adr,adrlen); /* copy port number in network format */ SADR6PORT (sadr) = htons (port); *len = SADR6LEN; break; default: /* non-IP?? */ sadr->sa_family = PF_UNSPEC; break; } return sadr; } /* Return name from socket address * Accepts: socket address * Returns: canonical name for that address or NIL if none */ char *ip_sockaddrtoname (struct sockaddr *sadr) { static char tmp[NI_MAXHOST]; switch (sadr->sa_family) { case PF_INET: /* IPv4 */ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD)) return tmp; break; case PF_INET6: /* IPv6 */ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD)) return tmp; break; } return NIL; } /* Return address from name * Accepts: name or NIL to return next address * pointer to previous/returned length * pointer to previous/returned address family * pointer to previous/returned canonical name * pointer to previous/return state for next-address calls * Returns: address with length/family/canonical updated if needed, or NIL */ void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next) { struct addrinfo *cur = NIL; static struct addrinfo *hints; static struct addrinfo *ai = NIL; static char lcname[MAILTMPLEN]; if (!hints) { /* hints set up yet? */ hints = (struct addrinfo *) /* one-time setup */ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo)); /* allow any address family */ hints->ai_family = AF_UNSPEC; hints->ai_socktype = SOCK_STREAM; /* need canonical name */ hints->ai_flags = AI_CANONNAME; } if (name) { /* name supplied? */ if (ai) { freeaddrinfo (ai); /* free old addrinfo */ ai = NIL; } /* case-independent lookup */ if ((strlen (name) < MAILTMPLEN) && (!getaddrinfo (lcase (strcpy (lcname,name)),NIL,hints,&ai))) { cur = ai; /* current block */ if (canonical) /* set canonical name */ *canonical = cur->ai_canonname ? cur->ai_canonname : lcname; /* remember as next block */ if (next) *next = (void *) ai; } else { /* error */ cur = NIL; if (len) *len = 0; if (family) *family = 0; if (canonical) *canonical = NIL; if (next) *next = NIL; } } /* return next in series */ else if (next && (cur = ((struct addrinfo *) *next)->ai_next)) { *next = cur; /* set as last address */ /* set canonical in case changed */ if (canonical && cur->ai_canonname) *canonical = cur->ai_canonname; } if (cur) { /* got data? */ if (family) *family = cur->ai_family; switch (cur->ai_family) { case AF_INET: if (len) *len = ADR4LEN; return (void *) &SADR4ADR (cur->ai_addr); case AF_INET6: if (len) *len = ADR6LEN; return (void *) &SADR6ADR (cur->ai_addr); } } if (len) *len = 0; /* error return */ return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/kerb_mit.c000066400000000000000000000030311137544547100232110ustar00rootroot00000000000000/* * Program: MIT Kerberos routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 4 March 2003 * Last Edited: 17 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define PROTOTYPE(x) x #include #include long kerberos_server_valid (void); long kerberos_try_kinit (OM_uint32 error); char *kerberos_login (char *user,char *authuser,int argc,char *argv[]); /* Kerberos server valid check * Returns: T if have keytab, NIL otherwise */ long kerberos_server_valid () { return NIL; } /* Kerberos check for missing or expired credentials * Returns: T if should suggest running kinit, NIL otherwise */ long kerberos_try_kinit (OM_uint32 error) { switch (error) { case KRB5KRB_AP_ERR_TKT_EXPIRED: case KRB5_FCC_NOFILE: /* MIT */ case KRB5_CC_NOTFOUND: /* Heimdal */ return LONGT; } return NIL; } /* Kerberos server log in * Accepts: authorization ID as user name * authentication ID as Kerberos principal * argument count * argument vector * Returns: logged in user name if logged in, NIL otherwise */ char *kerberos_login (char *user,char *authuser,int argc,char *argv[]) { return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/kerb_w2k.c000066400000000000000000000536771137544547100231500ustar00rootroot00000000000000/* * Program: GSSAPI Kerberos Shim 5 for Windows 2000/XP IMAP Toolkit * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 6 March 2000 * Last Edited: 3 May 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* The purpose of this module is to be a shim, so that the auth_gss.c module * (written for MIT Kerberos) will compile, link, and run with SSPI Kerberos * on Windows 2000 systems. * There is no attempt whatsoever to make this be a complete implementation * of GSSAPI. A number of shortcuts were taken that a real GSSAPI * implementation for SSPI can't do. * Nor is there any attempt to make the types identical with MIT Kerberos; * you can't link this library with object files compiled with the MIT * Kerberos .h files. */ /* GSSAPI generic definitions */ #define SECURITY_WIN32 #include /* GSSAPI types for which we use SSPI equivalent types */ typedef ULONG OM_uint32; typedef PCredHandle gss_cred_id_t; typedef ULONG gss_cred_usage_t; typedef PCtxtHandle gss_ctx_id_t; typedef SEC_CHAR * gss_name_t; typedef ULONG gss_qop_t; /* Major status codes */ #define GSS_S_COMPLETE SEC_E_OK #define GSS_S_BAD_MECH SEC_E_SECPKG_NOT_FOUND #define GSS_S_CONTINUE_NEEDED SEC_I_CONTINUE_NEEDED #define GSS_S_CREDENTIALS_EXPIRED SEC_E_CERT_EXPIRED #define GSS_S_FAILURE SEC_E_INTERNAL_ERROR #define GSS_S_NO_CRED SEC_E_NO_CREDENTIALS #define GSS_S_NO_CONTEXT SEC_E_INVALID_HANDLE /* Flag bits for context-level services */ #define GSS_C_DELEG_FLAG ISC_REQ_DELEGATE #define GSS_C_MUTUAL_FLAG ISC_REQ_MUTUAL_AUTH #define GSS_C_REPLAY_FLAG ISC_REQ_REPLAY_DETECT #define GSS_C_SEQUENCE_FLAG ISC_REQ_SEQUENCE_DETECT #define GSS_C_CONF_FLAG ISC_REQ_CONFIDENTIALITY /* Credential usage options */ #define GSS_C_BOTH SECPKG_CRED_BOTH #define GSS_C_INITIATE SECPKG_CRED_OUTBOUND #define GSS_C_ACCEPT SECPKG_CRED_INBOUND /* Major status codes defined by shim */ #define GSS_S_BAD_BINDINGS 100 #define GSS_S_BAD_NAME 101 #define GSS_S_BAD_NAMETYPE 102 #define GSS_S_BAD_STATUS 103 /* GSSAPI types as used in GSSAPI */ /* Buffer */ typedef struct gss_buffer_desc_struct { size_t length; void *value; } gss_buffer_desc,*gss_buffer_t; /* Object identifier */ typedef struct gss_OID_desc_struct { OM_uint32 length; void *elements; } gss_OID_desc,*gss_OID; typedef struct gss_OID_set_desc_struct { size_t count; gss_OID elements; } gss_OID_set_desc,*gss_OID_set; /* Unused, but needed in prototypes */ typedef void * gss_channel_bindings_t; /* Default constants */ #define GSS_C_EMPTY_BUFFER {0,NIL} #define GSS_C_NO_BUFFER ((gss_buffer_t) NIL) #define GSS_C_NO_OID ((gss_OID) NIL) #define GSS_C_NO_CONTEXT ((gss_ctx_id_t) NIL) #define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) NIL) #define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) NIL) #define GSS_C_QOP_DEFAULT NIL /* Status code types for gss_display_status */ #define GSS_C_GSS_CODE 1 #define GSS_C_MECH_CODE 2 /* GSSAPI constants */ const gss_OID gss_nt_service_name; #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name const gss_OID gss_mech_krb5; const gss_OID_set gss_mech_set_krb5; /* GSSAPI prototypes */ OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name,gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags,OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle); OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name, OM_uint32 time_req,gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs,OM_uint32 *time_rec); OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token); OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type); OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value, int status_type,gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string); OM_uint32 gss_import_name (OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type,gss_name_t *output_name); OM_uint32 gss_init_sec_context (OM_uint32 *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name,gss_OID mech_type, OM_uint32 req_flags,OM_uint32 time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token,OM_uint32 *ret_flags, OM_uint32 *time_rec); OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer); OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle); OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name); OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle, int conf_req_flag,gss_qop_t qop_req, gss_buffer_t input_message_buffer,int *conf_state, gss_buffer_t output_message_buffer); OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer,int *conf_state, gss_qop_t *qop_state); /* Kerberos definitions */ long kerberos_server_valid (void); long kerberos_try_kinit (OM_uint32 error); char *kerberos_login (char *user,char *authuser,int argc,char *argv[]); #define STRING WINSTRING /* conflict with mail.h */ #include /* GSSAPI build-in object identifiers */ static gss_OID_desc oids[] = { /* stupid C language makes this necessary */ {10,"\052\206\110\206\367\022\001\002\001\004"}, {9,"\052\206\110\206\367\022\001\002\002"} }; /* stupid C language ditto */ static gss_OID_set_desc oidsets[] = { {1,(gss_OID) oids+1} }; /* these are the real OIDs */ const gss_OID gss_nt_service_name = oids+0; const gss_OID gss_mech_krb5 = oids+1; const gss_OID_set gss_mech_set_krb5 = oidsets+0; /* Other globals */ /* substitute for GSS_C_NO_CREDENTIAL */ static gss_cred_id_t gss_default_cred = NIL; /* GSSAPI import name (convert to full service principal name) * Accepts: pointer to return minor status * buffer containining input name * type of input name * pointer to return output internal name * Returns: major status, always */ OM_uint32 gss_import_name (OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type,gss_name_t *output_name) { OM_uint32 major_status = GSS_S_COMPLETE; TimeStamp expiry; static CredHandle gss_cred; char *s,tmp[MAILTMPLEN]; *minor_status = 0; /* never any minor status */ if (!gss_default_cred) { /* default credentials set up yet? */ if (AcquireCredentialsHandle/* no, acquire them now */ (NIL,MICROSOFT_KERBEROS_NAME_A,SECPKG_CRED_OUTBOUND,NIL,NIL,NIL,NIL, &gss_cred,&expiry) != SEC_E_OK) return GSS_S_FAILURE; /* have default credentials now */ gss_default_cred = &gss_cred; } /* must be the gss_nt_service_name format */ if (input_name_type != gss_nt_service_name) major_status = GSS_S_BAD_NAMETYPE; /* name must be of sane length */ else if (input_name_buffer->length > (MAILTMPLEN/2)) major_status = GSS_S_BAD_NAME; else { /* copy name */ memcpy (tmp,input_name_buffer->value,input_name_buffer->length); tmp[input_name_buffer->length] = '\0'; if (s = strchr (tmp,'@')) { /* find service/host/delimiter */ *s = '/'; /* convert to full service principal name */ *output_name = cpystr (tmp); } else major_status = GSS_S_BAD_NAME; } return major_status; } /* GSSAPI Initialize security context * Accepts: pointer to return minor status * claimant credential handle * context (NIL means "none assigned yet") * desired principal * desired mechanisms * required context attributes * desired lifetime * input channel bindings * input token buffer * pointer to return mechanism type * buffer to return output token * pointer to return flags * pointer to return context lifetime * Returns: major status, always */ OM_uint32 gss_init_sec_context (OM_uint32 *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name,gss_OID mech_type, OM_uint32 req_flags,OM_uint32 time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token,OM_uint32 *ret_flags, OM_uint32 *time_rec) { OM_uint32 i; OM_uint32 major_status; TimeStamp expiry; SecBuffer ibuf[1],obuf[1]; SecBufferDesc ibufs,obufs; *minor_status = 0; /* never any minor status */ /* error if non-default time requested */ if (time_req) return GSS_S_FAILURE; if (mech_type && memcmp (mech_type,gss_mech_krb5,sizeof (gss_OID))) return GSS_S_BAD_MECH; /* ditto if any channel bindings */ if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) return GSS_S_BAD_BINDINGS; /* apply default credential if necessary */ if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) claimant_cred_handle = gss_default_cred; /* create output buffer storage as needed */ req_flags |= ISC_REQ_ALLOCATE_MEMORY; /* make output buffer */ obuf[0].BufferType = SECBUFFER_TOKEN; obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL; /* output buffer descriptor */ obufs.ulVersion = SECBUFFER_VERSION; obufs.cBuffers = 1; obufs.pBuffers = obuf; /* first time caller? */ if (*context_handle == GSS_C_NO_CONTEXT) { /* yes, set up output context handle */ PCtxtHandle ctx = (PCtxtHandle) fs_get (sizeof (CtxtHandle)); major_status = InitializeSecurityContext (claimant_cred_handle,NIL, target_name,req_flags,0, SECURITY_NETWORK_DREP,NIL,0,ctx, &obufs, ret_flags ? ret_flags : &i, &expiry); *context_handle = ctx; /* return updated context */ } else { /* no, make SSPI buffer from GSSAPI buffer */ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; ibuf[0].cbBuffer = input_token->length; ibuf[0].pvBuffer = input_token->value; /* input buffer descriptor */ ibufs.ulVersion = SECBUFFER_VERSION; ibufs.cBuffers = 1; ibufs.pBuffers = ibuf; major_status = InitializeSecurityContext (claimant_cred_handle, *context_handle,target_name, req_flags,0, SECURITY_NETWORK_DREP,&ibufs,0, *context_handle,&obufs, ret_flags ? ret_flags : &i, &expiry); } /* return output */ output_token->value = obuf[0].pvBuffer; output_token->length = obuf[0].cbBuffer; /* in case client wanted lifetime returned */ if (time_rec) *time_rec = expiry.LowPart; return major_status; } /* GSSAPI display status text * Accepts: pointer to return minor status * status to display * status type * message context for continuation * buffer to write status string * Returns: major status, always */ OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value, int status_type,gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string) { char *s,tmp[MAILTMPLEN]; *minor_status = 0; /* never any minor status */ if (*message_context) return GSS_S_FAILURE; switch (status_type) { /* what type of status code? */ case GSS_C_GSS_CODE: /* major_status */ switch (status_value) { /* analyze status value */ case GSS_S_FAILURE: s = "Unspecified failure"; break; case GSS_S_CREDENTIALS_EXPIRED: s = "Credentials expired"; break; case GSS_S_BAD_BINDINGS: s = "Bad bindings"; break; case GSS_S_BAD_MECH: s = "Bad mechanism type"; break; case GSS_S_BAD_NAME: s = "Bad name"; break; case GSS_S_BAD_NAMETYPE: s = "Bad name type"; break; case GSS_S_BAD_STATUS: s = "Bad status"; break; case GSS_S_NO_CONTEXT: s = "Invalid context handle"; break; case GSS_S_NO_CRED: s = "Unable to authenticate to Kerberos service"; mail_parameters (NIL,DISABLE_AUTHENTICATOR,"GSSAPI"); break; case SEC_E_NO_AUTHENTICATING_AUTHORITY: s = "No authenticating authority"; break; case SEC_E_TARGET_UNKNOWN: s = "Destination server unknown to Kerberos service"; break; default: sprintf (s = tmp,"SSPI code %lx",status_value); } break; case GSS_C_MECH_CODE: /* minor status - drop into default */ default: return GSS_S_BAD_STATUS; /* bad status type */ } /* return status string */ status_string->length = strlen (status_string->value = cpystr (s)); return GSS_S_COMPLETE; } /* GSSAPI delete security context * Accepts: pointer to return minor status * context to delete * output context token * Returns: major status, always */ OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token) { OM_uint32 major_status; *minor_status = 0; /* never any minor status */ /* output token not supported */ major_status = output_token ? GSS_S_FAILURE : DeleteSecurityContext (*context_handle); fs_give ((void **) context_handle); return major_status; } /* GSSAPI release buffer * Accepts: pointer to return minor status * buffer to release * Returns: GSS_S_COMPLETE, always */ OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer) { *minor_status = 0; /* never any minor status */ fs_give (&buffer->value); return GSS_S_COMPLETE; } /* GSSAPI release name * Accepts: pointer to return minor status * pointer to name to release * Returns: GSS_S_COMPLETE, always */ OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name) { *minor_status = 0; /* never any minor status */ fs_give (input_name); return GSS_S_COMPLETE; } /* GSSAPI wrap data * Accepts: pointer to return minor status * context handle * requested confidentiality * requested quality of protection * input message buffer * pointer to return confidentiality state * output message buffer * Returns: major status, always */ OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle, int conf_req_flag,gss_qop_t qop_req, gss_buffer_t input_message_buffer,int *conf_state, gss_buffer_t output_message_buffer) { OM_uint32 major_status; SecBuffer buf[3]; SecBufferDesc bufs; SecPkgContext_Sizes sizes; *minor_status = NIL; /* never any minor status */ *conf_state = conf_req_flag; /* same as requested */ if ((major_status = /* get trailer and padding sizes */ QueryContextAttributes (context_handle,SECPKG_ATTR_SIZES,&sizes)) == SEC_E_OK) { /* create big enough output buffer */ output_message_buffer->value = fs_get (sizes.cbSecurityTrailer + input_message_buffer->length + sizes.cbBlockSize); /* MSDN claims that for EncryptMessage() in Kerberos, you need an * uninitialized SECBUFFER_STREAM_HEADER; a SECBUFFER_DATA that "contains * the message to be encrypted. The message is encrypted in place, * overwirting the original contents of its buffer"; an uninitialized * SECBUFFER_STREAM_TRAILER, and an uninitialized SECBUFFER_EMPTY. I've * never been able to get it to work that way. */ bufs.cBuffers = 3; /* set up buffer descriptor */ bufs.pBuffers = buf; bufs.ulVersion = SECBUFFER_VERSION; buf[0].BufferType = SECBUFFER_TOKEN; buf[0].pvBuffer = output_message_buffer->value; buf[0].cbBuffer = sizes.cbSecurityTrailer; /* I/O buffer */ buf[1].BufferType = SECBUFFER_DATA; buf[1].pvBuffer = ((char *) buf[0].pvBuffer) + buf[0].cbBuffer; buf[1].cbBuffer = input_message_buffer->length; memcpy (buf[1].pvBuffer,input_message_buffer->value,buf[1].cbBuffer); buf[2].BufferType = SECBUFFER_PADDING; buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer; buf[2].cbBuffer = sizes.cbBlockSize; if ((major_status = EncryptMessage (context_handle,qop_req,&bufs,0)) == GSS_S_COMPLETE) { /* slide data as necessary (how annoying!) */ unsigned long i = sizes.cbSecurityTrailer - buf[0].cbBuffer; if (i) buf[1].pvBuffer = memmove (((char *) buf[0].pvBuffer) + buf[0].cbBuffer, buf[1].pvBuffer,buf[1].cbBuffer); if (i += (input_message_buffer->length - buf[1].cbBuffer)) buf[1].pvBuffer = memmove (((char *)buf[1].pvBuffer) + buf[1].cbBuffer, buf[2].pvBuffer,buf[2].cbBuffer); output_message_buffer->length = buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer; } else fs_give (&output_message_buffer->value); } return major_status; /* return status */ } /* GSSAPI unwrap data * Accepts: pointer to return minor status * context handle * input message buffer * output message buffer * pointer to return confidentiality state * pointer to return quality of protection * Returns: major status, always */ OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer,int *conf_state, gss_qop_t *qop_state) { OM_uint32 major_status; SecBuffer buf[2]; SecBufferDesc bufs; *minor_status = NIL; /* never any minor status */ *conf_state = NIL; /* or confidentiality state */ /* MSDN implies that all that is needed for DecryptMessage() in Kerberos * is a single SECBUFFER_DATA which "contains the encrypted message. The * encrypted message is decrypted in place, overwriting the original * contents of its buffer." I've never been able to get it to work without * using a SECBUFFER_STREAM for input and an uninitialized SECBUFFER_DATA * for output. * It *does* overwrite the input buffer, but not at the same point; e.g. * with an input pointer of 0xa140a8 and size of 53, the output ends up * at 0xa140d5 and size of 4. */ bufs.cBuffers = 2; /* set up buffer descriptor */ bufs.pBuffers = buf; bufs.ulVersion = SECBUFFER_VERSION; /* input buffer */ buf[0].BufferType = SECBUFFER_STREAM; buf[0].pvBuffer = input_message_buffer->value; buf[0].cbBuffer = input_message_buffer->length; /* output buffer */ buf[1].BufferType = SECBUFFER_DATA; buf[1].pvBuffer = NIL; buf[1].cbBuffer = 0; /* decrypt and copy to output buffer */ if ((major_status = DecryptMessage (context_handle,&bufs,0,qop_state)) == SEC_E_OK) memcpy (output_message_buffer->value = fs_get (buf[1].cbBuffer), buf[1].pvBuffer,output_message_buffer->length = buf[1].cbBuffer); return major_status; /* return status */ } /* From here on are server-only functions, currently unused */ /* GSSAPI acquire credentials * Accepts: pointer to return minor status * desired principal * desired lifetime * desired mechanisms * credentials usage * pointer to return credentials handle * pointer to return mechanisms * pointer to return lifetime * Returns: GSS_S_FAILURE, always */ OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name, OM_uint32 time_req,gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs,OM_uint32 *time_rec) { *minor_status = 0; /* never any minor status */ return GSS_S_FAILURE; /* server only */ } /* GSSAPI release credentials * Accepts: pointer to return minor status * credentials handle to free * Returns: GSS_S_COMPLETE, always */ OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle) { *minor_status = 0; /* never any minor status */ return GSS_S_FAILURE; /* server only */ } /* GSSAPI Accept security context * Accepts: pointer to return minor status * context * acceptor credentials * input token buffer * input channel bindings * pointer to return source name * pointer to return mechanism type * buffer to return output token * pointer to return flags * pointer to return context lifetime * pointer to return delegated credentials * Returns: GSS_S_FAILURE, always */ OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name,gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags,OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle) { *minor_status = 0; /* never any minor status */ return GSS_S_FAILURE; /* server only */ } /* GSSAPI return printable name * Accepts: pointer to return minor status * internal name * buffer to return output name * output name type * Returns: GSS_S_FAILURE, always */ OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type) { *minor_status = 0; /* never any minor status */ return GSS_S_FAILURE; /* server only */ } /* Kerberos server valid check * Returns: T if have keytab, NIL otherwise */ long kerberos_server_valid () { return NIL; } /* Kerberos check for missing or expired credentials * Returns: T if should suggest running kinit, NIL otherwise */ long kerberos_try_kinit (OM_uint32 error) { return NIL; } /* Kerberos server log in * Accepts: authorization ID as user name * authentication ID as Kerberos principal * argument count * argument vector * Returns: logged in user name if logged in, NIL otherwise */ char *kerberos_login (char *user,char *authuser,int argc,char *argv[]) { return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/mailfile.h000066400000000000000000000011021137544547100232010ustar00rootroot00000000000000/* * Program: Mail Spool file name * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 8 February 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define MAILFILE "C:\\WINSMTP\\%s.MBX" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/makefile.nt000066400000000000000000000056301137544547100234000ustar00rootroot00000000000000# Program: Portable C client makefile -- NT version # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 6 July 2004 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. EXTRAAUTHENTICATORS = EXTRADRIVERS = EXTRACFLAGS = AUTHENTICATORS = md5 pla log DRIVERS = imap nntp pop3 mbx mtx tenex unix CREATEDRIVER = mbx APPENDDRIVER = unix CFLAGS = /MT /W3 /Ox /DWIN32 /D_WIN32_WINNT=0x0400 -nologo /I.. $(EXTRACFLAGS) CC = cl CCLIENTLIB = cclient.lib all: $(CCLIENTLIB) .c.obj: $(CC) -c $(CFLAGS) $*.c osdep.h: os_nt.h copy os_nt.h osdep.h drivers $(EXTRADRIVERS) $(DRIVERS) dummy setproto $(CREATEDRIVER) $(APPENDDRIVER) echo ssl_onceonlyinit (); >> linkage.c mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) ip_nt.c: ip4_nt.c copy ip4_nt.c ip_nt.c mail.obj: mail.h misc.h osdep.h mail.c misc.obj: mail.h misc.h misc.c fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c rfc822.obj: mail.h rfc822.h misc.h rfc822.c smanager.obj: mail.h misc.h smanager.c utf8.obj: mail.h misc.h osdep.h utf8.h imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c os_nt.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \ os_nt.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \ mailfile.h auth_md5.c auth_pla.c auth_log.c mbxnt.obj: mail.h mbxnt.h misc.h osdep.h mbxnt.c mtxnt.obj: mail.h misc.h osdep.h mtxnt.c tenexnt.obj: mail.h misc.h osdep.h tenexnt.c unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c pseudo.obj: pseudo.h $(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj if exist $(CCLIENTLIB) del $(CCLIENTLIB) LIB /NOLOGO /OUT:cclient.lib \ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj clean: del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/makefile.ntk000066400000000000000000000057121137544547100235540ustar00rootroot00000000000000# Program: Portable C client makefile -- NT version + Kerberos # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 6 July 2004 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. EXTRAAUTHENTICATORS = EXTRADRIVERS = EXTRACFLAGS = AUTHENTICATORS = gss md5 pla log DRIVERS = imap nntp pop3 mbx mtx tenex unix CREATEDRIVER = mbx APPENDDRIVER = unix CFLAGS = /MT /W3 /Ox /DWIN32 /D_WIN32_WINNT=0x0400 -nologo /I\k5\include $(EXTRACFLAGS) CC = cl CCLIENTLIB = cclient.lib all: $(CCLIENTLIB) .c.obj: $(CC) -c $(CFLAGS) $*.c osdep.h: os_nt.h copy os_nt.h osdep.h drivers $(EXTRADRIVERS) $(DRIVERS) dummy setproto $(CREATEDRIVER) $(APPENDDRIVER) echo ssl_onceonlyinit (); >> linkage.c mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) ip_nt.c: ip4_nt.c copy ip4_nt.c ip_nt.c mail.obj: mail.h misc.h osdep.h mail.c misc.obj: mail.h misc.h misc.c fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c rfc822.obj: mail.h rfc822.h misc.h rfc822.c smanager.obj: mail.h misc.h smanager.c utf8.obj: mail.h misc.h osdep.h utf8.h imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c os_ntk.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \ os_ntk.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_mit.c mbxnt.obj: mail.h mbxnt.h misc.h osdep.h mbxnt.c mtxnt.obj: mail.h misc.h osdep.h mtxnt.c tenexnt.obj: mail.h misc.h osdep.h tenexnt.c unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c pseudo.obj: pseudo.h $(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj if exist $(CCLIENTLIB) del $(CCLIENTLIB) LIB /NOLOGO /OUT:cclient.lib \ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj clean: del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/makefile.w2k000066400000000000000000000056731137544547100234710ustar00rootroot00000000000000# Program: Portable C client makefile -- Windows 2000/XP version # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 6 July 2004 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. IP=6 EXTRAAUTHENTICATORS = EXTRADRIVERS = EXTRACFLAGS = AUTHENTICATORS = gss md5 pla log DRIVERS = imap nntp pop3 mbx mtx tenex unix CREATEDRIVER = mbx APPENDDRIVER = unix CFLAGS = /MT /W3 /Ox /DWIN32 -nologo /I.. $(EXTRACFLAGS) CC = cl CCLIENTLIB = cclient.lib all: $(CCLIENTLIB) .c.obj: $(CC) -c $(CFLAGS) $*.c osdep.h: os_nt.h copy os_nt.h osdep.h drivers $(EXTRADRIVERS) $(DRIVERS) dummy setproto $(CREATEDRIVER) $(APPENDDRIVER) echo ssl_onceonlyinit (); >> linkage.c mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) ip_nt.c: ip$(IP)_nt.c copy ip$(IP)_nt.c ip_nt.c mail.obj: mail.h misc.h osdep.h mail.c misc.obj: mail.h misc.h misc.c fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c rfc822.obj: mail.h rfc822.h misc.h rfc822.c smanager.obj: mail.h misc.h smanager.c utf8.obj: mail.h misc.h osdep.h utf8.h imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c os_w2k.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \ os_w2k.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_w2k.c ssl_none.c \ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_w2k.c mbxnt.obj: mail.h mbxnt.h misc.h osdep.h mbxnt.c mtxnt.obj: mail.h misc.h osdep.h mtxnt.c tenexnt.obj: mail.h misc.h osdep.h tenexnt.c unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c pseudo.obj: pseudo.h $(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj if exist $(CCLIENTLIB) del $(CCLIENTLIB) LIB /NOLOGO /OUT:cclient.lib \ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj clean: del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/mbxnt.c000066400000000000000000001501241137544547100225530ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "mbxnt.h" #include "misc.h" #include "dummy.h" /* MBX I/O stream local data */ typedef struct mbx_local { unsigned int flagcheck: 1; /* if ping should sweep for flags */ unsigned int expok: 1; /* if expunging OK in ping */ unsigned int expunged : 1; /* if one or more expunged messages */ int fd; /* file descriptor for I/O */ int ld; /* lock file descriptor */ int ffuserflag; /* first free user flag */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ char lock[MAILTMPLEN]; /* buffer to write lock name */ } MBXLOCAL; /* Convenient access to local data */ #define LOCAL ((MBXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mbx_valid (char *name); int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp); void *mbx_parameters (long function,void *value); void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mbx_list (MAILSTREAM *stream,char *ref,char *pat); void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mbx_create (MAILSTREAM *stream,char *mailbox); long mbx_delete (MAILSTREAM *stream,char *mailbox); long mbx_rename (MAILSTREAM *stream,char *old,char *newname); long mbx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mbx_open (MAILSTREAM *stream); void mbx_close (MAILSTREAM *stream,long options); void mbx_abort (MAILSTREAM *stream); void mbx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mbx_ping (MAILSTREAM *stream); void mbx_check (MAILSTREAM *stream); void mbx_expunge (MAILSTREAM *stream); void mbx_snarf (MAILSTREAM *stream); long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); long mbx_parse (MAILSTREAM *stream); MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok); unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mbx_update_header (MAILSTREAM *stream); void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags); unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr); unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags); long mbx_flaglock (MAILSTREAM *stream); /* MBX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mbxdriver = { "mbx", /* driver name */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING, /* driver flags */ (DRIVER *) NIL, /* next driver */ mbx_valid, /* mailbox is valid for us */ mbx_parameters, /* manipulate parameters */ mbx_scan, /* scan mailboxes */ mbx_list, /* list mailboxes */ mbx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mbx_create, /* create mailbox */ mbx_delete, /* delete mailbox */ mbx_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mbx_open, /* open mailbox */ mbx_close, /* close mailbox */ mbx_flags, /* fetch message "fast" attributes */ mbx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mbx_header, /* fetch message header */ mbx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mbx_flag, /* modify flags */ mbx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mbx_ping, /* ping mailbox to see if still alive */ mbx_check, /* check for new messages */ mbx_expunge, /* expunge deleted messages */ mbx_copy, /* copy messages to another mailbox */ mbx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mbxproto = {&mbxdriver}; /* MBX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mbx_valid (char *name) { char tmp[MAILTMPLEN]; return mbx_isvalid (NIL,name,tmp) ? &mbxdriver : NIL; } /* MBX mail test for valid mailbox * Accepts: returned stream with valid mailbox keywords * mailbox name * scratch buffer * Returns: T if valid, NIL otherwise */ int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp) { int fd; int ret = NIL; unsigned long i; unsigned char *s,*t,hdr[HDRSIZE]; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = dummy_file (tmp,name)) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG) && ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) >= 0)) { errno = -1; /* bogus format */ if ((read (fd,hdr,HDRSIZE) == HDRSIZE) && (hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8]) && isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (hdr[15]) && isxdigit (hdr[16]) && isxdigit (hdr[17]) && isxdigit (hdr[18]) && isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) && isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) { ret = T; if (stream) { /* stream specified? */ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0, sizeof (MAILSTREAM)); for (i = 0, s = hdr + 25; /* parse user flags */ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (strlen (s) <= MAXUSERFLAG) (*stream)->user_flags[i] = cpystr (s); } } } close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { /* preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (tmp,×); /* set the times */ } } /* in case INBOX but not mbx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MBX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mbx_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_ONETIMEEXPUNGEATPING: if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T; case GET_ONETIMEEXPUNGEATPING: if (value) ret = (void *) (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL); break; } return ret; } /* MBX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MBX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MBX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MBX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mbx_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[HDRSIZE]; long ret = NIL; int i,fd; if (!(s = dummy_file (mbx,mailbox))) { sprintf (mbx,"Can't create %.80s: invalid name",mailbox); mm_log (mbx,ERROR); } /* create underlying file */ else if (dummy_create (stream,s)) { /* done if made directory */ if ((s = strrchr (s,'\\')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else { memset (tmp,'\0',HDRSIZE);/* initialize header */ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012", (unsigned long) time (0)); for (i = 0; i < NUSERFLAGS; ++i) sprintf (s += strlen (s),"%s\015\012", (stream && stream->user_flags[i]) ? stream->user_flags[i] : ""); if (write (fd,tmp,HDRSIZE) != HDRSIZE) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s", mbx,strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ close (fd); /* close file */ } } return ret; } /* MBX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mbx_delete (MAILSTREAM *stream,char *mailbox) { return mbx_rename (stream,mailbox,NIL); } /* MBX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mbx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); mm_log (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,tmp)) ret = NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } else { flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } unlockfd (ld,lock); /* release exclusive parse/append permission */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX"); return ret; /* return success */ } /* MBX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mbx_open (MAILSTREAM *stream) { int fd,ld; short silent; char tmp[MAILTMPLEN]; if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */ if (stream->local) fatal ("mbx recycle stream"); /* canonicalize the mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ mm_log ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->ld = -1; /* no flaglock */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get parse/append permission */ if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock open mailbox",ERROR); return NIL; } flock (LOCAL->fd,LOCK_SH); /* lock the file */ unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */ LOCAL->filetime = 0; /* time not set up yet */ LOCAL->expok = LOCAL->flagcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; silent = stream->silent; /* defer events */ stream->silent = T; if (mbx_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); stream->silent = silent; /* now notify upper level */ mail_exists (stream,stream->nmsgs); mail_recent (stream,stream->recent); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T; /* can we create new user flags? */ return stream; /* return stream to caller */ } /* MBX mail close * Accepts: MAIL stream * close options */ void mbx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ /* do an expunge if requested */ if (options & CL_EXPUNGE) mbx_expunge (stream); else { /* otherwise do a checkpoint to purge */ LOCAL->expok = T; /* possible expunged messages */ mbx_ping (stream); } stream->silent = silent; /* restore previous status */ mbx_abort (stream); } } /* MBX mail abort stream * Accepts: MAIL stream */ void mbx_abort (MAILSTREAM *stream) { if (stream && LOCAL) { /* only if a file is open */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MBX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mbx_flags (MAILSTREAM *stream,char *sequence,long flags) { MESSAGECACHE *elt; unsigned long i; if (mbx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence && !elt->valid) mbx_elt (stream,i,NIL); } /* MBX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i; char *s; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get header position, possibly header */ i = mbx_hdrpos (stream,msgno,length,&s); if (!s) { /* mbx_hdrpos() returned header? */ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */ /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->buf,*length); } s[*length] = '\0'; /* tie off string */ return s; } /* MBX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; char *s = LOCAL->text.data; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = mbx_elt (stream,msgno,NIL); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mbx_update_status (stream,msgno,NIL); mm_flags (stream,msgno); /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } if (!LOCAL) i = 0; /* mbx_flaglock() could have aborted */ /* in case previous text cached */ else if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mbx_hdrpos (stream,msgno,&j,NIL); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* MBX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { struct utimbuf times; struct stat sbuf; /* make sure the update takes */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) { fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; /* update header */ if ((LOCAL->ffuserflag < NUSERFLAGS) && stream->user_flags[LOCAL->ffuserflag]) mbx_update_header (stream); times.actime = time (0); /* make sure read comes after all that */ utime (stream->mailbox,×); unlockfd (LOCAL->ld,LOCAL->lock); LOCAL->ld = -1; } } /* MBX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL); } /* MBX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mbx_ping (MAILSTREAM *stream) { unsigned long i,pos; long ret = NIL; int ld; char lock[MAILTMPLEN]; MESSAGECACHE *elt; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ ret = LONGT; /* assume OK */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ /* allow expunge if permitted at ping */ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T; /* if external modification */ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; /* upgrade to flag checking */ /* new mail or flagcheck handling needed? */ if (((i = (sbuf.st_size - LOCAL->filesize)) || LOCAL->flagcheck || !stream->nmsgs) && ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) { if (LOCAL->flagcheck) { /* sweep mailbox for changed message status */ if (ret = mbx_parse (stream)) { LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ) if (mbx_elt (stream,i,LOCAL->expok)) ++i; LOCAL->flagcheck =NIL;/* got all the updates */ } } else if (i) ret = mbx_parse (stream); unlockfd (ld,lock); /* release shared parse/append permission */ } if (ret) { /* must still be alive */ if (!LOCAL->expunged) /* look for holes if none known yet */ for (i = 1, pos = HDRSIZE; !LOCAL->expunged && (i <= stream->nmsgs); i++, pos += elt->private.special.text.size + elt->rfc822_size) if ((elt = mail_elt (stream,i))->private.special.offset != pos) LOCAL->expunged = T;/* found a hole */ /* burp any holes */ if (LOCAL->expunged && !stream->rdonly) { if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check"); if (i) { /* any space reclaimed? */ LOCAL->expunged = NIL;/* no more pending expunge */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i); mm_log (LOCAL->buf,(long) NIL); } } LOCAL->expok = NIL; /* no more expok */ } } return ret; /* return result of the parse */ } /* MBX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mbx_check (MAILSTREAM *stream) { if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */ if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL); } /* MBX mail expunge mailbox * Accepts: MAIL stream */ void mbx_expunge (MAILSTREAM *stream) { unsigned long nexp,reclaimed; if (!mbx_ping (stream)); /* do nothing if stream dead */ else if (stream->rdonly) /* won't do on readonly files! */ mm_log ("Expunge ignored on readonly mailbox",WARN); /* if expunged any messages */ else if (nexp = mbx_rewrite (stream,&reclaimed,T)) { sprintf (LOCAL->buf,"Expunged %lu messages",nexp); mm_log (LOCAL->buf,(long) NIL); } else if (reclaimed) { /* or if any prior expunged space reclaimed */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed); mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); } /* MBX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; struct utimbuf times; MESSAGECACHE *elt; unsigned long i,j,k,m; long ret = LONGT; int fd,ld; char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (dummy_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); return NIL; } mm_critical (stream); /* go critical */ /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock copy mailbox",ERROR); mm_nocritical (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); mail_date(LOCAL->buf,elt);/* build target header */ /* get target keyword mask */ for (j = elt->user_flags, k = 0; j; ) if (s = stream->user_flags[find_rightmost_bit (&j)]) for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++) if (!compare_cstring (s,t) && (k |= 1 << m)) break; sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-00000000\015\012", elt->rfc822_size,k,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* write target header */ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){ read (LOCAL->fd,LOCAL->buf,j); ret = write (fd,LOCAL->buf,j) >= 0; } } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } /* set atime to now-1 if successful copy */ if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) { for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) { /* mark message deleted */ mbx_elt (stream,i,NIL)->deleted = T; /* recalculate status */ mbx_update_status (stream,i,NIL); } /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } return ret; } /* MBX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = NIL; MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mbx_create (dstream = stream ? stream : &mbxproto,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (dstream,data,&flags,&date,&message)); /* open destination mailbox */ else if (((fd = open (dummy_file (file,mailbox), O_WRONLY|O_APPEND|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); } /* get parse/append permission */ else if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock append mailbox",ERROR); close (fd); } else { mm_critical (dstream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; for (ret = LONGT; ret && message; ) { if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (dstream,flags,&uf); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%08lx%04lx-00000000\015\012",tmp, i = SIZE (message),uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !(*af) (dstream,data,&flags,&date,&message)) ret = NIL; } } /* if error... */ if (!ret || (fflush (df) == EOF)) { /* revert file */ ftruncate (fd,sbuf.st_size); close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); } ret = NIL; } if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); /* preserve mtime */ times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ mm_nocritical (dstream); /* release critical */ } if (dstream != stream) mail_close (dstream); return ret; } /* Internal routines */ /* MBX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mbx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j,k,m; off_t curpos = LOCAL->filesize; unsigned long nmsgs = stream->nmsgs; unsigned long recent = stream->recent; unsigned long lastuid = 0; short dirty = NIL; short added = NIL; short silent = stream->silent; short uidwarn = T; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } lseek (LOCAL->fd,0,L_SET); /* rewind file */ /* read internal header */ read (LOCAL->fd,LOCAL->buf,HDRSIZE); LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */ c = LOCAL->buf[15]; /* save first character of last UID */ LOCAL->buf[15] = '\0'; /* parse UID validity */ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16); LOCAL->buf[15] = c; /* restore first character of last UID */ /* parse last UID */ i = strtoul (LOCAL->buf + 15,NIL,16); stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i; /* parse user flags */ for (i = 0, s = LOCAL->buf + 25; (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG)) stream->user_flags[i] = cpystr (s); } LOCAL->ffuserflag = (int) i; /* first free user flag */ stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s", (unsigned long) curpos,i,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) && isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) && isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) && isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) { sprintf (tmp,"Unable to parse message flags at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } if ((t[13] != '-') || t[22] || !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) && isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) && isxdigit (t[20]) && isxdigit (t[21]))) { sprintf (tmp,"Unable to parse message UID at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } *s++ = '\0'; *t++ = '\0'; /* break up fields */ /* get message size */ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) { sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s", (unsigned long) curpos,(char *) LOCAL->buf,(char *) s, (char *) t); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } /* make sure didn't run off end of file */ if (((off_t) (curpos + i + j)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", (unsigned long) curpos,(unsigned long) (curpos + i + j), (unsigned long) sbuf.st_size); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } /* parse UID */ if ((m = strtoul (t+13,NIL,16)) && ((m <= lastuid) || (m > stream->uid_last))) { if (uidwarn) { sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs", m,nmsgs+1); mm_log (tmp,WARN); uidwarn = NIL; /* restart UID validity */ stream->uid_validity = time (0); } m = 0; /* lose this UID */ dirty = T; /* mark dirty, set new lastuid */ stream->uid_last = lastuid; } t[12] = '\0'; /* parse system flags */ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) { if (m) lastuid = m; /* expunge message, update last UID seen */ else { /* no UID assigned? */ lastuid = ++stream->uid_last; dirty = T; } } else { /* not expunged, swell the cache */ added = T; /* note that a new message was added */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; /* parse the date */ if (!mail_parse_date (elt,LOCAL->buf)) { sprintf (tmp,"Unable to parse message date at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } /* note file offset of header */ elt->private.special.offset = curpos; /* and internal header size */ elt->private.special.text.size = i; /* header size not known yet */ elt->private.msg.header.text.size = 0; elt->rfc822_size = j; /* note message size */ /* calculate system flags */ if (k & fSEEN) elt->seen = T; if (k & fDELETED) elt->deleted = T; if (k & fFLAGGED) elt->flagged = T; if (k & fANSWERED) elt->answered = T; if (k & fDRAFT) elt->draft = T; t[8] = '\0'; /* get user flags value */ elt->user_flags = strtoul (t,NIL,16); /* UID already assigned? */ if (!(elt->private.uid = m)) { elt->recent = T; /* no, mark as recent */ ++recent; /* count up a new recent message */ dirty = T; /* and must rewrite header */ /* assign new UID */ elt->private.uid = ++stream->uid_last; mbx_update_status (stream,elt->msgno,NIL); } /* update last parsed UID */ lastuid = elt->private.uid; } curpos += i + j; /* update position */ } if (dirty && !stream->rdonly){/* update header */ mbx_update_header (stream); fsync (LOCAL->fd); /* make sure all the UID updates take */ } /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ struct utimbuf times; times.actime = time (0); times.modtime = LOCAL->filetime; utime (stream->mailbox,×); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MBX get cache element with status updating from file * Accepts: MAIL stream * message number * expunge OK flag * Returns: cache element */ MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; /* get new flags */ if (mbx_read_flags (stream,elt) && expok) { mail_expunged (stream,elt->msgno); return NIL; /* return this message was expunged */ } if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) mm_flags (stream,msgno); /* let top level know */ return elt; } /* MBX read flags from file * Accepts: MAIL stream * cache element * Returns: non-NIL if message expunged */ unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i; struct stat sbuf; fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } LOCAL->buf[13] = '\0'; /* tie off buffer */ /* calculate system flags */ i = strtoul (LOCAL->buf+9,NIL,16); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->expunged |= i & fEXPUNGED ? T : NIL; LOCAL->buf[9] = '\0'; /* tie off flags */ /* get user flags value */ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16); elt->valid = T; /* have valid flags now */ return i & fEXPUNGED; } /* MBX update header * Accepts: MAIL stream */ #define NTKLUDGEOFFSET 7 void mbx_update_header (MAILSTREAM *stream) { int i; char *s = LOCAL->buf; memset (s,'\0',HDRSIZE); /* initialize header */ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012", stream->uid_validity,stream->uid_last); for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]); LOCAL->ffuserflag = i; /* first free user flag */ /* can we create more user flags? */ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL; /* write reserved lines */ while (i++ < NUSERFLAGS) strcat (s,"\015\012"); while (T) { /* rewind file */ lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET); /* write new header */ if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET, HDRSIZE - NTKLUDGEOFFSET) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } } /* MBX update status string * Accepts: MAIL stream * message number * flags */ void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags) { struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); /* readonly */ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt); else { /* readwrite */ fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } /* print new flag string */ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned) (((elt->deleted && flags) ? fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft)),elt->private.uid); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 23,L_SET); /* write new flags and UID */ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } } } /* MBX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * pointer to possible returned header * Returns: position of header in file */ #define HDRBUFLEN 4096 /* good enough for most headers */ #define SLOP 4 /* CR LF CR LF */ unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr) { unsigned long siz,done; long i; unsigned char *s,*t,*te; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; if (hdr) *hdr = NIL; /* assume no header returned */ /* is header size known? */ if (*size = elt->private.msg.header.text.size) return ret; /* paranoia check */ if (LOCAL->buflen < (HDRBUFLEN + SLOP)) fatal ("LOCAL->buf smaller than HDRBUFLEN"); lseek (LOCAL->fd,ret,L_SET); /* get to header position */ /* read HDRBUFLEN chunks with 4 byte slop */ for (done = siz = 0, s = LOCAL->buf; (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) && (read (LOCAL->fd,s,i) == i); done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) { te = (t = s + i) - 12; /* calculate end of fast scan */ /* fast scan for CR */ for (s = LOCAL->buf; s < te;) if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } for (te = t - 3; (s < te);) /* final character-at-a-time scan */ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } if (i <= SLOP) break; /* end of data */ /* slide over last 4 bytes */ memmove (LOCAL->buf,t - SLOP,SLOP); hdr = NIL; /* can't return header this way */ } /* not found: header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; if (hdr) *hdr = LOCAL->buf; /* possibly return header too */ return ret; } /* MBX mail rewrite mailbox * Accepts: MAIL stream * pointer to return reclaimed size * flags (non-NIL to do expunge) * Returns: number of expunged messages */ unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags) { struct utimbuf times; struct stat sbuf; off_t pos,ppos; int ld; unsigned long i,j,k,m,n,delta; unsigned long recent = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; /* get parse/append permission */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock expunge mailbox",ERROR); return *reclaimed = 0; } fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime && !LOCAL->flagcheck && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */ unlockfd (ld,lock); /* failed?? */ return *reclaimed = 0; } if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */ LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL); LOCAL->flagcheck = NIL; } /* get exclusive access */ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { mm_critical (stream); /* go critical */ for (i = 1,n = delta = *reclaimed = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) { /* note if message not at predicted location */ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) { ppos = elt->private.special.offset; *reclaimed += m; /* note reclaimed message space */ delta += m; /* and as expunge delta */ } /* number of bytes to smash or preserve */ ppos += (k = elt->private.special.text.size + elt->rfc822_size); if (flags & elt->deleted){/* if deleted */ delta += k; /* number of bytes to delete */ mail_expunged(stream,i);/* notify upper levels */ n++; /* count up one more expunged message */ } else { /* preserved message */ i++; /* count this message */ if (elt->recent) ++recent; if (delta) { /* moved, note first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages yet */ else pos = elt->private.special.offset + k; } } /* deltaed file size match position? */ if (m = (LOCAL->filesize -= delta) - pos) { *reclaimed += m; /* probably an fEXPUNGED msg */ LOCAL->filesize = pos; /* set correct size */ } /* truncate file after last message */ ftruncate (LOCAL->fd,LOCAL->filesize); fsync (LOCAL->fd); /* force disk update */ mm_nocritical (stream); /* release critical */ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ unlockfd (ld,lock); /* release exclusive parse/append permission */ } else { /* can't get exclusive */ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */ unlockfd (ld,lock); /* release exclusive parse/append permission */ /* checkpoint while shared? */ if (!flags) n = *reclaimed = 0; /* no, do hide-expunge */ else for (i = 1,n = *reclaimed = 0; i <= stream->nmsgs; ) { if (elt = mbx_elt (stream,i,T)) { if (elt->deleted) { /* make deleted message invisible */ mbx_update_status (stream,elt->msgno,LONGT); /* notify upper levels */ mail_expunged (stream,i); n++; /* count up one more expunged message */ } else { i++; /* preserved message */ if (elt->recent) ++recent; } } else n++; /* count up one more expunged message */ } fsync (LOCAL->fd); /* force disk update */ } fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* reset atime to now */ utime (stream->mailbox,×); /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); return n; /* return number of expunged messages */ } /* MBX mail lock for flag updating * Accepts: stream * Returns: T if successful, NIL if failure */ long mbx_flaglock (MAILSTREAM *stream) { struct stat sbuf; unsigned long i; int ld; char lock[MAILTMPLEN]; /* no-op if readonly or already locked */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) { /* lock now */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) return NIL; if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */ if (LOCAL->filetime) { /* know previous time? */ fstat (LOCAL->fd,&sbuf);/* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } if (!mbx_parse (stream)) {/* parse mailbox */ unlockfd (ld,lock); /* shouldn't happen */ return NIL; } if (LOCAL->flagcheck) /* invalidate cache if flagcheck */ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL; } LOCAL->ld = ld; /* copy to stream for subseuent calls */ memcpy (LOCAL->lock,lock,MAILTMPLEN); } return LONGT; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/mbxnt.h000066400000000000000000000012211137544547100225510ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define HDRSIZE 2048 /* Private driver flags, should be in mail.h? */ #define fEXPUNGED 32768 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/mkautaux.bat000066400000000000000000000016631137544547100236110ustar00rootroot00000000000000@ECHO OFF REM Program: Authenticator Linkage Generator auxillary for NT/Win9x REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 6 December 1995 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H REM Note the introduction of the caret to quote the ampersand in NT if "%OS%" == "Windows_NT" ECHO auth_link (^&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C if "%OS%" == "" ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C ECHO #include "auth_%1.c" >> AUTHS.C tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/mkauths.bat000066400000000000000000000013451137544547100234230ustar00rootroot00000000000000@ECHO OFF REM Program: Authenticator Linkage Generator for DOS REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 6 December 1995 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Erase old authenticators list IF EXIST AUTHS.C DEL AUTHS.C REM Now define the new list FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D EXIT 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/mtxnt.c000066400000000000000000001164671137544547100226110ustar00rootroot00000000000000/* * Program: MTX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "misc.h" #include "dummy.h" /* MTX I/O stream local data */ typedef struct mtx_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } MTXLOCAL; /* Convenient access to local data */ #define LOCAL ((MTXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mtx_valid (char *name); int mtx_isvalid (char *name,char *tmp); void *mtx_parameters (long function,void *value); void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mtx_list (MAILSTREAM *stream,char *ref,char *pat); void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mtx_create (MAILSTREAM *stream,char *mailbox); long mtx_delete (MAILSTREAM *stream,char *mailbox); long mtx_rename (MAILSTREAM *stream,char *old,char *newname); long mtx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mtx_open (MAILSTREAM *stream); void mtx_close (MAILSTREAM *stream,long options); void mtx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mtx_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mtx_ping (MAILSTREAM *stream); void mtx_check (MAILSTREAM *stream); void mtx_snarf (MAILSTREAM *stream); void mtx_expunge (MAILSTREAM *stream); long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); long mtx_parse (MAILSTREAM *stream); MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno); void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag); unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* MTX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mtxdriver = { "mtx", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ mtx_valid, /* mailbox is valid for us */ mtx_parameters, /* manipulate parameters */ mtx_scan, /* scan mailboxes */ mtx_list, /* list mailboxes */ mtx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mtx_create, /* create mailbox */ mtx_delete, /* delete mailbox */ mtx_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mtx_open, /* open mailbox */ mtx_close, /* close mailbox */ mtx_flags, /* fetch message "fast" attributes */ mtx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mtx_header, /* fetch message header */ mtx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mtx_flag, /* modify flags */ mtx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mtx_ping, /* ping mailbox to see if still alive */ mtx_check, /* check for new messages */ mtx_expunge, /* expunge deleted messages */ mtx_copy, /* copy messages to another mailbox */ mtx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mtxproto = {&mtxdriver}; /* MTX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mtx_valid (char *name) { char tmp[MAILTMPLEN]; return mtx_isvalid (name,tmp) ? &mtxdriver : NIL; } /* MTX mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int mtx_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) && (s[1] == '\012')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { /* preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ } } } /* in case INBOX but not mtx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MTX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mtx_parameters (long function,void *value) { return NIL; } /* MTX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MTX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MTX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MTX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mtx_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN]; if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s); sprintf (mbx,"Can't create %.80s: invalid name",mailbox); mm_log (mbx,ERROR); return NIL; } /* MTX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mtx_delete (MAILSTREAM *stream,char *mailbox) { return mtx_rename (stream,mailbox,NIL); } /* MTX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mtx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); mm_log (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,tmp)) ret = NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } else { flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } unlockfd (ld,lock); /* release exclusive parse/append permission */ return ret; /* return success */ } /* MTX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mtx_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &mtxproto; if (stream->local) fatal ("mtx recycle stream"); /* canonicalize the mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ mm_log ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (MTXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) { mm_log ("Unable to lock open mailbox",ERROR); return NIL; } flock (LOCAL->fd,LOCK_SH); /* lock the file */ unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ LOCAL->filetime = 0; /* time not set up yet */ LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ stream->uid_validity = time (0); /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (mtx_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* MTX mail close * Accepts: MAIL stream * close options */ void mtx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mtx_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MTX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mtx_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if (mtx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) mtx_elt (stream,i); } /* MTX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET); /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } LOCAL->buf[*length] = '\0'; /* tie off string */ /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length); return LOCAL->buf; } /* MTX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mtx_elt (stream,msgno); /* get message status */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mtx_update_status (stream,msgno,T); mm_flags (stream,msgno); } /* in case previous text cached */ if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mtx_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); return T; /* success */ } /* MTX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { struct utimbuf times; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read comes after all that */ utime (stream->mailbox,×); } } /* MTX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ mtx_update_status (stream,elt->msgno,NIL); } /* MTX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mtx_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ mm_notify (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) mtx_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (mtx_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } return r; /* return result of the parse */ } /* MTX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mtx_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL); } /* MTX mail expunge mailbox * Accepts: MAIL stream */ void mtx_expunge (MAILSTREAM *stream) { struct utimbuf times; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; /* do nothing if stream dead */ if (!mtx_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ mm_log ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* get exclusive parse/append permission */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!mtx_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ mm_log("Can't expunge because mailbox is in use by another process",ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } mm_critical (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = mtx_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + elt->rfc822_size; if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); mm_log (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* reset atime to now */ utime (stream->mailbox,×); mm_nocritical (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* MTX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; struct utimbuf times; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (dummy_file (file,mailbox),O_BINARY|O_RDWR|O_CREAT, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno)); mm_log (LOCAL->buf,ERROR); return NIL; } mm_critical (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock copy mailbox",ERROR); mm_nocritical (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + elt->rfc822_size; do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } /* set atime to now-1 if successful copy */ if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = mtx_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ mtx_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure atime remains greater */ utime (stream->mailbox,×); } } return ret; } /* MTX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &mtxproto; /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open(dummy_file (file,mailbox),O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } mm_critical (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf, (unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); } ret = NIL; } if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ mm_nocritical (stream); /* release critical */ return ret; } /* Internal routines */ /* MTX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mtx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ mtx_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ struct utimbuf times; times.actime = time (0); times.modtime = LOCAL->filetime; utime (stream->mailbox,×); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MTX get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; mtx_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) mm_flags (stream,msgno); /* let top level know */ return elt; } /* MTX read flags from file * Accepts: MAIL stream * Returns: cache element */ void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* MTX update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { struct utimbuf times; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* write new flags */ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read is later */ utime (stream->mailbox,×); } } } /* MTX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; int q = 0; char *s,tmp[MAILTMPLEN]; MESSAGECACHE *elt = mtx_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for CRLF CRLF */ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) { /* read another buffer as necessary */ if ((--i <= 0) && /* buffer empty? */ (read (LOCAL->fd,s = tmp, i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)) return ret; /* I/O error? */ switch (q) { /* sniff at buffer */ case 0: /* first character */ q = (*s++ == '\015') ? 1 : 0; break; case 1: /* second character */ q = (*s++ == '\012') ? 2 : 0; break; case 2: /* third character */ q = (*s++ == '\015') ? 3 : 0; break; case 3: /* fourth character */ if (*s++ == '\012') { /* have the sequence? */ /* yes, note for later */ elt->private.msg.header.text.size = *size = siz; return ret; } q = 0; /* lost... */ break; } } /* header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/nl_nt.c000066400000000000000000000027511137544547100225370ustar00rootroot00000000000000/* * Program: Windows/TOPS-20 newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { /* flush destination buffer if too small */ if (*dst && (srcl > *dstl)) fs_give ((void **) dst); if (!*dst) { /* make a new buffer if needed */ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1); if (dstl) *dstl = srcl; /* return new buffer length to main program */ } /* copy strings */ if (srcl) memcpy (*dst,src,(size_t) srcl); *(*dst + srcl) = '\0'; /* tie off destination */ return srcl; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { return SIZE (s); /* no-brainer on DOS! */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/os_nt.c000066400000000000000000000017731137544547100225520ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- NT version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 15 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_nt.h" /* must be before osdep includes tcp.h */ #undef ERROR /* quell conflicting def warning */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include "misc.h" #include "mailfile.h" #include "fs_nt.c" #include "ftl_nt.c" #include "nl_nt.c" #include "yunchan.c" #include "env_nt.c" #include "ssl_nt.c" #include "tcp_nt.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/os_nt.h000066400000000000000000000017631137544547100225560ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- NT version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 23 December 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #undef ERROR /* quell conflicting defintion warning */ #include #include #undef ERROR #define ERROR (long) 2 /* must match mail.h */ #include "env_nt.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "yunchan.h" #undef noErr #undef MAC tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/os_ntk.c000066400000000000000000000021251137544547100227150ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- NT version + Kerberos * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 4 March 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_nt.h" /* must be before osdep includes tcp.h */ #undef ERROR /* quell conflicting def warning */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include "misc.h" #include "mailfile.h" #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name #include "fs_nt.c" #include "ftl_nt.c" #include "nl_nt.c" #include "yunchan.c" #include "kerb_mit.c" #include "env_nt.c" #include "ssl_nt.c" #include "tcp_nt.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/os_old.c000066400000000000000000000017751137544547100227110ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- NT version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 4 March 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_nt.h" /* must be before osdep includes tcp.h */ #undef ERROR /* quell conflicting def warning */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include "misc.h" #include "mailfile.h" #include "fs_nt.c" #include "ftl_nt.c" #include "nl_nt.c" #include "yunchan.c" #include "env_nt.c" #include "ssl_old.c" #include "tcp_nt.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/os_w2k.c000066400000000000000000000020351137544547100226240ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Windows 2000 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 4 March 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_nt.h" /* must be before osdep includes tcp.h */ #undef ERROR /* quell conflicting def warning */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include "misc.h" #include "mailfile.h" #include "fs_nt.c" #include "ftl_nt.c" #include "nl_nt.c" #include "yunchan.c" #include "kerb_w2k.c" #include "env_nt.c" #include "ssl_w2k.c" #include "tcp_nt.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/pmatch.c000066400000000000000000000053271137544547100227030ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-independent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/pseudo.c000066400000000000000000000020221137544547100227130ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Local sites may wish to alter this text */ char *pseudo_from = "MAILER-DAEMON"; char *pseudo_name = "Mail System Internal Data"; char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA"; char *pseudo_msg = "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values." ; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/pseudo.h000066400000000000000000000011421137544547100227220ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/setproto.bat000066400000000000000000000012671137544547100236310ustar00rootroot00000000000000@ECHO OFF REM Program: Set default prototype for DOS/NT REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 9 October 1995 REM Last Edited: 6 July 2004 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 1988-2004 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Set the default drivers ECHO #define CREATEPROTO %1proto >> LINKAGE.H ECHO #define APPENDPROTO %2proto >> LINKAGE.H tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/ssl_none.c000066400000000000000000000045401137544547100232430ustar00rootroot00000000000000/* * Program: Dummy (no SSL) authentication/encryption module * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 7 February 2001 * Last Edited: 6 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Init server for SSL * Accepts: server name */ void ssl_server_init (char *server) { syslog (LOG_ERR,"This server does not support SSL"); exit (1); /* punt this program too */ } /* Start TLS * Accepts: /etc/services service name * Returns: cpystr'd error string if TLS failed, else NIL for success */ char *ssl_start_tls (char *server) { return cpystr ("This server does not support TLS"); } /* Get character * Returns: character or EOF */ int PBIN (void) { return getchar (); } /* Get string * Accepts: destination string pointer * number of bytes available * Returns: destination string pointer or NIL if EOF */ char *PSIN (char *s,int n) { return fgets (s,n,stdin); } /* Get record * Accepts: destination string pointer * number of bytes to read * Returns: T if success, NIL otherwise */ long PSINR (char *s,unsigned long n) { unsigned long i; while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i; return n ? NIL : LONGT; } /* Wait for input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long INWAIT (long seconds) { return server_input_wait (seconds); } /* Put character * Accepts: character * Returns: character written or EOF */ int PBOUT (int c) { return putchar (c); } /* Put string * Accepts: source string pointer * Returns: 0 or EOF if error */ int PSOUT (char *s) { return fputs (s,stdout); } /* Put record * Accepts: source sized text * Returns: 0 or EOF if error */ int PSOUTR (SIZEDTEXT *s) { unsigned char *t; unsigned long i,j; for (t = s->data,i = s->size; (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR))); t += j,i -= j); return i ? EOF : NIL; } /* Flush output * Returns: 0 or EOF if error */ int PFLUSH (void) { return fflush (stdout); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/ssl_nt.c000066400000000000000000000535351137544547100227350ustar00rootroot00000000000000/* * Program: SSL authentication/encryption module for Windows 9x and NT * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 September 1998 * Last Edited: 11 December 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SECURITY_WIN32 #include #include #define SSLBUFLEN 8192 /* SSL I/O stream */ typedef struct ssl_stream { TCPSTREAM *tcpstream; /* TCP stream */ CredHandle cred; /* SSL credentials */ CtxtHandle context; /* SSL context */ /* stream encryption sizes */ SecPkgContext_StreamSizes sizes; size_t bufsize; int ictr; /* input counter */ char *iptr; /* input pointer */ int iextractr; /* extra input counter */ char *iextraptr; /* extra input pointer */ char *ibuf; /* input buffer */ char *obuf; /* output buffer */ } SSLSTREAM; #include "sslio.h" /* Function prototypes */ static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags); static char *ssl_analyze_status (SECURITY_STATUS err,char *buf); static long ssl_abort (SSLSTREAM *stream); /* Secure Sockets Layer network driver dispatch */ static struct ssl_driver ssldriver = { ssl_open, /* open connection */ ssl_aopen, /* open preauthenticated connection */ ssl_getline, /* get a line */ ssl_getbuffer, /* get a buffer */ ssl_soutr, /* output pushed data */ ssl_sout, /* output string */ ssl_close, /* close connection */ ssl_host, /* return host name */ ssl_remotehost, /* return remote host name */ ssl_port, /* return port number */ ssl_localhost /* return local host name */ }; /* security function table */ static SecurityFunctionTable *sft = NIL; static unsigned long ssltsz = 0;/* SSL maximum token length */ /* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */ typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD); typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME, HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID, PCCERT_CHAIN_CONTEXT *); typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT, PCERT_CHAIN_POLICY_PARA, PCERT_CHAIN_POLICY_STATUS); typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT); static CNTS certNameToStr = NIL; static CGCC certGetCertificateChain = NIL; static CVCCP certVerifyCertificateChainPolicy = NIL; static CFCC certFreeCertificateChain = NIL; /* One-time SSL initialization */ static int sslonceonly = 0; void ssl_onceonlyinit (void) { if (!sslonceonly++) { /* only need to call it once */ HINSTANCE lib; FARPROC pi; ULONG np; SecPkgInfo *pp; int i; /* get security library */ if (((lib = LoadLibrary ("schannel.dll")) || (lib = LoadLibrary ("security.dll"))) && (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) && (sft = (SecurityFunctionTable *) pi ()) && !(sft->EnumerateSecurityPackages (&np,&pp))) { /* look for an SSL package */ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) { /* note maximum token size and name */ ssltsz = pp[i].cbMaxToken; /* apply runtime linkage */ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver); mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start); if ((lib = LoadLibrary ("crypt32.dll")) && (certGetCertificateChain = (CGCC) GetProcAddress (lib,"CertGetCertificateChain")) && (certVerifyCertificateChainPolicy = (CVCCP) GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) && (certFreeCertificateChain = (CFCC) GetProcAddress (lib,"CertFreeCertificateChain"))) certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA"); return; /* all done */ } } } } /* SSL open * Accepts: host name * contact service name * contact port number * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = tcp_open (host,service,port); return stream ? ssl_start (stream,host,port) : NIL; } /* SSL authenticated open * Accepts: host name * service name * returned user name buffer * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* don't use this mechanism with SSL */ } /* Start SSL/TLS negotiations * Accepts: open TCP stream of session * user's host name * flags * Returns: SSL stream if success else NIL */ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags) { SECURITY_STATUS e; ULONG a; TimeStamp t; SecBuffer ibuf[2],obuf[1]; SecBufferDesc ibufs,obufs; SCHANNEL_CRED tlscred; CERT_CONTEXT *cert; CERT_CHAIN_PARA chparam; CERT_CHAIN_CONTEXT *chain; SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy; CERT_CHAIN_POLICY_PARA polparam; CERT_CHAIN_POLICY_STATUS status; char tmp[MAILTMPLEN],certname[256]; char *reason = NIL; ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | ISC_REQ_MANUAL_CRED_VALIDATION; LPSTR usage[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; PWSTR whost = NIL; char *buf = (char *) fs_get (ssltsz); unsigned long size = 0; sslcertificatequery_t scq = (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL); SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); stream->tcpstream = tstream; /* bind TCP stream */ /* initialize TLS credential */ memset (&tlscred,0,sizeof (SCHANNEL_CRED)); tlscred.dwVersion = SCHANNEL_CRED_VERSION; tlscred.grbitEnabledProtocols = SP_PROT_TLS1; /* acquire credentials */ if (sft->AcquireCredentialsHandle (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ? &tlscred : NIL,NIL,NIL,&stream->cred,&t) != SEC_E_OK) reason = "Acquire credentials handle failed"; else while (!reason) { /* negotiate security context */ /* initialize buffers */ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf; ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL; obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL; ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; ibuf[1].BufferType = SECBUFFER_EMPTY; /* initialize buffer descriptors */ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; ibufs.cBuffers = 2; obufs.cBuffers = 1; ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; /* negotiate security */ e = sft->InitializeSecurityContext (&stream->cred,size ? &stream->context : NIL,host,req,0, SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t); /* have an output buffer we need to send? */ if (obuf[0].pvBuffer && obuf[0].cbBuffer) { if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer)) reason = "Unexpected TCP output disconnect"; /* free the buffer */ sft->FreeContextBuffer (obuf[0].pvBuffer); } if (!reason) switch (e) { /* negotiation state */ case SEC_I_INCOMPLETE_CREDENTIALS: break; /* server wants client auth */ case SEC_I_CONTINUE_NEEDED: if (size) { /* continue, read any data? */ /* yes, anything regurgiated back to us? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); size = ibuf[1].cbBuffer; break; } size = 0; /* otherwise, read more stuff from server */ } case SEC_E_INCOMPLETE_MESSAGE: /* need to read more data from server */ if (!tcp_getdata (stream->tcpstream)) reason = "Unexpected TCP input disconnect"; else { memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr); size += stream->tcpstream->ictr; /* empty it from TCP's buffers */ stream->tcpstream->iptr += stream->tcpstream->ictr; stream->tcpstream->ictr = 0; } break; case SEC_E_OK: /* success, any data to be regurgitated? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf, buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); stream->tcpstream->ictr = ibuf[1].cbBuffer; } if (certNameToStr && !(flags & NET_NOVALIDATECERT)) { /* need validation, make wchar of host */ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) && (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) && MultiByteToWideChar (CP_ACP,0,host,-1,whost,size))) fatal ("Can't make wchar of host name!"); /* get certificate */ if ((sft->QueryContextAttributes (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) != SEC_E_OK) || !cert) { reason = "*Unable to get certificate"; strcpy (certname,""); } else { /* get certificate subject name */ (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &cert->pCertInfo->Subject,CERT_X500_NAME_STR, certname,255); /* build certificate chain */ memset (&chparam,0,sizeof (chparam)); chparam.cbSize = sizeof (chparam); chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage; chparam.RequestedUsage.Usage.cUsageIdentifier = sizeof (usage) / sizeof (LPSTR); if (!(*certGetCertificateChain) (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain)) reason = ssl_analyze_status (GetLastError (),tmp); else { /* validate certificate chain */ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA)); policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA); policy.dwAuthType = AUTHTYPE_SERVER; policy.fdwChecks = NIL; policy.pwszServerName = whost; memset (&polparam,0,sizeof (polparam)); polparam.cbSize = sizeof (polparam); polparam.pvExtraPolicyPara = &policy; memset (&status,0,sizeof (status)); status.cbSize = sizeof (status); if (!(*certVerifyCertificateChainPolicy) (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status)) reason = ssl_analyze_status (GetLastError (),tmp); else if (status.dwError) reason = ssl_analyze_status (status.dwError,tmp); (*certFreeCertificateChain) (chain); } } if (whost) fs_give ((void **) &whost); if (reason) { /* got an error? */ /* application callback */ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason, host,certname) ? NIL : ""; else if (*certname) { /* error message to return via mm_log() */ sprintf (buf,"*%.128s: %.255s", (*reason == '*') ? reason + 1 : reason,certname); reason = buf; } } } if (reason || (reason = ssl_analyze_status (sft->QueryContextAttributes (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf))) break; /* error in certificate or getting sizes */ fs_give ((void **) &buf); /* flush temporary buffer */ /* make maximum-sized buffers */ stream->bufsize = stream->sizes.cbHeader + stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer; if (stream->sizes.cbMaximumMessage < SSLBUFLEN) fatal ("cbMaximumMessage is less than SSLBUFLEN!"); else if (stream->sizes.cbMaximumMessage < 16384) { sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384", (long) stream->sizes.cbMaximumMessage); mm_log (tmp,NIL); } stream->ibuf = (char *) fs_get (stream->bufsize); stream->obuf = (char *) fs_get (stream->bufsize); return stream; default: reason = ssl_analyze_status (e,buf); } } ssl_close (stream); /* failed to do SSL */ stream = NIL; /* no stream returned */ switch (*reason) { /* analyze reason */ case '*': /* certificate failure */ ++reason; /* skip over certificate failure indication */ /* pass to error callback */ if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } case '\0': /* user answered no to certificate callback */ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); break; default: /* non-certificate failure */ if (flags & NET_TRYSSL); /* no error output if tryssl */ /* pass to error callback */ else if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } break; } fs_give ((void **) &buf); /* flush temporary buffer */ return stream; } /* Generate error text from SSL error code * Accepts: SSL status * scratch buffer * Returns: text if error status, else NIL */ static char *ssl_analyze_status (SECURITY_STATUS err,char *buf) { switch (err) { case SEC_E_OK: /* no error */ case SEC_I_CONTINUE_NEEDED: case SEC_I_INCOMPLETE_CREDENTIALS: case SEC_E_INCOMPLETE_MESSAGE: return NIL; case SEC_E_NO_AUTHENTICATING_AUTHORITY: mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL); return "*No authority could be contacted for authentication"; case SEC_E_WRONG_PRINCIPAL: mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL); case CERT_E_CN_NO_MATCH: return "*Server name does not match certificate"; case SEC_E_UNTRUSTED_ROOT: mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL); case CERT_E_UNTRUSTEDROOT: return "*Self-signed certificate or untrusted authority"; case SEC_E_CERT_EXPIRED: mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL); case CERT_E_EXPIRED: return "*Certificate has expired"; case CERT_E_REVOKED: return "*Certificate revoked"; case SEC_E_INVALID_TOKEN: return "Invalid token, probably not an SSL server"; case SEC_E_UNSUPPORTED_FUNCTION: return "SSL not supported on this machine - upgrade your system software"; } sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err); return buf; } /* SSL receive line * Accepts: SSL stream * Returns: text line string or NIL if failure */ char *ssl_getline (SSLSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!ssl_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!ssl_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = ssl_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* SSL receive buffer * Accepts: SSL stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; while (size > 0) { /* until request satisfied */ if (!ssl_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (buffer,stream->iptr,n); buffer += n; /* update pointer */ stream->iptr += n; size -= n; /* update # of bytes to do */ stream->ictr -= n; } buffer[0] = '\0'; /* tie off string */ return T; } /* SSL receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long ssl_getdata (SSLSTREAM *stream) { SECURITY_STATUS status; SecBuffer buf[4]; SecBufferDesc msg; size_t n = 0; size_t i; while (stream->ictr < 1) { /* decrypted buffer empty? */ do { /* yes, make sure have data from TCP */ if (stream->iextractr) { /* have previous unread data? */ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr); n += stream->iextractr; /* update number of bytes read */ stream->iextractr = 0; /* no more extra data */ } else { /* read from TCP */ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream); /* maximum amount of data to copy */ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr))) fatal ("incomplete SecBuffer exceeds maximum buffer size"); /* do the copy */ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i); stream->tcpstream->iptr += i; stream->tcpstream->ictr -= i; n += i; /* update number of bytes to decrypt */ } buf[0].cbBuffer = n; /* first SecBuffer gets data */ buf[0].pvBuffer = stream->ibuf; buf[0].BufferType = SECBUFFER_DATA; /* subsequent ones are for spares */ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; /* number of SecBuffers */ msg.pBuffers = buf; /* first SecBuffer */ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4) (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE); switch (status) { case SEC_E_OK: /* won */ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */ /* hunt for a buffer */ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++); if (i < 4) { /* found a buffer? */ /* yes, set up pointer and counter */ stream->iptr = buf[i].pvBuffer; stream->ictr = buf[i].cbBuffer; /* any unprocessed data? */ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) { /* yes, note for next time around */ stream->iextraptr = buf[i].pvBuffer; stream->iextractr = buf[i].cbBuffer; } } break; default: /* anything else means we've lost */ return ssl_abort (stream); } } return LONGT; } /* SSL send string as record * Accepts: SSL stream * string pointer * Returns: T if success else NIL */ long ssl_soutr (SSLSTREAM *stream,char *string) { return ssl_sout (stream,string,(unsigned long) strlen (string)); } /* SSL send string * Accepts: SSL stream * string pointer * byte count * Returns: T if success else NIL */ long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size) { SecBuffer buf[4]; SecBufferDesc msg; char *s; size_t n; if (!stream->tcpstream) return NIL; /* until request satisfied */ for (s = stream->ibuf,n = 0; size;) { /* header */ buf[0].BufferType = SECBUFFER_STREAM_HEADER; memset (buf[0].pvBuffer = stream->obuf,0, buf[0].cbBuffer = stream->sizes.cbHeader); /* message (up to maximum size) */ buf[1].BufferType = SECBUFFER_DATA; memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string, buf[1].cbBuffer = min (size,SSLBUFLEN)); /* trailer */ buf[2].BufferType = SECBUFFER_STREAM_TRAILER; memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0, buf[2].cbBuffer = stream->sizes.cbTrailer); /* spare */ buf[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; /* number of SecBuffers */ msg.pBuffers = buf; /* first SecBuffer */ string += buf[1].cbBuffer; size -= buf[1].cbBuffer; /* this many bytes processed */ /* encrypt and send message */ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3) (&stream->context,0,&msg,NIL) != SEC_E_OK) || !tcp_sout (stream->tcpstream,stream->obuf, buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer)) return ssl_abort (stream);/* encryption or sending failed */ } return LONGT; } /* SSL close * Accepts: SSL stream */ void ssl_close (SSLSTREAM *stream) { ssl_abort (stream); /* nuke the stream */ fs_give ((void **) &stream); /* flush the stream */ } /* SSL abort stream * Accepts: SSL stream * Returns: NIL always */ static long ssl_abort (SSLSTREAM *stream) { if (stream->tcpstream) { /* close TCP stream */ sft->DeleteSecurityContext (&stream->context); sft->FreeCredentialHandle (&stream->cred); tcp_close (stream->tcpstream); stream->tcpstream = NIL; } if (stream->ibuf) fs_give ((void **) &stream->ibuf); if (stream->obuf) fs_give ((void **) &stream->obuf); return NIL; } /* SSL get host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_host (SSLSTREAM *stream) { return tcp_host (stream->tcpstream); } /* SSL get remote host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_remotehost (SSLSTREAM *stream) { return tcp_remotehost (stream->tcpstream); } /* SSL return port for this stream * Accepts: SSL stream * Returns: port number for this stream */ unsigned long ssl_port (SSLSTREAM *stream) { return tcp_port (stream->tcpstream); } /* SSL get local host name * Accepts: SSL stream * Returns: local host name */ char *ssl_localhost (SSLSTREAM *stream) { return tcp_localhost (stream->tcpstream); } #include "ssl_none.c" /* currently no server support */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/ssl_old.c000066400000000000000000000437501137544547100230700ustar00rootroot00000000000000/* * Program: SSL authentication/encryption module for Windows 9x and NT * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 September 1998 * Last Edited: 11 December 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SECURITY_WIN32 #include #if(_WIN32_WINNT < 0x0400) typedef unsigned int ALG_ID; #else #include ALGIDDEF #endif #include #include /* in case a binary runs on Windows 2000 */ #ifndef ISC_REQ_MANUAL_CRED_VALIDATION #define ISC_REQ_MANUAL_CRED_VALIDATION 0x00080000 #endif #ifndef SEC_E_UNTRUSTED_ROOT #define SEC_E_UNTRUSTED_ROOT ((HRESULT) 0x80090325L) #endif #ifndef SEC_E_CERT_EXPIRED #define SEC_E_CERT_EXPIRED ((HRESULT) 0x80090328L) #endif #define SSLBUFLEN 8192 /* SSL I/O stream */ typedef struct ssl_stream { TCPSTREAM *tcpstream; /* TCP stream */ CredHandle cred; /* SSL credentials */ CtxtHandle context; /* SSL context */ /* stream encryption sizes */ SecPkgContext_StreamSizes sizes; size_t bufsize; int ictr; /* input counter */ char *iptr; /* input pointer */ int iextractr; /* extra input counter */ char *iextraptr; /* extra input pointer */ char *ibuf; /* input buffer */ char *obuf; /* output buffer */ } SSLSTREAM; #include "sslio.h" /* Function prototypes */ static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags); static long ssl_abort (SSLSTREAM *stream); /* Secure Sockets Layer network driver dispatch */ static struct ssl_driver ssldriver = { ssl_open, /* open connection */ ssl_aopen, /* open preauthenticated connection */ ssl_getline, /* get a line */ ssl_getbuffer, /* get a buffer */ ssl_soutr, /* output pushed data */ ssl_sout, /* output string */ ssl_close, /* close connection */ ssl_host, /* return host name */ ssl_remotehost, /* return remote host name */ ssl_port, /* return port number */ ssl_localhost /* return local host name */ }; /* security function table */ static SecurityFunctionTable *sft = NIL; static unsigned long ssltsz = 0;/* SSL maximum token length */ /* One-time SSL initialization */ static int sslonceonly = 0; void ssl_onceonlyinit (void) { if (!sslonceonly++) { /* only need to call it once */ HINSTANCE lib; FARPROC pi; ULONG np; SecPkgInfo *pp; int i; /* get security library */ if (((lib = LoadLibrary ("schannel.dll")) || (lib = LoadLibrary ("security.dll"))) && (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) && (sft = (SecurityFunctionTable *) pi ()) && !(sft->EnumerateSecurityPackages (&np,&pp))) { /* look for an SSL package */ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) { /* note maximum token size and name */ ssltsz = pp[i].cbMaxToken; /* apply runtime linkage */ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver); mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start); return; /* all done */ } } } } /* SSL open * Accepts: host name * contact service name * contact port number * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = tcp_open (host,service,port); return stream ? ssl_start (stream,host,port) : NIL; } /* SSL authenticated open * Accepts: host name * service name * returned user name buffer * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* don't use this mechanism with SSL */ } /* Start SSL/TLS negotiations * Accepts: open TCP stream of session * user's host name * flags * Returns: SSL stream if success else NIL */ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags) { SECURITY_STATUS e; ULONG a; TimeStamp t; SecBuffer ibuf[2],obuf[1]; SecBufferDesc ibufs,obufs; char tmp[MAILTMPLEN]; char *reason = NIL; ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR + ((flags & NET_NOVALIDATECERT) ? ISC_REQ_MANUAL_CRED_VALIDATION : ISC_REQ_MUTUAL_AUTH); SCHANNEL_CRED tlscred; char *buf = (char *) fs_get (ssltsz); unsigned long size = 0; sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL); SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); stream->tcpstream = tstream; /* bind TCP stream */ /* initialize TLS credential */ memset (&tlscred,0,sizeof (SCHANNEL_CRED)); tlscred.dwVersion = SCHANNEL_CRED_VERSION; tlscred.grbitEnabledProtocols = SP_PROT_TLS1; /* acquire credentials */ if (sft->AcquireCredentialsHandle (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ? &tlscred : NIL,NIL,NIL,&stream->cred,&t) != SEC_E_OK) reason = "Acquire credentials handle failed"; else while (!reason) { /* negotiate security context */ /* initialize buffers */ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf; ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL; obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL; ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; ibuf[1].BufferType = SECBUFFER_EMPTY; /* initialize buffer descriptors */ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; ibufs.cBuffers = 2; obufs.cBuffers = 1; ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; /* negotiate security */ e = sft->InitializeSecurityContext (&stream->cred,size ? &stream->context : NIL,host,req,0, SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t); /* have an output buffer we need to send? */ if (obuf[0].pvBuffer && obuf[0].cbBuffer) { if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer)) reason = "Unexpected TCP output disconnect"; /* free the buffer */ sft->FreeContextBuffer (obuf[0].pvBuffer); } if (!reason) switch (e) { /* negotiation state */ case SEC_I_INCOMPLETE_CREDENTIALS: break; /* server wants client auth */ case SEC_I_CONTINUE_NEEDED: if (size) { /* continue, read any data? */ /* yes, anything regurgiated back to us? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); size = ibuf[1].cbBuffer; break; } size = 0; /* otherwise, read more stuff from server */ } case SEC_E_INCOMPLETE_MESSAGE: /* need to read more data from server */ if (!tcp_getdata (stream->tcpstream)) reason = "Unexpected TCP input disconnect"; else { memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr); size += stream->tcpstream->ictr; /* empty it from TCP's buffers */ stream->tcpstream->iptr += stream->tcpstream->ictr; stream->tcpstream->ictr = 0; } break; case SEC_E_OK: /* success, any data to be regurgitated? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf, buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); stream->tcpstream->ictr = ibuf[1].cbBuffer; } if (reason = ssl_analyze_status (sft->QueryContextAttributes (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)) break; /* error getting sizes */ fs_give ((void **) &buf); /* flush temporary buffer */ /* make maximum-sized buffers */ stream->bufsize = stream->sizes.cbHeader + stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer; if (stream->sizes.cbMaximumMessage < SSLBUFLEN) fatal ("cbMaximumMessage is less than SSLBUFLEN!"); else if (stream->sizes.cbMaximumMessage < 16384) { sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384", (long) stream->sizes.cbMaximumMessage); mm_log (tmp,NIL); } stream->ibuf = (char *) fs_get (stream->bufsize); stream->obuf = (char *) fs_get (stream->bufsize); return stream; default: reason = ssl_analyze_status (e,buf); } } ssl_close (stream); /* failed to do SSL */ stream = NIL; /* no stream returned */ fs_give ((void **) &buf); /* flush temporary buffer */ switch (*reason) { /* analyze reason */ case '*': /* certificate failure */ ++reason; /* skip over certificate failure indication */ /* pass to error callback */ if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } case '\0': /* user answered no to certificate callback */ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); break; default: /* non-certificate failure */ if (flags & NET_TRYSSL); /* no error output if tryssl */ /* pass to error callback */ else if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } break; } return stream; } /* Generate error text from SSL error code * Accepts: SSL status * scratch buffer * Returns: text if error status, else NIL */ static char *ssl_analyze_status (SECURITY_STATUS err,char *buf) { switch (err) { case SEC_E_OK: /* no error */ case SEC_I_CONTINUE_NEEDED: case SEC_I_INCOMPLETE_CREDENTIALS: case SEC_E_INCOMPLETE_MESSAGE: return NIL; case SEC_E_NO_AUTHENTICATING_AUTHORITY: return "*No authority could be contacted for authentication"; case SEC_E_WRONG_PRINCIPAL: return "*Server name does not match certificate"; case SEC_E_UNTRUSTED_ROOT: return "*Self-signed certificate or untrusted authority"; case SEC_E_CERT_EXPIRED: return "*Certificate has expired"; case SEC_E_INVALID_TOKEN: return "Invalid token, probably not an SSL server"; case SEC_E_UNSUPPORTED_FUNCTION: return "SSL not supported on this machine - upgrade your system software"; } sprintf (buf,"Unexpected SChannel error %lx - report this",err); return buf; } /* SSL receive line * Accepts: SSL stream * Returns: text line string or NIL if failure */ char *ssl_getline (SSLSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!ssl_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!ssl_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = ssl_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* SSL receive buffer * Accepts: SSL stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; while (size > 0) { /* until request satisfied */ if (!ssl_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (buffer,stream->iptr,n); buffer += n; /* update pointer */ stream->iptr += n; size -= n; /* update # of bytes to do */ stream->ictr -= n; } buffer[0] = '\0'; /* tie off string */ return T; } /* SSL receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long ssl_getdata (SSLSTREAM *stream) { SECURITY_STATUS status; SecBuffer buf[4]; SecBufferDesc msg; size_t n = 0; size_t i; while (stream->ictr < 1) { /* decrypted buffer empty? */ do { /* yes, make sure have data from TCP */ if (stream->iextractr) { /* have previous unread data? */ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr); n += stream->iextractr; /* update number of bytes read */ stream->iextractr = 0; /* no more extra data */ } else { /* read from TCP */ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream); /* maximum amount of data to copy */ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr))) fatal ("incomplete SecBuffer exceeds maximum buffer size"); /* do the copy */ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i); stream->tcpstream->iptr += i; stream->tcpstream->ictr -= i; n += i; /* update number of bytes to decrypt */ } buf[0].cbBuffer = n; /* first SecBuffer gets data */ buf[0].pvBuffer = stream->ibuf; buf[0].BufferType = SECBUFFER_DATA; /* subsequent ones are for spares */ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; /* number of SecBuffers */ msg.pBuffers = buf; /* first SecBuffer */ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4) (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE); switch (status) { case SEC_E_OK: /* won */ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */ /* hunt for a buffer */ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++); if (i < 4) { /* found a buffer? */ /* yes, set up pointer and counter */ stream->iptr = buf[i].pvBuffer; stream->ictr = buf[i].cbBuffer; /* any unprocessed data? */ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) { /* yes, note for next time around */ stream->iextraptr = buf[i].pvBuffer; stream->iextractr = buf[i].cbBuffer; } } break; default: /* anything else means we've lost */ return ssl_abort (stream); } } return LONGT; } /* SSL send string as record * Accepts: SSL stream * string pointer * Returns: T if success else NIL */ long ssl_soutr (SSLSTREAM *stream,char *string) { return ssl_sout (stream,string,(unsigned long) strlen (string)); } /* SSL send string * Accepts: SSL stream * string pointer * byte count * Returns: T if success else NIL */ long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size) { SecBuffer buf[4]; SecBufferDesc msg; char *s = stream->ibuf; size_t n = 0; while (size) { /* until satisfied request */ /* header */ buf[0].BufferType = SECBUFFER_STREAM_HEADER; memset (buf[0].pvBuffer = stream->obuf,0, buf[0].cbBuffer = stream->sizes.cbHeader); /* message (up to maximum size) */ buf[1].BufferType = SECBUFFER_DATA; memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string, buf[1].cbBuffer = min (size,SSLBUFLEN)); /* trailer */ buf[2].BufferType = SECBUFFER_STREAM_TRAILER; memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0, buf[2].cbBuffer = stream->sizes.cbTrailer); /* spare */ buf[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; /* number of SecBuffers */ msg.pBuffers = buf; /* first SecBuffer */ string += buf[1].cbBuffer; size -= buf[1].cbBuffer; /* this many bytes processed */ /* encrypt and send message */ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3) (&stream->context,0,&msg,NIL) != SEC_E_OK) || !tcp_sout (stream->tcpstream,stream->obuf, buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer)) return ssl_abort (stream);/* encryption or sending failed */ } return LONGT; } /* SSL close * Accepts: SSL stream */ void ssl_close (SSLSTREAM *stream) { ssl_abort (stream); /* nuke the stream */ fs_give ((void **) &stream); /* flush the stream */ } /* SSL abort stream * Accepts: SSL stream * Returns: NIL always */ static long ssl_abort (SSLSTREAM *stream) { if (stream->tcpstream) { /* close TCP stream */ sft->DeleteSecurityContext (&stream->context); sft->FreeCredentialHandle (&stream->cred); tcp_close (stream->tcpstream); stream->tcpstream = NIL; } if (stream->ibuf) fs_give ((void **) &stream->ibuf); if (stream->obuf) fs_give ((void **) &stream->obuf); return NIL; } /* SSL get host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_host (SSLSTREAM *stream) { return tcp_host (stream->tcpstream); } /* SSL get remote host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_remotehost (SSLSTREAM *stream) { return tcp_remotehost (stream->tcpstream); } /* SSL return port for this stream * Accepts: SSL stream * Returns: port number for this stream */ unsigned long ssl_port (SSLSTREAM *stream) { return tcp_port (stream->tcpstream); } /* SSL get local host name * Accepts: SSL stream * Returns: local host name */ char *ssl_localhost (SSLSTREAM *stream) { return tcp_localhost (stream->tcpstream); } #include "ssl_none.c" /* currently no server support */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/ssl_w2k.c000066400000000000000000000506111137544547100230070ustar00rootroot00000000000000/* * Program: SSL authentication/encryption module for Windows 2000 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 September 1998 * Last Edited: 11 December 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SECURITY_WIN32 #include #include #define SSLBUFLEN 8192 /* SSL I/O stream */ typedef struct ssl_stream { TCPSTREAM *tcpstream; /* TCP stream */ CredHandle cred; /* SSL credentials */ CtxtHandle context; /* SSL context */ /* stream encryption sizes */ SecPkgContext_StreamSizes sizes; size_t bufsize; int ictr; /* input counter */ char *iptr; /* input pointer */ int iextractr; /* extra input counter */ char *iextraptr; /* extra input pointer */ char *ibuf; /* input buffer */ char *obuf; /* output buffer */ } SSLSTREAM; #include "sslio.h" /* Function prototypes */ static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags); static char *ssl_analyze_status (SECURITY_STATUS err,char *buf); static long ssl_abort (SSLSTREAM *stream); /* Secure Sockets Layer network driver dispatch */ static struct ssl_driver ssldriver = { ssl_open, /* open connection */ ssl_aopen, /* open preauthenticated connection */ ssl_getline, /* get a line */ ssl_getbuffer, /* get a buffer */ ssl_soutr, /* output pushed data */ ssl_sout, /* output string */ ssl_close, /* close connection */ ssl_host, /* return host name */ ssl_remotehost, /* return remote host name */ ssl_port, /* return port number */ ssl_localhost /* return local host name */ }; static unsigned long ssltsz = 0;/* SSL maximum token length */ /* One-time SSL initialization */ static int sslonceonly = 0; void ssl_onceonlyinit (void) { if (!sslonceonly++) { /* only need to call it once */ ULONG np; SecPkgInfo *pp; int i; /* get security library */ if (!EnumerateSecurityPackages (&np,&pp)) { /* look for an SSL package */ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) { /* note maximum token size and name */ ssltsz = pp[i].cbMaxToken; /* apply runtime linkage */ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver); mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start); return; /* all done */ } } } } /* SSL open * Accepts: host name * contact service name * contact port number * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = tcp_open (host,service,port); return stream ? ssl_start (stream,host,port) : NIL; } /* SSL authenticated open * Accepts: host name * service name * returned user name buffer * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* don't use this mechanism with SSL */ } /* Start SSL/TLS negotiations * Accepts: open TCP stream of session * user's host name * flags * Returns: SSL stream if success else NIL */ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags) { SECURITY_STATUS e; ULONG a; TimeStamp t; SecBuffer ibuf[2],obuf[1]; SecBufferDesc ibufs,obufs; SCHANNEL_CRED tlscred; CERT_CONTEXT *cert; CERT_CHAIN_PARA chparam; CERT_CHAIN_CONTEXT *chain; SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy; CERT_CHAIN_POLICY_PARA polparam; CERT_CHAIN_POLICY_STATUS status; char tmp[MAILTMPLEN],certname[256]; char *reason = NIL; ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | ISC_REQ_MANUAL_CRED_VALIDATION; LPSTR usage[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; PWSTR whost = NIL; char *buf = (char *) fs_get (ssltsz); unsigned long size = 0; sslcertificatequery_t scq = (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL); SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); stream->tcpstream = tstream; /* bind TCP stream */ /* initialize TLS credential */ memset (&tlscred,0,sizeof (SCHANNEL_CRED)); tlscred.dwVersion = SCHANNEL_CRED_VERSION; tlscred.grbitEnabledProtocols = SP_PROT_TLS1; /* acquire credentials */ if (AcquireCredentialsHandle (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ? &tlscred : NIL,NIL,NIL,&stream->cred,&t) != SEC_E_OK) reason = "Acquire credentials handle failed"; else while (!reason) { /* negotiate security context */ /* initialize buffers */ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf; ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL; obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL; ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; ibuf[1].BufferType = SECBUFFER_EMPTY; /* initialize buffer descriptors */ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; ibufs.cBuffers = 2; obufs.cBuffers = 1; ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; /* negotiate security */ e = InitializeSecurityContext (&stream->cred,size ? &stream->context : NIL,host,req,0, SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t); /* have an output buffer we need to send? */ if (obuf[0].pvBuffer && obuf[0].cbBuffer) { if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer)) reason = "Unexpected TCP output disconnect"; /* free the buffer */ FreeContextBuffer (obuf[0].pvBuffer); } if (!reason) switch (e) { /* negotiation state */ case SEC_I_INCOMPLETE_CREDENTIALS: break; /* server wants client auth */ case SEC_I_CONTINUE_NEEDED: if (size) { /* continue, read any data? */ /* yes, anything regurgiated back to us? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); size = ibuf[1].cbBuffer; break; } size = 0; /* otherwise, read more stuff from server */ } case SEC_E_INCOMPLETE_MESSAGE: /* need to read more data from server */ if (!tcp_getdata (stream->tcpstream)) reason = "Unexpected TCP input disconnect"; else { memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr); size += stream->tcpstream->ictr; /* empty it from TCP's buffers */ stream->tcpstream->iptr += stream->tcpstream->ictr; stream->tcpstream->ictr = 0; } break; case SEC_E_OK: /* success, any data to be regurgitated? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf, buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); stream->tcpstream->ictr = ibuf[1].cbBuffer; } if (!(flags & NET_NOVALIDATECERT)) { /* need validation, make wchar of host */ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) && (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) && MultiByteToWideChar (CP_ACP,0,host,-1,whost,size))) fatal ("Can't make wchar of host name!"); /* get certificate */ if ((QueryContextAttributes (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) != SEC_E_OK) || !cert) { reason = "*Unable to get certificate"; strcpy (certname,""); } else { /* get certificate subject name */ CertNameToStr (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &cert->pCertInfo->Subject,CERT_X500_NAME_STR, certname,255); /* build certificate chain */ memset (&chparam,0,sizeof (chparam)); chparam.cbSize = sizeof (chparam); chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage; chparam.RequestedUsage.Usage.cUsageIdentifier = sizeof (usage) / sizeof (LPSTR); if (!CertGetCertificateChain (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain)) reason = ssl_analyze_status (GetLastError (),tmp); else { /* validate certificate chain */ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA)); policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA); policy.dwAuthType = AUTHTYPE_SERVER; policy.fdwChecks = NIL; policy.pwszServerName = whost; memset (&polparam,0,sizeof (polparam)); polparam.cbSize = sizeof (polparam); polparam.pvExtraPolicyPara = &policy; memset (&status,0,sizeof (status)); status.cbSize = sizeof (status); if (!CertVerifyCertificateChainPolicy (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status)) reason = ssl_analyze_status (GetLastError (),tmp); else if (status.dwError) reason = ssl_analyze_status (status.dwError,tmp); CertFreeCertificateChain (chain); } } if (whost) fs_give ((void **) &whost); if (reason) { /* got an error? */ /* application callback */ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason, host,certname) ? NIL : ""; else if (*certname) { /* error message to return via mm_log() */ sprintf (buf,"*%.128s: %.255s", (*reason == '*') ? reason + 1 : reason,certname); reason = buf; } } } if (reason || (reason = ssl_analyze_status (QueryContextAttributes (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf))) break; /* error in certificate or getting sizes */ fs_give ((void **) &buf); /* flush temporary buffer */ /* make maximum-sized buffers */ stream->bufsize = stream->sizes.cbHeader + stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer; if (stream->sizes.cbMaximumMessage < SSLBUFLEN) fatal ("cbMaximumMessage is less than SSLBUFLEN!"); else if (stream->sizes.cbMaximumMessage < 16384) { sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384", (long) stream->sizes.cbMaximumMessage); mm_log (tmp,NIL); } stream->ibuf = (char *) fs_get (stream->bufsize); stream->obuf = (char *) fs_get (stream->bufsize); return stream; default: reason = ssl_analyze_status (e,buf); } } ssl_close (stream); /* failed to do SSL */ stream = NIL; /* no stream returned */ switch (*reason) { /* analyze reason */ case '*': /* certificate failure */ ++reason; /* skip over certificate failure indication */ /* pass to error callback */ if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } case '\0': /* user answered no to certificate callback */ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); break; default: /* non-certificate failure */ if (flags & NET_TRYSSL); /* no error output if tryssl */ /* pass to error callback */ else if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } break; } fs_give ((void **) &buf); /* flush temporary buffer */ return stream; } /* Generate error text from SSL error code * Accepts: SSL status * scratch buffer * Returns: text if error status, else NIL */ static char *ssl_analyze_status (SECURITY_STATUS err,char *buf) { switch (err) { case SEC_E_OK: /* no error */ case SEC_I_CONTINUE_NEEDED: case SEC_I_INCOMPLETE_CREDENTIALS: case SEC_E_INCOMPLETE_MESSAGE: return NIL; case SEC_E_NO_AUTHENTICATING_AUTHORITY: mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL); return "*No authority could be contacted for authentication"; case SEC_E_WRONG_PRINCIPAL: mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL); case CERT_E_CN_NO_MATCH: return "*Server name does not match certificate"; case SEC_E_UNTRUSTED_ROOT: mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL); case CERT_E_UNTRUSTEDROOT: return "*Self-signed certificate or untrusted authority"; case SEC_E_CERT_EXPIRED: mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL); case CERT_E_EXPIRED: return "*Certificate has expired"; case CERT_E_REVOKED: return "*Certificate revoked"; case SEC_E_INVALID_TOKEN: return "Invalid token, probably not an SSL server"; case SEC_E_UNSUPPORTED_FUNCTION: return "SSL not supported on this machine - upgrade your system software"; } sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err); return buf; } /* SSL receive line * Accepts: SSL stream * Returns: text line string or NIL if failure */ char *ssl_getline (SSLSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!ssl_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!ssl_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = ssl_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* SSL receive buffer * Accepts: SSL stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; while (size > 0) { /* until request satisfied */ if (!ssl_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (buffer,stream->iptr,n); buffer += n; /* update pointer */ stream->iptr += n; size -= n; /* update # of bytes to do */ stream->ictr -= n; } buffer[0] = '\0'; /* tie off string */ return T; } /* SSL receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long ssl_getdata (SSLSTREAM *stream) { SECURITY_STATUS status; SecBuffer buf[4]; SecBufferDesc msg; size_t n = 0; size_t i; while (stream->ictr < 1) { /* decrypted buffer empty? */ do { /* yes, make sure have data from TCP */ if (stream->iextractr) { /* have previous unread data? */ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr); n += stream->iextractr; /* update number of bytes read */ stream->iextractr = 0; /* no more extra data */ } else { /* read from TCP */ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream); /* maximum amount of data to copy */ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr))) fatal ("incomplete SecBuffer exceeds maximum buffer size"); /* do the copy */ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i); stream->tcpstream->iptr += i; stream->tcpstream->ictr -= i; n += i; /* update number of bytes to decrypt */ } buf[0].cbBuffer = n; /* first SecBuffer gets data */ buf[0].pvBuffer = stream->ibuf; buf[0].BufferType = SECBUFFER_DATA; /* subsequent ones are for spares */ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; /* number of SecBuffers */ msg.pBuffers = buf; /* first SecBuffer */ } while ((status = DecryptMessage (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE); switch (status) { case SEC_E_OK: /* won */ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */ /* hunt for a buffer */ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++); if (i < 4) { /* found a buffer? */ /* yes, set up pointer and counter */ stream->iptr = buf[i].pvBuffer; stream->ictr = buf[i].cbBuffer; /* any unprocessed data? */ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) { /* yes, note for next time around */ stream->iextraptr = buf[i].pvBuffer; stream->iextractr = buf[i].cbBuffer; } } break; default: /* anything else means we've lost */ return ssl_abort (stream); } } return LONGT; } /* SSL send string as record * Accepts: SSL stream * string pointer * Returns: T if success else NIL */ long ssl_soutr (SSLSTREAM *stream,char *string) { return ssl_sout (stream,string,(unsigned long) strlen (string)); } /* SSL send string * Accepts: SSL stream * string pointer * byte count * Returns: T if success else NIL */ long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size) { SecBuffer buf[4]; SecBufferDesc msg; char *s; size_t n; if (!stream->tcpstream) return NIL; /* until request satisfied */ for (s = stream->ibuf,n = 0; size;) { /* header */ buf[0].BufferType = SECBUFFER_STREAM_HEADER; memset (buf[0].pvBuffer = stream->obuf,0, buf[0].cbBuffer = stream->sizes.cbHeader); /* message (up to maximum size) */ buf[1].BufferType = SECBUFFER_DATA; memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string, buf[1].cbBuffer = min (size,SSLBUFLEN)); /* trailer */ buf[2].BufferType = SECBUFFER_STREAM_TRAILER; memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0, buf[2].cbBuffer = stream->sizes.cbTrailer); /* spare */ buf[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; /* number of SecBuffers */ msg.pBuffers = buf; /* first SecBuffer */ string += buf[1].cbBuffer; size -= buf[1].cbBuffer; /* this many bytes processed */ /* encrypt and send message */ if ((EncryptMessage (&stream->context,0,&msg,NIL) != SEC_E_OK) || !tcp_sout (stream->tcpstream,stream->obuf, buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer)) return ssl_abort (stream);/* encryption or sending failed */ } return LONGT; } /* SSL close * Accepts: SSL stream */ void ssl_close (SSLSTREAM *stream) { ssl_abort (stream); /* nuke the stream */ fs_give ((void **) &stream); /* flush the stream */ } /* SSL abort stream * Accepts: SSL stream * Returns: NIL always */ static long ssl_abort (SSLSTREAM *stream) { if (stream->tcpstream) { /* close TCP stream */ DeleteSecurityContext (&stream->context); FreeCredentialHandle (&stream->cred); tcp_close (stream->tcpstream); stream->tcpstream = NIL; } if (stream->ibuf) fs_give ((void **) &stream->ibuf); if (stream->obuf) fs_give ((void **) &stream->obuf); return NIL; } /* SSL get host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_host (SSLSTREAM *stream) { return tcp_host (stream->tcpstream); } /* SSL get remote host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_remotehost (SSLSTREAM *stream) { return tcp_remotehost (stream->tcpstream); } /* SSL return port for this stream * Accepts: SSL stream * Returns: port number for this stream */ unsigned long ssl_port (SSLSTREAM *stream) { return tcp_port (stream->tcpstream); } /* SSL get local host name * Accepts: SSL stream * Returns: local host name */ char *ssl_localhost (SSLSTREAM *stream) { return tcp_localhost (stream->tcpstream); } #include "ssl_none.c" /* currently no server support */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/tcp_nt.c000066400000000000000000000566121137544547100227210ustar00rootroot00000000000000/* * Program: Winsock TCP/IP routines * * Author: Mark Crispin from Mike Seibel's Winsock code * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 8 August 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "ip_nt.c" #define TCPMAXSEND 32768 /* Private functions */ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port, char *tmp,char *hst); long tcp_abort (SOCKET *sock); char *tcp_name (struct sockaddr *sadr,long flag); char *tcp_name_valid (char *s); /* Private data */ int wsa_initted = 0; /* init ? */ static int wsa_sock_open = 0; /* keep track of open sockets */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_read = 0; /* TCP timeouts, in seconds */ static long ttmo_write = 0; static long allowreversedns = T;/* allow reverse DNS lookup */ static long tcpdebug = NIL; /* extra TCP debugging telemetry */ /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; case SET_ALLOWREVERSEDNS: allowreversedns = (long) value; case GET_ALLOWREVERSEDNS: ret = (void *) allowreversedns; break; case SET_TCPDEBUG: tcpdebug = (long) value; case GET_TCPDEBUG: ret = (void *) tcpdebug; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number and optional silent flag * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; int i,family; SOCKET sock = INVALID_SOCKET; int silent = (port & NET_SILENT) ? T : NIL; char *s,*hostname,tmp[MAILTMPLEN]; void *adr,*next; size_t adrlen; struct servent *sv = NIL; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (i = (int) WSAStartup (WINSOCK_VERSION,&wsock)) { wsa_initted = 0; /* in case we try again */ sprintf (tmp,"Unable to start Windows Sockets (%d)",i); mm_log (tmp,ERROR); return NIL; } } port &= 0xffff; /* erase flags */ /* lookup service */ if (service && (sv = getservbyname (service,"tcp"))) port = ntohs (sv->s_port); /* The domain literal form is used (rather than simply the dotted decimal as with other Windows programs) because it has to be a valid "host name" in mailsystem terminology. */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (tmp,host+1); /* yes, copy number part */ tmp[strlen (tmp)-1] = '\0'; if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) { (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (family,adr,adrlen,(unsigned short) port,tmp, hostname = host); (*bn) (BLOCK_NONE,NIL); fs_give ((void **) &adr); } else sprintf (tmp,"Bad format domain-literal: %.80s",host); } else { /* lookup host name */ if (tcpdebug) { sprintf (tmp,"DNS resolution %.80s",host); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */ if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next))) sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError (),host); (*bn) (BLOCK_NONE,NIL); if (s) { /* DNS resolution won? */ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG); wsa_sock_open++; /* prevent tcp_abort() from freeing in loop */ do { (*bn) (BLOCK_TCPOPEN,NIL); if (((sock = tcp_socket_open (family,s,adrlen,(unsigned short) port, tmp,hostname)) == INVALID_SOCKET) && (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) && !silent) mm_log (tmp,WARN); (*bn) (BLOCK_NONE,NIL); } while ((sock == INVALID_SOCKET) && s); wsa_sock_open--; /* undo protection */ } } if (sock == INVALID_SOCKET) { /* do possible cleanup action */ if (!silent) mm_log (tmp,ERROR); tcp_abort (&sock); } else { /* got a socket, create TCP/IP stream */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); stream->port = port; /* port number */ /* init socket */ stream->tcpsi = stream->tcpso = sock; stream->ictr = 0; /* init input counter */ /* copy official host name */ stream->host = cpystr (hostname); if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG); } return stream; /* return success */ } /* Open a TCP socket * Accepts: protocol family * address to connect to * address length * port * scratch buffer * host name * Returns: socket if success, else SOCKET_ERROR with error string in scratch */ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port, char *tmp,char *hst) { int sock; size_t len; char *s; struct protoent *pt = getprotobyname ("tcp"); struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len); sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr)); mm_log (tmp,NIL); /* get a TCP stream */ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) == INVALID_SOCKET) sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError ()); else { wsa_sock_open++; /* count this socket as open */ /* open connection */ if (connect (sock,sadr,len) == SOCKET_ERROR) { switch (WSAGetLastError ()) { case WSAECONNREFUSED: s = "Refused"; break; case WSAENOBUFS: s = "Insufficient system resources"; break; case WSAETIMEDOUT: s = "Timed out"; break; case WSAEHOSTUNREACH: s = "Host unreachable"; break; default: s = "Unknown error"; break; } sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s, WSAGetLastError ()); tcp_abort (&sock); /* flush socket */ sock = INVALID_SOCKET; } } fs_give ((void **) &sadr); return sock; /* return the socket */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* always NIL on Windows */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s) { unsigned long n; /* make sure socket still alive */ if (stream->tcpsi == INVALID_SOCKET) return NIL; /* can transfer bytes from buffer? */ if (n = min (size,stream->ictr)) { memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */ s += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } if (size) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_TCPREAD,NIL); while (size > 0) { /* until request satisfied */ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG); /* simple case if not a socket */ if (stream->tcpsi != stream->tcpso) while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) && (errno == EINTR)); else { /* socket case */ time_t tl = time (0); time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ /* set bit in selection vectors */ FD_SET (stream->tcpsi,&fds); FD_SET (stream->tcpsi,&efds); errno = NIL; /* initially no error */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == WSAEINTR)); /* success from select, read what we can */ if (i > 0) while (((i = recv (stream->tcpsi,s, (int) min (maxposint,size),0)) == SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ if (tmoh && (*tmoh) (now - t,now - tl)) continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (&stream->tcpsi); } } if (i <= 0) { /* error seen? */ if (tcpdebug) { char tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno); else s = "TCP buffer read end of file"; mm_log (s,TCPDEBUG); } return tcp_abort (&stream->tcpsi); } s += i; /* point at new place to write */ size -= i; /* reduce byte count */ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } (*bn) (BLOCK_NONE,NIL); } *s = '\0'; /* tie off string */ return LONGT; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->tcpsi == INVALID_SOCKET) return NIL; (*bn) (BLOCK_TCPREAD,NIL); while (stream->ictr < 1) { /* if nothing in the buffer */ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG); /* simple case if not a socket */ if (stream->tcpsi != stream->tcpso) while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); else { time_t tl = time (0); time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ /* set bit in selection vectors */ FD_SET (stream->tcpsi,&fds); FD_SET (stream->tcpsi,&efds); errno = NIL; /* initially no error */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == WSAEINTR)); /* success from select, read what we can */ if (i > 0) while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) == SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ if (tmoh && (*tmoh) (now - t,now - tl)) continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG); return tcp_abort (&stream->tcpsi); } } if (i <= 0) { /* error seen? */ if (tcpdebug) { char *s,tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno); else s = "TCP data read end of file"; mm_log (tmp,TCPDEBUG); } return tcp_abort (&stream->tcpsi); } stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG); } (*bn) (BLOCK_NONE,NIL); return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; struct timeval tmo; fd_set fds,efds; time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); tmo.tv_sec = ttmo_write; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ if (stream->tcpso == INVALID_SOCKET) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); while (size > 0) { /* until request satisfied */ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG); /* simple case if not a socket */ if (stream->tcpsi != stream->tcpso) while (((i = write (stream->tcpso,string,min (size,TCPMAXSEND))) < 0) && (errno == EINTR)); else { time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_write ? now + ttmo_write : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ /* set bit in selection vectors */ FD_SET (stream->tcpso,&fds); FD_SET(stream->tcpso,&efds); errno = NIL; /* block and write */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == WSAEINTR)); /* OK to send data? */ if (i > 0) while (((i = send (stream->tcpso,string, (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ if (tmoh && (*tmoh) (now - t,now - tl)) continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG); return tcp_abort (&stream->tcpsi); } } if (i <= 0) { /* error seen? */ if (tcpdebug) { char tmp[MAILTMPLEN]; sprintf (tmp,"TCP write I/O error %d",errno); mm_log (tmp,TCPDEBUG); } return tcp_abort (&stream->tcpsi); } string += i; /* how much we sent */ size -= i; /* count this size */ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); } (*bn) (BLOCK_NONE,NIL); return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (&stream->tcpsi); /* nuke the socket */ /* flush host names */ if (stream->host) fs_give ((void **) &stream->host); if (stream->remotehost) fs_give ((void **) &stream->remotehost); if (stream->localhost) fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: WinSock socket * Returns: NIL, always */ long tcp_abort (SOCKET *sock) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* something to close? */ if (sock && (*sock != INVALID_SOCKET)) { (*bn) (BLOCK_TCPCLOSE,NIL); closesocket (*sock); /* WinSock socket close */ *sock = INVALID_SOCKET; (*bn) (BLOCK_NONE,NIL); wsa_sock_open--; /* drop this socket */ } /* no more open streams? */ if (wsa_initted && !wsa_sock_open) { mm_log ("Winsock cleanup",NIL); wsa_initted = 0; /* no more sockets, so... */ WSACleanup (); /* free up resources until needed */ } return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* use tcp_remotehost() if want guarantees */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { if (!stream->remotehost) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); stream->remotehost = /* get socket's peer name */ ((getpeername (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) || (sadrlen <= 0)) ? cpystr (stream->host) : tcp_name (sadr,NIL); fs_give ((void **) &sadr); } return stream->remotehost; } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { if (!stream->localhost) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); stream->localhost = /* get socket's name */ ((stream->port & 0xffff000) || ((getsockname (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) || (sadrlen <= 0))) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL); fs_give ((void **) &sadr); } return stream->localhost; /* return local host name */ } /* TCP/IP get client host address (server calls only) * Returns: client host address */ char *tcp_clientaddr () { if (!myClientAddr) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); myClientAddr = /* get stdin's peer name */ ((getpeername (0,sadr,&sadrlen) == SOCKET_ERROR) || (sadrlen <= 0)) ? cpystr ("UNKNOWN") : ip_sockaddrtostring (sadr); fs_give ((void **) &sadr); } return myClientAddr; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { if (!myClientHost) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); myClientHost = /* get stdin's peer name */ ((getpeername (0,sadr,&sadrlen) == SOCKET_ERROR) || (sadrlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (sadr,T); fs_give ((void **) &sadr); } return myClientHost; } /* TCP/IP get server host address (server calls only) * Returns: server host address */ char *tcp_serveraddr () { if (!myServerAddr) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); myServerAddr = /* get stdin's peer name */ ((getsockname (0,sadr,&sadrlen) == SOCKET_ERROR) || (sadrlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (ip_sockaddrtostring (sadr)); fs_give ((void **) &sadr); } return myServerAddr; } /* TCP/IP get server host name (server calls only) * Returns: server host name */ static long myServerPort = -1; char *tcp_serverhost () { if (!myServerHost) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (WSAStartup (WINSOCK_VERSION,&wsock)) { wsa_initted = 0; return "random-pc"; /* try again later? */ } } /* get stdin's name */ myServerHost = ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR)|| (sadrlen <= 0) || ((myServerPort = ip_sockaddrtoport (sadr)) < 0)) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL); fs_give ((void **) &sadr); } return myServerHost; } /* TCP/IP get server port number (server calls only) * Returns: server port number */ long tcp_serverport () { if (!myServerHost) tcp_serverhost (); return myServerPort; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { char *ret,host[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name; (*bn) (BLOCK_DNSLOOKUP,NIL); if (tcpdebug) { sprintf (host,"DNS canonicalization %.80s",name); mm_log (host,TCPDEBUG); } /* get canonical name */ if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name; (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG); return ret; } /* TCP/IP return name from socket * Accepts: socket * verbose flag * Returns: cpystr name */ char *tcp_name (struct sockaddr *sadr,long flag) { char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN]; sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr)); if (allowreversedns) { blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL); if (tcpdebug) { sprintf (tmp,"Reverse DNS resolution %s",adr); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ /* translate address to name */ if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) { /* produce verbose form if needed */ if (flag) sprintf (ret = tmp,"%s %s",t,adr); else ret = t; } (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG); } return cpystr (ret); } /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { char tmp[MAILTMPLEN]; if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (WSAStartup (WINSOCK_VERSION,&wsock)) { wsa_initted = 0; return "random-pc"; /* try again later? */ } } myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ? "random-pc" : tcp_canonical (tmp)); } return myLocalHost; } /* Validate name * Accepts: domain name * Returns: T if valid, NIL otherwise */ char *tcp_name_valid (char *s) { int c; char *ret,*tail; /* must be non-empty and not too long */ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) { /* must be alnum, dot, or hyphen */ while ((c = *s++) && (s <= tail) && (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.'))); if (c) ret = NIL; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/tcp_nt.h000066400000000000000000000023261137544547100227170ustar00rootroot00000000000000/* * Program: Winsock TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 23 December 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer -- must be large enough to prevent overflow */ #define BUFLEN 16384 /* 32768 causes stdin read() to barf */ #include #include #undef ERROR /* quell conflicting definition diagnostic */ /* TCP I/O stream (must be before osdep.h is included) */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ char *remotehost; /* remote host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ SOCKET tcpsi; /* tcp socket */ SOCKET tcpso; /* tcp socket */ long ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/tenexnt.c000066400000000000000000001233671137544547100231210ustar00rootroot00000000000000/* * Program: Tenex mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. * * TEXT SIZE SEMANTICS * * Most of the text sizes are in internal (LF-only) form, except for the * msg.text size. Beware. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "misc.h" #include "dummy.h" /* TENEX I/O stream local data */ typedef struct tenex_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* local snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } TENEXLOCAL; /* Convenient access to local data */ #define LOCAL ((TENEXLOCAL *) stream->local) /* Function prototypes */ DRIVER *tenex_valid (char *name); int tenex_isvalid (char *name,char *tmp); void *tenex_parameters (long function,void *value); void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void tenex_list (MAILSTREAM *stream,char *ref,char *pat); void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat); long tenex_create (MAILSTREAM *stream,char *mailbox); long tenex_delete (MAILSTREAM *stream,char *mailbox); long tenex_rename (MAILSTREAM *stream,char *old,char *newname); long tenex_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *tenex_open (MAILSTREAM *stream); void tenex_close (MAILSTREAM *stream,long options); void tenex_fast (MAILSTREAM *stream,char *sequence,long flags); void tenex_flags (MAILSTREAM *stream,char *sequence,long flags); char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long tenex_ping (MAILSTREAM *stream); void tenex_check (MAILSTREAM *stream); void tenex_snarf (MAILSTREAM *stream); void tenex_expunge (MAILSTREAM *stream); long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); unsigned long tenex_size (MAILSTREAM *stream,unsigned long m); long tenex_parse (MAILSTREAM *stream); MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno); void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void tenex_update_status (MAILSTREAM *stream,unsigned long msgno, long syncflag); unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* Tenex mail routines */ /* Driver dispatch used by MAIL */ DRIVER tenexdriver = { "tenex", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ tenex_valid, /* mailbox is valid for us */ tenex_parameters, /* manipulate parameters */ tenex_scan, /* scan mailboxes */ tenex_list, /* list mailboxes */ tenex_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ tenex_create, /* create mailbox */ tenex_delete, /* delete mailbox */ tenex_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ tenex_open, /* open mailbox */ tenex_close, /* close mailbox */ tenex_flags, /* fetch message "fast" attributes */ tenex_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ tenex_header, /* fetch message header */ tenex_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ tenex_flag, /* modify flags */ tenex_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ tenex_ping, /* ping mailbox to see if still alive */ tenex_check, /* check for new messages */ tenex_expunge, /* expunge deleted messages */ tenex_copy, /* copy messages to another mailbox */ tenex_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM tenexproto = {&tenexdriver}; /* Tenex mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *tenex_valid (char *name) { char tmp[MAILTMPLEN]; return tenex_isvalid (name,tmp) ? &tenexdriver : NIL; } /* Tenex mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int tenex_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) && (s[-1] != '\015')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { /* preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ } } } /* in case INBOX but not tenex format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* Tenex manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tenex_parameters (long function,void *value) { return NIL; } /* Tenex mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* Tenex mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* Tenex mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* Tenex mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long tenex_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN]; if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s); sprintf (mbx,"Can't create %.80s: invalid name",mailbox); mm_log (mbx,ERROR); return NIL; } /* Tenex mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long tenex_delete (MAILSTREAM *stream,char *mailbox) { return tenex_rename (stream,mailbox,NIL); } /* Tenex mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long tenex_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); mm_log (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,tmp)) ret = NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } else { flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } unlockfd (ld,lock); /* release exclusive parse/append permission */ return ret; /* return success */ } /* Tenex mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *tenex_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &tenexproto; if (stream->local) fatal ("tenex recycle stream"); /* canonicalize the mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ mm_log ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (TENEXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) { mm_log ("Unable to lock open mailbox",ERROR); return NIL; } flock (LOCAL->fd,LOCK_SH); /* lock the file */ unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ LOCAL->filetime = 0; /* time not set up yet */ LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ stream->uid_validity = time (0); /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (tenex_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* Tenex mail close * Accepts: MAIL stream * close options */ void tenex_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) tenex_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* Tenex mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to get flags */ void tenex_flags (MAILSTREAM *stream,char *sequence,long flags) { STRING bs; MESSAGECACHE *elt; unsigned long i; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { if (!elt->rfc822_size) { /* have header size yet? */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); /* resize bigbuf if necessary */ if (LOCAL->buflen < elt->private.msg.full.text.size) { fs_give ((void **) &LOCAL->buf); LOCAL->buflen = elt->private.msg.full.text.size; LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1); } /* tie off string */ LOCAL->buf[elt->private.msg.full.text.size] = '\0'; /* read in the message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size); INIT (&bs,mail_string,(void *) LOCAL->buf, elt->private.msg.full.text.size); /* calculate its CRLF size */ elt->rfc822_size = unix_crlflen (&bs); } tenex_elt (stream,i); /* get current flags from file */ } } /* TENEX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { char *s; unsigned long i; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET); if (flags & FT_INTERNAL) { if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length = i); } else { s = (char *) fs_get (i + 1);/* get readin buffer */ s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i); fs_give ((void **) &s); /* free readin buffer */ } return LOCAL->buf; } /* TENEX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T, always */ long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = tenex_elt (stream,msgno); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ tenex_update_status (stream,msgno,T); mm_flags (stream,msgno); } if (flags & FT_INTERNAL) { /* if internal representation wanted */ /* find header position */ i = tenex_hdrpos (stream,msgno,&j); if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* slurp the data */ if (read (LOCAL->fd,LOCAL->buf,i) != (long) i) return NIL; /* set up stringstruct for internal */ INIT (bs,mail_string,LOCAL->buf,i); } else { /* normal form, previous text cached? */ if (elt->private.uid == LOCAL->uid) i = elt->private.msg.text.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = tenex_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1); s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ i = elt->private.msg.text.text.size = strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i); fs_give ((void **) &s); /* free readin buffer */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); } return T; /* success */ } /* Tenex mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { struct utimbuf times; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read comes after all that */ utime (stream->mailbox,×); } } /* Tenex mail per-message modify flags * Accepts: MAIL stream * message cache element */ void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ tenex_update_status (stream,elt->msgno,NIL); } /* Tenex mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long tenex_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ mm_notify (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) tenex_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (tenex_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } return r; /* return result of the parse */ } /* Tenex mail check mailbox (reparses status too) * Accepts: MAIL stream */ void tenex_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL); } /* Tenex mail expunge mailbox * Accepts: MAIL stream */ void tenex_expunge (MAILSTREAM *stream) { struct utimbuf times; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; /* do nothing if stream dead */ if (!tenex_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ mm_log ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* get exclusive access */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!tenex_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ mm_log("Can't expunge because mailbox is in use by another process",ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } mm_critical (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = tenex_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + tenex_size (stream,i); if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); mm_log (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* reset atime to now */ utime (stream->mailbox,×); mm_nocritical (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* Tenex mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; struct utimbuf times; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (dummy_file (file,mailbox),O_BINARY|O_RDWR|O_CREAT, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno)); mm_log (LOCAL->buf,ERROR); return NIL; } mm_critical (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock copy mailbox",ERROR); mm_nocritical (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + tenex_size (stream,i); do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* delete all requested messages */ if (ret && (options & CP_MOVE)) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } /* set atime to now-1 if successful copy */ if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = tenex_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ tenex_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure atime remains greater */ utime (stream->mailbox,×); } } return ret; } /* Tenex mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; FILE *df; MESSAGECACHE elt; long f; unsigned long i,j,uf,size; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &tenexproto; /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) tenex_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open(dummy_file(file,mailbox),O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } mm_critical (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ i = GETPOS (message); /* remember current position */ for (j = SIZE (message), size = 0; j; --j) if (SNX (message) != '\015') ++size; SETPOS (message,i); /* restore position */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ while (size) if ((c = 0xff & SNX (message)) != '\015') { if (putc (c,df) != EOF) --size; else break; } /* get next message */ if (size || !(*af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); } ret = NIL; } if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ mm_nocritical (stream); /* release critical */ return ret; } /* Internal routines */ /* Tenex mail return internal message size in bytes * Accepts: MAIL stream * message # * Returns: internal size of message */ unsigned long tenex_size (MAILSTREAM *stream,unsigned long m) { MESSAGECACHE *elt = mail_elt (stream,m); return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset : LOCAL->filesize) - (elt->private.special.offset + elt->private.special.text.size); } /* Tenex mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long tenex_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!(s = strchr (LOCAL->buf,'\012'))) { sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 1) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ tenex_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ struct utimbuf times; times.actime = time (0); times.modtime = LOCAL->filetime; utime (stream->mailbox,×); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* Tenex get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; tenex_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) mm_flags (stream,msgno); /* let top level know */ return elt; } /* Tenex read flags from file * Accepts: MAIL stream * Returns: cache element */ void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* Tenex update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { struct utimbuf times; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* write new flags */ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read is later */ utime (stream->mailbox,×); } } } /* Tenex locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; char c = '\0'; char *s = NIL; MESSAGECACHE *elt = tenex_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; unsigned long msiz = tenex_size (stream,msgno); /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for LF LF */ for (siz = 0; siz < msiz; siz++) { if (--i <= 0) /* read another buffer as necessary */ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN)); /* two newline sequence? */ if ((c == '\012') && (*s == '\012')) { /* yes, note for later */ elt->private.msg.header.text.size = (*size = siz + 1); return ret; /* return to caller */ } else c = *s++; /* next character */ } /* header consumes entire message */ elt->private.msg.header.text.size = *size = msiz; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/unixnt.c000066400000000000000000002234141137544547100227530ustar00rootroot00000000000000/* * Program: UNIX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) && ((mailbox[1] == 'N') || (mailbox[1] == 'n')) && ((mailbox[2] == 'B') || (mailbox[2] == 'b')) && ((mailbox[3] == 'O') || (mailbox[3] == 'o')) && ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) { * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "unixnt.h" #include "pseudo.h" #include "fdstring.h" #include "misc.h" #include "dummy.h" /* UNIX I/O stream local data */ typedef struct unix_local { unsigned int dirty : 1; /* disk copy needs updating */ unsigned int pseudo : 1; /* uses a pseudo message */ int fd; /* mailbox file descriptor */ int ld; /* lock file descriptor */ char *lname; /* lock file name */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time (for mbox driver) */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ unsigned long textlen; /* current text length */ char *line; /* returned line */ } UNIXLOCAL; /* Convenient access to local data */ #define LOCAL ((UNIXLOCAL *) stream->local) /* UNIX protected file structure */ typedef struct unix_file { MAILSTREAM *stream; /* current stream */ off_t curpos; /* current file position */ off_t protect; /* protected position */ off_t filepos; /* current last written file position */ char *buf; /* overflow buffer */ size_t buflen; /* current overflow buffer length */ char *bufpos; /* current buffer position */ } UNIXFILE; /* Function prototypes */ DRIVER *unix_valid (char *name); long unix_isvalid_fd (int fd); void *unix_parameters (long function,void *value); void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void unix_list (MAILSTREAM *stream,char *ref,char *pat); void unix_lsub (MAILSTREAM *stream,char *ref,char *pat); long unix_create (MAILSTREAM *stream,char *mailbox); long unix_delete (MAILSTREAM *stream,char *mailbox); long unix_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *unix_open (MAILSTREAM *stream); void unix_close (MAILSTREAM *stream,long options); char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long unix_ping (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_expunge (MAILSTREAM *stream); long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void unix_abort (MAILSTREAM *stream); char *unix_file (char *dst,char *name); int unix_lock (char *file,int flags,int mode,char *lock,int op); void unix_unlock (int fd,MAILSTREAM *stream,char *lock); int unix_parse (MAILSTREAM *stream,char *lock,int op); char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr); unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag); long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock); long unix_extend (MAILSTREAM *stream,unsigned long size); void unix_write (UNIXFILE *f,char *s,unsigned long i); void unix_phys_write (UNIXFILE *f,char *buf,size_t size); /* UNIX mail routines */ /* Driver dispatch used by MAIL */ DRIVER unixdriver = { "unix", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ unix_valid, /* mailbox is valid for us */ unix_parameters, /* manipulate parameters */ unix_scan, /* scan mailboxes */ unix_list, /* list mailboxes */ unix_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ unix_create, /* create mailbox */ unix_delete, /* delete mailbox */ unix_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ unix_open, /* open mailbox */ unix_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ unix_header, /* fetch message header */ unix_text, /* fetch message text */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ unix_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ unix_ping, /* ping mailbox to see if still alive */ unix_check, /* check for new messages */ unix_expunge, /* expunge deleted messages */ unix_copy, /* copy messages to another mailbox */ unix_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM unixproto = {&unixdriver}; /* driver parameters */ static long unix_fromwidget = T; /* UNIX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *unix_valid (char *name) { int fd; DRIVER *ret = NIL; int c,r; char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* must be non-empty file */ if ((t = dummy_file (file,name)) && !stat (t,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1; else { /* ignore leading whitespace */ for (s = tmp,c = '\n'; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t'); c = *s++); if (c == '\n') { /* at start of a line? */ VALID (s,t,r,c); /* yes, validate format */ if (r) ret = &unixdriver; else errno = -1; /* invalid format */ } } close (fd); /* close the file */ /* \Marked status? */ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) { /* yes, preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ } } } return ret; /* return what we should */ } /* UNIX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *unix_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_FROMWIDGET: unix_fromwidget = (long) value; case GET_FROMWIDGET: ret = (void *) unix_fromwidget; break; } return ret; } /* UNIX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* UNIX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* UNIX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* UNIX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long unix_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN]; long ret = NIL; int fd; time_t ti = time (0); if (!(s = dummy_file (mbx,mailbox))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); mm_log (tmp,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,NIL)) { /* done if made directory */ if ((s = strrchr (s,'\\')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else { /* initialize header */ memset (tmp,'\0',MAILTMPLEN); sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti)); if (s = strpbrk (tmp,"\r\n")) *s = '\0'; strcat (tmp,"\r\nDate: "); rfc822_fixed_date (s = tmp + strlen (tmp)); sprintf (s += strlen (s), /* write the pseudo-header */ "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n", pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) ti,pseudo_msg); if ((write (fd,tmp,strlen (tmp)) < 0) || close (fd)) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx, strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ } close (fd); /* close file */ } return ret; } /* UNIX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long unix_delete (MAILSTREAM *stream,char *mailbox) { return unix_rename (stream,mailbox,NIL); } /* UNIX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long unix_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = NIL; char c,*s = NIL; char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN]; int fd,ld; struct stat sbuf; mm_critical (stream); /* get the c-client lock */ if (!dummy_file (file,old) || (newname && (!(s = dummy_file (tmp,newname)) || ((s = strrchr (s,'\\')) && !s[1])))) sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); else if ((ld = lockname (lock,file,NIL)) < 0) sprintf (tmp,"Can't get lock for mailbox %.80s",old); else { /* lock out other c-clients */ if (flock (ld,LOCK_EX|LOCK_NB)) { close (ld); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); } /* lock out non c-client applications */ else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx, LOCK_EX)) < 0) sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno)); else { unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */ if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,newname)) { flock (ld,LOCK_UN); close (ld); /* close c-client lock */ unlink (lock); /* and delete it */ mm_nocritical (stream); return NIL; /* couldn't create superior */ } } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } if (rename (file,tmp)) sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); else ret = T; /* set success */ } else if (unlink (file)) /* want delete */ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); else ret = T; /* set success */ flock (ld,LOCK_UN); /* release c-client lock */ close (ld); /* close c-client lock */ unlink (lock); /* and delete it */ } } mm_nocritical (stream); /* no longer critical */ if (!ret) mm_log (tmp,ERROR); /* log error */ return ret; /* return success or failure */ } /* UNIX mail open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *unix_open (MAILSTREAM *stream) { int fd; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &unixproto; if (stream->local) fatal ("unix recycle stream"); stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); /* canonicalize the stream mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); return NIL; } /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); stream->sequence++; /* bump sequence number */ if (!stream->rdonly) { /* make lock for read/write access */ if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0) mm_log ("Can't open mailbox lock, access is readonly",WARN); /* can get the lock? */ else if (flock (fd,LOCK_EX|LOCK_NB)) { mm_log ("Mailbox is open by another process, access is readonly",WARN); close (fd); } else { /* got the lock, nobody else can alter state */ LOCAL->ld = fd; /* note lock's fd and name */ LOCAL->lname = cpystr (tmp); } } /* parse mailbox */ stream->nmsgs = stream->recent = 0; /* will we be able to get write access? */ if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) { mm_log ("Can't get write access to mailbox, access is readonly",WARN); flock (LOCAL->ld,LOCK_UN); /* release the lock */ close (LOCAL->ld); /* close the lock file */ LOCAL->ld = -1; /* no more lock fd */ unlink (LOCAL->lname); /* delete it */ } /* reset UID validity */ stream->uid_validity = stream->uid_last = 0; if (stream->silent && !stream->rdonly && (LOCAL->ld < 0)) unix_abort (stream); /* abort if can't get RW silent stream */ /* parse mailbox */ else if (unix_parse (stream,tmp,LOCK_SH)) { unix_unlock (LOCAL->fd,stream,tmp); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ } if (!LOCAL) return NIL; /* failure if stream died */ /* make sure upper level knows readonly */ stream->rdonly = (LOCAL->ld < 0); /* notify about empty mailbox */ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL); if (!stream->rdonly) { /* flags stick if readwrite */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = T; /* have permanent keywords */ stream->perm_user_flags = 0xffffffff; /* and maybe can create them too */ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T; } return stream; /* return stream alive to caller */ } /* UNIX mail close * Accepts: MAIL stream * close options */ void unix_close (MAILSTREAM *stream,long options) { int silent = stream->silent; stream->silent = T; /* go silent */ /* expunge if requested */ if (options & CL_EXPUNGE) unix_expunge (stream); /* else dump final checkpoint */ else if (LOCAL->dirty) unix_check (stream); stream->silent = silent; /* restore old silence state */ unix_abort (stream); /* now punt the file and local data */ } /* UNIX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ /* lines to filter from header */ static STRINGLIST *unix_hlines = NIL; char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { MESSAGECACHE *elt; unsigned char *s; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get cache */ if (!unix_hlines) { /* once only code */ STRINGLIST *lines = unix_hlines = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Keywords")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-UID")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAP")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAPbase")); } /* go to header position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.header.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.header.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.header.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; } else { /* need to make a CRLF version */ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1), elt->private.msg.header.text.size); /* tie off string, and convert to CRLF */ s[elt->private.msg.header.text.size] = '\0'; *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s, elt->private.msg.header.text.size); fs_give ((void **) &s); /* free readin buffer */ } *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT); return LOCAL->buf; /* return processed copy */ } /* UNIX mail fetch message text * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL if failure */ long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get cache element */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { /* mark message seen and dirty */ elt->seen = elt->private.dirty = LOCAL->dirty = T; mm_flags (stream,msgno); } s = unix_text_work (stream,elt,&i,flags); INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* UNIX mail fetch message text worker routine * Accepts: MAIL stream * message cache element * pointer to returned header text length * option flags */ char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags) { FDDATA d; STRING bs; unsigned char *s,tmp[CHUNK]; /* go to text position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.text.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.text.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.text.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; return LOCAL->buf; } /* have it cached already? */ if (elt->private.uid != LOCAL->uid) { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->text.size) { /* excessively conservative, but the right thing is too hard to do */ fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); } d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = elt->private.special.offset + elt->private.msg.text.offset; d.chunk = tmp; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) { case '\r': /* carriage return seen */ *s++ = SNX (&bs); /* copy it and any succeeding LF */ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); break; case '\n': *s++ = '\r'; /* insert a CR */ default: *s++ = SNX (&bs); /* copy characters */ } *s = '\0'; /* tie off buffer */ /* calculate length of cached data */ LOCAL->textlen = s - LOCAL->text.data; } *length = LOCAL->textlen; /* return from cache */ return (char *) LOCAL->text.data; } /* UNIX per-message modify flag * Accepts: MAIL stream * message cache element */ void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { /* only after finishing */ if (elt->valid) elt->private.dirty = LOCAL->dirty = T; } /* UNIX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long unix_ping (MAILSTREAM *stream) { char lock[MAILTMPLEN]; struct stat sbuf; /* big no-op if not readwrite */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) { if (stream->rdonly) { /* does he want to give up readwrite? */ /* checkpoint if we changed something */ if (LOCAL->dirty) unix_check (stream); flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ close (LOCAL->ld); /* close the readwrite lock file */ LOCAL->ld = -1; /* no more readwrite lock fd */ unlink (LOCAL->lname); /* delete the readwrite lock file */ } else { /* get current mailbox size */ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); else stat (stream->mailbox,&sbuf); /* parse if mailbox changed */ if ((sbuf.st_size != LOCAL->filesize) && unix_parse (stream,lock,LOCK_SH)) { /* unlock mailbox */ unix_unlock (LOCAL->fd,stream,lock); mail_unlock (stream); /* and stream */ mm_nocritical (stream); /* done with critical */ } } } return LOCAL ? LONGT : NIL; /* return if still alive */ } /* UNIX mail check mailbox * Accepts: MAIL stream */ void unix_check (MAILSTREAM *stream) { char lock[MAILTMPLEN]; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,lock,LOCK_EX)) { /* any unsaved changes? */ if (LOCAL->dirty && unix_rewrite (stream,NIL,lock)) { if (!stream->silent) mm_log ("Checkpoint completed",NIL); } /* no checkpoint needed, just unlock */ else unix_unlock (LOCAL->fd,stream,lock); mail_unlock (stream); /* unlock the stream */ mm_nocritical (stream); /* done with critical */ } } /* UNIX mail expunge mailbox * Accepts: MAIL stream */ void unix_expunge (MAILSTREAM *stream) { unsigned long i; char lock[MAILTMPLEN]; char *msg = NIL; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,lock,LOCK_EX)) { /* count expunged messages if not dirty */ if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; if (!LOCAL->dirty) { /* not dirty and no expunged messages */ unix_unlock (LOCAL->fd,stream,lock); msg = "No messages deleted, so no update needed"; } else if (unix_rewrite (stream,&i,lock)) { if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); else msg = "Mailbox checkpointed, but no messages expunged"; } /* rewrite failed */ else unix_unlock (LOCAL->fd,stream,lock); mail_unlock (stream); /* unlock the stream */ mm_nocritical (stream); /* done with critical */ if (msg && !stream->silent) mm_log (msg,NIL); } else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN); } /* UNIX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; int fd; char *s,file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; unsigned long i,j; MESSAGECACHE *elt; long ret = T; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { if (pc) return (*pc) (stream,sequence,mailbox,options); unix_create (NIL,"INBOX");/* create empty INBOX */ break; } mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } LOCAL->buf[0] = '\0'; mm_critical (stream); /* go critical */ if ((fd = unix_lock (dummy_file (file,mailbox), O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE, lock,LOCK_EX)) < 0) { mm_nocritical (stream); /* done with critical */ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); /* log the error */ return NIL; /* failed */ } fstat (fd,&sbuf); /* get current file size */ /* write all requested messages to mailbox */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') { LOCAL->buf[j - 1] = '\r'; LOCAL->buf[j++] = '\n'; } if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* internal header succeeded */ s = unix_header (stream,i,&j,NIL); /* header size, sans trailing newline */ if (j && (s[j - 4] == '\r')) j -= 2; if (write (fd,s,j) < 0) ret = NIL; else { /* message header succeeded */ j = unix_xstatus (stream,LOCAL->buf,elt,NIL); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* message status succeeded */ s = unix_text_work (stream,elt,&j,NIL); if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0)) ret = NIL; } } } } if (!ret || fsync (fd)) { /* force out the update */ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); ftruncate (fd,sbuf.st_size); ret = NIL; } times.modtime = time (0); /* set mtime to now */ /* set atime to now-1 if successful copy */ if (ret) times.actime = times.modtime - 1; else times.actime = /* else preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : times.modtime; utime (file,×); /* set the times */ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */ mm_nocritical (stream); /* release critical */ /* log the error */ if (!ret) mm_log (LOCAL->buf,ERROR); /* delete if requested message */ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = elt->private.dirty = LOCAL->dirty = T; return ret; } /* UNIX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ #define BUFLEN 8*MAILTMPLEN long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN], lock[MAILTMPLEN]; struct utimbuf times; FILE *sf,*df; MESSAGECACHE elt; STRING *message; long ret = LONGT; if (!stream) { /* stream specified? */ stream = &unixproto; /* no, default stream to prototype */ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) fs_give ((void **) &stream->user_flags[i]); stream->kwd_create = T; /* allow new flags */ } /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { unix_create (NIL,"INBOX");/* create empty INBOX */ break; } mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } unlink (tmp); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR); else if (!unix_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); } /* get next message */ else if ((*af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ mm_critical (stream); /* go critical */ if (((fd = unix_lock (dummy_file (file,mailbox), O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE, lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { mm_nocritical (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ rewind (sf); for (; i && ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) && (fwrite (buf,1,j,df) == j)); i -= j); fclose (sf); /* done with scratch file */ times.modtime = time (0); /* set mtime to now */ /* make sure append wins, fsync() necessary */ if (i || (fflush (df) == EOF) || fsync (fd)) { sprintf (buf,"Message append failed: %s",strerror (errno)); mm_log (buf,ERROR); ftruncate (fd,sbuf.st_size);/* revert file */ times.actime = /* preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : times.modtime; ret = NIL; /* return error */ } /* set atime to now-1 if successful copy */ else times.actime = times.modtime - 1; utime (file,×); /* set the times */ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */ fclose (df); /* note that unix_unlock() released the fd */ mm_nocritical (stream); /* release critical */ return ret; } /* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */ int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg) { int ti,zn,c; unsigned long i,uf; char *x,tmp[MAILTMPLEN]; int hdrp = T; long f = mail_parse_flags (stream,flags,&uf); /* tie off date without newline */ if (x = strpbrk (strcpy (tmp,date),"\r\n")) *x = '\0'; /* build initial header */ if ((fprintf (sf,"From %s@%s %s\r\nStatus: ", myusername (),mylocalhost (),tmp) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\r\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\r\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if ((putc ('\r', sf) == EOF) || (putc ('\n',sf) == EOF)) return NIL; while (SIZE (msg)) { /* copy text to scratch file */ /* disregard CRs */ while ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') == '\r'); /* see if line needs special treatment */ if ((c == 'F') || (hdrp && ((c == 'S') || (c == 'X')))) { /* copy line to buffer */ for (i = 1,tmp[0] = c; (c != '\n') && (i < (MAILTMPLEN - 1)); ) switch (c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') { case '\r': /* ignore CR */ break; case '\n': /* write CRLF for NL */ tmp[i++] = '\r'; default: /* other characters */ tmp[i++] = c; } if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') && (tmp[4] == ' ')) { /* possible "From " line? */ /* yes, see if need to write a widget */ if (!(ti = unix_fromwidget || (c != '\n'))) VALID (tmp,x,ti,zn); if (ti && (putc ('>',sf) == EOF)) return NIL; } /* insert X- before metadata header */ else if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && (tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && (tmp[5] == 's') && (tmp[6] == ':')) || (((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && (tmp[5] == ':')) || ((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && (tmp[4] == 'A') && (tmp[5] == 'P') && ((tmp[6] == ':') || ((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && (tmp[8] == 's') && (tmp[9] == 'e') && (tmp[10] == ':')))) || ((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && (tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && (tmp[7] == 's') && (tmp[8] == ':')) || ((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && (tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && (tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && (tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF)) return NIL; /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; /* set up to copy remainder of line */ if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); else continue; /* end of line or end of message */ } /* check for end of header */ if (hdrp && (c == '\n')) hdrp = NIL; do switch (c) { /* copy line, writing CRLF for NL */ case '\r': /* ignore CR */ break; case '\n': /* insert CR before LF */ if (putc ('\r',sf) == EOF) return NIL; default: if (putc (c,sf) == EOF) return NIL; } while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* write trailing newline and return */ return ((putc ('\r', sf) == EOF) || (putc ('\n',sf) == EOF)) ? NIL : T; } /* Internal routines */ /* UNIX mail abort stream * Accepts: MAIL stream */ void unix_abort (MAILSTREAM *stream) { if (LOCAL) { /* only if a file is open */ if (LOCAL->fd >= 0) close (LOCAL->fd); if (LOCAL->ld >= 0) { /* have a mailbox lock? */ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ close (LOCAL->ld); /* close the lock file */ unlink (LOCAL->lname); /* and delete it */ } if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); /* free local text buffers */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* UNIX open and lock mailbox * Accepts: file name to open/lock * file open mode * destination buffer for lock file name * type of locking operation (LOCK_SH or LOCK_EX) */ int unix_lock (char *file,int flags,int mode,char *lock,int op) { int fd,ld,j; int i = LOCKTIMEOUT * 60 - 1; char tmp[MAILTMPLEN]; time_t t; struct stat sb; sprintf (lock,"%s.lock",file);/* build lock filename */ do { /* until OK or out of tries */ t = time (0); /* get the time now */ /* try to get the lock */ if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0) close (ld); /* got it, close the lock file! */ else if (errno != EEXIST) { /* miscellaneous error */ sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno)); if (!(i%15)) mm_log (tmp,WARN); } /* lock exists, still active? */ else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) && ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0)) close (ld); /* got timed-out lock file */ else { /* active lock, try again */ if (!(i%15)) { sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...", file,i); mm_log (tmp,WARN); } sleep (1); /* wait a second before next retry */ } } while (*lock && ld < 0 && i--); /* open file */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else { /* open failed */ j = errno; /* preserve error code */ if (*lock) unlink (lock); /* flush the lock file if any */ errno = j; /* restore error code */ } return fd; } /* UNIX unlock and close mailbox * Accepts: file descriptor * (optional) mailbox stream to check atime/mtime * (optional) lock file name */ void unix_unlock (int fd,MAILSTREAM *stream,char *lock) { if (stream) { /* need to muck with times? */ struct stat sbuf; struct utimbuf times; time_t now = time (0); fstat (fd,&sbuf); /* get file times */ if (LOCAL->ld >= 0) { /* yes, readwrite session? */ times.actime = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else if (stream->recent) { /* readonly with recent messages */ if ((sbuf.st_atime >= sbuf.st_mtime) || (sbuf.st_atime >= sbuf.st_ctime)) /* keep past mtime, whack back atime */ times.actime = (times.modtime = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; else now = 0; /* no time change needed */ } /* readonly with no recent messages */ else if ((sbuf.st_atime < sbuf.st_mtime) || (sbuf.st_atime < sbuf.st_ctime)) { times.actime = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else now = 0; /* no time change needed */ /* set the times, note change */ if (now && !utime (stream->mailbox,×)) LOCAL->filetime = times.modtime; } flock (fd,LOCK_UN); /* release flock'ers */ if (!stream) close (fd); /* close the file if no stream */ /* flush the lock file if any */ if (lock && *lock) unlink (lock); } /* UNIX mail parse and lock mailbox * Accepts: MAIL stream * space to write lock file name * type of locking operation * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure */ int unix_parse (MAILSTREAM *stream,char *lock,int op) { int zn; unsigned long i,j,k,m; unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; int ti = 0,retain = T; unsigned long nmsgs = stream->nmsgs; unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; unsigned long recent = stream->recent; unsigned long oldnmsgs = stream->nmsgs; short silent = stream->silent; short pseudoseen = NIL; struct stat sbuf; STRING bs; FDDATA d; MESSAGECACHE *elt; mail_lock (stream); /* guard against recursion or pingers */ /* toss out previous descriptor */ if (LOCAL->fd >= 0) close (LOCAL->fd); mm_critical (stream); /* open and lock mailbox (shared OK) */ if ((LOCAL->fd = unix_lock (stream->mailbox, O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY), NIL,lock,op)) < 0) { sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); mm_log (tmp,ERROR); unix_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } fstat (LOCAL->fd,&sbuf); /* get status */ /* validate change in size */ if (sbuf.st_size < LOCAL->filesize) { sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); mm_log (tmp,ERROR); /* this is pretty bad */ unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } /* new data? */ else if (i = sbuf.st_size - LOCAL->filesize) { d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = LOCAL->filesize; /* get to that position in the file */ d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ /* skip leading whitespace for broken MTAs */ while (((c = CHR (&bs)) == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) SNX (&bs); if (SIZE (&bs)) { /* read new data */ /* remember internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = unix_mbxline (stream,&bs,&i); t = NIL,zn = 0; if (i) VALID (s,t,ti,zn); /* see if valid From line */ if (!ti) { /* someone pulled the rug from under us */ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", (char *) s); mm_log (tmp,ERROR); unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } stream->silent = T; /* quell main program new message events */ do { /* found a message */ /* instantiate first new message */ mail_exists (stream,++nmsgs); (elt = mail_elt (stream,nmsgs))->valid = T; recent++; /* assume recent by default */ elt->recent = T; /* note position/size of internal header */ elt->private.special.offset = j; elt->private.msg.header.offset = elt->private.special.text.size = i; /* generate plausible IMAPish date string */ date[2] = date[6] = date[20] = '-'; date[11] = ' '; date[14] = date[17] = ':'; /* dd */ date[0] = t[ti - 2]; date[1] = t[ti - 1]; /* mmm */ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; /* hh */ date[12] = t[ti + 1]; date[13] = t[ti + 2]; /* mm */ date[15] = t[ti + 4]; date[16] = t[ti + 5]; if (t[ti += 6] == ':') {/* ss */ date[18] = t[++ti]; date[19] = t[++ti]; ti++; /* move to space */ } else date[18] = date[19] = '0'; /* yy -- advance over timezone if necessary */ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4); date[7] = t[ti + 1]; date[8] = t[ti + 2]; date[9] = t[ti + 3]; date[10] = t[ti + 4]; /* zzz */ t = zn ? (t + zn + 1) : (unsigned char *) "LCL"; date[21] = *t++; date[22] = *t++; date[23] = *t++; if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0'; else { /* numeric time zone */ date[24] = *t++; date[25] = *t++; date[26] = '\0'; date[20] = ' '; } /* set internal date */ if (!mail_parse_date (elt,date)) { sprintf (tmp,"Unable to parse internal date: %s",(char *) date); mm_log (tmp,WARN); } do { /* look for message body */ s = t = unix_mbxline (stream,&bs,&i); if (i) switch (*s) { /* check header lines */ case 'X': /* possible X-???: line */ if (s[1] == '-') { /* must be immediately followed by hyphen */ /* X-Status: becomes Status: in S case */ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' && s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2; /* possible X-Keywords */ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' && s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && s[8] == 'd' && s[9] == 's' && s[10] == ':') { SIZEDTEXT uf; retain = NIL; /* don't retain continuation */ s += 11; /* flush leading whitespace */ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){ while (*s == ' ') s++; /* find end of keyword */ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); /* got a keyword? */ if ((k = (u - s)) && (k < MAXUSERFLAG)) { uf.data = (unsigned char *) s; uf.size = k; for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j) if (!compare_csizedtext (stream->user_flags[j],&uf)) { elt->user_flags |= ((long) 1) << j; break; } } s = u; /* advance to next keyword */ } break; } /* possible X-IMAP */ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') && (s[5] == 'P') && ((m = (s[6] == ':')) || ((s[6] == 'b') && (s[7] == 'a') && (s[8] == 's') && (s[9] == 'e') && (s[10] == ':')))) { retain = NIL; /* don't retain continuation */ if ((nmsgs == 1) && !stream->uid_validity) { /* advance to data */ s += m ? 7 : 11; /* flush whitespace */ while (*s == ' ') s++; j = 0; /* slurp UID validity */ /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush whitespace */ while (*s == ' ') s++; /* must have valid UID validity and UID last */ if (j && isdigit (*s)) { /* pseudo-header seen if X-IMAP */ if (m) pseudoseen = LOCAL->pseudo = T; /* save UID validity */ stream->uid_validity = j; j = 0; /* slurp UID last */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* save UID last */ stream->uid_last = j; /* process keywords */ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n')); s = u,j++) { /* flush leading whitespace */ while (*s == ' ') s++; u = strpbrk (s," \n\r"); /* got a keyword? */ if ((k = (u - s)) && j < NUSERFLAGS) { if (stream->user_flags[j]) fs_give ((void **) &stream->user_flags[j]); stream->user_flags[j] = (char *) fs_get (k + 1); strncpy (stream->user_flags[j],s,k); stream->user_flags[j][k] = '\0'; } } } } break; } /* possible X-UID */ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' && s[5] == ':') { retain = NIL; /* don't retain continuation */ /* only believe if have a UID validity */ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) { s += 6; /* advance to UID value */ /* flush whitespace */ while (*s == ' ') s++; j = 0; /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush remainder of line */ while (*s != '\n') s++; /* make sure not duplicated */ if (elt->private.uid) sprintf (tmp,"Message %lu UID %lu already has UID %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,elt->private.uid); /* make sure UID doesn't go backwards */ else if (j <= prevuid) sprintf (tmp,"Message %lu UID %lu less than %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,prevuid + 1); /* or skip by mailbox's recorded last */ else if (j > stream->uid_last) sprintf (tmp,"Message %lu UID %lu greater than last %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,stream->uid_last); else { /* normal UID case */ prevuid = elt->private.uid = j; break; /* exit this cruft */ } mm_log (tmp,WARN); /* invalidate UID validity */ stream->uid_validity = 0; elt->private.uid = 0; } break; } } /* otherwise fall into S case */ case 'S': /* possible Status: line */ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' && s[5] == 's' && s[6] == ':') { retain = NIL; /* don't retain continuation */ s += 6; /* advance to status flags */ do switch (*s++) {/* parse flags */ case 'R': /* message read */ elt->seen = T; break; case 'O': /* message old */ if (elt->recent) { elt->recent = NIL; recent--; /* it really wasn't recent */ } break; case 'D': /* message deleted */ elt->deleted = T; break; case 'F': /* message flagged */ elt->flagged = T; break; case 'A': /* message answered */ elt->answered = T; break; case 'T': /* message is a draft */ elt->draft = T; break; default: /* some other crap */ break; } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))); break; /* all done */ } /* otherwise fall into default case */ default: /* ordinary header line */ if ((*s == 'S') || (*s == 's') || (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) { unsigned char *e,*v; /* must match what mail_filter() does */ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1); (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') && ((c > ' ') || ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))); *v++ = *u++); *v = '\0'; /* tie off */ /* matches internal header? */ if (!compare_cstring (tmp,"STATUS") || !compare_cstring (tmp,"X-STATUS") || !compare_cstring (tmp,"X-KEYWORDS") || !compare_cstring (tmp,"X-UID") || !compare_cstring (tmp,"X-IMAP") || !compare_cstring (tmp,"X-IMAPBASE")) { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus %s header in message %lu", (char *) tmp,elt->msgno); mm_log (err,WARN); retain = NIL; /* don't retain continuation */ break; /* different case or something */ } } /* retain or non-continuation? */ if (retain || ((*s != ' ') && (*s != '\t'))) { retain = T; /* retaining continuation now */ /* line length in CRLF format newline */ k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0); /* "internal" header size */ elt->private.data += k; /* message size */ elt->rfc822_size += k; } else { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus continuation in msg %lu: %.80s", elt->msgno,(char *) s); if (u = strpbrk (err,"\r\n")) *u = '\0'; mm_log (err,WARN); break; /* different case or something */ } break; } } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); /* "internal" header sans trailing newline */ if (i) elt->private.data -= 2; /* assign a UID if none found */ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { prevuid = elt->private.uid = ++stream->uid_last; elt->private.dirty = T; } else elt->private.dirty = elt->recent; /* note size of header, location of text */ elt->private.msg.header.text.size = (elt->private.msg.text.offset = (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - elt->private.special.text.size; k = m = 0; /* no previous line size yet */ /* note current position */ j = LOCAL->filesize + GETPOS (&bs); if (i) do { /* look for next message */ s = unix_mbxline (stream,&bs,&i); if (i) { /* got new data? */ VALID (s,t,ti,zn); /* yes, parse line */ if (!ti) { /* not a header line, add it to message */ elt->rfc822_size += k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0)); /* update current position */ j = LOCAL->filesize + GETPOS (&bs); } } } while (i && !ti); /* until found a header */ elt->private.msg.text.text.size = j - (elt->private.special.offset + elt->private.msg.text.offset); if (k == 2) { /* last line was blank? */ elt->private.msg.text.text.size -= (m ? 1 : 2); elt->rfc822_size -= 2; } } while (i); /* until end of buffer */ if (pseudoseen) { /* flush pseudo-message if present */ /* decrement recent count */ if (mail_elt (stream,1)->recent) recent--; /* and the exists count */ mail_exists (stream,nmsgs--); mail_expunged(stream,1);/* fake an expunge of that message */ } /* need to start a new UID validity? */ if (!stream->uid_validity) { stream->uid_validity = time (0); if (nmsgs) { /* don't bother if empty file */ LOCAL->dirty = T; /* make dirty to restart UID epoch */ /* need to rewrite msg 1 if not pseudo */ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; mm_log ("Assigning new unique identifiers to all messages",NIL); } } stream->nmsgs = oldnmsgs; /* whack it back down */ stream->silent = silent; /* restore old silent setting */ /* notify upper level of new mailbox sizes */ mail_exists (stream,nmsgs); mail_recent (stream,recent); /* mark dirty so O flags are set */ if (recent) LOCAL->dirty = T; } } /* no change, don't babble if never got time */ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime) mm_log ("New mailbox modification time but apparently no changes",WARN); /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; LOCAL->filetime = sbuf.st_mtime; return T; /* return the winnage */ } /* UNIX read line from mailbox * Accepts: mail stream * stringstruct * pointer to line size * Returns: pointer to input line */ char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) { unsigned long i,j,k,m; char *s,*t,*te,p1[CHUNK]; char *ret = ""; /* flush old buffer */ if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* if buffer needs refreshing */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) { /* find newline */ /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* difficult case if line spans buffer */ if ((i = s - bs->curpos) == bs->cursize) { memcpy (p1,bs->curpos,i); /* remember what we have so far */ /* load next buffer */ SETPOS (bs,k = GETPOS (bs) + i); /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; /* fast scan in overlap buffer */ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* huge line? */ if ((j = s - bs->curpos) == bs->cursize) { SETPOS (bs,GETPOS (bs) + j); /* look for end of line (s-l-o-w!!) */ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); SETPOS (bs,k); /* go back to where it started */ } /* got size of data, make buffer for return */ ret = LOCAL->line = (char *) fs_get (i + j + 2); memcpy (ret,p1,i); /* copy first chunk */ while (j) { /* copy remainder */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); i += k; /* account for this much read in */ j -= k; bs->curpos += k; /* increment new position */ bs->cursize -= k; /* eat that many bytes */ } if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */ ret[i++] = '\n'; /* make sure newline at end */ ret[i] = '\0'; /* makes debugging easier */ } else { /* this is easy */ ret = bs->curpos; /* string it at this position */ bs->curpos += ++i; /* increment new position */ bs->cursize -= i; /* eat that many bytes */ } *size = i; /* return that to user */ } else *size = 0; /* end of data, return empty */ return ret; } /* UNIX make pseudo-header * Accepts: MAIL stream * buffer to write pseudo-header * Returns: length of pseudo-header */ unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr) { int i; char *s,*t,tmp[MAILTMPLEN]; time_t now = time(0); rfc822_fixed_date (tmp); sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld", pseudo_from,ctime (&now), tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) now,mylocalhost (),stream->uid_validity, stream->uid_last); for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) sprintf (t += strlen (t)," %s",stream->user_flags[i]); strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n"); for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++) if (*s == '\n') *t++ = '\r'; *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n'; *t = '\0'; /* tie off pseudo header */ return t - hdr; /* return length of pseudo header */ } /* UNIX make status string * Accepts: MAIL stream * destination string to write * message cache entry * non-zero flag to write UID (.LT. 0 to write UID base info too) * Returns: length of string */ unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag) { char *t,stack[64]; char *s = status; unsigned long n; unsigned long pad = 50; /* This used to use sprintf(), but thanks to certain cretinous C libraries with horribly slow implementations of sprintf() I had to change it to this mess. At least it should be fast. */ /* need to write X-IMAPbase: header? */ if ((flag < 0) && !stream->uid_nosticky) { *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P'; *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' '; t = stack; n = stream->uid_validity; /* push UID validity digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID validity digits from stack */ while (t > stack) *s++ = *--t; *s++ = ' '; n = stream->uid_last; /* push UID last digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID last digits from stack */ while (t > stack) *s++ = *--t; for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n]) for (*s++ = ' '; *t; *s++ = *t++); *s++ = '\r'; *s++ = '\n'; pad += 30; /* increased padding if have IMAPbase */ } *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->seen) *s++ = 'R'; if (flag) *s++ = 'O'; /* only write O if have a UID */ *s++ = '\r'; *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->deleted) *s++ = 'D'; if (elt->flagged) *s++ = 'F'; if (elt->answered) *s++ = 'A'; if (elt->draft) *s++ = 'T'; *s++ = '\r'; *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w'; *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':'; if (n = elt->user_flags) do { *s++ = ' '; for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); } while (n); n = s - status; /* get size of stuff so far */ /* pad X-Keywords to make size constant */ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; *s++ = '\r'; *s++ = '\n'; if (flag) { /* want to include UID? */ t = stack; n = elt->private.uid; /* push UID digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; *s++ = ' '; /* pop UID from stack */ while (t > stack) *s++ = *--t; *s++ = '\r'; *s++ = '\n'; } /* end of extended message status */ *s++ = '\r'; *s++ = '\n'; *s = '\0'; return s - status; /* return size of resulting string */ } /* Rewrite mailbox file * Accepts: MAIL stream, must be critical and locked * return pointer to number of expunged messages if want expunge * lock file name * Returns: T if success and mailbox unlocked, NIL if failure */ #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock) { MESSAGECACHE *elt; UNIXFILE f; char *s; struct utimbuf times; long ret,flag; unsigned long i,j; unsigned long recent = stream->recent; unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0; if (nexp) *nexp = 0; /* initially nothing expunged */ /* calculate size of mailbox after rewrite */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) if (!(elt = mail_elt (stream,i))->deleted || !nexp) { /* add RFC822 size of this message */ size += elt->private.special.text.size + elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag) + elt->private.msg.text.text.size + 2; flag = 1; /* only count X-IMAPbase once */ } if (!size) { /* no messages and no pseudo, make one now */ size = unix_pseudo (stream,LOCAL->buf); LOCAL->pseudo = T; } /* extend the file as necessary */ if (ret = unix_extend (stream,size)) { /* Set up buffered I/O file structure * curpos current position being written through buffering * filepos current position being written physically to the disk * bufpos current position being written in the buffer * protect current maximum position that can be written to the disk * before buffering is forced * The code tries to buffer so that that disk is written in multiples of * OVERBLOWBUFLEN bytes. */ f.stream = stream; /* note mail stream */ f.curpos = f.filepos = 0; /* start of file */ f.protect = stream->nmsgs ? /* initial protection pointer */ mail_elt (stream,1)->private.special.offset : 8192; f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); if (LOCAL->pseudo) /* update pseudo-header */ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf)); /* loop through all messages */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { elt = mail_elt (stream,i);/* get cache */ if (nexp && elt->deleted){/* expunge this message? */ /* one less recent message */ if (elt->recent) --recent; mail_expunged(stream,i);/* notify upper levels */ ++*nexp; /* count up one more expunged message */ } else { /* preserve this message */ i++; /* advance to next message */ if ((flag < 0) || /* need to rewrite message? */ elt->private.dirty || (((unsigned long) f.curpos) != elt->private.special.offset) || (elt->private.msg.header.text.size != (elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag)))) { unsigned long newoffset = f.curpos; /* yes, seek to internal header */ lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); /* protection pointer moves to RFC822 header */ f.protect = elt->private.special.offset + elt->private.msg.header.offset; /* write internal header */ unix_write (&f,LOCAL->buf,elt->private.special.text.size); /* get RFC822 header */ s = unix_header (stream,elt->msgno,&j,NIL); /* in case this got decremented */ elt->private.msg.header.offset = elt->private.special.text.size; /* header size, sans trailing newline */ if ((j < 4) || (s[j - 4] == '\r')) j -= 2; if (j != elt->private.data) fatal ("header size inconsistent"); /* protection pointer moves to RFC822 text */ f.protect = elt->private.special.offset + elt->private.msg.text.offset; unix_write (&f,s,j); /* write RFC822 header */ /* write status and UID */ unix_write (&f,LOCAL->buf, j = unix_xstatus (stream,LOCAL->buf,elt,flag)); flag = 1; /* only write X-IMAPbase once */ /* new file header size */ elt->private.msg.header.text.size = elt->private.data + j; /* did text move? */ if (f.curpos != f.protect) { /* get message text */ s = unix_text_work (stream,elt,&j,FT_INTERNAL); /* can't happen it says here */ if (j > elt->private.msg.text.text.size) fatal ("text size inconsistent"); /* new text offset, status/UID may change it */ elt->private.msg.text.offset = f.curpos - newoffset; /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2); unix_write (&f,s,j);/* write text */ /* write trailing newline */ unix_write (&f,"\r\n",2); } else { /* tie off header and status */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\r\n",2); } } /* new internal header offset */ elt->private.special.offset = newoffset; elt->private.dirty =NIL;/* message is now clean */ } else { /* no need to rewrite this message */ /* tie off previous message if needed */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.special.text.size + elt->private.msg.header.text.size + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\r\n",2); } } } } unix_write (&f,NIL,NIL); /* tie off final message */ if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent"); fs_give ((void **) &f.buf); /* free buffer */ /* make sure tied off */ ftruncate (LOCAL->fd,LOCAL->filesize = size); fsync (LOCAL->fd); /* make sure the updates take */ if (size && (flag < 0)) fatal ("lost UID base information"); LOCAL->dirty = NIL; /* no longer dirty */ /* notify upper level of new mailbox sizes */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); /* set atime to now, mtime a second earlier */ times.modtime = (times.actime = time (0)) -1; /* set the times, note change */ if (!utime (stream->mailbox,×)) LOCAL->filetime = times.modtime; /* flush the lock file */ unix_unlock (LOCAL->fd,stream,lock); } return ret; /* return state from algorithm */ } /* Extend UNIX mailbox file * Accepts: MAIL stream * new desired size * Return: T if success, else NIL */ long unix_extend (MAILSTREAM *stream,unsigned long size) { unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ? size - ((unsigned long) LOCAL->filesize) : 0; if (i) { /* does the mailbox need to grow? */ if (i > LOCAL->buflen) { /* make sure have enough space */ /* this user won the lottery all right */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1); } memset (LOCAL->buf,'\0',i); /* get a block of nulls */ while (T) { /* until write successful or punt */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break; else { long e = errno; /* note error before doing ftruncate */ ftruncate (LOCAL->fd,LOCAL->filesize); if (mm_diskerror (stream,e,NIL)) { fsync (LOCAL->fd); /* user chose to punt */ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e)); if (!stream->silent) mm_log (LOCAL->buf,ERROR); return NIL; } } } } return LONGT; } /* Write data to buffered file * Accepts: buffered file pointer * file data or NIL to indicate "flush buffer" * date size (ignored for "flush buffer") * Does not return until success */ void unix_write (UNIXFILE *f,char *buf,unsigned long size) { unsigned long i,j,k; if (buf) { /* doing buffered write? */ i = f->bufpos - f->buf; /* yes, get size of current buffer data */ /* yes, have space in current buffer chunk? */ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) { /* yes, fill up buffer as much as we can */ memcpy (f->bufpos,buf,k = min (j,size)); f->bufpos += k; /* new buffer position */ f->curpos += k; /* new current position */ if (j -= k) return; /* all done if still have buffer free space */ buf += k; /* full, get new unwritten data pointer */ size -= k; /* new data size */ i += k; /* new buffer data size */ } /* This chunk of the buffer is full. See if can make some space by * writing to the disk, if there's enough unprotected space to do so. * Try to fill out any unaligned chunk, along with any subsequent full * chunks that will fit in unprotected space. */ /* any unprotected space we can write to? */ if (j = min (i,f->protect - f->filepos)) { /* yes, filepos not at chunk boundary? */ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j)) j -= k; /* yes, and can write out partial chunk */ else k = 0; /* no partial chunk to write */ /* if at least a chunk free, write that too */ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN); if (k) { /* write data if there is anything we can */ unix_phys_write (f,f->buf,k); /* slide buffer */ if (i -= k) memmove (f->buf,f->buf + k,i); f->bufpos = f->buf + i; /* new end of buffer */ } } /* Have flushed the buffer as best as possible. All done if no more * data to write. Otherwise, if the buffer is empty AND if the unwritten * data is larger than a chunk AND the unprotected space is also larger * than a chunk, then write as many chunks as we can directly from the * data. Buffer the rest, expanding the buffer as needed. */ if (size) { /* have more data that we need to buffer? */ /* can write any of it to disk instead? */ if ((f->bufpos == f->buf) && ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) { /* write as much as we can right now */ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN)); buf += j; /* new data pointer */ size -= j; /* new data size */ f->curpos += j; /* advance current pointer */ } if (size) { /* still have data that we need to buffer? */ /* yes, need to expand the buffer? */ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) { /* note current position in buffer */ j = f->bufpos - f->buf; i += OVERFLOWBUFLEN; /* yes, grow another chunk */ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN)); /* in case buffer relocated */ f->bufpos = f->buf + j; } /* buffer remaining data */ memcpy (f->bufpos,buf,size); f->bufpos += size; /* new end of buffer */ f->curpos += size; /* advance current pointer */ } } } else { /* flush buffer to disk */ unix_phys_write (f,f->buf,i = f->bufpos - f->buf); f->bufpos = f->buf; /* reset buffer */ /* update positions */ f->curpos = f->protect = f->filepos; } } /* Physical disk write * Accepts: buffered file pointer * buffer address * buffer size * Does not return until success */ void unix_phys_write (UNIXFILE *f,char *buf,size_t size) { MAILSTREAM *stream = f->stream; /* write data at desired position */ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) || (write (LOCAL->fd,buf,size) < 0))) { int e; char tmp[MAILTMPLEN]; sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno)); mm_log (tmp,ERROR); mm_diskerror (NIL,e,T); /* serious problem, must retry */ } f->filepos += size; /* update file position */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/unixnt.h000066400000000000000000000146011137544547100227540ustar00rootroot00000000000000/* * Program: UNIX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ /* Validate line * Accepts: pointer to candidate string to validate as a From header * return pointer to end of date/time field * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') * return pointer to offset from t of time zone (if non-zero) * Returns: t,ti,zn set if valid From string, else ti is NIL */ #define VALID(s,x,ti,zn) { \ ti = 0; \ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ (s[4] == ' ')) { \ for (x = s + 5; *x && *x != '\012'; x++); \ if (*x) { \ if (x[-1] == '\015') --x; \ if (x - s >= 41) { \ for (zn = -1; x[zn] != ' '; zn--); \ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ x += zn - 12; \ } \ if (x - s >= 27) { \ if (x[-5] == ' ') { \ if (x[-8] == ':') zn = 0,ti = -5; \ else if (x[-9] == ' ') ti = zn = -9; \ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ ti = zn = -11; \ } \ else if (x[-4] == ' ') { \ if (x[-9] == ' ') zn = -4,ti = -9; \ } \ else if (x[-6] == ' ') { \ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ zn = -6,ti = -11; \ } \ if (ti && !((x[ti - 3] == ':') && \ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ (x[ti - 11] == ' '))) ti = 0; \ } \ } \ } \ } /* You are not expected to understand this macro, but read the next page if * you are not faint of heart. * * Known formats to the VALID macro are: * From user Wed Dec 2 05:53 1992 * BSD From user Wed Dec 2 05:53:22 1992 * SysV From user Wed Dec 2 05:53 PST 1992 * rn From user Wed Dec 2 05:53:22 PST 1992 * From user Wed Dec 2 05:53 -0700 1992 * emacs From user Wed Dec 2 05:53:22 -0700 1992 * From user Wed Dec 2 05:53 1992 PST * From user Wed Dec 2 05:53:22 1992 PST * From user Wed Dec 2 05:53 1992 -0700 * Solaris From user Wed Dec 2 05:53:22 1992 -0700 * * Plus all of the above with `` remote from xxx'' after it. Thank you very * much, smail and Solaris, for making my life considerably more complicated. */ /* * What? You want to understand the VALID macro anyway? Alright, since you * insist. Actually, it isn't really all that difficult, provided that you * take it step by step. * * Line 1 Initializes the return ti value to failure (0); * Lines 2-3 Validates that the 1st-5th characters are ``From ''. * Lines 4-6 Validates that there is an end of line and points x at it. * Lines 7-14 First checks to see if the line is at least 41 characters long. * If so, it scans backwards to find the rightmost space. From * that point, it scans backwards to see if the string matches * `` remote from''. If so, it sets x to point to the space at * the start of the string. * Line 15 Makes sure that there are at least 27 characters in the line. * Lines 16-21 Checks if the date/time ends with the year (there is a space * five characters back). If there is a colon three characters * further back, there is no timezone field, so zn is set to 0 * and ti is set in front of the year. Otherwise, there must * either to be a space four characters back for a three-letter * timezone, or a space six characters back followed by a + or - * for a numeric timezone; in either case, zn and ti become the * offset of the space immediately before it. * Lines 22-24 Are the failure case for line 14. If there is a space four * characters back, it is a three-letter timezone; there must be a * space for the year nine characters back. zn is the zone * offset; ti is the offset of the space. * Lines 25-28 Are the failure case for line 20. If there is a space six * characters back, it is a numeric timezone; there must be a * space eleven characters back and a + or - five characters back. * zn is the zone offset; ti is the offset of the space. * Line 29-32 If ti is valid, make sure that the string before ti is of the * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise * invalidate ti. There must be a colon three characters back * and a space six or nine characters back (depending upon * whether or not the character six characters back is a colon). * There must be a space three characters further back (in front * of the day), one seven characters back (in front of the month), * and one eleven characters back (in front of the day of week). * ti is set to be the offset of the space before the time. * * Why a macro? It gets invoked a *lot* in a tight loop. On some of the * newer pipelined machines it is faster being open-coded than it would be if * subroutines are called. * * Why does it scan backwards from the end of the line, instead of doing the * much easier forward scan? There is no deterministic way to parse the * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to * see if unquoted spaces were possible. They are, and I've encountered enough * evil mail to be totally unwilling to trust that ``it will never happen''. */ /* Build parameters */ #define KODRETRY 15 /* kiss-of-death retry in seconds */ #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define CHUNK 16384 /* read-in chunk size */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/write.c000066400000000000000000000030231137544547100225500ustar00rootroot00000000000000/* * Program: Write data, treating partial writes as an error * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* The whole purpose of this unfortunate routine is to deal with DOS and * certain cretinous versions of UNIX which decided that the "bytes actually * written" return value from write() gave them license to use that for things * that are really errors, such as disk quota exceeded, maximum file size * exceeded, disk full, etc. * * BSD won't screw us this way on the local filesystem, but who knows what * some NFS-mounted filesystem will do. */ #undef write /* Write data to file * Accepts: file descriptor * I/O vector structure * number of vectors in structure * Returns: number of bytes written if successful, -1 if failure */ long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1); long safe_write (int fd,char *buf,long nbytes) { long i,j; if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) { while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) && (errno == EINTR)); if (j < 0) return j; } return nbytes; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/yunchan.c000066400000000000000000000202771137544547100230750ustar00rootroot00000000000000/* * Program: Unix compatibility routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 14 September 1996 * Last Edited: 25 June 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ /* Emulator for BSD flock() call * Accepts: file descriptor * operation bitmask * Returns: 0 if successful, -1 if failure */ /* Our friends in Redmond have decided that you can not write to any segment * which has a shared lock. This screws up the shared-write mailbox drivers * (mbx, mtx, and tenex). As a workaround, we'll only lock the first byte of * the file, meaning that you can't write that byte shared. * This behavior seems to be new as of NT 4.0. */ int flock (int fd,int op) { HANDLE hdl = (HANDLE) _get_osfhandle (fd); DWORD flags = (op & LOCK_NB) ? LOCKFILE_FAIL_IMMEDIATELY : 0; OVERLAPPED offset = {NIL,NIL,0,0,NIL}; int ret = -1; blocknotify_t bn = (blocknotify_t) ((op & LOCK_NB) ? NIL : mail_parameters (NIL,GET_BLOCKNOTIFY,NIL)); if (hdl < 0) errno = EBADF; /* error in file descriptor */ else switch (op & ~LOCK_NB) { /* translate to LockFileEx() op */ case LOCK_EX: /* exclusive */ flags |= LOCKFILE_EXCLUSIVE_LOCK; case LOCK_SH: /* shared */ if (!check_nt ()) return 0; /* always succeeds if not NT */ if (bn) (*bn) (BLOCK_FILELOCK,NIL); /* bug for bug compatible with Unix */ UnlockFileEx (hdl,NIL,1,0,&offset); /* lock the file as requested */ if (LockFileEx (hdl,flags,NIL,1,0,&offset)) ret = 0; if (bn) (*bn) (BLOCK_NONE,NIL); /* if failed */ if (ret) errno = (op & LOCK_NB) ? EAGAIN : EBADF; break; case LOCK_UN: /* unlock */ if (check_nt ()) UnlockFileEx (hdl,NIL,1,0,&offset); ret = 0; /* always succeeds */ default: /* default */ errno = EINVAL; /* bad call */ break; } return ret; } /* Local storage */ static char *loghdr; /* log file header string */ static HANDLE loghdl = NIL; /* handle of event source */ /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ void syslog (int priority,const char *message,...) { va_list args; LPTSTR strs[2]; char tmp[MAILTMPLEN]; /* callers must be careful not to pop this */ unsigned short etype; if (!check_nt ()) return; /* no-op on non-NT system */ /* default event source */ if (!loghdl) openlog ("c-client",LOG_PID,LOG_MAIL); switch (priority) { /* translate UNIX type into NT type */ case LOG_ALERT: etype = EVENTLOG_ERROR_TYPE; break; case LOG_INFO: etype = EVENTLOG_INFORMATION_TYPE; break; default: etype = EVENTLOG_WARNING_TYPE; } va_start (args,message); /* initialize vararg mechanism */ vsprintf (tmp,message,args); /* build message */ strs[0] = loghdr; /* write header */ strs[1] = tmp; /* then the message */ /* report the event */ ReportEvent (loghdl,etype,(unsigned short) priority,2000,NIL,2,0,strs,NIL); va_end (args); } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ void openlog (const char *ident,int logopt,int facility) { char tmp[MAILTMPLEN]; if (!check_nt ()) return; /* no-op on non-NT system */ if (loghdl) fatal ("Duplicate openlog()!"); loghdl = RegisterEventSource (NIL,ident); sprintf (tmp,(logopt & LOG_PID) ? "%s[%d]" : "%s",ident,getpid ()); loghdr = cpystr (tmp); /* save header for later */ } /* Copy Unix string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src, unsigned long srcl) { unsigned long i,j; char *d = src; /* count number of LF's in source string(s) */ for (i = srcl,j = 0; j < srcl; j++) if (*d++ == '\012') i++; /* flush destination buffer if too small */ if (*dst && (i > *dstl)) fs_give ((void **) dst); if (!*dst) { /* make a new buffer if needed */ *dst = (char *) fs_get ((*dstl = i) + 1); if (dstl) *dstl = i; /* return new buffer length to main program */ } d = *dst; /* destination string */ /* copy strings, inserting CR's before LF's */ while (srcl--) switch (*src) { case '\015': /* unlikely carriage return */ *d++ = *src++; /* copy it and any succeeding linefeed */ if (srcl && *src == '\012') { *d++ = *src++; srcl--; } break; case '\012': /* line feed? */ *d++ ='\015'; /* yes, prepend a CR, drop into default case */ default: /* ordinary chararacter */ *d++ = *src++; /* just copy character */ break; } *d = '\0'; /* tie off destination */ return d - *dst; /* return length */ } /* Length of Unix string after unix_crlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long unix_crlflen (STRING *s) { unsigned long pos = GETPOS (s); unsigned long i = SIZE (s); unsigned long j = i; while (j--) switch (SNX (s)) {/* search for newlines */ case '\015': /* unlikely carriage return */ if (j && (CHR (s) == '\012')) { SNX (s); /* eat the line feed */ j--; } break; case '\012': /* line feed? */ i++; default: /* ordinary chararacter */ break; } SETPOS (s,pos); /* restore old position */ return i; } /* Undoubtably, I'm going to regret these two routines in the future. I * regret them now. Their purpose is to work around two problems in the * VC++ 6.0 C library: * (1) tmpfile() creates the file in the current directory instead of a * temporary directory * (2) tmpfile() and fclose() think that on NT systems, it works to unlink * the file while it's still open, so there's no need for the _tmpfname * hook at fclose(). Unfortunately, that doesn't work in Win2K. * I would be delighted to have a better alternative. */ #undef fclose /* use the real fclose() in close_file() */ /* Substitute for Microsoft's tmpfile() that uses the real temporary directory * Returns: FILE structure if success, NIL if failure */ FILE *create_tempfile (void) { FILE *ret = NIL; char *s = _tempnam (getenv ("TEMP"),"msg"); if (s) { /* if got temporary name... */ /* open file, and stash name on _tmpfname */ if (ret = fopen (s,"w+b")) ret->_tmpfname = s; else fs_give ((void **) &s);/* flush temporary string */ } return ret; } /* Substitute for Microsoft's fclose() that always flushes _tmpfname * Returns: FILE structure if success, NIL if failure */ int close_file (FILE *stream) { int ret; char *s = stream->_tmpfname; stream->_tmpfname = NIL; /* just in case fclose() tries to delete it */ ret = fclose (stream); /* close the file */ if (s) { /* was there a _tmpfname? */ unlink (s); /* yup, delete it */ fs_give ((void **) &s); /* and flush the name */ } return ret; } /* Get password from console * Accepts: prompt * Returns: password */ #define PWDLEN 128 /* used by Linux */ char *getpass (const char *prompt) { static char pwd[PWDLEN]; int ch,i,done; fputs (prompt,stderr); /* output prompt */ for (i = done = 0; !done; ) switch (ch = _getch()) { case 0x03: /* CTRL/C stops program */ _exit (1); case '\b': /* BACKSPACE erase previous character */ if (i) pwd[--i] = '\0'; break; case '\n': case '\r': /* CR or LF terminates string */ done = 1; break; default: /* any other character is a pwd char */ if (i < (PWDLEN - 1)) pwd[i++] = ch; break; } pwd[i] = '\0'; /* tie off string with null */ putchar ('\n'); /* echo newline */ return pwd; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/nt/yunchan.h000066400000000000000000000046711137544547100231020ustar00rootroot00000000000000/* * Program: Unix compatibility routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 14 September 1996 * Last Edited: 25 June 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ /* For flock() emulation */ #define LOCK_SH 1 #define LOCK_EX 2 #define LOCK_NB 4 #define LOCK_UN 8 /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ #define tmpfile create_tempfile #define fclose close_file #define fsync _commit #define ftruncate chsize #define gethostid clock #define sleep(x) Sleep (1000 * x) long alarm (long seconds); int flock (int fd,int op); void openlog (const char *ident,int logopt,int facility); void syslog (int priority,const char *message,...); unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src, unsigned long srcl); unsigned long unix_crlflen (STRING *s); FILE *create_tempfile (void); int close_file (FILE *stream); char *getpass (const char *prompt); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/000077500000000000000000000000001137544547100213365ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/auths.cmd000066400000000000000000000022371137544547100231530ustar00rootroot00000000000000/* rexx */ /* * Program: Authenticator Linkage Generator for OS/2 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 June 1999 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ '@echo off' /* Erase old authenticators list */ 'if exist auths.c del auths.c' parse arg args n=words(args) a_file='auths.c' c_file='linkage.c' h_file='linkage.h' call stream a_file, 'C', 'open write' call stream c_file, 'C', 'open write' call stream h_file, 'C', 'open write' do i=1 to n arg=word(args,i) call lineout a_file, '#include "auth_'arg'.c"' call lineout h_file, 'extern AUTHENTICATOR auth_'arg';' call lineout c_file, ' auth_link (&auth_'arg'); /* link in the 'arg' authenticator */' end call stream h_file, 'C', 'close' call stream c_file, 'C', 'close' call stream a_file, 'C', 'close' exit 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/drivers.cmd000066400000000000000000000020611137544547100235000ustar00rootroot00000000000000/* rexx */ /* * Program: Authenticator Linkage Generator for OS/2 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 June 1999 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ '@echo off' /* Erase old authenticators list */ 'if exist linkage.h del linkage.h' 'if exist linkage.c del linkage.c' parse arg args n=words(args) c_file='linkage.c' h_file='linkage.h' call stream c_file, 'C', 'open write' call stream h_file, 'C', 'open write' do i=1 to n arg=word(args,i) call lineout h_file, 'extern DRIVER 'arg'driver;' call lineout c_file, ' mail_link (&'arg'driver); /* link in the 'arg' driver */' end call stream h_file, 'C', 'close' call stream c_file, 'C', 'close' exit 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/dummy.h000066400000000000000000000021051137544547100226400ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/dummyos2.c000066400000000000000000000503451137544547100232700ustar00rootroot00000000000000/* * Program: Dummy routines for OS2 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 May 1993 * Last Edited: 26 August 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Thanks to Nicholas Sheppard for the original version */ #include #include #include #include #include #include #include #include #include #include #undef ADDRESS #include "mail.h" #include "osdep.h" #include "misc.h" #include "dummy.h" /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level); long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents); long dummy_subscribe (MAILSTREAM *stream,char *mailbox); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ dummy_subscribe, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char *s,*t,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { /* indeterminate INBOX */ if (!*s) return &dummydriver; /* remove trailing \ */ if ((t = strrchr (s,'\\')) && !t[1]) *t = '\0'; if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { case S_IFREG: /* file */ case S_IFDIR: /* future use */ return &dummydriver; } } return NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { return NIL; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i = 0; if (!pat || !*pat) { /* empty pattern? */ if (dummy_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'\\')) *++s = '\0'; else test[0] = '\0'; dummy_listed (stream,'\\',test,LATT_NOSELECT,NIL); } } /* get canonical form of name */ else if (dummy_canonicalize (test,ref,pat)) { /* found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test,(size_t) (i = s - test)); file[i] = '\0'; /* tie off */ } else strcpy (file,test); /* use just that name then */ /* find directory name */ if (s = strrchr (file,'\\')) { *++s = '\0'; /* found, tie off at that point */ s = file; } /* silly case */ else if (file[0] == '#') s = file; /* do the work */ dummy_list_work (stream,s,test,contents,0); if (pmatch ("INBOX",test)) /* always an INBOX */ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents); } } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { dummy_scan (stream,ref,pat,NIL); } /* Dummy list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { void *sdb = NIL; char *s,*t,test[MAILTMPLEN]; int showuppers = pat[strlen (pat) - 1] == '%'; /* get canonical form of name */ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do if (*s != '{') { if (pmatch_full (s,test,'\\')) { if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS); else mm_lsub (stream,'\\',s,NIL); } else while (showuppers && (t = strrchr (s,'\\'))) { *t = '\0'; /* tie off the name */ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT); } } while (s = sm_read (&sdb)); /* until no more subscriptions */ } /* Dummy subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long dummy_subscribe (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox); sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox); mm_log (tmp,ERROR); return NIL; } /* Dummy list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * string to scan * search level */ void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level) { unsigned long i = 1; FILEFINDBUF3 f; HDIR hd = HDIR_CREATE; struct stat sbuf; char tmp[MAILTMPLEN]; /* punt if bogus name */ if (!mailboxdir (tmp,dir,NIL)) return; /* make directory wildcard */ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*"); /* do nothing if can't open directory */ if (!DosFindFirst (tmp,&hd,FILE_NORMAL,&f,sizeof (f),&i,FIL_STANDARD)) { /* list it if not at top-level */ if (!level && dir && pmatch_full (dir,pat,'\\')) dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents); /* scan directory */ if (!dir || dir[strlen (dir) -1] == '\\') do { if (((f.name[0] != '.') || (f.name[1] && ((f.name[1] != '.') || f.name[2]))) && (strlen (f.name) <= NETMAXMBX)) { /* see if name is useful */ if (dir) sprintf (tmp,"%s%s",dir,f.name); else strcpy (tmp,f.name); /* make sure useful and can get info */ if ((pmatch_full (tmp,pat,'\\') || pmatch_full (strcat (tmp,"\\"),pat,'\\') || dmatch (tmp,pat,'\\')) && mailboxdir (tmp,dir,f.name) && tmp[0] && !stat (tmp,&sbuf)) { /* now make name we'd return */ if (dir) sprintf (tmp,"%s%s",dir,f.name); else strcpy (tmp,f.name); /* only interested in file type */ switch (sbuf.st_mode & S_IFMT) { case S_IFDIR: /* directory? */ if (pmatch_full (tmp,pat,'\\')) { if (!dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))break; strcat (tmp,"\\");/* set up for dmatch call */ } /* try again with trailing \ */ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\') && !dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents)) break; if (dmatch (tmp,pat,'\\') && (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) dummy_list_work (stream,tmp,pat,contents,level+1); break; case S_IFREG: /* ordinary name */ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp)) dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents); break; } } } i = 1; } while (!DosFindNext (h,&f,sizeof (f),&i)); } } /* Mailbox found * Accepts: hierarchy delimiter * mailbox name * attributes * contents to search before calling mm_list() * Returns: T, always */ #define BUFSIZE 4*MAILTMPLEN long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents) { struct stat sbuf; int fd; long csiz,ssiz,bsiz; char *s,*buf,tmp[MAILTMPLEN]; if (contents) { /* want to search contents? */ /* forget it if can't select or open */ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) || !(s = dummy_file (tmp,name)) || stat (s,&sbuf) || (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0)) return T; /* get buffer including slop */ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1); memset (buf,'\0',ssiz); /* no slop area the first time */ while (sbuf.st_size) { /* until end of file */ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE)); if (search ((unsigned char *) buf,bsiz+ssiz, (unsigned char *) contents,csiz)) break; memcpy (buf,buf+BUFSIZE,ssiz); sbuf.st_size -= bsiz; /* note that we read that much */ } fs_give ((void **) &buf); /* flush buffer */ close (fd); /* finished with file */ if (!sbuf.st_size) return T;/* not found */ } /* notify main program */ mm_list (stream,delimiter,name,attributes); return T; } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { char tmp[MAILTMPLEN]; if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox)) return dummy_create_path (stream,tmp,NIL); sprintf (tmp,"Can't create %.80s: invalid name",mailbox); mm_log (tmp,ERROR); return NIL; } /* Dummy create path * Accepts: mail stream * path name to create * directory mode * Returns: T on success, NIL on failure */ long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN]; int fd; long ret = NIL; char *t = strrchr (path,'\\'); char *pt = (path[1] == ':') ? path + 2 : path; int wantdir = t && !t[1]; if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */ /* found superior to this name? */ if ((s = strrchr (pt,'\\')) && (s != pt)) { strncpy (tmp,path,(size_t) (s - path)); tmp[s - path] = '\0'; /* make directory name for stat */ c = *++s; /* tie off in case need to recurse */ *s = '\0'; /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,path,dirmode)) return NIL; *s = c; /* restore full name */ } if (wantdir) { /* want to create directory? */ ret = !mkdir (path); *t = '\\'; /* restore directory delimiter */ } /* create file */ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0) ret = !close (fd); /* close file */ if (!ret) { /* error? */ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path, strerror (errno)); mm_log (tmp,ERROR); } return ret; /* return status */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { struct stat sbuf; char *s,tmp[MAILTMPLEN]; if (!(s = dummy_file (tmp,mailbox))) { sprintf (tmp,"Can't delete - invalid name: %.80s",s); mm_log (tmp,ERROR); } /* no trailing \ */ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0'; if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? rmdir (tmp) : unlink (tmp)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno)); mm_log (tmp,ERROR); return NIL; } return T; /* return success */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; long ret = NIL; /* no trailing \ allowed */ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || ((s = strrchr (s,'\\')) && !s[1])) { sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); mm_log (mbx,ERROR); return NIL; } /* found superior to destination name? */ if (s && (s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,mbx)) return NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } /* rename of non-ex INBOX creates dest */ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, strerror (errno)); mm_log (tmp,ERROR); return NIL; } return LONGT; /* return success */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { int fd; char err[MAILTMPLEN],tmp[MAILTMPLEN]; struct stat sbuf; /* OP_PROTOTYPE call */ if (!stream) return &dummyproto; err[0] = '\0'; /* no error message yet */ /* can we open the file? */ if (!dummy_file (tmp,stream->mailbox)) sprintf (err,"Can't open this name: %.80s",stream->mailbox); else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { /* no, error unless INBOX */ if (compare_cstring (stream->mailbox,"INBOX")) sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox); } else { /* file had better be empty then */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); if (sbuf.st_size) /* bogus format if non-empty */ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format", stream->mailbox,tmp); } if (err[0]) { /* if an error happened */ mm_log (err,stream->silent ? WARN : ERROR); return NIL; } else if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); /* and certainly no recent ones! */ stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long dummy_ping (MAILSTREAM *stream) { MAILSTREAM *test; /* time to do another test? */ if (time (0) >= ((time_t) (stream->gensym + 30))) { /* has mailbox format changed? */ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) && (test->dtb != stream->dtb) && (test = mail_open (NIL,stream->mailbox,NIL))) { /* preserve some resources */ test->original_mailbox = stream->original_mailbox; stream->original_mailbox = NIL; test->sparep = stream->sparep; stream->sparep = NIL; test->sequence = stream->sequence; mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */ memcpy (fs_get (sizeof (MAILSTREAM)),stream, sizeof (MAILSTREAM))); /* swap the streams */ memcpy (stream,test,sizeof (MAILSTREAM)); fs_give ((void **) &test);/* flush test now that copied */ /* make sure application knows */ mail_exists (stream,stream->recent = stream->nmsgs); } /* still hasn't changed */ else stream->gensym = time (0); } return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd = -1; int e; char tmp[MAILTMPLEN]; MAILSTREAM *ts = default_proto (T); if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) { if ((e = errno) == ENOENT) /* failed, was it no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before append", (long) NIL); sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } if (fd >= 0) { /* found file? */ fstat (fd,&sbuf); /* get its size */ close (fd); /* toss out the fd */ if (sbuf.st_size) ts = NIL; /* non-empty file? */ } if (ts) return (*ts->dtb->append) (stream,mailbox,af,data); sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* Dummy mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *dummy_file (char *dst,char *name) { char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? strcpy (dst,sysinbox ()) : s; } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { char dev[4]; /* initially no device */ dev[0] = dev[1] = dev[2] = dev[3] = '\0'; if (ref) switch (*ref) { /* preliminary reference check */ case '{': /* remote names not allowed */ return NIL; /* disallowed */ case '\0': /* empty reference string */ break; default: /* all other names */ if (ref[1] == ':') { /* start with device name? */ dev[0] = *ref++; dev[1] = *ref++; } break; } if (pat[1] == ':') { /* device name in pattern? */ dev[0] = *pat++; dev[1] = *pat++; ref = NIL; /* ignore reference */ } switch (*pat) { case '#': /* namespace names */ if (mailboxfile (tmp,pat)) strcpy (tmp,pat); else return NIL; /* unknown namespace */ break; case '{': /* remote names not allowed */ return NIL; case '\\': /* rooted name */ ref = NIL; /* ignore reference */ break; } /* make sure device names are rooted */ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\'; /* build name */ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat); ucase (tmp); /* force upper case */ return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/env_os2.c000066400000000000000000000176141137544547100230660ustar00rootroot00000000000000/* * Program: OS/2 environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 18 January 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static char *myLocalHost = NIL; /* local host name */ static char *myHomeDir = NIL; /* home directory name */ static char *myNewsrc = NIL; /* newsrc file name */ static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ #include "write.c" /* include safe writing routines */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Get all authenticators */ #include "auths.c" /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_HOMEDIR: myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: if (!myNewsrc) { /* set news file name if not defined */ char tmp[MAILTMPLEN]; sprintf (tmp,"%s\\newsrc",myhomedir ()); myNewsrc = cpystr (tmp); } ret = (void *) myNewsrc; break; case SET_DISABLE822TZTEXT: no822tztext = value ? T : NIL; case GET_DISABLE822TZTEXT: ret = (void *) (no822tztext ? VOIDT : NIL); break; } return ret; } /* Write current time * Accepts: destination string * optional format of day-of-week prefix * format of date and time * flag whether to append symbolic timezone */ static void do_date (char *date,char *prefix,char *fmt,int suffix) { time_t tn = time (0); struct tm *t = gmtime (&tn); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&tn); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; if (prefix) { /* want day of week? */ sprintf (date,prefix,days[t->tm_wday]); date += strlen (date); /* make next sprintf append */ } /* output the date */ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); if (suffix) { /* append timezone suffix if desired */ char *tz; tzset (); /* get timezone from TZ environment stuff */ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]; if (tz && tz[0]) { char *s; for (s = tz; *s; s++) if (*s & 0x80) return; sprintf (date + strlen (date)," (%.50s)",tz); } } } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d", no822tztext ? NIL : T); } /* Write current time in fixed-width RFC 822 format * Accepts: destination string */ void rfc822_fixed_date (char *date) { do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL); } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { if (!myHomeDir) { /* get home directory name if not yet known */ char *s; if ((s = getenv ("PINEHOME")) || (s = getenv ("HOME")) || (s = getenv ("ETC"))) { myHomeDir = cpystr (s); while (s = strchr (myHomeDir,'/')) *s = '\\'; if ((s = strrchr (myHomeDir,'\\')) && !s[1]) *s = '\0'; } else myHomeDir = cpystr (""); } return myHomeDir; } /* Return mailbox file name * Accepts: destination buffer * mailbox name * Returns: file name */ char *mailboxfile (char *dst,char *name) { char *s; char *ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL); /* forbid extraneous extensions */ if ((s = strchr ((s = strrchr (name,'\\')) ? s : name,'.')) && ((ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL)) || strchr (s+1,'.'))) return NIL; /* absolute path name? */ if ((*name == '\\') || (name[1] == ':')) strcpy (dst,name); else sprintf (dst,"%s\\%s",myhomedir (),name); if (ext) sprintf (dst + strlen (dst),".%s",ext); return dst; } /* Lock file name * Accepts: return buffer for file name * file name * locking to be placed on file if non-NIL * Returns: file descriptor of lock or -1 if error */ int lockname (char *lock,char *fname,int op) { int ld; char c,*s; if (!((s = lockdir (lock,getenv ("TEMP"),NIL)) || (s = lockdir (lock,getenv ("TMP"),NIL)) || (s = lockdir (lock,getenv ("TMPDIR"),NIL)) || /* C:\TEMP is last resort */ (s = lockdir (lock,defaultDrive (),"TEMP")))) { mm_log ("Unable to find temporary directory",ERROR); return -1; } /* generate file name */ while (c = *fname++) switch (c) { case '/': case '\\': case ':': *s++ = '!'; /* convert bad chars to ! */ break; default: *s++ = c; break; } *s++ = c; /* tie off name */ /* get the lock */ if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op) flock (ld,op); /* apply locking function */ return ld; /* return locking file descriptor */ } /* Build lock directory, check to see if it exists * Accepts: return buffer for lock directory * first part of possible name * optional second part * Returns: pointer to end of buffer if buffer has a good name, else NIL */ char *lockdir (char *lock,char *first,char *last) { struct stat sbuf; char c,*s; if (first && *first) { /* first part must be non-NIL */ /* copy first part */ for (s = lock; *first; c = *s++ = *first++); if (last && *last) { /* copy last part if specified */ /* write trailing \ in case not in first */ if (c != '\\') *s++ = '\\'; while (*last) c = *s++ = *last++; } if (c == '\\') --s; /* delete trailing \ if any */ *s = '\0'; /* tie off name at this point */ return stat (lock,&sbuf) ? NIL : s; } return NIL; /* failed */ } /* Unlock file descriptor * Accepts: file descriptor * lock file name from lockfd() */ void unlockfd (int fd,char *lock) { flock (fd,LOCK_UN); /* unlock it */ close (fd); /* close it */ } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { extern MAILSTREAM DEFAULTPROTO; return &DEFAULTPROTO; /* return default driver's prototype */ } /* Global data */ static unsigned rndm = 0; /* initial `random' number */ /* Return random number */ long random () { if (!rndm) srand (rndm = (unsigned) time (0L)); return (long) rand (); } /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ void syslog (int priority,const char *message,...) { } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ void openlog (const char *ident,int logopt,int facility) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/env_os2.h000066400000000000000000000033461137544547100230700ustar00rootroot00000000000000/* * Program: OS/2 environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 14 March 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Function prototypes */ #include "env.h" long random (void); /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ void openlog (const char *ident,int logopt,int facility); void syslog (int priority,const char *message,...); void rfc822_fixed_date (char *date); int lockname (char *lock,char *fname,int op); char *lockdir (char *lock,char *first,char *last); void unlockfd (int fd,char *lock); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/fs_os2.c000066400000000000000000000022341137544547100226760ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { free (*block); *block = NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/ftl_os2.c000066400000000000000000000013271137544547100230550ustar00rootroot00000000000000/* * Program: DOS/VMS/TOPS-20 crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/makefile.os2000066400000000000000000000053121137544547100235410ustar00rootroot00000000000000e# Program: Portable C client makefile -- OS/2 version # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 14 October 2003 # # The IMAP toolkit provided in this Distribution is # Copyright 2001 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. EXTRAAUTHENTICATORS = EXTRADRIVERS = EXTRACFLAGS = DEFAULTAUTHENTICATORS = md5 pla log DRIVERS = imap nntp pop3 mbx mtx tenex unix DEFAULTDRIVER = mbx CFLAGS = -DOMF -O2 -Zomf $(EXTRACFLAGS) CC = gcc CCLIENTLIB = cclient.lib all: $(CCLIENTLIB) .c.obj: $(CC) $(CFLAGS) -o $@ -c $*.c osdep.h: os_os2.h copy os_os2.h osdep.h drivers.cmd $(EXTRADRIVERS) $(DRIVERS) dummy auths.cmd $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS) setproto.cmd $(DEFAULTDRIVER) mail.obj: mail.h misc.h osdep.h mail.c misc.obj: mail.h misc.h misc.c fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c rfc822.obj: mail.h rfc822.h misc.h rfc822.c smanager.obj: mail.h misc.h smanager.c utf8.obj: mail.h misc.h osdep.h utf8.h imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c os_os2.obj: mail.h osdep.h env_os2.h fs.h ftl.h nl.h tcp.h tcp_os2.h \ os_os2.c fs_os2.c ftl_os2.c nl_os2.c env_os2.c tcp_os2.c \ mailfile.h auth_md5.c auth_log.c pmatch.c write.c mbxnt.obj: mail.h mbxnt.h misc.h osdep.h mbxnt.c mtxnt.obj: mail.h misc.h osdep.h mtxnt.c tenexnt.obj: mail.h misc.h osdep.h tenexnt.c unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c dummyos2.obj: mail.h dummy.h misc.h osdep.h dummyos2.c pseudo.obj: pseudo.h $(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_os2.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj del $(CCLIENTLIB) LIB /NOLOGO /OUT:$(CCLIENTLIB) \ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_os2.obj \ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj clean: del *.lib *.obj linkage.* osdep.* auths.c *.exe *.exp # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/mbxnt.c000066400000000000000000001501241137544547100226350ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "mbxnt.h" #include "misc.h" #include "dummy.h" /* MBX I/O stream local data */ typedef struct mbx_local { unsigned int flagcheck: 1; /* if ping should sweep for flags */ unsigned int expok: 1; /* if expunging OK in ping */ unsigned int expunged : 1; /* if one or more expunged messages */ int fd; /* file descriptor for I/O */ int ld; /* lock file descriptor */ int ffuserflag; /* first free user flag */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ char lock[MAILTMPLEN]; /* buffer to write lock name */ } MBXLOCAL; /* Convenient access to local data */ #define LOCAL ((MBXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mbx_valid (char *name); int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp); void *mbx_parameters (long function,void *value); void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mbx_list (MAILSTREAM *stream,char *ref,char *pat); void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mbx_create (MAILSTREAM *stream,char *mailbox); long mbx_delete (MAILSTREAM *stream,char *mailbox); long mbx_rename (MAILSTREAM *stream,char *old,char *newname); long mbx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mbx_open (MAILSTREAM *stream); void mbx_close (MAILSTREAM *stream,long options); void mbx_abort (MAILSTREAM *stream); void mbx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mbx_ping (MAILSTREAM *stream); void mbx_check (MAILSTREAM *stream); void mbx_expunge (MAILSTREAM *stream); void mbx_snarf (MAILSTREAM *stream); long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); long mbx_parse (MAILSTREAM *stream); MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok); unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mbx_update_header (MAILSTREAM *stream); void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags); unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr); unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags); long mbx_flaglock (MAILSTREAM *stream); /* MBX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mbxdriver = { "mbx", /* driver name */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING, /* driver flags */ (DRIVER *) NIL, /* next driver */ mbx_valid, /* mailbox is valid for us */ mbx_parameters, /* manipulate parameters */ mbx_scan, /* scan mailboxes */ mbx_list, /* list mailboxes */ mbx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mbx_create, /* create mailbox */ mbx_delete, /* delete mailbox */ mbx_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mbx_open, /* open mailbox */ mbx_close, /* close mailbox */ mbx_flags, /* fetch message "fast" attributes */ mbx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mbx_header, /* fetch message header */ mbx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mbx_flag, /* modify flags */ mbx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mbx_ping, /* ping mailbox to see if still alive */ mbx_check, /* check for new messages */ mbx_expunge, /* expunge deleted messages */ mbx_copy, /* copy messages to another mailbox */ mbx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mbxproto = {&mbxdriver}; /* MBX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mbx_valid (char *name) { char tmp[MAILTMPLEN]; return mbx_isvalid (NIL,name,tmp) ? &mbxdriver : NIL; } /* MBX mail test for valid mailbox * Accepts: returned stream with valid mailbox keywords * mailbox name * scratch buffer * Returns: T if valid, NIL otherwise */ int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp) { int fd; int ret = NIL; unsigned long i; unsigned char *s,*t,hdr[HDRSIZE]; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = dummy_file (tmp,name)) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG) && ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) >= 0)) { errno = -1; /* bogus format */ if ((read (fd,hdr,HDRSIZE) == HDRSIZE) && (hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8]) && isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (hdr[15]) && isxdigit (hdr[16]) && isxdigit (hdr[17]) && isxdigit (hdr[18]) && isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) && isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) { ret = T; if (stream) { /* stream specified? */ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0, sizeof (MAILSTREAM)); for (i = 0, s = hdr + 25; /* parse user flags */ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (strlen (s) <= MAXUSERFLAG) (*stream)->user_flags[i] = cpystr (s); } } } close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { /* preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (tmp,×); /* set the times */ } } /* in case INBOX but not mbx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MBX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mbx_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_ONETIMEEXPUNGEATPING: if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T; case GET_ONETIMEEXPUNGEATPING: if (value) ret = (void *) (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL); break; } return ret; } /* MBX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MBX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MBX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MBX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mbx_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[HDRSIZE]; long ret = NIL; int i,fd; if (!(s = dummy_file (mbx,mailbox))) { sprintf (mbx,"Can't create %.80s: invalid name",mailbox); mm_log (mbx,ERROR); } /* create underlying file */ else if (dummy_create (stream,s)) { /* done if made directory */ if ((s = strrchr (s,'\\')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else { memset (tmp,'\0',HDRSIZE);/* initialize header */ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012", (unsigned long) time (0)); for (i = 0; i < NUSERFLAGS; ++i) sprintf (s += strlen (s),"%s\015\012", (stream && stream->user_flags[i]) ? stream->user_flags[i] : ""); if (write (fd,tmp,HDRSIZE) != HDRSIZE) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s", mbx,strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ close (fd); /* close file */ } } return ret; } /* MBX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mbx_delete (MAILSTREAM *stream,char *mailbox) { return mbx_rename (stream,mailbox,NIL); } /* MBX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mbx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); mm_log (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,tmp)) ret = NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } else { flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } unlockfd (ld,lock); /* release exclusive parse/append permission */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX"); return ret; /* return success */ } /* MBX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mbx_open (MAILSTREAM *stream) { int fd,ld; short silent; char tmp[MAILTMPLEN]; if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */ if (stream->local) fatal ("mbx recycle stream"); /* canonicalize the mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ mm_log ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->ld = -1; /* no flaglock */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get parse/append permission */ if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock open mailbox",ERROR); return NIL; } flock (LOCAL->fd,LOCK_SH); /* lock the file */ unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */ LOCAL->filetime = 0; /* time not set up yet */ LOCAL->expok = LOCAL->flagcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; silent = stream->silent; /* defer events */ stream->silent = T; if (mbx_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); stream->silent = silent; /* now notify upper level */ mail_exists (stream,stream->nmsgs); mail_recent (stream,stream->recent); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T; /* can we create new user flags? */ return stream; /* return stream to caller */ } /* MBX mail close * Accepts: MAIL stream * close options */ void mbx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ /* do an expunge if requested */ if (options & CL_EXPUNGE) mbx_expunge (stream); else { /* otherwise do a checkpoint to purge */ LOCAL->expok = T; /* possible expunged messages */ mbx_ping (stream); } stream->silent = silent; /* restore previous status */ mbx_abort (stream); } } /* MBX mail abort stream * Accepts: MAIL stream */ void mbx_abort (MAILSTREAM *stream) { if (stream && LOCAL) { /* only if a file is open */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MBX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mbx_flags (MAILSTREAM *stream,char *sequence,long flags) { MESSAGECACHE *elt; unsigned long i; if (mbx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence && !elt->valid) mbx_elt (stream,i,NIL); } /* MBX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i; char *s; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get header position, possibly header */ i = mbx_hdrpos (stream,msgno,length,&s); if (!s) { /* mbx_hdrpos() returned header? */ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */ /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->buf,*length); } s[*length] = '\0'; /* tie off string */ return s; } /* MBX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; char *s = LOCAL->text.data; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = mbx_elt (stream,msgno,NIL); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mbx_update_status (stream,msgno,NIL); mm_flags (stream,msgno); /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } if (!LOCAL) i = 0; /* mbx_flaglock() could have aborted */ /* in case previous text cached */ else if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mbx_hdrpos (stream,msgno,&j,NIL); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* MBX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { struct utimbuf times; struct stat sbuf; /* make sure the update takes */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) { fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; /* update header */ if ((LOCAL->ffuserflag < NUSERFLAGS) && stream->user_flags[LOCAL->ffuserflag]) mbx_update_header (stream); times.actime = time (0); /* make sure read comes after all that */ utime (stream->mailbox,×); unlockfd (LOCAL->ld,LOCAL->lock); LOCAL->ld = -1; } } /* MBX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL); } /* MBX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mbx_ping (MAILSTREAM *stream) { unsigned long i,pos; long ret = NIL; int ld; char lock[MAILTMPLEN]; MESSAGECACHE *elt; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ ret = LONGT; /* assume OK */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ /* allow expunge if permitted at ping */ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T; /* if external modification */ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; /* upgrade to flag checking */ /* new mail or flagcheck handling needed? */ if (((i = (sbuf.st_size - LOCAL->filesize)) || LOCAL->flagcheck || !stream->nmsgs) && ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) { if (LOCAL->flagcheck) { /* sweep mailbox for changed message status */ if (ret = mbx_parse (stream)) { LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ) if (mbx_elt (stream,i,LOCAL->expok)) ++i; LOCAL->flagcheck =NIL;/* got all the updates */ } } else if (i) ret = mbx_parse (stream); unlockfd (ld,lock); /* release shared parse/append permission */ } if (ret) { /* must still be alive */ if (!LOCAL->expunged) /* look for holes if none known yet */ for (i = 1, pos = HDRSIZE; !LOCAL->expunged && (i <= stream->nmsgs); i++, pos += elt->private.special.text.size + elt->rfc822_size) if ((elt = mail_elt (stream,i))->private.special.offset != pos) LOCAL->expunged = T;/* found a hole */ /* burp any holes */ if (LOCAL->expunged && !stream->rdonly) { if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check"); if (i) { /* any space reclaimed? */ LOCAL->expunged = NIL;/* no more pending expunge */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i); mm_log (LOCAL->buf,(long) NIL); } } LOCAL->expok = NIL; /* no more expok */ } } return ret; /* return result of the parse */ } /* MBX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mbx_check (MAILSTREAM *stream) { if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */ if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL); } /* MBX mail expunge mailbox * Accepts: MAIL stream */ void mbx_expunge (MAILSTREAM *stream) { unsigned long nexp,reclaimed; if (!mbx_ping (stream)); /* do nothing if stream dead */ else if (stream->rdonly) /* won't do on readonly files! */ mm_log ("Expunge ignored on readonly mailbox",WARN); /* if expunged any messages */ else if (nexp = mbx_rewrite (stream,&reclaimed,T)) { sprintf (LOCAL->buf,"Expunged %lu messages",nexp); mm_log (LOCAL->buf,(long) NIL); } else if (reclaimed) { /* or if any prior expunged space reclaimed */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed); mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); } /* MBX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; struct utimbuf times; MESSAGECACHE *elt; unsigned long i,j,k,m; long ret = LONGT; int fd,ld; char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (dummy_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); return NIL; } mm_critical (stream); /* go critical */ /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock copy mailbox",ERROR); mm_nocritical (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); mail_date(LOCAL->buf,elt);/* build target header */ /* get target keyword mask */ for (j = elt->user_flags, k = 0; j; ) if (s = stream->user_flags[find_rightmost_bit (&j)]) for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++) if (!compare_cstring (s,t) && (k |= 1 << m)) break; sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-00000000\015\012", elt->rfc822_size,k,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* write target header */ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){ read (LOCAL->fd,LOCAL->buf,j); ret = write (fd,LOCAL->buf,j) >= 0; } } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } /* set atime to now-1 if successful copy */ if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) { for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) { /* mark message deleted */ mbx_elt (stream,i,NIL)->deleted = T; /* recalculate status */ mbx_update_status (stream,i,NIL); } /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } return ret; } /* MBX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = NIL; MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mbx_create (dstream = stream ? stream : &mbxproto,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (dstream,data,&flags,&date,&message)); /* open destination mailbox */ else if (((fd = open (dummy_file (file,mailbox), O_WRONLY|O_APPEND|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); } /* get parse/append permission */ else if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock append mailbox",ERROR); close (fd); } else { mm_critical (dstream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; for (ret = LONGT; ret && message; ) { if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (dstream,flags,&uf); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%08lx%04lx-00000000\015\012",tmp, i = SIZE (message),uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !(*af) (dstream,data,&flags,&date,&message)) ret = NIL; } } /* if error... */ if (!ret || (fflush (df) == EOF)) { /* revert file */ ftruncate (fd,sbuf.st_size); close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); } ret = NIL; } if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); /* preserve mtime */ times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ mm_nocritical (dstream); /* release critical */ } if (dstream != stream) mail_close (dstream); return ret; } /* Internal routines */ /* MBX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mbx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j,k,m; off_t curpos = LOCAL->filesize; unsigned long nmsgs = stream->nmsgs; unsigned long recent = stream->recent; unsigned long lastuid = 0; short dirty = NIL; short added = NIL; short silent = stream->silent; short uidwarn = T; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } lseek (LOCAL->fd,0,L_SET); /* rewind file */ /* read internal header */ read (LOCAL->fd,LOCAL->buf,HDRSIZE); LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */ c = LOCAL->buf[15]; /* save first character of last UID */ LOCAL->buf[15] = '\0'; /* parse UID validity */ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16); LOCAL->buf[15] = c; /* restore first character of last UID */ /* parse last UID */ i = strtoul (LOCAL->buf + 15,NIL,16); stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i; /* parse user flags */ for (i = 0, s = LOCAL->buf + 25; (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG)) stream->user_flags[i] = cpystr (s); } LOCAL->ffuserflag = (int) i; /* first free user flag */ stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s", (unsigned long) curpos,i,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) && isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) && isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) && isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) { sprintf (tmp,"Unable to parse message flags at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } if ((t[13] != '-') || t[22] || !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) && isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) && isxdigit (t[20]) && isxdigit (t[21]))) { sprintf (tmp,"Unable to parse message UID at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } *s++ = '\0'; *t++ = '\0'; /* break up fields */ /* get message size */ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) { sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s", (unsigned long) curpos,(char *) LOCAL->buf,(char *) s, (char *) t); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } /* make sure didn't run off end of file */ if (((off_t) (curpos + i + j)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", (unsigned long) curpos,(unsigned long) (curpos + i + j), (unsigned long) sbuf.st_size); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } /* parse UID */ if ((m = strtoul (t+13,NIL,16)) && ((m <= lastuid) || (m > stream->uid_last))) { if (uidwarn) { sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs", m,nmsgs+1); mm_log (tmp,WARN); uidwarn = NIL; /* restart UID validity */ stream->uid_validity = time (0); } m = 0; /* lose this UID */ dirty = T; /* mark dirty, set new lastuid */ stream->uid_last = lastuid; } t[12] = '\0'; /* parse system flags */ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) { if (m) lastuid = m; /* expunge message, update last UID seen */ else { /* no UID assigned? */ lastuid = ++stream->uid_last; dirty = T; } } else { /* not expunged, swell the cache */ added = T; /* note that a new message was added */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; /* parse the date */ if (!mail_parse_date (elt,LOCAL->buf)) { sprintf (tmp,"Unable to parse message date at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mbx_abort (stream); return NIL; } /* note file offset of header */ elt->private.special.offset = curpos; /* and internal header size */ elt->private.special.text.size = i; /* header size not known yet */ elt->private.msg.header.text.size = 0; elt->rfc822_size = j; /* note message size */ /* calculate system flags */ if (k & fSEEN) elt->seen = T; if (k & fDELETED) elt->deleted = T; if (k & fFLAGGED) elt->flagged = T; if (k & fANSWERED) elt->answered = T; if (k & fDRAFT) elt->draft = T; t[8] = '\0'; /* get user flags value */ elt->user_flags = strtoul (t,NIL,16); /* UID already assigned? */ if (!(elt->private.uid = m)) { elt->recent = T; /* no, mark as recent */ ++recent; /* count up a new recent message */ dirty = T; /* and must rewrite header */ /* assign new UID */ elt->private.uid = ++stream->uid_last; mbx_update_status (stream,elt->msgno,NIL); } /* update last parsed UID */ lastuid = elt->private.uid; } curpos += i + j; /* update position */ } if (dirty && !stream->rdonly){/* update header */ mbx_update_header (stream); fsync (LOCAL->fd); /* make sure all the UID updates take */ } /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ struct utimbuf times; times.actime = time (0); times.modtime = LOCAL->filetime; utime (stream->mailbox,×); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MBX get cache element with status updating from file * Accepts: MAIL stream * message number * expunge OK flag * Returns: cache element */ MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; /* get new flags */ if (mbx_read_flags (stream,elt) && expok) { mail_expunged (stream,elt->msgno); return NIL; /* return this message was expunged */ } if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) mm_flags (stream,msgno); /* let top level know */ return elt; } /* MBX read flags from file * Accepts: MAIL stream * cache element * Returns: non-NIL if message expunged */ unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i; struct stat sbuf; fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } LOCAL->buf[13] = '\0'; /* tie off buffer */ /* calculate system flags */ i = strtoul (LOCAL->buf+9,NIL,16); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->expunged |= i & fEXPUNGED ? T : NIL; LOCAL->buf[9] = '\0'; /* tie off flags */ /* get user flags value */ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16); elt->valid = T; /* have valid flags now */ return i & fEXPUNGED; } /* MBX update header * Accepts: MAIL stream */ #define NTKLUDGEOFFSET 7 void mbx_update_header (MAILSTREAM *stream) { int i; char *s = LOCAL->buf; memset (s,'\0',HDRSIZE); /* initialize header */ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012", stream->uid_validity,stream->uid_last); for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]); LOCAL->ffuserflag = i; /* first free user flag */ /* can we create more user flags? */ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL; /* write reserved lines */ while (i++ < NUSERFLAGS) strcat (s,"\015\012"); while (T) { /* rewind file */ lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET); /* write new header */ if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET, HDRSIZE - NTKLUDGEOFFSET) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } } /* MBX update status string * Accepts: MAIL stream * message number * flags */ void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags) { struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); /* readonly */ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt); else { /* readwrite */ fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } /* print new flag string */ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned) (((elt->deleted && flags) ? fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft)),elt->private.uid); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 23,L_SET); /* write new flags and UID */ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } } } /* MBX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * pointer to possible returned header * Returns: position of header in file */ #define HDRBUFLEN 4096 /* good enough for most headers */ #define SLOP 4 /* CR LF CR LF */ unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr) { unsigned long siz,done; long i; unsigned char *s,*t,*te; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; if (hdr) *hdr = NIL; /* assume no header returned */ /* is header size known? */ if (*size = elt->private.msg.header.text.size) return ret; /* paranoia check */ if (LOCAL->buflen < (HDRBUFLEN + SLOP)) fatal ("LOCAL->buf smaller than HDRBUFLEN"); lseek (LOCAL->fd,ret,L_SET); /* get to header position */ /* read HDRBUFLEN chunks with 4 byte slop */ for (done = siz = 0, s = LOCAL->buf; (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) && (read (LOCAL->fd,s,i) == i); done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) { te = (t = s + i) - 12; /* calculate end of fast scan */ /* fast scan for CR */ for (s = LOCAL->buf; s < te;) if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } for (te = t - 3; (s < te);) /* final character-at-a-time scan */ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } if (i <= SLOP) break; /* end of data */ /* slide over last 4 bytes */ memmove (LOCAL->buf,t - SLOP,SLOP); hdr = NIL; /* can't return header this way */ } /* not found: header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; if (hdr) *hdr = LOCAL->buf; /* possibly return header too */ return ret; } /* MBX mail rewrite mailbox * Accepts: MAIL stream * pointer to return reclaimed size * flags (non-NIL to do expunge) * Returns: number of expunged messages */ unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags) { struct utimbuf times; struct stat sbuf; off_t pos,ppos; int ld; unsigned long i,j,k,m,n,delta; unsigned long recent = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; /* get parse/append permission */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock expunge mailbox",ERROR); return *reclaimed = 0; } fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime && !LOCAL->flagcheck && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */ unlockfd (ld,lock); /* failed?? */ return *reclaimed = 0; } if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */ LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL); LOCAL->flagcheck = NIL; } /* get exclusive access */ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { mm_critical (stream); /* go critical */ for (i = 1,n = delta = *reclaimed = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) { /* note if message not at predicted location */ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) { ppos = elt->private.special.offset; *reclaimed += m; /* note reclaimed message space */ delta += m; /* and as expunge delta */ } /* number of bytes to smash or preserve */ ppos += (k = elt->private.special.text.size + elt->rfc822_size); if (flags & elt->deleted){/* if deleted */ delta += k; /* number of bytes to delete */ mail_expunged(stream,i);/* notify upper levels */ n++; /* count up one more expunged message */ } else { /* preserved message */ i++; /* count this message */ if (elt->recent) ++recent; if (delta) { /* moved, note first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages yet */ else pos = elt->private.special.offset + k; } } /* deltaed file size match position? */ if (m = (LOCAL->filesize -= delta) - pos) { *reclaimed += m; /* probably an fEXPUNGED msg */ LOCAL->filesize = pos; /* set correct size */ } /* truncate file after last message */ ftruncate (LOCAL->fd,LOCAL->filesize); fsync (LOCAL->fd); /* force disk update */ mm_nocritical (stream); /* release critical */ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ unlockfd (ld,lock); /* release exclusive parse/append permission */ } else { /* can't get exclusive */ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */ unlockfd (ld,lock); /* release exclusive parse/append permission */ /* checkpoint while shared? */ if (!flags) n = *reclaimed = 0; /* no, do hide-expunge */ else for (i = 1,n = *reclaimed = 0; i <= stream->nmsgs; ) { if (elt = mbx_elt (stream,i,T)) { if (elt->deleted) { /* make deleted message invisible */ mbx_update_status (stream,elt->msgno,LONGT); /* notify upper levels */ mail_expunged (stream,i); n++; /* count up one more expunged message */ } else { i++; /* preserved message */ if (elt->recent) ++recent; } } else n++; /* count up one more expunged message */ } fsync (LOCAL->fd); /* force disk update */ } fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* reset atime to now */ utime (stream->mailbox,×); /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); return n; /* return number of expunged messages */ } /* MBX mail lock for flag updating * Accepts: stream * Returns: T if successful, NIL if failure */ long mbx_flaglock (MAILSTREAM *stream) { struct stat sbuf; unsigned long i; int ld; char lock[MAILTMPLEN]; /* no-op if readonly or already locked */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) { /* lock now */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) return NIL; if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */ if (LOCAL->filetime) { /* know previous time? */ fstat (LOCAL->fd,&sbuf);/* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } if (!mbx_parse (stream)) {/* parse mailbox */ unlockfd (ld,lock); /* shouldn't happen */ return NIL; } if (LOCAL->flagcheck) /* invalidate cache if flagcheck */ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL; } LOCAL->ld = ld; /* copy to stream for subseuent calls */ memcpy (LOCAL->lock,lock,MAILTMPLEN); } return LONGT; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/mbxnt.h000066400000000000000000000012211137544547100226330ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define HDRSIZE 2048 /* Private driver flags, should be in mail.h? */ #define fEXPUNGED 32768 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/mtxnt.c000066400000000000000000001164671137544547100226730ustar00rootroot00000000000000/* * Program: MTX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "misc.h" #include "dummy.h" /* MTX I/O stream local data */ typedef struct mtx_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } MTXLOCAL; /* Convenient access to local data */ #define LOCAL ((MTXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mtx_valid (char *name); int mtx_isvalid (char *name,char *tmp); void *mtx_parameters (long function,void *value); void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mtx_list (MAILSTREAM *stream,char *ref,char *pat); void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mtx_create (MAILSTREAM *stream,char *mailbox); long mtx_delete (MAILSTREAM *stream,char *mailbox); long mtx_rename (MAILSTREAM *stream,char *old,char *newname); long mtx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mtx_open (MAILSTREAM *stream); void mtx_close (MAILSTREAM *stream,long options); void mtx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mtx_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mtx_ping (MAILSTREAM *stream); void mtx_check (MAILSTREAM *stream); void mtx_snarf (MAILSTREAM *stream); void mtx_expunge (MAILSTREAM *stream); long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); long mtx_parse (MAILSTREAM *stream); MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno); void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag); unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* MTX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mtxdriver = { "mtx", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ mtx_valid, /* mailbox is valid for us */ mtx_parameters, /* manipulate parameters */ mtx_scan, /* scan mailboxes */ mtx_list, /* list mailboxes */ mtx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mtx_create, /* create mailbox */ mtx_delete, /* delete mailbox */ mtx_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mtx_open, /* open mailbox */ mtx_close, /* close mailbox */ mtx_flags, /* fetch message "fast" attributes */ mtx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mtx_header, /* fetch message header */ mtx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mtx_flag, /* modify flags */ mtx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mtx_ping, /* ping mailbox to see if still alive */ mtx_check, /* check for new messages */ mtx_expunge, /* expunge deleted messages */ mtx_copy, /* copy messages to another mailbox */ mtx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mtxproto = {&mtxdriver}; /* MTX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mtx_valid (char *name) { char tmp[MAILTMPLEN]; return mtx_isvalid (name,tmp) ? &mtxdriver : NIL; } /* MTX mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int mtx_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) && (s[1] == '\012')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { /* preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ } } } /* in case INBOX but not mtx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MTX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mtx_parameters (long function,void *value) { return NIL; } /* MTX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MTX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MTX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MTX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mtx_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN]; if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s); sprintf (mbx,"Can't create %.80s: invalid name",mailbox); mm_log (mbx,ERROR); return NIL; } /* MTX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mtx_delete (MAILSTREAM *stream,char *mailbox) { return mtx_rename (stream,mailbox,NIL); } /* MTX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mtx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); mm_log (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,tmp)) ret = NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } else { flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } unlockfd (ld,lock); /* release exclusive parse/append permission */ return ret; /* return success */ } /* MTX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mtx_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &mtxproto; if (stream->local) fatal ("mtx recycle stream"); /* canonicalize the mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ mm_log ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (MTXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) { mm_log ("Unable to lock open mailbox",ERROR); return NIL; } flock (LOCAL->fd,LOCK_SH); /* lock the file */ unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ LOCAL->filetime = 0; /* time not set up yet */ LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ stream->uid_validity = time (0); /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (mtx_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* MTX mail close * Accepts: MAIL stream * close options */ void mtx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mtx_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MTX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mtx_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if (mtx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) mtx_elt (stream,i); } /* MTX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET); /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } LOCAL->buf[*length] = '\0'; /* tie off string */ /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length); return LOCAL->buf; } /* MTX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mtx_elt (stream,msgno); /* get message status */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mtx_update_status (stream,msgno,T); mm_flags (stream,msgno); } /* in case previous text cached */ if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mtx_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); return T; /* success */ } /* MTX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { struct utimbuf times; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read comes after all that */ utime (stream->mailbox,×); } } /* MTX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ mtx_update_status (stream,elt->msgno,NIL); } /* MTX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mtx_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ mm_notify (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) mtx_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (mtx_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } return r; /* return result of the parse */ } /* MTX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mtx_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL); } /* MTX mail expunge mailbox * Accepts: MAIL stream */ void mtx_expunge (MAILSTREAM *stream) { struct utimbuf times; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; /* do nothing if stream dead */ if (!mtx_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ mm_log ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* get exclusive parse/append permission */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!mtx_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ mm_log("Can't expunge because mailbox is in use by another process",ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } mm_critical (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = mtx_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + elt->rfc822_size; if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); mm_log (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* reset atime to now */ utime (stream->mailbox,×); mm_nocritical (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* MTX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; struct utimbuf times; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (dummy_file (file,mailbox),O_BINARY|O_RDWR|O_CREAT, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno)); mm_log (LOCAL->buf,ERROR); return NIL; } mm_critical (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock copy mailbox",ERROR); mm_nocritical (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + elt->rfc822_size; do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } /* set atime to now-1 if successful copy */ if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = mtx_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ mtx_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure atime remains greater */ utime (stream->mailbox,×); } } return ret; } /* MTX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &mtxproto; /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open(dummy_file (file,mailbox),O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } mm_critical (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf, (unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); } ret = NIL; } if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ mm_nocritical (stream); /* release critical */ return ret; } /* Internal routines */ /* MTX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mtx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); mm_log (tmp,ERROR); mtx_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ mtx_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ struct utimbuf times; times.actime = time (0); times.modtime = LOCAL->filetime; utime (stream->mailbox,×); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MTX get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; mtx_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) mm_flags (stream,msgno); /* let top level know */ return elt; } /* MTX read flags from file * Accepts: MAIL stream * Returns: cache element */ void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* MTX update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { struct utimbuf times; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* write new flags */ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read is later */ utime (stream->mailbox,×); } } } /* MTX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; int q = 0; char *s,tmp[MAILTMPLEN]; MESSAGECACHE *elt = mtx_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for CRLF CRLF */ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) { /* read another buffer as necessary */ if ((--i <= 0) && /* buffer empty? */ (read (LOCAL->fd,s = tmp, i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)) return ret; /* I/O error? */ switch (q) { /* sniff at buffer */ case 0: /* first character */ q = (*s++ == '\015') ? 1 : 0; break; case 1: /* second character */ q = (*s++ == '\012') ? 2 : 0; break; case 2: /* third character */ q = (*s++ == '\015') ? 3 : 0; break; case 3: /* fourth character */ if (*s++ == '\012') { /* have the sequence? */ /* yes, note for later */ elt->private.msg.header.text.size = *size = siz; return ret; } q = 0; /* lost... */ break; } } /* header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/nl_os2.c000066400000000000000000000027511137544547100227030ustar00rootroot00000000000000/* * Program: Windows/TOPS-20 newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { /* flush destination buffer if too small */ if (*dst && (srcl > *dstl)) fs_give ((void **) dst); if (!*dst) { /* make a new buffer if needed */ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1); if (dstl) *dstl = srcl; /* return new buffer length to main program */ } /* copy strings */ if (srcl) memcpy (*dst,src,(size_t) srcl); *(*dst + srcl) = '\0'; /* tie off destination */ return srcl; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { return SIZE (s); /* no-brainer on DOS! */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/os_os2.c000066400000000000000000000043671137544547100227200ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- OS/2 emx version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 14 March 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include "tcp_os2.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include "misc.h" #include "fs_os2.c" #include "ftl_os2.c" #include "nl_os2.c" #include "env_os2.c" #include "tcp_os2.c" /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { /* known yet? */ char *s,tmp[MAILTMPLEN]; struct hostent *he; /* could we get local id? */ gethostname (tmp,MAILTMPLEN-1); if (he = gethostbyname (lcase (tmp))) { if (he->h_name) s = he->h_name; else sprintf (s = tmp,"[%i.%i.%i.%i]",he->h_addr[0],he->h_addr[1], he->h_addr[2],he->h_addr[3]); } else s = "random-pc"; myLocalHost = cpystr (s); /* record for subsequent use */ } return myLocalHost; } /* Look up host address * Accepts: pointer to pointer to host name * socket address block * Returns: non-zero with host address in socket, official host name in host; * else NIL */ long lookuphost (char **host,struct sockaddr_in *sin) { long ret = -1; char tmp[MAILTMPLEN]; struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host))); if (!hn) return NIL; /* got a host name? */ *host = cpystr (hn->h_name); /* set official name */ /* copy host addresses */ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length); return T; } /* * Emulator for BSD syslog() routine. */ void syslog (int priority,const char *message,...) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/os_os2.h000066400000000000000000000013471137544547100227200ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- OS/2 emx version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 14 March 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include "env_os2.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/pmatch.c000066400000000000000000000053271137544547100227650ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-independent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/pseudo.c000066400000000000000000000020221137544547100227750ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Local sites may wish to alter this text */ char *pseudo_from = "MAILER-DAEMON"; char *pseudo_name = "Mail System Internal Data"; char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA"; char *pseudo_msg = "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values." ; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/pseudo.h000066400000000000000000000011421137544547100230040ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/setproto.cmd000066400000000000000000000013531137544547100237040ustar00rootroot00000000000000/* rexx */ /* * Program: Set default prototype for OS/2 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 June 1999 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ '@echo off' parse arg default args h_file='linkage.h' call stream h_file, 'C', 'open write' call lineout h_file, '#define DEFAULTPROTO' default'proto' call stream h_file, 'C', 'close' exit 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/tcp_os2.c000066400000000000000000000242541137544547100230620ustar00rootroot00000000000000/* * Program: MS-DOS TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_read = 0; /* TCP timeouts, in seconds */ static long ttmo_write = 0; /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; struct sockaddr_in sin; int sock; char *s,tmp[MAILTMPLEN]; port &= 0xffff; /* erase flags */ /* The domain literal form is used (rather than simply the dotted decimal as with other Unix programs) because it has to be a valid "host name" in mailsystem terminology. */ sin.sin_family = AF_INET; /* family is always Internet */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (tmp,host+1); /* yes, copy number part */ tmp[strlen (tmp)-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } /* look up host name */ else if (!lookuphost (&host,&sin)) { sprintf (tmp,"Host not found: %s",host); mm_log (tmp,ERROR); return NIL; } /* copy port number in network format */ if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open"); /* get a TCP stream */ if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) { sprintf (tmp,"Unable to create TCP socket (%d)",errno); mm_log (tmp,ERROR); fs_give ((void **) &host); return NIL; } /* open connection */ if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) { switch (errno) { /* analyze error */ case ECONNREFUSED: s = "Refused"; break; case ENOBUFS: s = "Insufficient system resources"; break; case ETIMEDOUT: s = "Timed out"; break; default: s = "Unknown error"; break; } sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno); mm_log (tmp,ERROR); close (sock); fs_give ((void **) &host); return NIL; } /* create TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); stream->host = host; /* official host name */ stream->localhost = cpystr (mylocalhost ()); stream->port = port; /* port number */ stream->tcps = sock; /* init socket */ stream->ictr = 0; /* init input counter */ return stream; /* return success */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* always NIL on DOS */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) return NIL; /* special case of newline broken by buffer */ if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; char *bufptr = buffer; while (size > 0) { /* until request satisfied */ if (!tcp_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (bufptr,stream->iptr,n); bufptr += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } bufptr[0] = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); if (stream->tcps < 0) return NIL; while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_read; /* read timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcps,&fds);/* set bit in selection vector */ FD_SET(stream->tcps,&efds);/* set bit in error selection vector */ errno = NIL; /* block and read */ while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); if (i < 1) return tcp_abort (stream); stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ } return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; fd_set fds; struct timeval tmo; time_t t = time (0); if (stream->tcps < 0) return NIL; while (size > 0) { /* until request satisfied */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_write; /* write timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_SET (stream->tcps,&fds);/* set bit in selection vector */ errno = NIL; /* block and write */ while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR)); if (i < 0) return tcp_abort (stream); size -= i; /* how much we sent */ string += i; } return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (stream); /* nuke the socket */ /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { if (stream->tcps >= 0) close (stream->tcps); stream->tcps = -1; return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* all we can do for now */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { return name; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/tcp_os2.h000066400000000000000000000021441137544547100230610ustar00rootroot00000000000000/* * Program: MS-DOS TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer -- must be large enough to prevent overflow */ #define BUFLEN 8192 /* TCP I/O stream (must be before osdep.h is included) */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ int tcps; /* tcp socket */ long ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; /* Local function prototypes */ long lookuphost (char **host,struct sockaddr_in *sin); long tcp_abort (TCPSTREAM *stream); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/tenexnt.c000066400000000000000000001233671137544547100232030ustar00rootroot00000000000000/* * Program: Tenex mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. * * TEXT SIZE SEMANTICS * * Most of the text sizes are in internal (LF-only) form, except for the * msg.text size. Beware. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "misc.h" #include "dummy.h" /* TENEX I/O stream local data */ typedef struct tenex_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* local snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } TENEXLOCAL; /* Convenient access to local data */ #define LOCAL ((TENEXLOCAL *) stream->local) /* Function prototypes */ DRIVER *tenex_valid (char *name); int tenex_isvalid (char *name,char *tmp); void *tenex_parameters (long function,void *value); void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void tenex_list (MAILSTREAM *stream,char *ref,char *pat); void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat); long tenex_create (MAILSTREAM *stream,char *mailbox); long tenex_delete (MAILSTREAM *stream,char *mailbox); long tenex_rename (MAILSTREAM *stream,char *old,char *newname); long tenex_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *tenex_open (MAILSTREAM *stream); void tenex_close (MAILSTREAM *stream,long options); void tenex_fast (MAILSTREAM *stream,char *sequence,long flags); void tenex_flags (MAILSTREAM *stream,char *sequence,long flags); char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long tenex_ping (MAILSTREAM *stream); void tenex_check (MAILSTREAM *stream); void tenex_snarf (MAILSTREAM *stream); void tenex_expunge (MAILSTREAM *stream); long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); unsigned long tenex_size (MAILSTREAM *stream,unsigned long m); long tenex_parse (MAILSTREAM *stream); MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno); void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void tenex_update_status (MAILSTREAM *stream,unsigned long msgno, long syncflag); unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* Tenex mail routines */ /* Driver dispatch used by MAIL */ DRIVER tenexdriver = { "tenex", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ tenex_valid, /* mailbox is valid for us */ tenex_parameters, /* manipulate parameters */ tenex_scan, /* scan mailboxes */ tenex_list, /* list mailboxes */ tenex_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ tenex_create, /* create mailbox */ tenex_delete, /* delete mailbox */ tenex_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ tenex_open, /* open mailbox */ tenex_close, /* close mailbox */ tenex_flags, /* fetch message "fast" attributes */ tenex_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ tenex_header, /* fetch message header */ tenex_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ tenex_flag, /* modify flags */ tenex_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ tenex_ping, /* ping mailbox to see if still alive */ tenex_check, /* check for new messages */ tenex_expunge, /* expunge deleted messages */ tenex_copy, /* copy messages to another mailbox */ tenex_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM tenexproto = {&tenexdriver}; /* Tenex mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *tenex_valid (char *name) { char tmp[MAILTMPLEN]; return tenex_isvalid (name,tmp) ? &tenexdriver : NIL; } /* Tenex mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int tenex_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) && (s[-1] != '\015')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { /* preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ } } } /* in case INBOX but not tenex format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* Tenex manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tenex_parameters (long function,void *value) { return NIL; } /* Tenex mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* Tenex mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* Tenex mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* Tenex mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long tenex_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN]; if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s); sprintf (mbx,"Can't create %.80s: invalid name",mailbox); mm_log (mbx,ERROR); return NIL; } /* Tenex mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long tenex_delete (MAILSTREAM *stream,char *mailbox) { return tenex_rename (stream,mailbox,NIL); } /* Tenex mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long tenex_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); mm_log (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,tmp)) ret = NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } else { flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } unlockfd (ld,lock); /* release exclusive parse/append permission */ return ret; /* return success */ } /* Tenex mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *tenex_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &tenexproto; if (stream->local) fatal ("tenex recycle stream"); /* canonicalize the mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ mm_log ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (TENEXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) { mm_log ("Unable to lock open mailbox",ERROR); return NIL; } flock (LOCAL->fd,LOCK_SH); /* lock the file */ unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ LOCAL->filetime = 0; /* time not set up yet */ LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ stream->uid_validity = time (0); /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (tenex_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* Tenex mail close * Accepts: MAIL stream * close options */ void tenex_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) tenex_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* Tenex mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to get flags */ void tenex_flags (MAILSTREAM *stream,char *sequence,long flags) { STRING bs; MESSAGECACHE *elt; unsigned long i; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { if (!elt->rfc822_size) { /* have header size yet? */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); /* resize bigbuf if necessary */ if (LOCAL->buflen < elt->private.msg.full.text.size) { fs_give ((void **) &LOCAL->buf); LOCAL->buflen = elt->private.msg.full.text.size; LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1); } /* tie off string */ LOCAL->buf[elt->private.msg.full.text.size] = '\0'; /* read in the message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size); INIT (&bs,mail_string,(void *) LOCAL->buf, elt->private.msg.full.text.size); /* calculate its CRLF size */ elt->rfc822_size = unix_crlflen (&bs); } tenex_elt (stream,i); /* get current flags from file */ } } /* TENEX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { char *s; unsigned long i; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET); if (flags & FT_INTERNAL) { if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length = i); } else { s = (char *) fs_get (i + 1);/* get readin buffer */ s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i); fs_give ((void **) &s); /* free readin buffer */ } return LOCAL->buf; } /* TENEX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T, always */ long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = tenex_elt (stream,msgno); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ tenex_update_status (stream,msgno,T); mm_flags (stream,msgno); } if (flags & FT_INTERNAL) { /* if internal representation wanted */ /* find header position */ i = tenex_hdrpos (stream,msgno,&j); if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* slurp the data */ if (read (LOCAL->fd,LOCAL->buf,i) != (long) i) return NIL; /* set up stringstruct for internal */ INIT (bs,mail_string,LOCAL->buf,i); } else { /* normal form, previous text cached? */ if (elt->private.uid == LOCAL->uid) i = elt->private.msg.text.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = tenex_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1); s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ i = elt->private.msg.text.text.size = strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i); fs_give ((void **) &s); /* free readin buffer */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); } return T; /* success */ } /* Tenex mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { struct utimbuf times; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read comes after all that */ utime (stream->mailbox,×); } } /* Tenex mail per-message modify flags * Accepts: MAIL stream * message cache element */ void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ tenex_update_status (stream,elt->msgno,NIL); } /* Tenex mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long tenex_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ mm_notify (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) tenex_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (tenex_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } return r; /* return result of the parse */ } /* Tenex mail check mailbox (reparses status too) * Accepts: MAIL stream */ void tenex_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL); } /* Tenex mail expunge mailbox * Accepts: MAIL stream */ void tenex_expunge (MAILSTREAM *stream) { struct utimbuf times; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; /* do nothing if stream dead */ if (!tenex_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ mm_log ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* get exclusive access */ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) { mm_log ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!tenex_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ mm_log("Can't expunge because mailbox is in use by another process",ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } mm_critical (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = tenex_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + tenex_size (stream,i); if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); mm_log (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* reset atime to now */ utime (stream->mailbox,×); mm_nocritical (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* Tenex mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; struct utimbuf times; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (dummy_file (file,mailbox),O_BINARY|O_RDWR|O_CREAT, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno)); mm_log (LOCAL->buf,ERROR); return NIL; } mm_critical (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock copy mailbox",ERROR); mm_nocritical (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + tenex_size (stream,i); do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* delete all requested messages */ if (ret && (options & CP_MOVE)) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } /* set atime to now-1 if successful copy */ if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ mm_nocritical (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = tenex_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ tenex_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure atime remains greater */ utime (stream->mailbox,×); } } return ret; } /* Tenex mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; FILE *df; MESSAGECACHE elt; long f; unsigned long i,j,uf,size; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &tenexproto; /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) tenex_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open(dummy_file(file,mailbox),O_BINARY|O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } mm_critical (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ i = GETPOS (message); /* remember current position */ for (j = SIZE (message), size = 0; j; --j) if (SNX (message) != '\015') ++size; SETPOS (message,i); /* restore position */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ while (size) if ((c = 0xff & SNX (message)) != '\015') { if (putc (c,df) != EOF) --size; else break; } /* get next message */ if (size || !(*af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); } ret = NIL; } if (ret) times.actime = time (0) - 1; /* else preserved \Marked status */ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time (0); times.modtime = sbuf.st_mtime;/* preserve mtime */ utime (file,×); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ mm_nocritical (stream); /* release critical */ return ret; } /* Internal routines */ /* Tenex mail return internal message size in bytes * Accepts: MAIL stream * message # * Returns: internal size of message */ unsigned long tenex_size (MAILSTREAM *stream,unsigned long m) { MESSAGECACHE *elt = mail_elt (stream,m); return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset : LOCAL->filesize) - (elt->private.special.offset + elt->private.special.text.size); } /* Tenex mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long tenex_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!(s = strchr (LOCAL->buf,'\012'))) { sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 1) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); mm_log (tmp,ERROR); tenex_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ tenex_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ struct utimbuf times; times.actime = time (0); times.modtime = LOCAL->filetime; utime (stream->mailbox,×); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* Tenex get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; tenex_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) mm_flags (stream,msgno); /* let top level know */ return elt; } /* Tenex read flags from file * Accepts: MAIL stream * Returns: cache element */ void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* Tenex update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { struct utimbuf times; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* write new flags */ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break; mm_notify (stream,strerror (errno),WARN); mm_diskerror (stream,errno,T); } if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ times.modtime = LOCAL->filetime = sbuf.st_mtime; times.actime = time (0); /* make sure read is later */ utime (stream->mailbox,×); } } } /* Tenex locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; char c = '\0'; char *s = NIL; MESSAGECACHE *elt = tenex_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; unsigned long msiz = tenex_size (stream,msgno); /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for LF LF */ for (siz = 0; siz < msiz; siz++) { if (--i <= 0) /* read another buffer as necessary */ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN)); /* two newline sequence? */ if ((c == '\012') && (*s == '\012')) { /* yes, note for later */ elt->private.msg.header.text.size = (*size = siz + 1); return ret; /* return to caller */ } else c = *s++; /* next character */ } /* header consumes entire message */ elt->private.msg.header.text.size = *size = msiz; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/unixnt.c000066400000000000000000002234141137544547100230350ustar00rootroot00000000000000/* * Program: UNIX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) && ((mailbox[1] == 'N') || (mailbox[1] == 'n')) && ((mailbox[2] == 'B') || (mailbox[2] == 'b')) && ((mailbox[3] == 'O') || (mailbox[3] == 'o')) && ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) { * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include #include "unixnt.h" #include "pseudo.h" #include "fdstring.h" #include "misc.h" #include "dummy.h" /* UNIX I/O stream local data */ typedef struct unix_local { unsigned int dirty : 1; /* disk copy needs updating */ unsigned int pseudo : 1; /* uses a pseudo message */ int fd; /* mailbox file descriptor */ int ld; /* lock file descriptor */ char *lname; /* lock file name */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time (for mbox driver) */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ unsigned long textlen; /* current text length */ char *line; /* returned line */ } UNIXLOCAL; /* Convenient access to local data */ #define LOCAL ((UNIXLOCAL *) stream->local) /* UNIX protected file structure */ typedef struct unix_file { MAILSTREAM *stream; /* current stream */ off_t curpos; /* current file position */ off_t protect; /* protected position */ off_t filepos; /* current last written file position */ char *buf; /* overflow buffer */ size_t buflen; /* current overflow buffer length */ char *bufpos; /* current buffer position */ } UNIXFILE; /* Function prototypes */ DRIVER *unix_valid (char *name); long unix_isvalid_fd (int fd); void *unix_parameters (long function,void *value); void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void unix_list (MAILSTREAM *stream,char *ref,char *pat); void unix_lsub (MAILSTREAM *stream,char *ref,char *pat); long unix_create (MAILSTREAM *stream,char *mailbox); long unix_delete (MAILSTREAM *stream,char *mailbox); long unix_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *unix_open (MAILSTREAM *stream); void unix_close (MAILSTREAM *stream,long options); char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long unix_ping (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_expunge (MAILSTREAM *stream); long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void unix_abort (MAILSTREAM *stream); char *unix_file (char *dst,char *name); int unix_lock (char *file,int flags,int mode,char *lock,int op); void unix_unlock (int fd,MAILSTREAM *stream,char *lock); int unix_parse (MAILSTREAM *stream,char *lock,int op); char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr); unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag); long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock); long unix_extend (MAILSTREAM *stream,unsigned long size); void unix_write (UNIXFILE *f,char *s,unsigned long i); void unix_phys_write (UNIXFILE *f,char *buf,size_t size); /* UNIX mail routines */ /* Driver dispatch used by MAIL */ DRIVER unixdriver = { "unix", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ unix_valid, /* mailbox is valid for us */ unix_parameters, /* manipulate parameters */ unix_scan, /* scan mailboxes */ unix_list, /* list mailboxes */ unix_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ unix_create, /* create mailbox */ unix_delete, /* delete mailbox */ unix_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ unix_open, /* open mailbox */ unix_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ unix_header, /* fetch message header */ unix_text, /* fetch message text */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ unix_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ unix_ping, /* ping mailbox to see if still alive */ unix_check, /* check for new messages */ unix_expunge, /* expunge deleted messages */ unix_copy, /* copy messages to another mailbox */ unix_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM unixproto = {&unixdriver}; /* driver parameters */ static long unix_fromwidget = T; /* UNIX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *unix_valid (char *name) { int fd; DRIVER *ret = NIL; int c,r; char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* must be non-empty file */ if ((t = dummy_file (file,name)) && !stat (t,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1; else { /* ignore leading whitespace */ for (s = tmp,c = '\n'; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t'); c = *s++); if (c == '\n') { /* at start of a line? */ VALID (s,t,r,c); /* yes, validate format */ if (r) ret = &unixdriver; else errno = -1; /* invalid format */ } } close (fd); /* close the file */ /* \Marked status? */ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) { /* yes, preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ } } } return ret; /* return what we should */ } /* UNIX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *unix_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_FROMWIDGET: unix_fromwidget = (long) value; case GET_FROMWIDGET: ret = (void *) unix_fromwidget; break; } return ret; } /* UNIX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* UNIX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* UNIX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* UNIX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long unix_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN]; long ret = NIL; int fd; time_t ti = time (0); if (!(s = dummy_file (mbx,mailbox))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); mm_log (tmp,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,NIL)) { /* done if made directory */ if ((s = strrchr (s,'\\')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else { /* initialize header */ memset (tmp,'\0',MAILTMPLEN); sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti)); if (s = strpbrk (tmp,"\r\n")) *s = '\0'; strcat (tmp,"\r\nDate: "); rfc822_fixed_date (s = tmp + strlen (tmp)); sprintf (s += strlen (s), /* write the pseudo-header */ "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n", pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) ti,pseudo_msg); if ((write (fd,tmp,strlen (tmp)) < 0) || close (fd)) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx, strerror (errno)); mm_log (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ } close (fd); /* close file */ } return ret; } /* UNIX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long unix_delete (MAILSTREAM *stream,char *mailbox) { return unix_rename (stream,mailbox,NIL); } /* UNIX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long unix_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = NIL; char c,*s = NIL; char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN]; int fd,ld; struct stat sbuf; mm_critical (stream); /* get the c-client lock */ if (!dummy_file (file,old) || (newname && (!(s = dummy_file (tmp,newname)) || ((s = strrchr (s,'\\')) && !s[1])))) sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); else if ((ld = lockname (lock,file,NIL)) < 0) sprintf (tmp,"Can't get lock for mailbox %.80s",old); else { /* lock out other c-clients */ if (flock (ld,LOCK_EX|LOCK_NB)) { close (ld); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); } /* lock out non c-client applications */ else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx, LOCK_EX)) < 0) sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno)); else { unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */ if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,newname)) { flock (ld,LOCK_UN); close (ld); /* close c-client lock */ unlink (lock); /* and delete it */ mm_nocritical (stream); return NIL; /* couldn't create superior */ } } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } if (rename (file,tmp)) sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); else ret = T; /* set success */ } else if (unlink (file)) /* want delete */ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); else ret = T; /* set success */ flock (ld,LOCK_UN); /* release c-client lock */ close (ld); /* close c-client lock */ unlink (lock); /* and delete it */ } } mm_nocritical (stream); /* no longer critical */ if (!ret) mm_log (tmp,ERROR); /* log error */ return ret; /* return success or failure */ } /* UNIX mail open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *unix_open (MAILSTREAM *stream) { int fd; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &unixproto; if (stream->local) fatal ("unix recycle stream"); stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); /* canonicalize the stream mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); return NIL; } /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); stream->sequence++; /* bump sequence number */ if (!stream->rdonly) { /* make lock for read/write access */ if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0) mm_log ("Can't open mailbox lock, access is readonly",WARN); /* can get the lock? */ else if (flock (fd,LOCK_EX|LOCK_NB)) { mm_log ("Mailbox is open by another process, access is readonly",WARN); close (fd); } else { /* got the lock, nobody else can alter state */ LOCAL->ld = fd; /* note lock's fd and name */ LOCAL->lname = cpystr (tmp); } } /* parse mailbox */ stream->nmsgs = stream->recent = 0; /* will we be able to get write access? */ if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) { mm_log ("Can't get write access to mailbox, access is readonly",WARN); flock (LOCAL->ld,LOCK_UN); /* release the lock */ close (LOCAL->ld); /* close the lock file */ LOCAL->ld = -1; /* no more lock fd */ unlink (LOCAL->lname); /* delete it */ } /* reset UID validity */ stream->uid_validity = stream->uid_last = 0; if (stream->silent && !stream->rdonly && (LOCAL->ld < 0)) unix_abort (stream); /* abort if can't get RW silent stream */ /* parse mailbox */ else if (unix_parse (stream,tmp,LOCK_SH)) { unix_unlock (LOCAL->fd,stream,tmp); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ } if (!LOCAL) return NIL; /* failure if stream died */ /* make sure upper level knows readonly */ stream->rdonly = (LOCAL->ld < 0); /* notify about empty mailbox */ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL); if (!stream->rdonly) { /* flags stick if readwrite */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = T; /* have permanent keywords */ stream->perm_user_flags = 0xffffffff; /* and maybe can create them too */ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T; } return stream; /* return stream alive to caller */ } /* UNIX mail close * Accepts: MAIL stream * close options */ void unix_close (MAILSTREAM *stream,long options) { int silent = stream->silent; stream->silent = T; /* go silent */ /* expunge if requested */ if (options & CL_EXPUNGE) unix_expunge (stream); /* else dump final checkpoint */ else if (LOCAL->dirty) unix_check (stream); stream->silent = silent; /* restore old silence state */ unix_abort (stream); /* now punt the file and local data */ } /* UNIX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ /* lines to filter from header */ static STRINGLIST *unix_hlines = NIL; char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { MESSAGECACHE *elt; unsigned char *s; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get cache */ if (!unix_hlines) { /* once only code */ STRINGLIST *lines = unix_hlines = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Keywords")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-UID")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAP")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAPbase")); } /* go to header position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.header.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.header.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.header.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; } else { /* need to make a CRLF version */ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1), elt->private.msg.header.text.size); /* tie off string, and convert to CRLF */ s[elt->private.msg.header.text.size] = '\0'; *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s, elt->private.msg.header.text.size); fs_give ((void **) &s); /* free readin buffer */ } *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT); return LOCAL->buf; /* return processed copy */ } /* UNIX mail fetch message text * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL if failure */ long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get cache element */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { /* mark message seen and dirty */ elt->seen = elt->private.dirty = LOCAL->dirty = T; mm_flags (stream,msgno); } s = unix_text_work (stream,elt,&i,flags); INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* UNIX mail fetch message text worker routine * Accepts: MAIL stream * message cache element * pointer to returned header text length * option flags */ char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags) { FDDATA d; STRING bs; unsigned char *s,tmp[CHUNK]; /* go to text position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.text.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.text.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.text.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; return LOCAL->buf; } /* have it cached already? */ if (elt->private.uid != LOCAL->uid) { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->text.size) { /* excessively conservative, but the right thing is too hard to do */ fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); } d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = elt->private.special.offset + elt->private.msg.text.offset; d.chunk = tmp; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) { case '\r': /* carriage return seen */ *s++ = SNX (&bs); /* copy it and any succeeding LF */ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); break; case '\n': *s++ = '\r'; /* insert a CR */ default: *s++ = SNX (&bs); /* copy characters */ } *s = '\0'; /* tie off buffer */ /* calculate length of cached data */ LOCAL->textlen = s - LOCAL->text.data; } *length = LOCAL->textlen; /* return from cache */ return (char *) LOCAL->text.data; } /* UNIX per-message modify flag * Accepts: MAIL stream * message cache element */ void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { /* only after finishing */ if (elt->valid) elt->private.dirty = LOCAL->dirty = T; } /* UNIX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long unix_ping (MAILSTREAM *stream) { char lock[MAILTMPLEN]; struct stat sbuf; /* big no-op if not readwrite */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) { if (stream->rdonly) { /* does he want to give up readwrite? */ /* checkpoint if we changed something */ if (LOCAL->dirty) unix_check (stream); flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ close (LOCAL->ld); /* close the readwrite lock file */ LOCAL->ld = -1; /* no more readwrite lock fd */ unlink (LOCAL->lname); /* delete the readwrite lock file */ } else { /* get current mailbox size */ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); else stat (stream->mailbox,&sbuf); /* parse if mailbox changed */ if ((sbuf.st_size != LOCAL->filesize) && unix_parse (stream,lock,LOCK_SH)) { /* unlock mailbox */ unix_unlock (LOCAL->fd,stream,lock); mail_unlock (stream); /* and stream */ mm_nocritical (stream); /* done with critical */ } } } return LOCAL ? LONGT : NIL; /* return if still alive */ } /* UNIX mail check mailbox * Accepts: MAIL stream */ void unix_check (MAILSTREAM *stream) { char lock[MAILTMPLEN]; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,lock,LOCK_EX)) { /* any unsaved changes? */ if (LOCAL->dirty && unix_rewrite (stream,NIL,lock)) { if (!stream->silent) mm_log ("Checkpoint completed",NIL); } /* no checkpoint needed, just unlock */ else unix_unlock (LOCAL->fd,stream,lock); mail_unlock (stream); /* unlock the stream */ mm_nocritical (stream); /* done with critical */ } } /* UNIX mail expunge mailbox * Accepts: MAIL stream */ void unix_expunge (MAILSTREAM *stream) { unsigned long i; char lock[MAILTMPLEN]; char *msg = NIL; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,lock,LOCK_EX)) { /* count expunged messages if not dirty */ if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; if (!LOCAL->dirty) { /* not dirty and no expunged messages */ unix_unlock (LOCAL->fd,stream,lock); msg = "No messages deleted, so no update needed"; } else if (unix_rewrite (stream,&i,lock)) { if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); else msg = "Mailbox checkpointed, but no messages expunged"; } /* rewrite failed */ else unix_unlock (LOCAL->fd,stream,lock); mail_unlock (stream); /* unlock the stream */ mm_nocritical (stream); /* done with critical */ if (msg && !stream->silent) mm_log (msg,NIL); } else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN); } /* UNIX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; int fd; char *s,file[MAILTMPLEN],lock[MAILTMPLEN]; struct utimbuf times; unsigned long i,j; MESSAGECACHE *elt; long ret = T; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { if (pc) return (*pc) (stream,sequence,mailbox,options); unix_create (NIL,"INBOX");/* create empty INBOX */ break; } mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox); mm_log (LOCAL->buf,ERROR); return NIL; } LOCAL->buf[0] = '\0'; mm_critical (stream); /* go critical */ if ((fd = unix_lock (dummy_file (file,mailbox), O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE, lock,LOCK_EX)) < 0) { mm_nocritical (stream); /* done with critical */ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); /* log the error */ return NIL; /* failed */ } fstat (fd,&sbuf); /* get current file size */ /* write all requested messages to mailbox */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') { LOCAL->buf[j - 1] = '\r'; LOCAL->buf[j++] = '\n'; } if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* internal header succeeded */ s = unix_header (stream,i,&j,NIL); /* header size, sans trailing newline */ if (j && (s[j - 4] == '\r')) j -= 2; if (write (fd,s,j) < 0) ret = NIL; else { /* message header succeeded */ j = unix_xstatus (stream,LOCAL->buf,elt,NIL); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* message status succeeded */ s = unix_text_work (stream,elt,&j,NIL); if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0)) ret = NIL; } } } } if (!ret || fsync (fd)) { /* force out the update */ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); ftruncate (fd,sbuf.st_size); ret = NIL; } times.modtime = time (0); /* set mtime to now */ /* set atime to now-1 if successful copy */ if (ret) times.actime = times.modtime - 1; else times.actime = /* else preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : times.modtime; utime (file,×); /* set the times */ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */ mm_nocritical (stream); /* release critical */ /* log the error */ if (!ret) mm_log (LOCAL->buf,ERROR); /* delete if requested message */ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = elt->private.dirty = LOCAL->dirty = T; return ret; } /* UNIX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ #define BUFLEN 8*MAILTMPLEN long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN], lock[MAILTMPLEN]; struct utimbuf times; FILE *sf,*df; MESSAGECACHE elt; STRING *message; long ret = LONGT; if (!stream) { /* stream specified? */ stream = &unixproto; /* no, default stream to prototype */ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) fs_give ((void **) &stream->user_flags[i]); stream->kwd_create = T; /* allow new flags */ } /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { unix_create (NIL,"INBOX");/* create empty INBOX */ break; } mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } unlink (tmp); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR); else if (!unix_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); } /* get next message */ else if ((*af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ mm_critical (stream); /* go critical */ if (((fd = unix_lock (dummy_file (file,mailbox), O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE, lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { mm_nocritical (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ rewind (sf); for (; i && ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) && (fwrite (buf,1,j,df) == j)); i -= j); fclose (sf); /* done with scratch file */ times.modtime = time (0); /* set mtime to now */ /* make sure append wins, fsync() necessary */ if (i || (fflush (df) == EOF) || fsync (fd)) { sprintf (buf,"Message append failed: %s",strerror (errno)); mm_log (buf,ERROR); ftruncate (fd,sbuf.st_size);/* revert file */ times.actime = /* preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : times.modtime; ret = NIL; /* return error */ } /* set atime to now-1 if successful copy */ else times.actime = times.modtime - 1; utime (file,×); /* set the times */ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */ fclose (df); /* note that unix_unlock() released the fd */ mm_nocritical (stream); /* release critical */ return ret; } /* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */ int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg) { int ti,zn,c; unsigned long i,uf; char *x,tmp[MAILTMPLEN]; int hdrp = T; long f = mail_parse_flags (stream,flags,&uf); /* tie off date without newline */ if (x = strpbrk (strcpy (tmp,date),"\r\n")) *x = '\0'; /* build initial header */ if ((fprintf (sf,"From %s@%s %s\r\nStatus: ", myusername (),mylocalhost (),tmp) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\r\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\r\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if ((putc ('\r', sf) == EOF) || (putc ('\n',sf) == EOF)) return NIL; while (SIZE (msg)) { /* copy text to scratch file */ /* disregard CRs */ while ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') == '\r'); /* see if line needs special treatment */ if ((c == 'F') || (hdrp && ((c == 'S') || (c == 'X')))) { /* copy line to buffer */ for (i = 1,tmp[0] = c; (c != '\n') && (i < (MAILTMPLEN - 1)); ) switch (c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') { case '\r': /* ignore CR */ break; case '\n': /* write CRLF for NL */ tmp[i++] = '\r'; default: /* other characters */ tmp[i++] = c; } if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') && (tmp[4] == ' ')) { /* possible "From " line? */ /* yes, see if need to write a widget */ if (!(ti = unix_fromwidget || (c != '\n'))) VALID (tmp,x,ti,zn); if (ti && (putc ('>',sf) == EOF)) return NIL; } /* insert X- before metadata header */ else if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && (tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && (tmp[5] == 's') && (tmp[6] == ':')) || (((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && (tmp[5] == ':')) || ((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && (tmp[4] == 'A') && (tmp[5] == 'P') && ((tmp[6] == ':') || ((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && (tmp[8] == 's') && (tmp[9] == 'e') && (tmp[10] == ':')))) || ((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && (tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && (tmp[7] == 's') && (tmp[8] == ':')) || ((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && (tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && (tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && (tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF)) return NIL; /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; /* set up to copy remainder of line */ if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); else continue; /* end of line or end of message */ } /* check for end of header */ if (hdrp && (c == '\n')) hdrp = NIL; do switch (c) { /* copy line, writing CRLF for NL */ case '\r': /* ignore CR */ break; case '\n': /* insert CR before LF */ if (putc ('\r',sf) == EOF) return NIL; default: if (putc (c,sf) == EOF) return NIL; } while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* write trailing newline and return */ return ((putc ('\r', sf) == EOF) || (putc ('\n',sf) == EOF)) ? NIL : T; } /* Internal routines */ /* UNIX mail abort stream * Accepts: MAIL stream */ void unix_abort (MAILSTREAM *stream) { if (LOCAL) { /* only if a file is open */ if (LOCAL->fd >= 0) close (LOCAL->fd); if (LOCAL->ld >= 0) { /* have a mailbox lock? */ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ close (LOCAL->ld); /* close the lock file */ unlink (LOCAL->lname); /* and delete it */ } if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); /* free local text buffers */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* UNIX open and lock mailbox * Accepts: file name to open/lock * file open mode * destination buffer for lock file name * type of locking operation (LOCK_SH or LOCK_EX) */ int unix_lock (char *file,int flags,int mode,char *lock,int op) { int fd,ld,j; int i = LOCKTIMEOUT * 60 - 1; char tmp[MAILTMPLEN]; time_t t; struct stat sb; sprintf (lock,"%s.lock",file);/* build lock filename */ do { /* until OK or out of tries */ t = time (0); /* get the time now */ /* try to get the lock */ if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0) close (ld); /* got it, close the lock file! */ else if (errno != EEXIST) { /* miscellaneous error */ sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno)); if (!(i%15)) mm_log (tmp,WARN); } /* lock exists, still active? */ else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) && ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0)) close (ld); /* got timed-out lock file */ else { /* active lock, try again */ if (!(i%15)) { sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...", file,i); mm_log (tmp,WARN); } sleep (1); /* wait a second before next retry */ } } while (*lock && ld < 0 && i--); /* open file */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else { /* open failed */ j = errno; /* preserve error code */ if (*lock) unlink (lock); /* flush the lock file if any */ errno = j; /* restore error code */ } return fd; } /* UNIX unlock and close mailbox * Accepts: file descriptor * (optional) mailbox stream to check atime/mtime * (optional) lock file name */ void unix_unlock (int fd,MAILSTREAM *stream,char *lock) { if (stream) { /* need to muck with times? */ struct stat sbuf; struct utimbuf times; time_t now = time (0); fstat (fd,&sbuf); /* get file times */ if (LOCAL->ld >= 0) { /* yes, readwrite session? */ times.actime = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else if (stream->recent) { /* readonly with recent messages */ if ((sbuf.st_atime >= sbuf.st_mtime) || (sbuf.st_atime >= sbuf.st_ctime)) /* keep past mtime, whack back atime */ times.actime = (times.modtime = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; else now = 0; /* no time change needed */ } /* readonly with no recent messages */ else if ((sbuf.st_atime < sbuf.st_mtime) || (sbuf.st_atime < sbuf.st_ctime)) { times.actime = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else now = 0; /* no time change needed */ /* set the times, note change */ if (now && !utime (stream->mailbox,×)) LOCAL->filetime = times.modtime; } flock (fd,LOCK_UN); /* release flock'ers */ if (!stream) close (fd); /* close the file if no stream */ /* flush the lock file if any */ if (lock && *lock) unlink (lock); } /* UNIX mail parse and lock mailbox * Accepts: MAIL stream * space to write lock file name * type of locking operation * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure */ int unix_parse (MAILSTREAM *stream,char *lock,int op) { int zn; unsigned long i,j,k,m; unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; int ti = 0,retain = T; unsigned long nmsgs = stream->nmsgs; unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; unsigned long recent = stream->recent; unsigned long oldnmsgs = stream->nmsgs; short silent = stream->silent; short pseudoseen = NIL; struct stat sbuf; STRING bs; FDDATA d; MESSAGECACHE *elt; mail_lock (stream); /* guard against recursion or pingers */ /* toss out previous descriptor */ if (LOCAL->fd >= 0) close (LOCAL->fd); mm_critical (stream); /* open and lock mailbox (shared OK) */ if ((LOCAL->fd = unix_lock (stream->mailbox, O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY), NIL,lock,op)) < 0) { sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); mm_log (tmp,ERROR); unix_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } fstat (LOCAL->fd,&sbuf); /* get status */ /* validate change in size */ if (sbuf.st_size < LOCAL->filesize) { sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); mm_log (tmp,ERROR); /* this is pretty bad */ unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } /* new data? */ else if (i = sbuf.st_size - LOCAL->filesize) { d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = LOCAL->filesize; /* get to that position in the file */ d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ /* skip leading whitespace for broken MTAs */ while (((c = CHR (&bs)) == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) SNX (&bs); if (SIZE (&bs)) { /* read new data */ /* remember internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = unix_mbxline (stream,&bs,&i); t = NIL,zn = 0; if (i) VALID (s,t,ti,zn); /* see if valid From line */ if (!ti) { /* someone pulled the rug from under us */ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", (char *) s); mm_log (tmp,ERROR); unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } stream->silent = T; /* quell main program new message events */ do { /* found a message */ /* instantiate first new message */ mail_exists (stream,++nmsgs); (elt = mail_elt (stream,nmsgs))->valid = T; recent++; /* assume recent by default */ elt->recent = T; /* note position/size of internal header */ elt->private.special.offset = j; elt->private.msg.header.offset = elt->private.special.text.size = i; /* generate plausible IMAPish date string */ date[2] = date[6] = date[20] = '-'; date[11] = ' '; date[14] = date[17] = ':'; /* dd */ date[0] = t[ti - 2]; date[1] = t[ti - 1]; /* mmm */ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; /* hh */ date[12] = t[ti + 1]; date[13] = t[ti + 2]; /* mm */ date[15] = t[ti + 4]; date[16] = t[ti + 5]; if (t[ti += 6] == ':') {/* ss */ date[18] = t[++ti]; date[19] = t[++ti]; ti++; /* move to space */ } else date[18] = date[19] = '0'; /* yy -- advance over timezone if necessary */ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4); date[7] = t[ti + 1]; date[8] = t[ti + 2]; date[9] = t[ti + 3]; date[10] = t[ti + 4]; /* zzz */ t = zn ? (t + zn + 1) : (unsigned char *) "LCL"; date[21] = *t++; date[22] = *t++; date[23] = *t++; if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0'; else { /* numeric time zone */ date[24] = *t++; date[25] = *t++; date[26] = '\0'; date[20] = ' '; } /* set internal date */ if (!mail_parse_date (elt,date)) { sprintf (tmp,"Unable to parse internal date: %s",(char *) date); mm_log (tmp,WARN); } do { /* look for message body */ s = t = unix_mbxline (stream,&bs,&i); if (i) switch (*s) { /* check header lines */ case 'X': /* possible X-???: line */ if (s[1] == '-') { /* must be immediately followed by hyphen */ /* X-Status: becomes Status: in S case */ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' && s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2; /* possible X-Keywords */ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' && s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && s[8] == 'd' && s[9] == 's' && s[10] == ':') { SIZEDTEXT uf; retain = NIL; /* don't retain continuation */ s += 11; /* flush leading whitespace */ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){ while (*s == ' ') s++; /* find end of keyword */ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); /* got a keyword? */ if ((k = (u - s)) && (k < MAXUSERFLAG)) { uf.data = (unsigned char *) s; uf.size = k; for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j) if (!compare_csizedtext (stream->user_flags[j],&uf)) { elt->user_flags |= ((long) 1) << j; break; } } s = u; /* advance to next keyword */ } break; } /* possible X-IMAP */ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') && (s[5] == 'P') && ((m = (s[6] == ':')) || ((s[6] == 'b') && (s[7] == 'a') && (s[8] == 's') && (s[9] == 'e') && (s[10] == ':')))) { retain = NIL; /* don't retain continuation */ if ((nmsgs == 1) && !stream->uid_validity) { /* advance to data */ s += m ? 7 : 11; /* flush whitespace */ while (*s == ' ') s++; j = 0; /* slurp UID validity */ /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush whitespace */ while (*s == ' ') s++; /* must have valid UID validity and UID last */ if (j && isdigit (*s)) { /* pseudo-header seen if X-IMAP */ if (m) pseudoseen = LOCAL->pseudo = T; /* save UID validity */ stream->uid_validity = j; j = 0; /* slurp UID last */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* save UID last */ stream->uid_last = j; /* process keywords */ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n')); s = u,j++) { /* flush leading whitespace */ while (*s == ' ') s++; u = strpbrk (s," \n\r"); /* got a keyword? */ if ((k = (u - s)) && j < NUSERFLAGS) { if (stream->user_flags[j]) fs_give ((void **) &stream->user_flags[j]); stream->user_flags[j] = (char *) fs_get (k + 1); strncpy (stream->user_flags[j],s,k); stream->user_flags[j][k] = '\0'; } } } } break; } /* possible X-UID */ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' && s[5] == ':') { retain = NIL; /* don't retain continuation */ /* only believe if have a UID validity */ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) { s += 6; /* advance to UID value */ /* flush whitespace */ while (*s == ' ') s++; j = 0; /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush remainder of line */ while (*s != '\n') s++; /* make sure not duplicated */ if (elt->private.uid) sprintf (tmp,"Message %lu UID %lu already has UID %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,elt->private.uid); /* make sure UID doesn't go backwards */ else if (j <= prevuid) sprintf (tmp,"Message %lu UID %lu less than %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,prevuid + 1); /* or skip by mailbox's recorded last */ else if (j > stream->uid_last) sprintf (tmp,"Message %lu UID %lu greater than last %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,stream->uid_last); else { /* normal UID case */ prevuid = elt->private.uid = j; break; /* exit this cruft */ } mm_log (tmp,WARN); /* invalidate UID validity */ stream->uid_validity = 0; elt->private.uid = 0; } break; } } /* otherwise fall into S case */ case 'S': /* possible Status: line */ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' && s[5] == 's' && s[6] == ':') { retain = NIL; /* don't retain continuation */ s += 6; /* advance to status flags */ do switch (*s++) {/* parse flags */ case 'R': /* message read */ elt->seen = T; break; case 'O': /* message old */ if (elt->recent) { elt->recent = NIL; recent--; /* it really wasn't recent */ } break; case 'D': /* message deleted */ elt->deleted = T; break; case 'F': /* message flagged */ elt->flagged = T; break; case 'A': /* message answered */ elt->answered = T; break; case 'T': /* message is a draft */ elt->draft = T; break; default: /* some other crap */ break; } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))); break; /* all done */ } /* otherwise fall into default case */ default: /* ordinary header line */ if ((*s == 'S') || (*s == 's') || (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) { unsigned char *e,*v; /* must match what mail_filter() does */ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1); (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') && ((c > ' ') || ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))); *v++ = *u++); *v = '\0'; /* tie off */ /* matches internal header? */ if (!compare_cstring (tmp,"STATUS") || !compare_cstring (tmp,"X-STATUS") || !compare_cstring (tmp,"X-KEYWORDS") || !compare_cstring (tmp,"X-UID") || !compare_cstring (tmp,"X-IMAP") || !compare_cstring (tmp,"X-IMAPBASE")) { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus %s header in message %lu", (char *) tmp,elt->msgno); mm_log (err,WARN); retain = NIL; /* don't retain continuation */ break; /* different case or something */ } } /* retain or non-continuation? */ if (retain || ((*s != ' ') && (*s != '\t'))) { retain = T; /* retaining continuation now */ /* line length in CRLF format newline */ k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0); /* "internal" header size */ elt->private.data += k; /* message size */ elt->rfc822_size += k; } else { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus continuation in msg %lu: %.80s", elt->msgno,(char *) s); if (u = strpbrk (err,"\r\n")) *u = '\0'; mm_log (err,WARN); break; /* different case or something */ } break; } } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); /* "internal" header sans trailing newline */ if (i) elt->private.data -= 2; /* assign a UID if none found */ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { prevuid = elt->private.uid = ++stream->uid_last; elt->private.dirty = T; } else elt->private.dirty = elt->recent; /* note size of header, location of text */ elt->private.msg.header.text.size = (elt->private.msg.text.offset = (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - elt->private.special.text.size; k = m = 0; /* no previous line size yet */ /* note current position */ j = LOCAL->filesize + GETPOS (&bs); if (i) do { /* look for next message */ s = unix_mbxline (stream,&bs,&i); if (i) { /* got new data? */ VALID (s,t,ti,zn); /* yes, parse line */ if (!ti) { /* not a header line, add it to message */ elt->rfc822_size += k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0)); /* update current position */ j = LOCAL->filesize + GETPOS (&bs); } } } while (i && !ti); /* until found a header */ elt->private.msg.text.text.size = j - (elt->private.special.offset + elt->private.msg.text.offset); if (k == 2) { /* last line was blank? */ elt->private.msg.text.text.size -= (m ? 1 : 2); elt->rfc822_size -= 2; } } while (i); /* until end of buffer */ if (pseudoseen) { /* flush pseudo-message if present */ /* decrement recent count */ if (mail_elt (stream,1)->recent) recent--; /* and the exists count */ mail_exists (stream,nmsgs--); mail_expunged(stream,1);/* fake an expunge of that message */ } /* need to start a new UID validity? */ if (!stream->uid_validity) { stream->uid_validity = time (0); if (nmsgs) { /* don't bother if empty file */ LOCAL->dirty = T; /* make dirty to restart UID epoch */ /* need to rewrite msg 1 if not pseudo */ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; mm_log ("Assigning new unique identifiers to all messages",NIL); } } stream->nmsgs = oldnmsgs; /* whack it back down */ stream->silent = silent; /* restore old silent setting */ /* notify upper level of new mailbox sizes */ mail_exists (stream,nmsgs); mail_recent (stream,recent); /* mark dirty so O flags are set */ if (recent) LOCAL->dirty = T; } } /* no change, don't babble if never got time */ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime) mm_log ("New mailbox modification time but apparently no changes",WARN); /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; LOCAL->filetime = sbuf.st_mtime; return T; /* return the winnage */ } /* UNIX read line from mailbox * Accepts: mail stream * stringstruct * pointer to line size * Returns: pointer to input line */ char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) { unsigned long i,j,k,m; char *s,*t,*te,p1[CHUNK]; char *ret = ""; /* flush old buffer */ if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* if buffer needs refreshing */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) { /* find newline */ /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* difficult case if line spans buffer */ if ((i = s - bs->curpos) == bs->cursize) { memcpy (p1,bs->curpos,i); /* remember what we have so far */ /* load next buffer */ SETPOS (bs,k = GETPOS (bs) + i); /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; /* fast scan in overlap buffer */ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* huge line? */ if ((j = s - bs->curpos) == bs->cursize) { SETPOS (bs,GETPOS (bs) + j); /* look for end of line (s-l-o-w!!) */ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); SETPOS (bs,k); /* go back to where it started */ } /* got size of data, make buffer for return */ ret = LOCAL->line = (char *) fs_get (i + j + 2); memcpy (ret,p1,i); /* copy first chunk */ while (j) { /* copy remainder */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); i += k; /* account for this much read in */ j -= k; bs->curpos += k; /* increment new position */ bs->cursize -= k; /* eat that many bytes */ } if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */ ret[i++] = '\n'; /* make sure newline at end */ ret[i] = '\0'; /* makes debugging easier */ } else { /* this is easy */ ret = bs->curpos; /* string it at this position */ bs->curpos += ++i; /* increment new position */ bs->cursize -= i; /* eat that many bytes */ } *size = i; /* return that to user */ } else *size = 0; /* end of data, return empty */ return ret; } /* UNIX make pseudo-header * Accepts: MAIL stream * buffer to write pseudo-header * Returns: length of pseudo-header */ unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr) { int i; char *s,*t,tmp[MAILTMPLEN]; time_t now = time(0); rfc822_fixed_date (tmp); sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld", pseudo_from,ctime (&now), tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) now,mylocalhost (),stream->uid_validity, stream->uid_last); for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) sprintf (t += strlen (t)," %s",stream->user_flags[i]); strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n"); for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++) if (*s == '\n') *t++ = '\r'; *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n'; *t = '\0'; /* tie off pseudo header */ return t - hdr; /* return length of pseudo header */ } /* UNIX make status string * Accepts: MAIL stream * destination string to write * message cache entry * non-zero flag to write UID (.LT. 0 to write UID base info too) * Returns: length of string */ unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag) { char *t,stack[64]; char *s = status; unsigned long n; unsigned long pad = 50; /* This used to use sprintf(), but thanks to certain cretinous C libraries with horribly slow implementations of sprintf() I had to change it to this mess. At least it should be fast. */ /* need to write X-IMAPbase: header? */ if ((flag < 0) && !stream->uid_nosticky) { *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P'; *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' '; t = stack; n = stream->uid_validity; /* push UID validity digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID validity digits from stack */ while (t > stack) *s++ = *--t; *s++ = ' '; n = stream->uid_last; /* push UID last digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID last digits from stack */ while (t > stack) *s++ = *--t; for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n]) for (*s++ = ' '; *t; *s++ = *t++); *s++ = '\r'; *s++ = '\n'; pad += 30; /* increased padding if have IMAPbase */ } *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->seen) *s++ = 'R'; if (flag) *s++ = 'O'; /* only write O if have a UID */ *s++ = '\r'; *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->deleted) *s++ = 'D'; if (elt->flagged) *s++ = 'F'; if (elt->answered) *s++ = 'A'; if (elt->draft) *s++ = 'T'; *s++ = '\r'; *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w'; *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':'; if (n = elt->user_flags) do { *s++ = ' '; for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); } while (n); n = s - status; /* get size of stuff so far */ /* pad X-Keywords to make size constant */ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; *s++ = '\r'; *s++ = '\n'; if (flag) { /* want to include UID? */ t = stack; n = elt->private.uid; /* push UID digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; *s++ = ' '; /* pop UID from stack */ while (t > stack) *s++ = *--t; *s++ = '\r'; *s++ = '\n'; } /* end of extended message status */ *s++ = '\r'; *s++ = '\n'; *s = '\0'; return s - status; /* return size of resulting string */ } /* Rewrite mailbox file * Accepts: MAIL stream, must be critical and locked * return pointer to number of expunged messages if want expunge * lock file name * Returns: T if success and mailbox unlocked, NIL if failure */ #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock) { MESSAGECACHE *elt; UNIXFILE f; char *s; struct utimbuf times; long ret,flag; unsigned long i,j; unsigned long recent = stream->recent; unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0; if (nexp) *nexp = 0; /* initially nothing expunged */ /* calculate size of mailbox after rewrite */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) if (!(elt = mail_elt (stream,i))->deleted || !nexp) { /* add RFC822 size of this message */ size += elt->private.special.text.size + elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag) + elt->private.msg.text.text.size + 2; flag = 1; /* only count X-IMAPbase once */ } if (!size) { /* no messages and no pseudo, make one now */ size = unix_pseudo (stream,LOCAL->buf); LOCAL->pseudo = T; } /* extend the file as necessary */ if (ret = unix_extend (stream,size)) { /* Set up buffered I/O file structure * curpos current position being written through buffering * filepos current position being written physically to the disk * bufpos current position being written in the buffer * protect current maximum position that can be written to the disk * before buffering is forced * The code tries to buffer so that that disk is written in multiples of * OVERBLOWBUFLEN bytes. */ f.stream = stream; /* note mail stream */ f.curpos = f.filepos = 0; /* start of file */ f.protect = stream->nmsgs ? /* initial protection pointer */ mail_elt (stream,1)->private.special.offset : 8192; f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); if (LOCAL->pseudo) /* update pseudo-header */ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf)); /* loop through all messages */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { elt = mail_elt (stream,i);/* get cache */ if (nexp && elt->deleted){/* expunge this message? */ /* one less recent message */ if (elt->recent) --recent; mail_expunged(stream,i);/* notify upper levels */ ++*nexp; /* count up one more expunged message */ } else { /* preserve this message */ i++; /* advance to next message */ if ((flag < 0) || /* need to rewrite message? */ elt->private.dirty || (((unsigned long) f.curpos) != elt->private.special.offset) || (elt->private.msg.header.text.size != (elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag)))) { unsigned long newoffset = f.curpos; /* yes, seek to internal header */ lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); /* protection pointer moves to RFC822 header */ f.protect = elt->private.special.offset + elt->private.msg.header.offset; /* write internal header */ unix_write (&f,LOCAL->buf,elt->private.special.text.size); /* get RFC822 header */ s = unix_header (stream,elt->msgno,&j,NIL); /* in case this got decremented */ elt->private.msg.header.offset = elt->private.special.text.size; /* header size, sans trailing newline */ if ((j < 4) || (s[j - 4] == '\r')) j -= 2; if (j != elt->private.data) fatal ("header size inconsistent"); /* protection pointer moves to RFC822 text */ f.protect = elt->private.special.offset + elt->private.msg.text.offset; unix_write (&f,s,j); /* write RFC822 header */ /* write status and UID */ unix_write (&f,LOCAL->buf, j = unix_xstatus (stream,LOCAL->buf,elt,flag)); flag = 1; /* only write X-IMAPbase once */ /* new file header size */ elt->private.msg.header.text.size = elt->private.data + j; /* did text move? */ if (f.curpos != f.protect) { /* get message text */ s = unix_text_work (stream,elt,&j,FT_INTERNAL); /* can't happen it says here */ if (j > elt->private.msg.text.text.size) fatal ("text size inconsistent"); /* new text offset, status/UID may change it */ elt->private.msg.text.offset = f.curpos - newoffset; /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2); unix_write (&f,s,j);/* write text */ /* write trailing newline */ unix_write (&f,"\r\n",2); } else { /* tie off header and status */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\r\n",2); } } /* new internal header offset */ elt->private.special.offset = newoffset; elt->private.dirty =NIL;/* message is now clean */ } else { /* no need to rewrite this message */ /* tie off previous message if needed */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.special.text.size + elt->private.msg.header.text.size + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\r\n",2); } } } } unix_write (&f,NIL,NIL); /* tie off final message */ if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent"); fs_give ((void **) &f.buf); /* free buffer */ /* make sure tied off */ ftruncate (LOCAL->fd,LOCAL->filesize = size); fsync (LOCAL->fd); /* make sure the updates take */ if (size && (flag < 0)) fatal ("lost UID base information"); LOCAL->dirty = NIL; /* no longer dirty */ /* notify upper level of new mailbox sizes */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); /* set atime to now, mtime a second earlier */ times.modtime = (times.actime = time (0)) -1; /* set the times, note change */ if (!utime (stream->mailbox,×)) LOCAL->filetime = times.modtime; /* flush the lock file */ unix_unlock (LOCAL->fd,stream,lock); } return ret; /* return state from algorithm */ } /* Extend UNIX mailbox file * Accepts: MAIL stream * new desired size * Return: T if success, else NIL */ long unix_extend (MAILSTREAM *stream,unsigned long size) { unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ? size - ((unsigned long) LOCAL->filesize) : 0; if (i) { /* does the mailbox need to grow? */ if (i > LOCAL->buflen) { /* make sure have enough space */ /* this user won the lottery all right */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1); } memset (LOCAL->buf,'\0',i); /* get a block of nulls */ while (T) { /* until write successful or punt */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break; else { long e = errno; /* note error before doing ftruncate */ ftruncate (LOCAL->fd,LOCAL->filesize); if (mm_diskerror (stream,e,NIL)) { fsync (LOCAL->fd); /* user chose to punt */ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e)); if (!stream->silent) mm_log (LOCAL->buf,ERROR); return NIL; } } } } return LONGT; } /* Write data to buffered file * Accepts: buffered file pointer * file data or NIL to indicate "flush buffer" * date size (ignored for "flush buffer") * Does not return until success */ void unix_write (UNIXFILE *f,char *buf,unsigned long size) { unsigned long i,j,k; if (buf) { /* doing buffered write? */ i = f->bufpos - f->buf; /* yes, get size of current buffer data */ /* yes, have space in current buffer chunk? */ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) { /* yes, fill up buffer as much as we can */ memcpy (f->bufpos,buf,k = min (j,size)); f->bufpos += k; /* new buffer position */ f->curpos += k; /* new current position */ if (j -= k) return; /* all done if still have buffer free space */ buf += k; /* full, get new unwritten data pointer */ size -= k; /* new data size */ i += k; /* new buffer data size */ } /* This chunk of the buffer is full. See if can make some space by * writing to the disk, if there's enough unprotected space to do so. * Try to fill out any unaligned chunk, along with any subsequent full * chunks that will fit in unprotected space. */ /* any unprotected space we can write to? */ if (j = min (i,f->protect - f->filepos)) { /* yes, filepos not at chunk boundary? */ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j)) j -= k; /* yes, and can write out partial chunk */ else k = 0; /* no partial chunk to write */ /* if at least a chunk free, write that too */ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN); if (k) { /* write data if there is anything we can */ unix_phys_write (f,f->buf,k); /* slide buffer */ if (i -= k) memmove (f->buf,f->buf + k,i); f->bufpos = f->buf + i; /* new end of buffer */ } } /* Have flushed the buffer as best as possible. All done if no more * data to write. Otherwise, if the buffer is empty AND if the unwritten * data is larger than a chunk AND the unprotected space is also larger * than a chunk, then write as many chunks as we can directly from the * data. Buffer the rest, expanding the buffer as needed. */ if (size) { /* have more data that we need to buffer? */ /* can write any of it to disk instead? */ if ((f->bufpos == f->buf) && ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) { /* write as much as we can right now */ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN)); buf += j; /* new data pointer */ size -= j; /* new data size */ f->curpos += j; /* advance current pointer */ } if (size) { /* still have data that we need to buffer? */ /* yes, need to expand the buffer? */ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) { /* note current position in buffer */ j = f->bufpos - f->buf; i += OVERFLOWBUFLEN; /* yes, grow another chunk */ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN)); /* in case buffer relocated */ f->bufpos = f->buf + j; } /* buffer remaining data */ memcpy (f->bufpos,buf,size); f->bufpos += size; /* new end of buffer */ f->curpos += size; /* advance current pointer */ } } } else { /* flush buffer to disk */ unix_phys_write (f,f->buf,i = f->bufpos - f->buf); f->bufpos = f->buf; /* reset buffer */ /* update positions */ f->curpos = f->protect = f->filepos; } } /* Physical disk write * Accepts: buffered file pointer * buffer address * buffer size * Does not return until success */ void unix_phys_write (UNIXFILE *f,char *buf,size_t size) { MAILSTREAM *stream = f->stream; /* write data at desired position */ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) || (write (LOCAL->fd,buf,size) < 0))) { int e; char tmp[MAILTMPLEN]; sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno)); mm_log (tmp,ERROR); mm_diskerror (NIL,e,T); /* serious problem, must retry */ } f->filepos += size; /* update file position */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/unixnt.h000066400000000000000000000146011137544547100230360ustar00rootroot00000000000000/* * Program: UNIX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ /* Validate line * Accepts: pointer to candidate string to validate as a From header * return pointer to end of date/time field * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') * return pointer to offset from t of time zone (if non-zero) * Returns: t,ti,zn set if valid From string, else ti is NIL */ #define VALID(s,x,ti,zn) { \ ti = 0; \ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ (s[4] == ' ')) { \ for (x = s + 5; *x && *x != '\012'; x++); \ if (*x) { \ if (x[-1] == '\015') --x; \ if (x - s >= 41) { \ for (zn = -1; x[zn] != ' '; zn--); \ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ x += zn - 12; \ } \ if (x - s >= 27) { \ if (x[-5] == ' ') { \ if (x[-8] == ':') zn = 0,ti = -5; \ else if (x[-9] == ' ') ti = zn = -9; \ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ ti = zn = -11; \ } \ else if (x[-4] == ' ') { \ if (x[-9] == ' ') zn = -4,ti = -9; \ } \ else if (x[-6] == ' ') { \ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ zn = -6,ti = -11; \ } \ if (ti && !((x[ti - 3] == ':') && \ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ (x[ti - 11] == ' '))) ti = 0; \ } \ } \ } \ } /* You are not expected to understand this macro, but read the next page if * you are not faint of heart. * * Known formats to the VALID macro are: * From user Wed Dec 2 05:53 1992 * BSD From user Wed Dec 2 05:53:22 1992 * SysV From user Wed Dec 2 05:53 PST 1992 * rn From user Wed Dec 2 05:53:22 PST 1992 * From user Wed Dec 2 05:53 -0700 1992 * emacs From user Wed Dec 2 05:53:22 -0700 1992 * From user Wed Dec 2 05:53 1992 PST * From user Wed Dec 2 05:53:22 1992 PST * From user Wed Dec 2 05:53 1992 -0700 * Solaris From user Wed Dec 2 05:53:22 1992 -0700 * * Plus all of the above with `` remote from xxx'' after it. Thank you very * much, smail and Solaris, for making my life considerably more complicated. */ /* * What? You want to understand the VALID macro anyway? Alright, since you * insist. Actually, it isn't really all that difficult, provided that you * take it step by step. * * Line 1 Initializes the return ti value to failure (0); * Lines 2-3 Validates that the 1st-5th characters are ``From ''. * Lines 4-6 Validates that there is an end of line and points x at it. * Lines 7-14 First checks to see if the line is at least 41 characters long. * If so, it scans backwards to find the rightmost space. From * that point, it scans backwards to see if the string matches * `` remote from''. If so, it sets x to point to the space at * the start of the string. * Line 15 Makes sure that there are at least 27 characters in the line. * Lines 16-21 Checks if the date/time ends with the year (there is a space * five characters back). If there is a colon three characters * further back, there is no timezone field, so zn is set to 0 * and ti is set in front of the year. Otherwise, there must * either to be a space four characters back for a three-letter * timezone, or a space six characters back followed by a + or - * for a numeric timezone; in either case, zn and ti become the * offset of the space immediately before it. * Lines 22-24 Are the failure case for line 14. If there is a space four * characters back, it is a three-letter timezone; there must be a * space for the year nine characters back. zn is the zone * offset; ti is the offset of the space. * Lines 25-28 Are the failure case for line 20. If there is a space six * characters back, it is a numeric timezone; there must be a * space eleven characters back and a + or - five characters back. * zn is the zone offset; ti is the offset of the space. * Line 29-32 If ti is valid, make sure that the string before ti is of the * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise * invalidate ti. There must be a colon three characters back * and a space six or nine characters back (depending upon * whether or not the character six characters back is a colon). * There must be a space three characters further back (in front * of the day), one seven characters back (in front of the month), * and one eleven characters back (in front of the day of week). * ti is set to be the offset of the space before the time. * * Why a macro? It gets invoked a *lot* in a tight loop. On some of the * newer pipelined machines it is faster being open-coded than it would be if * subroutines are called. * * Why does it scan backwards from the end of the line, instead of doing the * much easier forward scan? There is no deterministic way to parse the * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to * see if unquoted spaces were possible. They are, and I've encountered enough * evil mail to be totally unwilling to trust that ``it will never happen''. */ /* Build parameters */ #define KODRETRY 15 /* kiss-of-death retry in seconds */ #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define CHUNK 16384 /* read-in chunk size */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/os2/write.c000066400000000000000000000030231137544547100226320ustar00rootroot00000000000000/* * Program: Write data, treating partial writes as an error * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* The whole purpose of this unfortunate routine is to deal with DOS and * certain cretinous versions of UNIX which decided that the "bytes actually * written" return value from write() gave them license to use that for things * that are really errors, such as disk quota exceeded, maximum file size * exceeded, disk full, etc. * * BSD won't screw us this way on the local filesystem, but who knows what * some NFS-mounted filesystem will do. */ #undef write /* Write data to file * Accepts: file descriptor * I/O vector structure * number of vectors in structure * Returns: number of bytes written if successful, -1 if failure */ long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1); long safe_write (int fd,char *buf,long nbytes) { long i,j; if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) { while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) && (errno == EINTR)); if (j < 0) return j; } return nbytes; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/000077500000000000000000000000001137544547100220375ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/build.ctl000066400000000000000000000024641137544547100236500ustar00rootroot00000000000000! Program: Portable C client build for TOPS-20 ! ! Author: Mark Crispin ! Networks and Distributed Computing ! Computing & Communications ! University of Washington ! Administration Building, AG-44 ! Seattle, WA 98195 ! Internet: MRC@CAC.Washington.EDU ! ! Date: 11 May 1989 ! Last Edited: 15 October 2003 ! ! The IMAP toolkit provided in this Distribution is ! Copyright 2004 University of Washington. ! ! The full text of our legal notices is contained in the file called ! CPYRIGHT, included with this Distribution. ! ! Build c-client library ! @COPY OS_T20.* OSDEP.* @CC -c -m -w=note MAIL.C DUMMYT20.C IMAP4R1.C SMTP.C NNTP.C POP3.C RFC822.C MISC.C OSDEP.C FLSTRING.C SMANAGER.C NEWSRC.C NETMSG.C UTF8.C ! ! Build c-client library file. Should use MAKLIB, but MAKLIB barfs on MAIL.REL ! @DELETE CCLINT.REL @APPEND MAIL.REL,DUMMYT20.REL,IMAP4R1.REL,SMTP.REL,NNTP.REL,POP3.REL,RFC822.REL,MISC.REL,OSDEP.REL,FLSTRING.REL,SMANAGER.REL,NEWSRC.REL,NETMSG.REL,UTF8.REL CCLINT.REL ! ! Build MTEST library test program ! @CC -c -m -w=note MTEST.C @LINK */SET:.HIGH.:200000 *C:LIBCKX.REL *MTEST.REL *CCLINT.REL *MTEST/SAVE */GO ! ! Build MAILUTIL ! @CC -c -m -w=note MAILUTIL.C @RENAME MAILUTIL.REL MUTIL.REL @LINK */SET:.HIGH.:200000 *C:LIBCKX.REL *MUTIL.REL *CCLINT.REL *MUTIL/SAVE */GO @RESET @RENAME MUTIL.EXE MAILUTIL.EXE tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/dummy.h000066400000000000000000000021051137544547100233410ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/dummyt20.c000066400000000000000000000161001137544547100236620ustar00rootroot00000000000000/* * Program: Dummy routines for TOPS-20 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include "mail.h" #include "osdep.h" #include "dummy.h" #include "misc.h" /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { /* must be valid local mailbox */ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ? &dummydriver : NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { return NIL; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { /* return silently */ } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * driver type to use * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { return NIL; /* always fails */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; /* OP_PROTOTYPE call or silence */ if (!stream || stream->silent) return NIL; if (compare_cstring (stream->mailbox,"INBOX")) { sprintf (tmp,"Not a mailbox: %s",stream->mailbox); mm_log (tmp,ERROR); return NIL; /* always fails */ } if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ long dummy_ping (MAILSTREAM *stream) { return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { char tmp[MAILTMPLEN]; sprintf (tmp,"Can't append to %s",mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { if (*pat == '{' || (ref && (*ref == '{'))) return NIL; /* write name with reference */ if (ref && *ref && !strpbrk (pat,":<")) sprintf (tmp,"%s%s",ref,pat); else strcpy (tmp,pat); /* ignore reference, only need mailbox name */ return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/env_t20.c000066400000000000000000000131171137544547100234630ustar00rootroot00000000000000/* * Program: Environment routines -- TOPS-20 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 16 January 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to the TOPS-20 operating system, which * set standards for user and programmer friendliness that have still not been * equaled by more `modern' operating systems. * Wasureru mon ka!!!! */ /* c-client environment parameters */ static char *myUserName = NIL; /* user name */ static char *myHomeDir = NIL; /* home directory name */ static char *myLocalHost = NIL; /* local host name */ static char *myNewsrc = NIL; /* newsrc file name */ static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_USERNAME: if (myUserName) fs_give ((void **) &myUserName); myUserName = cpystr ((char *) value); case GET_USERNAME: ret = (void *) myUserName; break; case SET_HOMEDIR: if (myHomeDir) fs_give ((void **) &myHomeDir); myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: if (myLocalHost) fs_give ((void **) &myLocalHost); myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: ret = (void *) myNewsrc; break; case SET_DISABLE822TZTEXT: no822tztext = value ? T : NIL; case GET_DISABLE822TZTEXT: ret = (void *) (no822tztext ? VOIDT : NIL); break; } return ret; } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { char *s; int argblk[4]; argblk[1] = (int) (date-1); argblk[2] = -1; /* time now */ argblk[3] = OT_822; /* want RFC [2]822 format */ jsys (ODTIM,argblk); /* suppress time zone text if desired */ if (no822tztext && (s = strstr (date," ("))) *s = NIL; } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { int argblk[5]; argblk[1] = (int) (date-1); argblk[2] = -1; /* time now */ argblk[3] = OT_4YR; /* output in 4-digit year format */ jsys (ODTIM,argblk); argblk[2] = ' '; /* delimit with space */ jsys (BOUT,argblk); argblk[2] = -1; /* time now */ argblk[4] = 0; /* no flags */ jsys (ODCNV,argblk); /* get time zone */ argblk[2] = ((argblk[4] & 077000000) >> 18) * -100; /* add an hour if summer time */ if (argblk[4] & IC_ADS) argblk[2] += 100; argblk[3] = 0340005000012; jsys (NOUT,argblk); } /* Return my user name * Accepts: pointer to optional flags * Returns: my user name */ char *myusername_full (unsigned long *flags) { if (!myUserName) { /* get user name if don't have it yet */ char tmp[MAILTMPLEN]; int argblk[5],i; jsys (GJINF,argblk); /* get job poop */ if (!(i = argblk[1])) { /* remember user number */ if (flags) *flags = MU_NOTLOGGEDIN; return "SYSTEM"; /* not logged in */ } argblk[1] = (int) (tmp-1); /* destination */ argblk[2] = i; /* user number */ jsys (DIRST,argblk); /* get user name string */ myUserName = cpystr (tmp); /* copy user name */ argblk[1] = 0; /* no flags */ argblk[2] = i; /* user number */ argblk[3] = 0; /* no stepping */ jsys (RCDIR,argblk); /* get home directory */ argblk[1] = (int) (tmp-1); /* destination */ argblk[2] = argblk[3]; /* home directory number */ jsys (DIRST,argblk); /* get home directory string */ myHomeDir = cpystr (tmp); /* copy home directory */ if (!myNewsrc) { /* set news file name if not defined */ sprintf (tmp,"%sNEWSRC",myhomedir ()); myNewsrc = cpystr (tmp); } if (flags) *flags = MU_LOGGEDIN; } return myUserName; } /* Return my local host name * Returns: my local host name */ char *mylocalhost () { if (!myLocalHost) { /* initialize if first time */ char tmp[MAILTMPLEN]; int argblk[5]; argblk[1] = _GTHNS; /* convert number to string */ argblk[2] = (int) (tmp-1); argblk[3] = -1; /* want local host */ if (!jsys (GTHST,argblk)) strcpy (tmp,"LOCAL"); myLocalHost = cpystr (tmp); } return myLocalHost; } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { if (!myHomeDir) myusername ();/* initialize if first time */ return myHomeDir ? myHomeDir : ""; } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { return NIL; /* no default prototype */ } /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ void syslog (int priority,const char *message,...) { } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ void openlog (const char *ident,int logopt,int facility) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/env_t20.h000066400000000000000000000041411137544547100234650ustar00rootroot00000000000000/* * Program: TOPS-20 environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 25 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to the TOPS-20 operating system, which * set standards for user and programmer friendliness that have still not been * equaled by more `modern' operating systems. * Wasureru mon ka!!!! */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\SUBSCRIPTIONS.TXT",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\SUBSCRIPTIONS.TMP",myhomedir ()) /* Function prototypes */ #include "env.h" char *myusername_full (unsigned long *flags); #define MU_LOGGEDIN 0 #define MU_NOTLOGGEDIN 1 #define MU_ANONYMOUS 2 #define myusername() \ myusername_full (NIL) /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ void openlog (const char *ident,int logopt,int facility); void syslog (int priority,const char *message,...); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/fs_t20.c000066400000000000000000000022341137544547100233010ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { free (*block); *block = NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/ftl_t20.c000066400000000000000000000013271137544547100234600ustar00rootroot00000000000000/* * Program: DOS/VMS/TOPS-20 crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/linkage.c000066400000000000000000000017001137544547100236130ustar00rootroot00000000000000/* * Program: Default driver linkage * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 7 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ mail_link (&imapdriver); /* link in the imap driver */ mail_link (&nntpdriver); /* link in the nntp driver */ mail_link (&pop3driver); /* link in the pop3 driver */ mail_link (&dummydriver); /* link in the dummy driver */ auth_link (&auth_md5); /* link in the md5 authenticator */ auth_link (&auth_pla); /* link in the plain authenticator */ auth_link (&auth_log); /* link in the log authenticator */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/linkage.h000066400000000000000000000013401137544547100236200ustar00rootroot00000000000000/* * Program: Default driver linkage * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 7 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ extern DRIVER imapdriver; extern DRIVER nntpdriver; extern DRIVER pop3driver; extern DRIVER dummydriver; extern AUTHENTICATOR auth_log; extern AUTHENTICATOR auth_md5; extern AUTHENTICATOR auth_pla; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/log_t20.c000066400000000000000000000037531137544547100234610ustar00rootroot00000000000000/* * Program: TOPS-20 server login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to the TOPS-20 operating system, which * set standards for user and programmer friendliness that have still not been * equaled by more `modern' operating systems. * Wasureru mon ka!!!! */ /* Server log in * Accepts: user name string * password string * authenticating user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ long server_login (char *user,char *pass,char *authuser,int argc,char *argv[]) { int uid; int argblk[5]; if (authuser && *authuser) { /* not available */ syslog (LOG_NOTICE|LOG_AUTH, "Login %s failed: invalid authentication ID %s host=%.80s", user,authuser,tcp_clienthost ()); sleep (3); return NIL; } argblk[1] = RC_EMO; /* require exact match */ argblk[2] = (int) (user-1); /* user name */ argblk[3] = 0; /* no stepping */ if (!jsys (RCUSR,argblk)) return NIL; uid = argblk[1] = argblk[3]; /* user number */ argblk[2] = (int) (pass-1); /* password */ argblk[3] = 0; /* no special account */ if (!jsys (LOGIN,argblk)) return NIL; return T; } /* Authenticated server log in * Accepts: user name string * authenticating user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ long authserver_login (char *user,char *authuser,int argc,char *argv[]) { return NIL; /* how to implement this on TOPS-20??? */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/nl_t20.c000066400000000000000000000027511137544547100233060ustar00rootroot00000000000000/* * Program: Windows/TOPS-20 newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { /* flush destination buffer if too small */ if (*dst && (srcl > *dstl)) fs_give ((void **) dst); if (!*dst) { /* make a new buffer if needed */ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1); if (dstl) *dstl = srcl; /* return new buffer length to main program */ } /* copy strings */ if (srcl) memcpy (*dst,src,(size_t) srcl); *(*dst + srcl) = '\0'; /* tie off destination */ return srcl; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { return SIZE (s); /* no-brainer on DOS! */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/os_t20.c000066400000000000000000000042651137544547100233200ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- TOPS-20 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 1 July 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to the TOPS-20 operating system, which * set standards for user and programmer friendliness that have still not been * equaled by more `modern' operating systems. * Wasureru mon ka!!!! */ #include "mail.h" #include /* must be before tcp_t20.h */ #include "tcp_t20.h" /* must be before osdep include tcp.h */ #include #include "osdep.h" #include #include "misc.h" #include #include #include #include #include "fs_t20.c" #include "ftl_t20.c" #include "nl_t20.c" #include "env_t20.c" #include "tcp_t20.c" #include "log_t20.c" #define MD5ENABLE "PS:CRAM-MD5.PWD" #include "auth_md5.c" #include "auth_pla.c" #include "auth_log.c" /* Emulator for UNIX gethostid() call * Returns: host id */ long gethostid () { int argblk[5]; #ifndef _APRID #define _APRID 28 #endif argblk[1] = _APRID; jsys (GETAB,argblk); return (long) argblk[1]; } /* Emulator for UNIX getpass() call * Accepts: prompt * Returns: password */ #define PWDLEN 128 /* used by Linux */ char *getpass (const char *prompt) { char *s; static char pwd[PWDLEN]; int argblk[5],mode; argblk[1] = (int) (prompt-1); /* prompt user */ jsys (PSOUT,argblk); argblk[1] = _PRIIN; /* get current TTY mode */ jsys (RFMOD,argblk); mode = argblk[2]; /* save for later */ argblk[2] &= ~06000; jsys (SFMOD,argblk); jsys (STPAR,argblk); fgets (pwd,PWDLEN-1,stdin); pwd[PWDLEN-1] = '\0'; if (s = strchr (pwd,'\n')) *s = '\0'; putchar ('\n'); argblk[2] = mode; jsys (SFMOD,argblk); jsys (STPAR,argblk); return pwd; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/os_t20.h000066400000000000000000000020631137544547100233170ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- TOPS-20 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 1 May 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to the TOPS-20 operating system, which * set standards for user and programmer friendliness that have still not been * equaled by more `modern' operating systems. * Wasureru mon ka!!!! */ #include #include #include #include #include #include "env_t20.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" long gethostid (void); char *getpass (const char *prompt); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/pmatch.c000066400000000000000000000053271137544547100234660ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-independent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/shortsym.h000066400000000000000000000373221137544547100241070ustar00rootroot00000000000000/* * Program: Definitions for compilers with 6-letter symbol limits * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 May 1995 * Last Edited: 6 December 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define auth_link a_link #define auth_log a_log #define auth_login_client al_cli #define auth_login_server al_ser #define auth_md5 a_md5 #define auth_md5_valid a5_val #define auth_md5_client a5_cli #define auth_md5_server a5_ser #define auth_pla a_pla #define auth_plain_valid ap_val #define auth_plain_client ap_cli #define auth_plain_server ap_ser #define authenticate a_auth #define authserver_login a_serv #define body_encodings bencds #define body_types btypes #define compare_csizedtext cm_szt #define compare_cstring cm_str #define compare_ulong cm_uln #define default_proto d_prot #define dummy_append d_appn #define dummy_canonicalize d_cano #define dummy_check d_chck #define dummy_close d_clos #define dummy_copy d_copy #define dummy_create d_crea #define dummy_create_path d_crep #define dummy_delete d_del #define dummy_expunge d_exp #define dummy_file d_fil #define dummy_list d_list #define dummy_list_work d_lstw #define dummy_listed d_lstd #define dummy_lsub d_lsub #define dummy_open d_open #define dummy_parameters d_parm #define dummy_ping d_ping #define dummy_rename d_ren #define dummy_scan d_scan #define dummy_search d_srch #define dummy_subscribe d_subs #define dummy_valid d_val #define env_parameters e_parm #define fatal fatal #define file_string fl_str #define file_string_init fl_ini #define file_string_next fl_nxt #define file_string_setpos fl_sps #define fs_get f_get #define fs_give f_give #define fs_resize f_rsiz #define hash_create h_crea #define hash_destory h_dest #define hash_index h_indx #define hash_lookup h_lkup #define hash_add h_add #define hash_lookup_and_add h_lad #define imap_OK i_OK #define imap_acl_work i_aclw #define imap_append i_appn #define imap_append_single i_apps #define imap_anon i_anon #define imap_auth i_auth #define imap_cache i_cach #define imap_cap i_cap #define imap_capability i_capa #define imap_challenge i_chln #define imap_check i_chck #define imap_close i_clos #define imap_copy i_copy #define imap_create i_crea #define imap_delete i_del #define imap_deleteacl i_dacl #define imap_expunge i_expn #define imap_fake i_fake #define imap_fast i_fast #define imap_fetch i_fetc #define imap_flag i_flag #define imap_flags i_flgs #define imap_gc i_gc #define imap_gc_body ig_bdy #define imap_getacl i_gacl #define imap_getquota i_gqot #define imap_getquotaroot i_gqtr #define imap_host i_host #define imap_list i_list #define imap_listrights i_lrgh #define imap_list_work il_wrk #define imap_login i_logn #define imap_lsub i_lsub #define imap_manage i_man #define imap_msgdata i_msgd #define imap_msgno i_msgn #define imap_myrights i_mrgh #define imap_open i_open #define imap_parameters i_parm #define imap_parse_address ip_adr #define imap_parse_adrlist ip_adl #define imap_parse_astring ip_ast #define imap_parse_body ip_bdy #define imap_parse_body_parameter ipb_pa #define imap_parse_body_structure ipb_st #define imap_parse_capabilities ip_cap #define imap_parse_disposition ip_dsp #define imap_parse_envelope ip_env #define imap_parse_extension ip_ext #define imap_parse_flags ip_flg #define imap_parse_header ip_hdr #define imap_parse_language ip_lng #define imap_parse_namespace ip_nam #define imap_parse_reply ip_rep #define imap_parse_response ip_rsp #define imap_parse_string ip_str #define imap_parse_stringlist ip_stl #define imap_parse_thread ip_thr #define imap_parse_unsolicited ip_uns #define imap_parse_user_flag ipu_fl #define imap_ping i_ping #define imap_reform_sequence i_rfrs #define imap_rename i_ren #define imap_reply i_rep #define imap_response i_rspn #define imap_scan i_scan #define imap_search i_srch #define imap_send i_send #define imap_send_astring is_ast #define imap_send_literal is_lit #define imap_send_sdate iss_da #define imap_send_slist iss_sl #define imap_send_spgm iss_pg #define imap_send_spgm_trim iss_pt #define imap_send_sset iss_st #define imap_send_sset_work iss_sw #define imap_setacl i_sacl #define imap_setquota i_sqot #define imap_sort i_sort #define imap_sout i_sout #define imap_soutr i_sotr #define imap_status i_stat #define imap_structure i_stru #define imap_subscribe i_sub #define imap_thread i_thrd #define imap_thread_work i_thrw #define imap_uid i_uid #define imap_unsubscribe i_uns #define imap_valid i_val #define internal_date in_dat #define mail_append_full m_appn #define mail_auth m_auth #define mail_body m_body #define mail_cdate m_cdat #define mail_check m_chck #define mail_close_full m_clos #define mail_copy_full m_copy #define mail_create m_crea #define mail_criteria m_crit #define mail_criteria_date mc_dat #define mail_criteria_string mc_str #define mail_date m_date #define mail_debug m_dbug #define mail_delete m_del #define mail_dlog m_dlog #define mail_elt m_elt #define mail_exists m_exst #define mail_expunge m_expn #define mail_expunged m_expd #define mail_fetch_body fs_bdy #define mail_fetch_fast mf_fst #define mail_fetch_flags mf_flg #define mail_fetch_header mf_hdr #define mail_fetch_message mf_msg #define mail_fetch_mime mf_mim #define mail_fetch_overview mf_ovr #define mail_fetch_overview_sequence mf_ovs #define mail_fetch_overview_default mf_ovd #define mail_fetch_structure mf_str #define mail_fetch_text mf_txt #define mail_fetch_text_return mf_txr #define mail_fetch_string_return mf_tsr #define mail_fetchfrom mf_frm #define mail_fetchsubject mf_sub #define mail_filter m_filt #define mail_flag m_flag #define mail_free_acl mr_acl #define mail_free_address mr_add #define mail_free_body mr_bdy #define mail_free_body_data mrb_da #define mail_free_body_parameter mrb_pr #define mail_free_body_part mrb_pt #define mail_free_cache mr_cac #define mail_free_elt mr_elt #define mail_free_envelope mr_env #define mail_free_handle mr_han #define mail_free_namespace mr_nsp #define mail_free_quotalist mr_qtl #define mail_free_searchheader mrs_hd #define mail_free_searchor mrs_or #define mail_free_searchpgm mrs_pg #define mail_free_searchpgmlist mrs_pl #define mail_free_searchset mrs_st #define mail_free_sortpgm mr_spg #define mail_free_stringlist mr_sls #define mail_free_threadnode mr_thn #define mail_gc m_gc #define mail_gc_msg m_gcm #define mail_gc_body m_gcb #define mail_initbody m_ibdy #define mail_link m_link #define mail_list m_list #define mail_lock m_lock #define mail_longdate ml_lda #define mail_lookup_auth m_laut #define mail_lookup_auth_name m_latn #define mail_lsub m_lsub #define mail_makehandle m_mhdl #define mail_match_lines m_mlns #define mail_msgno m_msgn #define mail_newacl mn_acl #define mail_newaddr mn_add #define mail_newbody mn_bdy #define mail_newbody_parameter mnb_pr #define mail_newbody_part mnb_pt #define mail_newbody_message_part mnb_mp #define mail_new_cache_elt mn_elt #define mail_newenvelope mn_env #define mail_newmsg mn_msg #define mail_newquotalist mn_qtl #define mail_newsearchheader mns_hd #define mail_newsearchor mns_or #define mail_newsearchpgm mns_pg #define mail_newsearchpgmlist mns_pl #define mail_newsearchset mns_st #define mail_newsortpgm mn_spg #define mail_newstringlist mn_sls #define mail_newthreadnode mn_thr #define mail_nodebug m_ndbg #define mail_open m_open #define mail_parameters m_parm #define mail_parse_date mp_dat #define mail_parse_flags mp_flg #define mail_partial_body mpt_bd #define mail_partial_text mpt_tx #define mail_ping m_ping #define mail_read m_read #define mail_recent m_rcent #define mail_rename m_ren #define mail_scan m_scan #define mail_search_addr ms_adr #define mail_search_body ms_bdy #define mail_search_default ms_def #define mail_search_full m_srch #define mail_search_gets ms_gts #define mail_search_header ms_hdr #define mail_search_header_text ms_hdt #define mail_search_keyword ms_key #define mail_search_msg ms_msg #define mail_search_string ms_str #define mail_search_text ms_txt #define mail_sequence m_seq #define mail_skip_fwd msk_fw #define mail_skip_re msk_re #define mail_sort ml_srt #define mail_sort_cache ms_csh #define mail_sort_compare ms_cmp #define mail_sort_loadcache ms_lcs #define mail_sort_msgs ms_mgs #define mail_status m_stat #define mail_status_default m_stad #define mail_stream m_strm #define mail_string m_strg #define mail_string_init mt_ini #define mail_string_next mt_nxt #define mail_string_setpos mt_sps #define mail_strip_subject mst_sb #define mail_strip_subject_wsp mst_ws #define mail_strip_subject_blob mst_bl #define mail_subscribe m_sub #define mail_thread m_thr #define mail_threadlist mt_lst #define mail_thread_c2node mt_c2n #define mail_thread_check_child mt_ckc #define mail_thread_compare_date mtc_da #define mail_thread_loadcache mt_ldc #define mail_thread_msgs mt_mgs #define mail_thread_orderedsubject mt_osb #define mail_thread_parse_msgid mtp_mi #define mail_thread_parse_references mtp_rf #define mail_thread_prune_dummy mt_prd #define mail_thread_references mt_ref #define mail_thread_sort mt_srt #define mail_uid m_uid #define mail_uid_sequence mu_seq #define mail_unlock m_unl #define mail_unsubscribe m_uns #define mail_valid m_val #define mail_valid_net mv_net #define mail_valid_net_parse mvn_pr #define mailboxfile mbxfil #define md5_init m5_ini #define md5_update m5_upd #define md5_final m5_fin #define mime2_decode m2_dec #define mime2_text m2_txt #define mime2_token m2_tok #define mm_cache mm_cac #define mm_critical mm_crt #define mm_diskerror mm_dse #define mm_dlog mm_dlg #define mm_exists mm_exs #define mm_expunged mm_exp #define mm_fatal mm_ftl #define mm_flags mm_flg #define mm_list mm_lst #define mm_log mm_log #define mm_login mm_lgi #define mm_lsub mm_lsb #define mm_mailbox mm_mbx #define mm_nocritical mm_ncr #define mm_notify mm_not #define mm_searched mm_src #define myhomedir myhome #define mylocalhost myhost #define myusername_full myuser #define net_aopen nt_aop #define net_close nt_cls #define net_getbuffer nt_gtb #define net_getdata nt_gtd #define net_getline nt_gtl #define net_host nt_hst #define net_localhost nt_lhs #define net_open nt_opn #define net_port nt_prt #define net_sout nt_sot #define net_soutr nt_str #define netmsg_read nm_rea #define netmsg_slurp nm_slr #define netmsg_slurp_text nm_slt #define newsrc_check_uid nsc_ui #define newsrc_create ns_crea #define newsrc_error ns_err #define newsrc_lsub ns_lsub #define newsrc_newmessages ns_nms #define newsrc_newstate ns_nst #define newsrc_read ns_rea #define newsrc_status ns_sta #define newsrc_update ns_upd #define newsrc_write ns_wri #define newsrc_write_error ns_wer #define nntp_append n_appn #define nntp_canonicalize n_cano #define nntp_check n_chck #define nntp_close n_clos #define nntp_copy n_copy #define nntp_create n_crea #define nntp_delete n_del #define nntp_expunge n_expn #define nntp_fake n_fake #define nntp_fetchfast nf_fst #define nntp_fetchflags nf_flg #define nntp_fetchmessage nf_msg #define nntp_flagmsg n_fmsg #define nntp_gc n_gc #define nntp_getmap n_gmap #define nntp_header n_head #define nntp_isvalid n_isvl #define nntp_list n_list #define nntp_lsub n_lsub #define nntp_mail n_mail #define nntp_mclose n_mcls #define nntp_mopen n_mopn #define nntp_open_full n_open #define nntp_over n_ovr #define nntp_overview n_over #define nntp_parameters n_parm #define nntp_parsestructure n_pars #define nntp_parse_overview n_povr #define nntp_ping n_ping #define nntp_rename n_ren #define nntp_reply n_repl #define nntp_scan n_scan #define nntp_search n_srch #define nntp_search_msg ns_msg #define nntp_send n_send #define nntp_send_auth ns_aut #define nntp_send_auth_work ns_atw #define nntp_send_work n_sndw #define nntp_sort n_sort #define nntp_sort_loadcache ns_lcs #define nntp_soutr n_sout #define nntp_status n_stat #define nntp_subscribe n_sub #define nntp_text n_text #define nntp_text_slurp nt_slp #define nntp_thread n_thrd #define nntp_unsubscribe n_uns #define nntp_valid n_val #define pop3_append p_appn #define pop3_auth p_auth #define pop3_cache p_cach #define pop3_challenge p_chal #define pop3_check p_chck #define pop3_close p_clos #define pop3_copy p_copy #define pop3_create p_crea #define pop3_delete p_del #define pop3_expunge p_exp #define pop3_fake p_fake #define pop3_fetchfast pf_fst #define pop3_fetchflags pf_flg #define pop3_fetchmessage pf_msg #define pop3_gc p_gc #define pop3_list p_list #define pop3_lsub p_lsub #define pop3_open p_open #define pop3_parameters p_parm #define pop3_parsestructure p_pars #define pop3_ping p_ping #define pop3_rename p_ren #define pop3_reply p_rep #define pop3_response p_resp #define pop3_scan p_scan #define pop3_send p_send #define pop3_send_num ps_num #define pop3_status p_stat #define pop3_subscribe p_sub #define pop3_unsubscribe p_uns #define pop3_valid p_val #define rfc822_8bit r #define rfc822_address r_addr #define rfc822_address_line ra_lin #define rfc822_base64 r_b64 #define rfc822_binary r_bin #define rfc822_cat r_cat #define rfc822_contents r_cont #define rfc822_cpy r_cpy #define rfc822_cpy_adr rc_adr #define rfc822_date r_date #define rfc822_default_subtype rd_sub #define rfc822_encode_body_7bit reb_7b #define rfc822_encode_body_8bit reb_8b #define rfc822_header r_head #define rfc822_header_line rh_lin #define rfc822_output r_out #define rfc822_output_body ro_bdy #define rfc822_parse_address rp_adr #define rfc822_parse_addrspec rp_ads #define rfc822_parse_adrlist rp_adl #define rfc822_parse_content rp_cnt #define rfc822_parse_content_header rpc_hd #define rfc822_parse_group rp_grp #define rfc822_parse_mailbox rp_mbx #define rfc822_parse_msg_full rp_msg #define rfc822_parse_parameter rp_par #define rfc822_parse_phrase rp_phr #define rfc822_parse_routeaddr rp_rte #define rfc822_parse_word rp_wrd #define rfc822_phraseonly r_poly #define rfc822_qprint r_qpnt #define rfc822_quote r_quot #define rfc822_skip_comment rs_cmt #define rfc822_skipws rs_ws #define rfc822_timezone r_tz #define rfc822_write_address_full rw_adr #define rfc822_write_body_header rwbh_8 #define server_input_wait s_iwat #define server_login s_log #define server_init s_init #define sm_read sm_rd #define sm_subscribe sm_sub #define sm_unsubscribe sm_uns #define smtp_auth s_auth #define smtp_challenge s_chal #define smtp_close s_clos #define smtp_ehlo s_ehlo #define smtp_fake s_fake #define smtp_mail s_mail #define smtp_open_full s_open #define smtp_rcpt s_rcpt #define smtp_reply s_repl #define smtp_response s_resp #define smtp_send s_send #define smtp_send_auth ss_aut #define smtp_send_auth_work ss_atw #define smtp_send_work ss_wrk #define smtp_soutr s_str #define strcrlfcpy sc_cpy #define strcrlflen sc_len #define tcp_aopen t_aopn #define tcp_canonical t_cnon #define tcp_clienthost t_chst #define tcp_close t_clos #define tcp_getbuffer tg_buf #define tcp_getdata tg_dat #define tcp_getline tg_lin #define tcp_host t_host #define tcp_localhost t_lhst #define tcp_open t_open #define tcp_parameters t_parameters #define tcp_port t_port #define tcp_remotehost t_rhst #define tcp_serverhost t_shst #define tcp_serverport t_sprt #define tcp_sout t_sout #define tcp_soutr t_str #define textcpy txcopy #define textcpystring txcpst #define textcpyoffstring txcpos #define utf8_cstext u8_cst #define utf8_cstocstext u8_cct #define utf8_get u8_get #define utf8_iso2022text u8_i22 #define utf8_mime2text u8_mi2 #define utf8_rmap u8_rmp #define utf8_searchpgm u8_spg #define utf8_stringlist u8_lst #define utf8_text u8_txt #define utf8_text_8859_1 u8t_we #define utf8_text_1byte0 u8t_10 #define utf8_text_1byte u8t_1b #define utf8_text_1byte8 u8t_18 #define utf8_text_euc u8t_eu #define utf8_text_dbyte u8t_db #define utf8_text_dbyte2 u8t_d2 #define utf8_text_sjis u8t_sj #define utf8_text_2022 u8t_22 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/tcp_t20.c000066400000000000000000000215421137544547100234620ustar00rootroot00000000000000/* * Program: TOPS-20 TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to the TOPS-20 operating system, which * set standards for user and programmer friendliness that have still not been * equaled by more `modern' operating systems. * Wasureru mon ka!!!! */ /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { return NIL; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { char *s,tmp[MAILTMPLEN]; TCPSTREAM *stream = NIL; int argblk[5],jfn; unsigned long i,j,k,l; char file[MAILTMPLEN]; port &= 0xffff; /* erase flags */ /* domain literal? */ if (host[0] == '[' && host[strlen (host)-1] == ']') { if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' && ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' && ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' && ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) { argblk[3] = (i << 24) + (j << 16) + (k << 8) + l; sprintf (tmp,"[%lu.%lu.%lu.%lu]",i,j,k,l); } else { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else { /* host name */ argblk[1] = _GTHPN; /* get IP address and primary name */ argblk[2] = (int) (host-1); /* pointer to host */ argblk[4] = (int) (tmp-1); if (!jsys (GTHST,argblk)) { /* first try DEC's domain way */ argblk[1] = _GTHPN; /* get IP address and primary name */ argblk[2] = (int) (host-1); argblk[4] = (int) (tmp-1); if (!jsys (GTDOM,argblk)){/* try the CHIVES domain way */ argblk[1] = _GTHSN; /* failed, do the host table then */ if (!jsys (GTHST,argblk)) { sprintf (tmp,"No such host as %s",host); mm_log (tmp,ERROR); return NIL; } argblk[1] = _GTHNS; /* convert number to string */ argblk[2] = (int) (tmp-1); /* get the official name */ if (!jsys (GTHST,argblk)) strcpy (tmp,host); } } } sprintf (file,"TCP:.%o-%d;PERSIST:30;CONNECTION:ACTIVE",argblk[3],port); argblk[1] = GJ_SHT; /* short form GTJFN% */ argblk[2] = (int) (file-1); /* pointer to file name */ /* get JFN for TCP: file */ if (!jsys (GTJFN,argblk)) fatal ("Unable to create TCP JFN"); jfn = argblk[1]; /* note JFN for later */ /* want 8-bit bidirectional I/O */ argblk[2] = OF_RD|OF_WR|(FLD (8,monsym("OF%BSZ"))); if (!jsys (OPENF,argblk)) { sprintf (file,"Can't connect to %s,%d server",tmp,port); mm_log (file,ERROR); return NIL; } /* create TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); stream->host = cpystr (tmp); /* copy official host name */ argblk[1] = _GTHNS; /* convert number to string */ argblk[2] = (int) (tmp-1); argblk[3] = -1; /* want local host */ if (!jsys (GTHST,argblk)) strcpy (tmp,"LOCAL"); stream->localhost = cpystr (tmp); stream->port = port; /* save port number */ stream->jfn = jfn; /* init JFN */ return stream; } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int argblk[5]; int n,m; char *ret,*stp,*st; argblk[1] = stream->jfn; /* read from TCP */ /* pointer to buffer */ argblk[2] = (int) (stream->ibuf-1); argblk[3] = BUFLEN; /* max number of bytes to read */ argblk[4] = '\012'; /* terminate on LF */ if (!jsys (SIN,argblk)) return NIL; n = BUFLEN - argblk[3]; /* number of bytes read */ /* got a complete line? */ if ((stream->ibuf[n - 2] == '\015') && (stream->ibuf[n - 1] == '\012')) { memcpy ((ret = (char *) fs_get (n)),stream->ibuf,n - 2); ret[n - 2] = '\0'; /* tie off string with null */ return ret; } /* copy partial string */ memcpy ((stp = ret = (char *) fs_get (n)),stream->ibuf,n); /* special case of newline broken by buffer */ if ((stream->ibuf[n - 1] == '\015') && jsys (BIN,argblk) && (argblk[2] == '\012')) { /* was it? */ ret[n - 1] = '\0'; /* tie off string with null */ } /* recurse to get remainder */ else if (jsys (BKJFN,argblk) && (st = tcp_getline (stream))) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { int argblk[5]; argblk[1] = stream->jfn; /* read from TCP */ argblk[2] = (int) (buffer-1); /* pointer to buffer */ argblk[3] = -size; /* number of bytes to read */ if (!jsys (SIN,argblk)) return NIL; buffer[size] = '\0'; /* tie off text */ return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { int argblk[5]; argblk[1] = stream->jfn; /* write to TCP */ argblk[2] = (int) (string-1); /* pointer to buffer */ argblk[3] = 0; /* write until NUL */ if (!jsys (SOUTR,argblk)) return NIL; return T; } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int argblk[5]; argblk[1] = stream->jfn; /* write to TCP */ argblk[2] = (int) (string-1); /* pointer to buffer */ argblk[3] = -size; /* write this many bytes */ if (!jsys (SOUTR,argblk)) return NIL; return T; } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { int argblk[5]; argblk[1] = stream->jfn; /* close TCP */ jsys (CLOSF,argblk); /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP return host for this stream * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return remote host for this stream * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP return local host for this stream * Accepts: TCP/IP stream * Returns: local host name for this stream */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { int argblk[5]; static char tmp[MAILTMPLEN]; /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name; argblk[1] = _GTHPN; /* get IP address and primary name */ argblk[2] = (int) (name-1); /* pointer to host */ argblk[4] = (int) (tmp-1); /* pointer to return destination */ if (!jsys (GTHST,argblk)) { /* first try DEC's domain way */ argblk[1] = _GTHPN; /* get IP address and primary name */ argblk[2] = (int) (name-1); argblk[4] = (int) (tmp-1); if (!jsys (GTDOM,argblk)) { /* try the CHIVES domain way */ argblk[1] = _GTHSN; /* failed, do the host table then */ if (!jsys (GTHST,argblk)) return name; argblk[1] = _GTHNS; /* convert number to string */ argblk[2] = (int) (tmp-1); /* get the official name */ if (!jsys (GTHST,argblk)) return name; } } return tmp; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/tops-20/tcp_t20.h000066400000000000000000000025021137544547100234620ustar00rootroot00000000000000/* * Program: TOPS-20 TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dedication: * This file is dedicated with affection to the TOPS-20 operating system, which * set standards for user and programmer friendliness that have still not been * equaled by more `modern' operating systems. * Wasureru mon ka!!!! */ /* TCP input buffer */ #define BUFLEN 8192 /* TCP I/O stream (must be before osdep.h is included) */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ int jfn; /* jfn for connection */ char ibuf[BUFLEN]; /* input buffer */ }; /* All of these should be in JSYS.H, but just in case... */ #ifndef _GTHPN #define _GTHPN 12 #endif #ifndef _GTDPN #define _GTDPN 12 #endif #ifndef GTDOM #define GTDOM (FLD (1,JSYS_CLASS) | 501 #endif tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/000077500000000000000000000000001137544547100216165ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/Makefile000066400000000000000000000722171137544547100232670ustar00rootroot00000000000000# Program: C client makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 30 April 2005 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2005 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Command line build parameters EXTRAAUTHENTICATORS= EXTRADRIVERS=mbox PASSWDTYPE=std SSLTYPE=nopwd IP=4 # The optimization level here for GCC ports is set here for a reason. It's # to get you to read this text. # The general concensus seems to be that -O2 is the one to use. # Over the years, I've been told to use many different settings, including -O6. # In recent versions of GCC [as of 2/2005], -O6 generates bad code that, among # other ill effects, causes infinite loops. # -O3 seems to be safe, but empirical observation from our local expert # indicates that in some (many?) cases -O3 code runs slower than -O2. GCCOPTLEVEL= -O2 # Extended flags needed for SSL. You may need to modify. SSLDIR=/usr/local/ssl SSLCERTS=$(SSLDIR)/certs SSLKEYS=$(SSLCERTS) SSLINCLUDE=$(SSLDIR)/include SSLLIB=$(SSLDIR)/lib SSLCRYPTO=-lcrypto # Older versions of MIT Kerberos also have a libcrypto. If so, you may need # to use this instead #SSLCRYPTO=$(SSLLIB)/libcrypto.a # RSA Security Inc. released the RSA public key encryption algorithm into # the public domain on September 6, 2000. There is no longer any need to # use RSAREF. SSLRSA= # -lRSAglue -lrsaref SSLCFLAGS= -I$(SSLINCLUDE) -I$(SSLINCLUDE)/openssl\ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" -DSSL_KEY_DIRECTORY=\"$(SSLKEYS)\" SSLLDFLAGS= -L$(SSLLIB) -lssl $(SSLCRYPTO) $(SSLRSA) # Extended flags needed for non-standard passwd types. You may need to modify. AFSDIR=/usr/afsws AFSCFLAGS=-I$(AFSDIR)/include AFSLIB=$(AFSDIR)/lib AFSLDFLAGS=-L$(AFSLIB)/afs -L$(AFSLIB) -L$(AFSDIR)/domestic/lib\ -lkauth -lprot -lubik -lauth -lrxkad -lrx -llwp -ldes -lcom_err\ $(AFSLIB)/afs/util.a -laudit -lsys # AFSLDFLAGS may also need -L/usr/ucblib -lucb DCECFLAGS= -DDCE_MINIMAL -DPASSWD_OVERRIDE=\"/opt/pop3/passwd/passwd\" DCELDFLAGS= -ldce PAMLDFLAGS= -lpam -ldl # Build parameters normally set by the individual port CHECKPW=std LOGINPW=std SIGTYPE=bsd CRXTYPE=std ACTIVEFILE=/usr/lib/news/active SPOOLDIR=/usr/spool MAILSPOOL=$(SPOOLDIR)/mail NEWSSPOOL=$(SPOOLDIR)/news RSHPATH=/usr/ucb/rsh LOCKPGM=/etc/mlock # Default formats for creating new mailboxes and for empty mailboxes in the # default namespace; must be set to the associated driver's prototype. # # The CREATEPROTO is the default format for new mailbox creation. # The EMPTYPROTO is the default format for handling zero-byte files. # # Normally, this is set by the individual port. # # NOTE: namespace formats (e.g. mh and news) can not be set as a default format # since they do not exist in the default namespace. Also, it is meaningless to # set certain other formats (e.g. mbx and mx) as the EMPTYPROTO since these # formats can never be empty files. CREATEPROTO=unixproto EMPTYPROTO=unixproto # Commands possibly overriden by the individual port ARRC=ar rc CC=cc LN=ln -s RANLIB=ranlib # Standard distribution build parameters DEFAULTAUTHENTICATORS=md5 pla log DEFAULTDRIVERS=imap nntp pop3 mh mx mbx tenex mtx mmdf unix news phile # Normally no need to change any of these ARCHIVE=c-client.a BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o CFLAGS=-g CAT=cat MAKE=make MV=mv RM=rm -rf SH=sh # Primary build command BUILD=$(MAKE) build EXTRACFLAGS='$(EXTRACFLAGS)'\ EXTRALDFLAGS='$(EXTRALDFLAGS)'\ EXTRADRIVERS='$(EXTRADRIVERS)' EXTRAAUTHENTICATORS='$(EXTRAAUTHENTICATORS)'\ PASSWDTYPE=$(PASSWDTYPE) SSLTYPE=$(SSLTYPE) IP=$(IP) # Here if no make argument established missing: osdep.h $(MAKE) all `$(CAT) SPECIALS` osdep.h: @echo You must specify what type of system @false # Current ports a32: # AIX 3.2 for RS/6000 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -Dunix=1 -D_BSD" \ BASELDFLAGS="-lbsd" a41: # AIX 4.1 for RS/6000 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \ SPOOLDIR=/var/spool \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -Dunix=1 -D_BSD -qro -qroconst" \ BASELDFLAGS="-ls" aix: # AIX/370 @echo You are building for AIX on an S/370 class machine @echo If you want AIX on an RS/6000 you need to use a32 or a41 instead! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g" \ BASELDFLAGS="-lbsd" aos: # AOS for RT $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g -Dconst=" art: # AIX 2.2.1 for RT $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/control/active \ RSHPATH=/bin/rsh \ BASECFLAGS="-g -Dconst= -Dvoid=char" \ RANLIB=true asv: # Altos SVR4 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 LOGINPW=old \ ACTIVEFILE=/usr/spool/news/active \ RSHPATH=/usr/bin/rcmd \ BASECFLAGS="-Dconst= -DSIGSTOP=SIGKILL" \ BASELDFLAGS="-lsocket -lrpc -lgen -lcrypt -lxenix" \ RANLIB=true aux: # A/UX $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ MAILSPOOL=/usr/mail \ BASECFLAGS="-g -B/usr/lib/big/ -Dvoid=char -Dconst=" \ RANLIB=true ARRC="ar -rc" bs3: # BSD/i386 3.0 or higher $(BUILD) `$(CAT) SPECIALS` OS=bsi \ CHECKPW=bsi LOGINPW=bsi CRXTYPE=nfs \ SPOOLDIR=/var NEWSSPOOL=/var/news/spool \ ACTIVEFILE=/var/news/etc/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g $(GCCOPTLEVEL) -pipe" CC=shlicc bsd: # BSD UNIX $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g -Dconst=" bsf: # FreeBSD $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O -pipe" \ BASELDFLAGS="-lcrypt" bsi: # BSD/i386 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ SPOOLDIR=/var NEWSSPOOL=/var/news/spool \ ACTIVEFILE=/var/news/etc/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g $(GCCOPTLEVEL) -pipe" bso: # OpenBSD $(BUILD) `$(CAT) SPECIALS` OS=bsi \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="$(GCCOPTLEVEL) -pipe" cvx: # Convex $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-O -ext -Dconst=" cyg: # Cygwin - note that most local file drivers don't work!! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O" \ BASELDFLAGS="-lcrypt" \ CC=gcc d-g: # Data General DG/UX $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 CRXTYPE=nfs \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/local/news/active \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g -Dconst=" \ BASELDFLAGS="-lnsl -lsocket" \ RANLIB=true d54: # Data General DG/UX 5.4 $(BUILD) `$(CAT) SPECIALS` OS=d-g \ SIGTYPE=sv4 CRXTYPE=nfs \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/local/news/active \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g -Dconst=" \ BASELDFLAGS="-lnsl -lsocket" \ RANLIB=true dpx: # Bull DPX/2 $(BUILD) `$(CAT) SPECIALS` OS=sv4 \ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-Dconst= -DSYSTEM5 -DSHORT_IDENT" \ BASELDFLAGS="-linet" \ RANLIB=true LN=ln drs: # ICL DRS/NX $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 CRXTYPE=nfs \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-O" \ BASELDFLAGS="-lsocket -lgen" \ RANLIB=true do4: # Apollo Domain/OS sr10.4 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-A systype,bsd4.3 -D_APOLLO_SOURCE" \ RANLIB=true dyn: # Dynix $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g -Dconst=" epx: # EP/IX $(BUILD) `$(CAT) SPECIALS` OS=sv4 \ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/usr/share/news/active \ RSHPATH=/usr/net/rsh \ BASECFLAGS="-g -systype svr4" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true ga4: # GCC AIX 4.1 for RS/6000 $(BUILD) `$(CAT) SPECIALS` OS=a41 \ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \ SPOOLDIR=/var/spool \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -Dunix=1 -D_BSD" \ BASELDFLAGS="-ls" gas: # GCC Altos SVR4 $(BUILD) `$(CAT) SPECIALS` OS=asv \ SIGTYPE=sv4 LOGINPW=old \ ACTIVEFILE=/usr/spool/news/active \ RSHPATH=/usr/bin/rcmd \ BASECFLAGS="-g -O -DALTOS_SYSTEM_V -DSIGSTOP=SIGKILL" \ BASELDFLAGS="-lsocket -lrpc -lgen -lcrypt -lxenix" \ RANLIB=true CC=gcc gh9: # GCC HP-UX 9.x $(BUILD) `$(CAT) SPECIALS` OS=hpp \ SIGTYPE=psx CRXTYPE=nfs \ MAILSPOOL=/usr/mail \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g $(GCCOPTLEVEL)" \ RANLIB=true CC=gcc ghp: # GCC HP-UX 10.x $(BUILD) `$(CAT) SPECIALS` OS=hpp \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var \ ACTIVEFILE=/var/news/active \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g $(GCCOPTLEVEL)" \ RANLIB=true CC=gcc ghs: # GCC HP-UX with Trusted Computer Base $(BUILD) `$(CAT) SPECIALS` OS=shp \ SIGTYPE=psx CHECKPW=sec CRXTYPE=nfs \ SPOOLDIR=/var \ ACTIVEFILE=/var/news/active \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g $(GCCOPTLEVEL)" \ BASELDFLAGS="-lnet -lV3 -lsec" \ RANLIB=true CC=gcc go5: # GCC 2.7.1 (95q4) SCO Open Server 5.0.x $(BUILD) `$(CAT) SPECIALS` OS=sc5 \ SIGTYPE=psx CHECKPW=sec LOGINPW=sec \ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rcmd \ BASECFLAGS="$(GCCOPTLEVEL) -I/usr/include -L/lib" \ BASELDFLAGS="-lsocket -lprot -lx -ltinfo -lm" \ RANLIB=true CC=gcc gsc: # Santa Cruz Operation $(BUILD) `$(CAT) SPECIALS` OS=sco \ SIGTYPE=sv4 CHECKPW=sec LOGINPW=sec \ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \ RSHPATH=/usr/bin/rcmd \ BASECFLAGS="$(GCCOPTLEVEL)" \ BASELDFLAGS="-lsocket -lprot -lcrypt_i -lx -los" \ RANLIB=true LN=ln CC=gcc gsg: # GCC Silicon Graphics $(BUILD) `$(CAT) SPECIALS` OS=sgi \ SIGTYPE=sv4 CRXTYPE=nfs \ MAILSPOOL=/usr/mail \ RSHPATH=/usr/bsd/rsh \ BASECFLAGS="-g $(GCCOPTLEVEL)" \ RANLIB=true CC=gcc gso: os_sol.h # GCC Solaris $(BUILD) `$(CAT) SPECIALS` OS=sol \ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/usr/share/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g $(GCCOPTLEVEL)" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true CC=gcc gsu: # GCC SUN-OS $(BUILD) `$(CAT) SPECIALS` OS=sun \ CRXTYPE=nfs \ BASECFLAGS="-g $(GCCOPTLEVEL)" \ BASELDFLAGS="-ldl" \ CC=gcc gul: # GCC Ultrix $(BUILD) `$(CAT) SPECIALS` OS=ult \ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \ BASECFLAGS="-g -O" \ BASELDFLAGS="-lauth -lc" \ CC=gcc hpp: # HP-UX 9.x $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CRXTYPE=nfs \ MAILSPOOL=/usr/mail \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g -Aa -D_HPUX_SOURCE" \ BASELDFLAGS="-lnet -lV3" \ RANLIB=true hpx: # HP-UX 10.x $(BUILD) `$(CAT) SPECIALS` OS=hpp \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var \ ACTIVEFILE=/var/news/active \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g -Ae" \ BASELDFLAGS="-lnet -lV3" \ RANLIB=true isc: # Interactive $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/var/spool/news/active \ BASECFLAGS="-Xp -D_SYSV3" \ BASELDFLAGS="-linet -lnsl_s -lgen -lx -lsec -liberty" \ RANLIB=true lnp: # Linux Pluggable Authentication modules $(BUILD) `$(CAT) SPECIALS` OS=lnx \ SIGTYPE=psx CHECKPW=pam CRXTYPE=nfs \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -fno-omit-frame-pointer $(GCCOPTLEVEL)" \ BASELDFLAGS="$(PAMLDFLAGS)" lnx: # Linux non-shadow passwords @echo You are building for traditional Linux *without* shadow @echo passwords and with the crypt function in the C library. @echo If your system has shadow passwords, or if crypt is not @echo in the C library, you must use slx, sl4, or sl5 instead! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O" lyn: # LynxOS $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ RSHPATH=/bin/rsh \ BASECFLAGS="-g -O -pipe" \ BASELDFLAGS=-lbsd \ CC=gcc mct: # MachTen - CRXTYPE=nfs doesn't work (at least not on 2.2) $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SPOOLDIR=/var/spool \ BASECFLAGS="-g -O -pipe" mnt: # Mint $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O" neb: # NetBSD $(BUILD) `$(CAT) SPECIALS` OS=bsi \ CRXTYPE=nfs \ SPOOLDIR=/var \ ACTIVEFILE=/var/db/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O -pipe" \ BASELDFLAGS="-lcrypt" nec: # NEC UX $(BUILD) `$(CAT) SPECIALS` OS=sv4 \ SIGTYPE=sv4 CHECKPW=sv4 \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/var/news/lib/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -Kopt=2 -KOlimit=2000" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true CC=/usr/abiccs/bin/cc nto: # QNX Neutrino RTP $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O" nxt: # NEXTSTEP $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g -O -pipe" nx3: # NEXTSTEP 3.0 single threaded $(BUILD) `$(CAT) SPECIALS` OS=nxt \ CRXTYPE=nfs \ BASECFLAGS="-g -O -pipe -Wall" echo "void malloc_singlethreaded (void);" >> linkage.h echo " malloc_singlethreaded ();" >> linkage.c osf: # OSF/1 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool \ BASECFLAGS="-g3 -w -O2 -Olimit 1500" # Note: sia_become_user() used by LOGINPW=os4 doesn't seem to work right. The # user doesn't get proper file access, and the process can't be killed. os4: # OSF/1 (Digital UNIX) 4 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=os4 LOGINPW=os4 CRXTYPE=nfs \ SPOOLDIR=/var/spool \ BASECFLAGS="-g3 -w -std0 -O2" osx: # Mac OS X $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ BASECFLAGS="-g -O -Wno-pointer-sign" ptx: # PTX $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=svo LOGINPW=sv4 CRXTYPE=nfs \ MAILSPOOL=/usr/mail \ RSHPATH=/usr/bin/resh \ BASECFLAGS="-Wc,-O3 -Wc,-seq -Dprivate=PRIVATE" \ BASELDFLAGS="-lseq -lsec -lsocket -linet -lnsl -lgen" \ RANLIB=true pyr: # Pyramid $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g -Dconst=" qnx: # QNX $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CHECKPW=psx LOGINPW=old \ RSHPATH=/usr/ucb/rsh \ BASECFLAGS="-Otax -g -Dunix=1 -D_POSIX_SOURCE" \ BASELDFLAGS="-g -N128k -llogin -lsocket -lunix" s40: # SUN-OS 4.0 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g -Dconst=" sc5: # SCO Open Server 5.0 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=sec LOGINPW=sec \ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rcmd \ BASECFLAGS="-O3 -s -belf" \ BASELDFLAGS="-lsocket -lprot -lx -ltinfo -lm" \ RANLIB=true sco: # Santa Cruz Operation $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 CHECKPW=sec LOGINPW=sec \ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \ RSHPATH=/usr/bin/rcmd \ BASECFLAGS="-O3" \ BASELDFLAGS="-lsocket -lprot -lcrypt_i -lx -los" \ RANLIB=true LN=ln # Note: setting _POSIX_SOURCE doesn't seem to build it as of SGI IRIX 5.3 sgi: # Silicon Graphics $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 CRXTYPE=nfs \ MAILSPOOL=/usr/mail \ RSHPATH=/usr/bsd/rsh \ BASECFLAGS="-g3 -O2 -Olimit 8192" \ RANLIB=true sg6: # Silicon Graphics, IRIX 6.5 MAKEFLAGS= $(BUILD) `$(CAT) SPECIALS` OS=sgi \ SIGTYPE=sv4 CRXTYPE=nfs \ MAILSPOOL=/usr/mail \ RSHPATH=/usr/bsd/rsh \ BASECFLAGS="-g3 -O2 -OPT:Olimit=0 -woff 1110,1116" \ RANLIB=true # Note: Mark Kaesling says that setluid() isn't in HP-UX with SecureWare. shp: # HP-UX with Trusted Computer Base $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=sec CRXTYPE=nfs \ SPOOLDIR=/var \ ACTIVEFILE=/var/news/active \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-g -Ae" \ BASELDFLAGS="-lnet -lV3 -lsec" \ RANLIB=true slx: # Secure Linux @echo You are building for libc6/glibc versions of Secure Linux @echo If you want libc5 versions you must use sl5 instead! @echo If you want libc4 versions you must use sl4 instead! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -fno-omit-frame-pointer $(GCCOPTLEVEL)" \ BASELDFLAGS="-lcrypt" sl4: # Secure Linux using libc4 @echo You are building for libc4 versions of Secure Linux @echo If you want libc6/glibc versions you must use slx instead! @echo If you want libc5 versions you must use sl5 instead! $(BUILD) `$(CAT) SPECIALS` OS=slx \ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -fno-omit-frame-pointer $(GCCOPTLEVEL)" \ BASELDFLAGS="-lshadow" sl5: # Secure Linux using libc5 @echo You are building for libc5 versions of Secure Linux @echo If you want libc6/glibc versions you must use slx instead! @echo If you want libc4 versions you must use sl4 instead! $(BUILD) `$(CAT) SPECIALS` OS=slx \ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool \ ACTIVEFILE=/var/lib/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -fno-omit-frame-pointer $(GCCOPTLEVEL)" snx: # Siemens Nixdorf SINIX and Reliant UNIX $(BUILD) `$(CAT) SPECIALS` OS=sv4 \ SIGTYPE=psx CHECKPW=sv4 \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/usr/share/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -D_SYS_CLOCK_H -Dconst=" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true # Sorry about the -w, but the cretinous SUN Workshop Pro C compiler barfs on # implicit casts between char and unsigned char. soc: os_sol.h # Solaris with cc $(BUILD) `$(CAT) SPECIALS` OS=sol \ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/usr/share/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O -w" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true CC=/opt/SUNWspro/bin/cc # Note: It is a long and disgusting story about why cc is set to ucbcc. You # need to invoke the C compiler so that it links with the SVR4 libraries and # not the BSD libraries, otherwise readdir() will return the wrong information. # Of all the names in the most common path, ucbcc is the only name to be found # (on /usr/ccs/bin) that points to a suitable compiler. cc is likely to be # /usr/ucb/cc which is absolutely not the compiler that you want. The real # SVR4 cc is probably something like /opt/SUNWspro/bin/cc which is rarely in # anyone's path. # # ucbcc is probably a link to acc, e.g. /opt/SUNWspro/SC4.0/bin/acc, and is # the UCB C compiler using the SVR4 libraries. # # If ucbcc isn't on your system, then punt on the SUN C compiler and use gcc # instead (the gso port instead of the sol port). # # If, in spite of all the above warnings, you choose to use the "soc" port # instead of the "sol" port, be sure to check the behavior of the LIST command # in imapd. Also, note that the "soc" port uses -O. If you want to use the # real SVR4 compiler, you must use -O. If it works to compile with -O2, then # cc is probably using the UCB compiler with BSD libraries, and will not build # a good binary # # To recap: # 1) The sol port is designed to be built using the UCB compiler using the # SVR4 libraries. This compiler is "ucbcc", which is lunk to acc. You # use -O2 as one of the CFLAGS. # 2) If you build the sol port with the UCB compiler using the BSD libraries, # you will get no error messages but you will get bad binaries (the most # obvious symptom is dropping the first two characters return filenames # from the imapd LIST command. This compiler also uses -O2, and is very # often what the user gets from "cc". BEWARE!!! # 3) If you build the sol port with the real SVR4 compiler, which is often # hidden away or unavailable on many systems, then you will get errors # from -O2 and you need to change that to -O. But you will get a good # binary. However, you should try it with -O2 first, to make sure that # you got this compiler and not the UCB compiler using BSD libraries. sol: os_sol.h # Solaris $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/usr/share/news/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g -O2" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true CC=ucbcc sos: # Secure OSF/1 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=sce LOGINPW=sec CRXTYPE=nfs \ BASECFLAGS="-g3 -w -O2 -Olimit 1500" \ BASELDFLAGS="-lsecurity -laud" ssn: # Secure SUN-OS $(BUILD) `$(CAT) SPECIALS` OS=sun \ CHECKPW=ssn CRXTYPE=nfs \ BASECFLAGS="-g -Dconst=" \ BASELDFLAGS="-ldl" sun: # SUN-OS $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-g -Dconst=" \ BASELDFLAGS="-ldl" sv2: # SVR2 @echo You are being *very* optimistic! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 LOGINPW=old \ MAILSPOOL=/usr/mail \ RSHPATH=/usr/bin/remsh \ BASECFLAGS="-Dconst= -DSYSTEM5 -DSHORT_IDENT -I/usr/ethernet/include" \ BASELDFLAGS="-lnet" \ RANLIB=true LN=ln sv4: # SVR4 $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/usr/share/news/active \ RSHPATH=/usr/bin/resh \ BASECFLAGS="-g -Dconst=" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true ult: # Ultrix $(BUILD) `$(CAT) SPECIALS` OS=$@ \ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \ BASECFLAGS="-g3 -O2 -Olimit 1500 -Dconst=" \ BASELDFLAGS="-lauth -lc" uw2: # UnixWare SVR4.2 $(BUILD) `$(CAT) SPECIALS` OS=sv4 \ SIGTYPE=sv4 CHECKPW=sv4 \ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \ ACTIVEFILE=/var/news/lib/active \ RSHPATH=/usr/bin/rsh \ BASECFLAGS="-g" \ BASELDFLAGS="-lsocket -lnsl -lgen" \ RANLIB=true vul: # VAX Ultrix $(BUILD) `$(CAT) SPECIALS` OS=ult \ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \ BASECFLAGS="-O2 -Dconst=" \ BASELDFLAGS="-lauth -lc" vu2: # VAX Ultrix 2.3, etc. $(BUILD) `$(CAT) SPECIALS` OS=$@ \ CRXTYPE=nfs \ BASECFLAGS="-O2 -Dconst= -Dvoid=char" # Build it! build: clean once $(ARCHIVE) all: $(ARCHIVE) $(ARCHIVE): $(BINARIES) sh -c '$(RM) $(ARCHIVE) || true' @$(CAT) ARCHIVE @$(SH) ARCHIVE .c.o: `$(CAT) CCTYPE` -c `$(CAT) CFLAGS` $*.c # Cleanup clean: sh -c '$(RM) auths.c crexcl.c nfstest.c linkage.[ch] siglocal.c osdep*.[ch] *.o ARCHIVE *FLAGS *TYPE $(ARCHIVE) || true' # Dependencies dummy.o: mail.h misc.h osdep.h dummy.h fdstring.o: mail.h misc.h osdep.h fdstring.h flstring.o: mail.h misc.h osdep.h flstring.h imap4r1.o: mail.h misc.h osdep.h imap4r1.h rfc822.h mail.o: mail.h misc.h osdep.h rfc822.h linkage.h mbx.o: mail.h misc.h osdep.h mbx.h dummy.h mh.o: mail.h misc.h osdep.h mh.h dummy.h mx.o: mail.h misc.h osdep.h mx.h dummy.h misc.o: mail.h misc.h osdep.h mmdf.o: mail.h misc.h osdep.h pseudo.h dummy.h mtx.o: mail.h misc.h osdep.h dummy.h netmsg.o: mail.h misc.h osdep.h netmsg.h news.o: mail.h misc.h osdep.h newsrc.o: mail.h misc.h osdep.h newsrc.h nntp.o: mail.h misc.h osdep.h netmsg.h smtp.h nntp.h rfc822.h phile.o: mail.h misc.h osdep.h rfc822.h dummy.h pseudo.o: pseudo.h pop3.o: mail.h misc.h osdep.h rfc822.h smanager.o: mail.h misc.h osdep.h smtp.o: mail.h misc.h osdep.h smtp.h rfc822.h rfc822.o: mail.h misc.h osdep.h rfc822.h tenex.o: mail.h misc.h osdep.h dummy.h unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h utf8.o: mail.h misc.h osdep.h utf8.h # OS-dependent osdep.o:mail.h misc.h env.h fs.h ftl.h nl.h tcp.h \ osdep.h env_unix.h tcp_unix.h \ osdep.c env_unix.c fs_unix.c ftl_unix.c nl_unix.c tcp_unix.c ip_unix.c\ auths.c crexcl.c flockcyg.c flocklnx.c flocksim.c nfstest.c fsync.c \ gethstid.c getspnam.c \ gr_wait.c gr_wait4.c gr_waitp.c \ kerb_mit.c \ auth_gss.c auth_log.c auth_md5.c auth_pla.c \ pmatch.c scandir.c setpgrp.c strerror.c truncate.c write.c \ memmove.c memmove2.c memset.c \ tz_bsd.c tz_nul.c tz_sv4.c \ write.c sslstdio.c \ strerror.c strpbrk.c strstr.c strtok.c strtoul.c \ OSCFLAGS @echo Building OS-dependent module @echo If you get No such file error messages for files x509.h, ssl.h, @echo pem.h, buffer.h, bio.h, and crypto.h, that means that OpenSSL @echo is not installed on your system. Either install OpenSSL first @echo or build with command: make `$(CAT) OSTYPE` SSLTYPE=none `$(CAT) CCTYPE` -c `$(CAT) CFLAGS` `$(CAT) OSCFLAGS` -c osdep.c osdep.c: osdepbas.c osdepckp.c osdeplog.c osdepssl.c $(CAT) osdepbas.c osdepckp.c osdeplog.c osdepssl.c > osdep.c osdepbas.c: @echo osdepbas.c not found...try make clean and new make @false osdepckp.c: @echo osdepckp.c not found...try make clean and new make @false osdeplog.c: @echo osdeplog.c not found...try make clean and new make @false osdepssl.c: @echo osdepssl.c not found...try make clean and new make @false nfstest.c: @echo nfstest.c not found...try make clean and new make @false siglocal.c: @echo siglocal.c not found...try make clean and new make @false crexcl.c: @echo crexcl.c not found...do make clean and new make @false ip_unix.c: @echo ip_unix.c not found...do make clean and new make @false os_sol.h: sh -c '(strings /lib/libc.a | grep getpassphrase > /dev/null) && $(LN) os_soln.h os_sol.h || $(LN) os_solo.h os_sol.h' # Once-only environment setup once: onceenv ckp$(PASSWDTYPE) ssl$(SSLTYPE) osdep.c onceenv: @echo Once-only environment setup... echo $(CC) > CCTYPE echo $(BASECFLAGS) '$(EXTRACFLAGS)' > CFLAGS echo -DCREATEPROTO=$(CREATEPROTO) -DEMPTYPROTO=$(EMPTYPROTO) \ -DMAILSPOOL=\"$(MAILSPOOL)\" \ -DANONYMOUSHOME=\"$(MAILSPOOL)/anonymous\" \ -DACTIVEFILE=\"$(ACTIVEFILE)\" -DNEWSSPOOL=\"$(NEWSSPOOL)\" \ -DRSHPATH=\"$(RSHPATH)\" -DLOCKPGM=\"$(LOCKPGM)\" > OSCFLAGS echo $(BASELDFLAGS) $(EXTRALDFLAGS) > LDFLAGS echo "$(ARRC) $(ARCHIVE) $(BINARIES);$(RANLIB) $(ARCHIVE)" > ARCHIVE echo $(OS) > OSTYPE ./drivers $(EXTRADRIVERS) $(DEFAULTDRIVERS) dummy ./mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS) $(LN) os_$(OS).h osdep.h $(LN) os_$(OS).c osdepbas.c $(LN) log_$(LOGINPW).c osdeplog.c $(LN) sig_$(SIGTYPE).c siglocal.c $(LN) crx_$(CRXTYPE).c crexcl.c $(LN) ip$(IP)_unix.c ip_unix.c sh -c '(test -f /usr/include/sys/statvfs.h -a $(OS) != sc5 -a $(OS) != sco) && $(LN) nfstnew.c nfstest.c || $(LN) nfstold.c nfstest.c' # Password checkers ckpafs: # AFS @echo AFS password authentication echo $(AFSCFLAGS) >> OSCFLAGS # echo $(AFSLDFLAGS) >> LDFLAGS # Note: Steve Roseman says that AFS libraries have to be lunk before SSL echo $(AFSLDFLAGS) `$(CAT) LDFLAGS` > LDFLAGS.tmp mv LDFLAGS.tmp LDFLAGS $(LN) ckp_afs.c osdepckp.c ckpdce: # DCE @echo DCE password authentication echo $(DCECFLAGS) >> OSCFLAGS echo $(DCELDFLAGS) >> LDFLAGS $(LN) ckp_dce.c osdepckp.c ckpgss: # Kerberos V (must have gss EXTRAAUTHENTICATOR as well) @echo Kerberos V password authentication $(LN) ckp_gss.c osdepckp.c ckpnul: # NUL authenticator (disables all plaintext authentication) @echo Plaintext authentication prohibited echo " mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 1);" >> linkage.c $(LN) ckp_nul.c osdepckp.c ckppam: # Pluggable Authentication Modules authenticator @echo PAM password authentication echo $(PAMLDFLAGS) >> LDFLAGS $(LN) ckp_pam.c osdepckp.c ckppmb: # Broken (e.g. SUN) Pluggable Authentication Modules authenticator @echo Broken PAM password authentication echo $(PAMLDFLAGS) >> LDFLAGS $(LN) ckp_pmb.c osdepckp.c ckpstd: # Port standard @echo Standard password authentication $(LN) ckp_$(CHECKPW).c osdepckp.c ckptwo: # Something plus standard @echo $(CHECKPWALT) password authentication first, then standard $(CAT) ckp_1st.c ckp_$(CHECKPWALT).c ckp_2nd.c ckp_$(CHECKPW).c \ ckp_3rd.c > osdepckp.c # SSL support sslnone:# No SSL @echo Building without SSL support $(LN) ssl_none.c osdepssl.c sslnopwd: sslunix snopwd sslunix.nopwd: sslnopwd sslsco.nopwd: sslsco snopwd sslunix: sbasic sldunix sslsco: sbasic sldsco sbasic: # UNIX OpenSSL @echo Building with SSL $(LN) ssl_unix.c osdepssl.c echo $(SSLCFLAGS) >> OSCFLAGS echo " ssl_onceonlyinit ();" >> linkage.c snopwd: # Plaintext disable @echo Building with SSL and plaintext passwords disabled unless SSL/TLS echo " mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);" >> linkage.c sldunix:# Normal UNIX SSL load flags echo $(SSLLDFLAGS) >> LDFLAGS sldsco: # SCO SSL load flags # Note: Tim Rice says that SSL has to be lunk before other libraries on SCO. echo $(SSLLDFLAGS) `cat LDFLAGS` > LDFLAGS.tmp mv LDFLAGS.tmp LDFLAGS # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/Makefile.gss000066400000000000000000000020301137544547100240440ustar00rootroot00000000000000# Program: GSSAPI makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 4 March 2003 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Extended flags needed for additional authenticators. You may need to modify. GSSDIR=/usr/local GSSCFLAGS= -I$(GSSDIR)/include -DGSS_C_NT_HOSTBASED_SERVICE=gss_nt_service_name GSSOLDLDFLAGS= -L$(GSSDIR)/lib -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err GSSNEWLDFLAGS= -L$(GSSDIR)/lib -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err gss: # GSSAPI Kerberos V flags echo $(GSSCFLAGS) >> OSCFLAGS sh -c '(test -f $(GSSDIR)/lib/libk5crypto.a) && echo $(GSSNEWLDFLAGS) || echo $(GSSOLDLDFLAGS)' >> LDFLAGS echo "#include \"kerb_mit.c\"" >> auths.c tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/Makefile.md5000066400000000000000000000012631137544547100237440ustar00rootroot00000000000000# Program: CRAM-MD5 makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Extended flags needed for additional authenticators. You may need to modify. MD5CFLAGS= -DMD5ENABLE=\"/etc/cram-md5.pwd\" md5: # CRAM-MD5 flags echo $(MD5CFLAGS) >> OSCFLAGS tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_1st.c000066400000000000000000000024141137544547100233270ustar00rootroot00000000000000/* * Program: Dual check password part 1 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ struct passwd *checkpw_alt(struct passwd *pw,char *pass,int argc,char *argv[]); struct passwd *checkpw_std(struct passwd *pw,char *pass,int argc,char *argv[]); /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { struct passwd *ret; /* in case first checker smashes it */ char *user = cpystr (pw->pw_name); if (!(ret = checkpw_alt (pw,pass,argc,argv))) ret = checkpw_std (getpwnam (user),pass,argc,argv); fs_give ((void **) &user); return ret; } /* Redefine alt checker's routine name */ #define checkpw checkpw_alt tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_2nd.c000066400000000000000000000012241137544547100233010ustar00rootroot00000000000000/* * Program: Dual check password part 2 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Undefine routine name */ #undef checkpw /* Redefine std checker's routine name */ #define checkpw checkpw_std tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_3rd.c000066400000000000000000000011141137544547100233040ustar00rootroot00000000000000/* * Program: Dual check password part 3 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Undefine routine name */ #undef checkpw tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_a41.c000066400000000000000000000022741137544547100232110ustar00rootroot00000000000000/* * Program: AIX 4.1 check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { int reenter = 0; char *msg = NIL; char *user = cpystr (pw->pw_name); /* validate password */ struct passwd *ret = (pw && !loginrestrictions (user,S_RLOGIN,NIL,&msg) && !authenticate (user,pass,&reenter,&msg)) ? getpwnam (user) : NIL; /* clean up any message returned */ if (msg) fs_give ((void **) &msg); if (user) fs_give ((void **) &user); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_afs.c000066400000000000000000000031771137544547100234000ustar00rootroot00000000000000/* * Program: AFS check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 22 June 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* AFS cleanup * Accepts: data */ void checkpw_cleanup (void *data) { ktc_ForgetAllTokens (); } /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ #undef INIT #define min AFS_MIN #define max AFS_MAX #include #include struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { char *reason; /* faster validation for POP servers */ if (!strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),"pop")) { struct ktc_encryptionKey key; struct ktc_token token; /* just check the password */ ka_StringToKey (pass,NIL,&key); if (ka_GetAdminToken (pw->pw_name,"","",&key,600,&token,1)) return NIL; } /* check password and get AFS token */ else if (ka_UserAuthenticateGeneral (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG,pw->pw_name,NIL,NIL, pass,0,0,0,&reason)) return NIL; /* arm hook to delete credentials */ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup); return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_bsi.c000066400000000000000000000022441137544547100233760ustar00rootroot00000000000000/* * Program: BSI check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ #include struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { char *s,tmp[MAILTMPLEN]; login_cap_t *lc; /* make service name */ sprintf (tmp,"auth-%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL)); /* validate password */ return ((lc = login_getclass (pw->pw_class)) && (s = login_getstyle (lc,NIL,tmp)) && (auth_response (pw->pw_name,lc->lc_class,s,"response",NIL,"",pass) > 0)) ? pw : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_cyg.c000066400000000000000000000030331137544547100234000ustar00rootroot00000000000000/* * Program: Cygwin check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 25 April 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* This module is against my better judgement. If you want to run an imapd * or ipop[23]d you should use the native NT or W2K ports intead of this * Cygwin port. There is no surety that this module works right or will work * right in the future. */ #undef ERROR #include #include /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ static char *cyg_user = NIL; static HANDLE cyg_hdl = NIL; struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { /* flush last pw-checked user */ if (cyg_user) fs_give ((void **) &cyg_user); /* forbid if UID 0 or SYSTEM uid */ if (!pw->pw_uid || (pw->pw_uid == SYSTEMUID) || ((cyg_hdl = cygwin_logon_user (pw,pass)) == INVALID_HANDLE_VALUE)) return NIL; /* bad UID or password */ /* remember user for this handle */ cyg_user = cpystr (pw->pw_name); return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_dce.c000066400000000000000000000045311137544547100233550ustar00rootroot00000000000000/* * Program: DCE check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ #include #include struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { sec_passwd_rec_t pwr; sec_login_handle_t lhdl; boolean32 rstpwd; sec_login_auth_src_t asrc; error_status_t status; FILE *fd; /* easy case */ if (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] && !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) return pw; /* try DCE password cache file */ if (fd = fopen (PASSWD_OVERRIDE,"r")) { char *usr = cpystr (pw->pw_name); while ((pw = fgetpwent (fd)) && strcmp (usr,pw->pw_name)); fclose (fd); /* finished with cache file */ /* validate cached password */ if (pw && pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] && !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) { fs_give ((void **) &usr); return pw; } if (!pw) pw = getpwnam (usr); fs_give ((void **) &usr); } if (pw) { /* try S-L-O-W DCE... */ sec_login_setup_identity ((unsigned_char_p_t) pw->pw_name, sec_login_no_flags,&lhdl,&status); if (status == error_status_ok) { pwr.key.tagged_union.plain = (idl_char *) pass; pwr.key.key_type = sec_passwd_plain; pwr.pepper = NIL; pwr.version_number = sec_passwd_c_version_none; /* validate password with login context */ sec_login_validate_identity (lhdl,&pwr,&rstpwd,&asrc,&status); if (!rstpwd && (asrc == sec_login_auth_src_network) && (status == error_status_ok)) { sec_login_purge_context (&lhdl,&status); if (status == error_status_ok) return pw; } } } return NIL; /* password validation failed */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_gss.c000066400000000000000000000041611137544547100234150ustar00rootroot00000000000000/* * Program: Kerberos 5 check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 5 June 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { char tmp[MAILTMPLEN]; krb5_context ctx; krb5_timestamp now; krb5_principal service; krb5_creds *crd = (krb5_creds *) memset (fs_get (sizeof (krb5_creds)),0, sizeof (krb5_creds)); struct passwd *ret = NIL; if (*pass) { /* only if password non-empty */ /* make service name */ sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL), tcp_serverhost ()); krb5_init_context (&ctx); /* get a context */ /* get time, client and server principals */ if (!krb5_timeofday (ctx,&now) && !krb5_parse_name (ctx,pw->pw_name,&crd->client) && !krb5_parse_name (ctx,tmp,&service) && !krb5_build_principal_ext (ctx,&crd->server, krb5_princ_realm (ctx,crd->client)->length, krb5_princ_realm (ctx,crd->client)->data, KRB5_TGS_NAME_SIZE,KRB5_TGS_NAME, krb5_princ_realm (ctx,crd->client)->length, krb5_princ_realm (ctx,crd->client)->data, 0)) { /* expire in 3 minutes */ crd->times.endtime = now + (3 * 60); if (!krb5_get_in_tkt_with_password (ctx,NIL,NIL,NIL,NIL,pass,0,crd,0) && !krb5_verify_init_creds (ctx,crd,service,0,0,0)) ret = pw; krb5_free_creds (ctx,crd);/* flush creds and service principal */ krb5_free_principal (ctx,service); } krb5_free_context (ctx); /* don't need context any more */ } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_nul.c000066400000000000000000000015051137544547100234160ustar00rootroot00000000000000/* * Program: Null check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 23 July 1998 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { return NIL; /* always fails */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_os4.c000066400000000000000000000036211137544547100233260ustar00rootroot00000000000000/* * Program: OSF/1 (Digital UNIX) 4 check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Dummy collection routine * Accepts: how long to wait for user * how to run parameter collection * title * number of prompts * prompts * Returns: collection status * * Because Spider Boardman, who wrote SIA, says that it's needed for buggy SIA * mechanisms, that's why. */ static int checkpw_collect (int timeout,int rendition,uchar_t *title, int nprompts,prompt_t *prompts) { switch (rendition) { case SIAONELINER: case SIAINFO: case SIAWARNING: return SIACOLSUCCESS; } return SIACOLABORT; /* another else is bogus */ } /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { int i; char *s; char *name = cpystr (pw->pw_name); char *host = cpystr (tcp_clienthost ()); struct passwd *ret = NIL; /* tie off address */ if (s = strchr (host,' ')) *s = '\0'; if (*host == '[') { /* convert [a.b.c.d] to a.b.c.d */ memmove (host,host+1,i = strlen (host + 2)); host[i] = '\0'; } /* validate password */ if (sia_validate_user (checkpw_collect,argc,argv,host,name,NIL,NIL,NIL,pass) == SIASUCCESS) ret = getpwnam (name); fs_give ((void **) &name); fs_give ((void **) &host); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_pam.c000066400000000000000000000071561137544547100234050ustar00rootroot00000000000000/* * Program: Pluggable Authentication Modules login services * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 21 June 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #ifdef MAC_OSX_KLUDGE /* why can't Apple be compatible? */ #include #else #include #endif struct checkpw_cred { char *uname; /* user name */ char *pass; /* password */ }; /* PAM conversation function * Accepts: number of messages * vector of messages * pointer to response return * application data * Returns: PAM_SUCCESS if OK, response vector filled in, else PAM_CONV_ERR */ static int checkpw_conv (int num_msg,const struct pam_message **msg, struct pam_response **resp,void *appdata_ptr) { int i; struct checkpw_cred *cred = (struct checkpw_cred *) appdata_ptr; struct pam_response *reply = fs_get (sizeof (struct pam_response) * num_msg); for (i = 0; i < num_msg; i++) switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* assume want user name */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = cpystr (cred->uname); break; case PAM_PROMPT_ECHO_OFF: /* assume want password */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = cpystr (cred->pass); break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = NULL; break; default: /* unknown message style */ fs_give ((void **) &reply); return PAM_CONV_ERR; } *resp = reply; return PAM_SUCCESS; } /* PAM cleanup * Accepts: handle */ static void checkpw_cleanup (pam_handle_t *hdl) { #if 0 /* see checkpw() for why this is #if 0 */ pam_close_session (hdl,NIL); /* close session [uw]tmp */ #endif pam_setcred (hdl,PAM_DELETE_CRED); pam_end (hdl,PAM_SUCCESS); } /* Server log in * Accepts: user name string * password string * Returns: T if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { pam_handle_t *hdl; struct pam_conv conv; struct checkpw_cred cred; conv.conv = &checkpw_conv; conv.appdata_ptr = &cred; cred.uname = pw->pw_name; cred.pass = pass; if ((pam_start ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL), pw->pw_name,&conv,&hdl) != PAM_SUCCESS) || (pam_set_item (hdl,PAM_RHOST,tcp_clientaddr ()) != PAM_SUCCESS) || (pam_authenticate (hdl,NIL) != PAM_SUCCESS) || (pam_acct_mgmt (hdl,NIL) != PAM_SUCCESS) || (pam_setcred (hdl,PAM_ESTABLISH_CRED) != PAM_SUCCESS)) { /* clean up */ pam_setcred (hdl,PAM_DELETE_CRED); pam_end (hdl,PAM_AUTH_ERR); /* failed */ return NIL; } #if 0 /* * Some people have reported that this causes a SEGV in strncpy() from * pam_unix.so.1 */ /* * This pam_open_session() call is inconsistant with how we handle other * platforms, where we don't write [uw]tmp records. However, unlike our * code on other platforms, pam_acct_mgmt() will check those records for * inactivity and deny the authentication. */ pam_open_session (hdl,NIL); /* make sure account doesn't go inactive */ #endif /* arm hook to delete credentials */ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup); mail_parameters (NIL,SET_LOGOUTDATA,(void *) hdl); return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_pmb.c000066400000000000000000000066771137544547100234150ustar00rootroot00000000000000/* * Program: Pluggable Authentication Modules login services, buggy systems * (use this instead of ckp_pam.c on Solaris) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 21 June 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include static char *pam_uname; /* user name */ static char *pam_pass; /* password */ /* PAM conversation function * Accepts: number of messages * vector of messages * pointer to response return * application data * Returns: PAM_SUCCESS if OK, response vector filled in, else PAM_CONV_ERR */ static int checkpw_conv (int num_msg,const struct pam_message **msg, struct pam_response **resp,void *appdata_ptr) { int i; struct pam_response *reply = fs_get (sizeof (struct pam_response) * num_msg); for (i = 0; i < num_msg; i++) switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* assume want user name */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = cpystr (pam_uname); break; case PAM_PROMPT_ECHO_OFF: /* assume want password */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = cpystr (pam_pass); break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = NULL; break; default: /* unknown message style */ fs_give ((void **) &reply); return PAM_CONV_ERR; } *resp = reply; return PAM_SUCCESS; } /* PAM cleanup * Accepts: handle */ static void checkpw_cleanup (pam_handle_t *hdl) { #if 0 /* see checkpw() for why this is #if 0 */ pam_close_session (hdl,NIL); /* close session [uw]tmp */ #endif pam_setcred (hdl,PAM_DELETE_CRED); pam_end (hdl,PAM_SUCCESS); } /* Server log in * Accepts: user name string * password string * Returns: T if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { pam_handle_t *hdl; struct pam_conv conv; conv.conv = &checkpw_conv; pam_uname = pw->pw_name; pam_pass = pass; if ((pam_start ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL), pw->pw_name,&conv,&hdl) != PAM_SUCCESS) || (pam_set_item (hdl,PAM_RHOST,tcp_clientaddr ()) != PAM_SUCCESS) || (pam_authenticate (hdl,NIL) != PAM_SUCCESS) || (pam_acct_mgmt (hdl,NIL) != PAM_SUCCESS) || (pam_setcred (hdl,PAM_ESTABLISH_CRED) != PAM_SUCCESS)) { /* clean up */ pam_setcred (hdl,PAM_DELETE_CRED); pam_end (hdl,PAM_AUTH_ERR); /* failed */ return NIL; } #if 0 /* * Some people have reported that this causes a SEGV in strncpy() from * pam_unix.so.1 */ /* * This pam_open_session() call is inconsistant with how we handle other * platforms, where we don't write [uw]tmp records. However, unlike our * code on other platforms, pam_acct_mgmt() will check those records for * inactivity and deny the authentication. */ pam_open_session (hdl,NIL); /* make sure account doesn't go inactive */ #endif /* arm hook to delete credentials */ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup); mail_parameters (NIL,SET_LOGOUTDATA,(void *) hdl); return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_psx.c000066400000000000000000000066031137544547100234360ustar00rootroot00000000000000/* * Program: POSIX check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 4 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { char tmp[MAILTMPLEN]; struct spwd *sp = NIL; time_t left; time_t now = time (0); struct tm *t = gmtime (&now); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&now); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; /* days since 1/1/1970 local time */ now = ((now /60) + zone) / (60*24); /* non-shadow authentication */ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] || strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) { /* As far as I've been able to determine, here is how the expiration * fields in the shadow authentication data work: * lstchg last password change date if non-negative. If zero, the * user can not log in without changing password. * max number of days a password is valid if positive * warn number of days of password expiration warning * expire date account expires if positive * inact number of days an accout can be inactive (not checked!) * The expiration day is the *last* day that the password or account * is valid. */ /* shadow authentication */ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg && ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) || ((sp->sp_lstchg + sp->sp_max) >= now)) && ((sp->sp_expire <= 0) || (sp->sp_expire >= now)) && sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] && !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) { if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) && ((left = (sp->sp_lstchg + sp->sp_max) - now) <= sp->sp_warn)) { if (left) { sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left); mm_notify (NIL,tmp,NIL); } else mm_notify (NIL,"[ALERT] Password expires today!",WARN); } if ((sp->sp_expire > 0) && ((left = sp->sp_expire - now) < 28)) { if (left) { sprintf (tmp,"[ALERT] Account expires in %ld day(s)",(long) left); mm_notify (NIL,tmp,NIL); } else mm_notify (NIL,"[ALERT] Account expires today!",WARN); } endspent (); /* don't need shadow password data any more */ } else pw = NIL; /* password failed */ } return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_sce.c000066400000000000000000000025641137544547100234000ustar00rootroot00000000000000/* * Program: SecureWare check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 29 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { struct es_passwd *pp; set_auth_parameters (argc,argv); if ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] && !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) || ((pp = getespwnam (pw->pw_name)) && !pp->ufld->fd_lock && !pp->ufld->fd_psw_chg_reqd && pp->ufld->fd_encrypt && pp->ufld->fd_encrypt[0] && pp->ufld->fd_encrypt[1] && !strcmp (pp->ufld->fd_encrypt,bigcrypt (pass,pp->ufld->fd_encrypt)))) endprpwent (); /* don't need shadow password data any more */ else pw = NIL; /* password failed */ return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_sec.c000066400000000000000000000025061137544547100233740ustar00rootroot00000000000000/* * Program: SecureWare check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { struct pr_passwd *pp; set_auth_parameters (argc,argv); if ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] && !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) || ((pp = getprpwnam (pw->pw_name)) && !pp->ufld.fd_lock && pp->ufld.fd_encrypt && pp->ufld.fd_encrypt[0] && pp->ufld.fd_encrypt[1] && !strcmp (pp->ufld.fd_encrypt,bigcrypt (pass,pp->ufld.fd_encrypt)))) endprpwent (); /* don't need shadow password data any more */ else pw = NIL; /* password failed */ return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_ssn.c000066400000000000000000000025621137544547100234270ustar00rootroot00000000000000/* * Program: Secure SUN-OS check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { struct passwd_adjunct *pa; char *user = cpystr (pw->pw_name); /* validate user and password */ struct passwd *ret = ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] && !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) || ((pa = getpwanam (pw->pw_name)) && pa->pwa_passwd && pa->pwa_passwd[0] && pa->pwa_passwd[1] && !strcmp (pa->pwa_passwd,(char *) crypt (pass,pa->pwa_passwd)))) ? getpwnam (user) : NIL; if (user) fs_give ((void **) &user); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_std.c000066400000000000000000000016731137544547100234200ustar00rootroot00000000000000/* * Program: Standard check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { return (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] && !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ? pw : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_sv4.c000066400000000000000000000057111137544547100233370ustar00rootroot00000000000000/* * Program: SVR4 check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { char tmp[MAILTMPLEN]; struct spwd *sp = NIL; time_t left; time_t now = time (0); struct tm *t = gmtime (&now); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&now); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; /* days since 1/1/1970 local time */ now = ((now /60) + zone) / (60*24); /* non-shadow authentication */ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] || strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) { /* As far as I've been able to determine, here is how the expiration * fields in the shadow authentication data work: * lstchg last password change date if non-negative. If zero, the * user can not log in without changing password. * max number of days a password is valid if positive * warn number of days of password expiration warning * The expiration day is the *last* day that the password is valid. */ /* shadow authentication */ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg && ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) || ((sp->sp_lstchg + sp->sp_max) >= now)) && sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] && !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) { if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) && ((left = (sp->sp_lstchg + sp->sp_max) - now) <= sp->sp_warn)) { if (left) { sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left); mm_notify (NIL,tmp,NIL); } else mm_notify (NIL,"[ALERT] Password expires today!",WARN); } endspent (); /* don't need shadow password data any more */ } else pw = NIL; /* password failed */ } return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_svo.c000066400000000000000000000056111137544547100234310ustar00rootroot00000000000000/* * Program: Old SVR4 check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { char tmp[MAILTMPLEN]; struct spwd *sp = NIL; time_t left; time_t now = time (0); struct tm *t = gmtime (&now); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&now); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; /* days since 1/1/1970 local time */ now = ((now /60) + zone) / (60*24); /* non-shadow authentication */ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] || strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) { /* As far as I've been able to determine, here is how the expiration * fields in the shadow authentication data work: * lstchg last password change date if non-negative. If zero, the * user can not log in without changing password. * max number of days a password is valid if positive * The expiration day is the *last* day that the password is valid. */ /* shadow authentication */ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg && ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) || ((sp->sp_lstchg + sp->sp_max) >= now)) && sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] && !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) { if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) && ((left = (sp->sp_lstchg + sp->sp_max) - now) <= 28)) { if (left) { sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left); mm_notify (NIL,tmp,NIL); } else mm_notify (NIL,"[ALERT] Password expires today!",WARN); } endspent (); /* don't need shadow password data any more */ } else pw = NIL; /* password failed */ } return pw; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ckp_ult.c000066400000000000000000000015411137544547100234240ustar00rootroot00000000000000/* * Program: ULTRIX check password * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Check password * Accepts: login passwd struct * password string * argument count * argument vector * Returns: passwd struct if password validated, NIL otherwise */ struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]) { return (authenticate_user (pw,pass,NIL) >= 0) ? pw : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/crx_nfs.c000066400000000000000000000043531137544547100234310ustar00rootroot00000000000000/* * Program: Exclusive create of a file, NFS kludge version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 17 December 1999 * Last Edited: 17 December 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* SUN-OS had an NFS, As kludgy as an albatross; * And everywhere that it was installed, It was a total loss. * -- MRC 9/25/91 */ /* Exclusive create of a file * Accepts: file name * Return: T if success, NIL if failed, -1 if retry */ long crexcl (char *name) { long ret = -1; int i; char hitch[MAILTMPLEN]; struct stat sb; int mask = umask (0); /* build hitching post file name */ sprintf (hitch,"%s.%lu.%d.",name,(unsigned long) time (0),getpid ()); i = strlen (hitch); /* append local host name */ gethostname (hitch + i,(MAILTMPLEN - i) - 1); /* try to get hitching-post file */ if ((i = open (hitch,O_WRONLY|O_CREAT|O_EXCL,(int) lock_protection)) >= 0) { close (i); /* close the hitching-post */ /* Note: link() may return an error even if it actually succeeded. So we * always check for success via the link count, and ignore the error if * the link count is right. */ /* tie hitching-post to lock */ i = link (hitch,name) ? errno : 0; /* success if link count now 2 */ if (!stat (hitch,&sb) && (sb.st_nlink == 2)) ret = LONGT; else if (i == EPERM) { /* links not allowed? */ /* Probably a FAT filesystem on Linux. It can't be NFS, so try creating * the lock file directly. */ if ((i = open (name,O_WRONLY|O_CREAT|O_EXCL,(int)lock_protection)) >= 0){ close (i); /* close the file */ ret = LONGT; /* success */ } /* fail unless error is EEXIST */ else if (errno != EEXIST) ret = NIL; } unlink (hitch); /* flush hitching post */ } /* fail unless error is EEXIST */ else if (errno != EEXIST) ret = NIL; umask (mask); /* restore previous mask */ return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/crx_std.c000066400000000000000000000017671137544547100234430ustar00rootroot00000000000000/* * Program: Exclusive create of a file * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 17 December 1999 * Last Edited: 17 Devember 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exclusive create of a file * Accepts: file name * Return: T if success, NIL if failed, -1 if retry */ long crexcl (char *name) { int i; int mask = umask (0); long ret = LONGT; /* try to get the lock */ if ((i = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) lock_protection)) < 0) ret = (errno == EEXIST) ? -1 : NIL; else close (i); /* made the file, now close it */ umask (mask); /* restore previous mask */ return LONGT; /* success */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/drivers000077500000000000000000000014061137544547100232230ustar00rootroot00000000000000#!/bin/sh # # Program: Driver Linkage Generator # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 October 1989 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Erase old driver linkage rm -f linkage.[ch] # Now define the new list for driver do echo "extern DRIVER "$driver"driver;" >> linkage.h echo " mail_link (&"$driver"driver); /* link in the $driver driver */" | cat >> linkage.c done tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/dummy.c000066400000000000000000000531011137544547100231150ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 10 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include "dummy.h" #include "misc.h" #include "mx.h" /* highly unfortunate */ /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level); long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents); long dummy_subscribe (MAILSTREAM *stream,char *mailbox); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ dummy_subscribe, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { /* indeterminate clearbox INBOX */ if (!*s) return &dummydriver; else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { case S_IFREG: case S_IFDIR: return &dummydriver; } /* blackbox INBOX does not exist yet */ else if (!compare_cstring (name,"INBOX")) return &dummydriver; } return NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = dummy_file ((char *) value,"INBOX"); break; } return ret; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i; if (!pat || !*pat) { /* empty pattern? */ if (dummy_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'/')) *++s = '\0'; else test[0] = '\0'; dummy_listed (stream,'/',test,LATT_NOSELECT,NIL); } } /* get canonical form of name */ else if (dummy_canonicalize (test,ref,pat)) { /* found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test,i = s - test); file[i] = '\0'; /* tie off */ } else strcpy (file,test); /* use just that name then */ if (s = strrchr (file,'/')){/* find directory name */ *++s = '\0'; /* found, tie off at that point */ s = file; } /* silly case */ else if ((file[0] == '~') || (file[0] == '#')) s = file; /* do the work */ dummy_list_work (stream,s,test,contents,0); /* always an INBOX */ if (pmatch ("INBOX",ucase (test))) dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents); } } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { dummy_scan (stream,ref,pat,NIL); } /* Dummy list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { void *sdb = NIL; char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN]; int showuppers = pat[strlen (pat) - 1] == '%'; /* get canonical form of name */ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do if (*s != '{') { if (!compare_cstring (s,"INBOX") && pmatch ("INBOX",ucase (strcpy (tmp,test)))) mm_lsub (stream,NIL,s,LATT_NOINFERIORS); else if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL); else while (showuppers && (t = strrchr (s,'/'))) { *t = '\0'; /* tie off the name */ if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,LATT_NOSELECT); } } while (s = sm_read (&sdb)); /* until no more subscriptions */ } /* Dummy subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long dummy_subscribe (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) #if 0 /* disable this temporarily for Netscape */ && ((sbuf.st_mode & S_IFMT) == S_IFREG) #endif ) return sm_subscribe (mailbox); sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* Dummy list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * string to scan * search level */ void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents, long level) { DIR *dp; struct direct *d; struct stat sbuf; int ismx; char tmp[MAILTMPLEN]; /* punt if bogus name */ if (!mailboxdir (tmp,dir,NIL)) return; if (dp = opendir (tmp)) { /* do nothing if can't open directory */ /* list it if not at top-level */ if (!level && dir && pmatch_full (dir,pat,'/')) dummy_listed (stream,'/',dir,LATT_NOSELECT,contents); /* scan directory, ignore . and .. */ ismx = (!stat (strcat (tmp,MXINDEXNAME),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)); if (!dir || dir[strlen (dir) - 1] == '/') while (d = readdir (dp)) if (((d->d_name[0] != '.') || (((int) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL : (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]) && strcmp (d->d_name+1,MXINDEXNAME+2))))) && (strlen (d->d_name) <= NETMAXMBX)) { /* see if name is useful */ if (dir) sprintf (tmp,"%s%s",dir,d->d_name); else strcpy (tmp,d->d_name); /* make sure useful and can get info */ if ((pmatch_full (tmp,pat,'/') || pmatch_full (strcat (tmp,"/"),pat,'/') || dmatch (tmp,pat,'/')) && mailboxdir (tmp,dir,d->d_name) && tmp[0] && !stat (tmp,&sbuf)) { /* now make name we'd return */ if (dir) sprintf (tmp,"%s%s",dir,d->d_name); else strcpy (tmp,d->d_name); /* only interested in file type */ switch (sbuf.st_mode & S_IFMT) { case S_IFDIR: /* directory? */ if (pmatch_full (tmp,pat,'/')) { if (!dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents)) break; strcat (tmp,"/"); /* set up for dmatch call */ } /* try again with trailing / */ else if (pmatch_full (strcat (tmp,"/"),pat,'/') && !dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents)) break; if (dmatch (tmp,pat,'/') && (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) dummy_list_work (stream,tmp,pat,contents,level+1); break; case S_IFREG: /* ordinary name */ /* ignore all-digit names from mx */ /* Must use ctime for systems that don't update mtime properly */ if (!(ismx && mx_select (d)) && pmatch_full (tmp,pat,'/') && compare_cstring (tmp,"INBOX")) dummy_listed (stream,'/',tmp,LATT_NOINFERIORS + ((sbuf.st_size && (sbuf.st_atime < sbuf.st_ctime))? LATT_MARKED : LATT_UNMARKED),contents); break; } } } closedir (dp); /* all done, flush directory */ } } /* Scan file for contents * Accepts: file name * desired contents * Returns: NIL if contents not found, T if found */ #define BUFSIZE 4*MAILTMPLEN long dummy_scan_contents (char *name,char *contents,unsigned long csiz, unsigned long fsiz) { int fd; unsigned long ssiz,bsiz; char *buf; /* forget it if can't select or open */ if ((fd = open (name,O_RDONLY,NIL)) >= 0) { /* get buffer including slop */ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1); memset (buf,'\0',ssiz); /* no slop area the first time */ while (fsiz) { /* until end of file */ read (fd,buf+ssiz,bsiz = min (fsiz,BUFSIZE)); if (search ((unsigned char *) buf,bsiz+ssiz, (unsigned char *) contents,csiz)) break; memcpy (buf,buf+BUFSIZE,ssiz); fsiz -= bsiz; /* note that we read that much */ } fs_give ((void **) &buf); /* flush buffer */ close (fd); /* finished with file */ if (fsiz) return T; /* found */ } return NIL; /* not found */ } /* Mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * attributes * contents to search before calling mm_list() * Returns: NIL if should abort hierarchy search, else T (currently always) */ long dummy_listed (MAILSTREAM *stream,char delimiter,char *name, long attributes,char *contents) { DRIVER *d = NIL; unsigned long csiz; struct stat sbuf; char *s,tmp[MAILTMPLEN]; /* don't \NoSelect dir if it has a driver */ if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) && (d != &dummydriver)) attributes &= ~LATT_NOSELECT; if (!contents || /* notify main program */ (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) && (s = mailboxfile (tmp,name)) && (*s || (s = mail_parameters (NIL,GET_INBOXPATH,tmp))) && !stat (s,&sbuf) && (csiz <= sbuf.st_size) && SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size))) mm_list (stream,delimiter,name,attributes); return T; } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; long ret = NIL; /* validate name */ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); MM_LOG (tmp,ERROR); } /* create the name, done if made directory */ else if ((ret = dummy_create_path (stream,tmp,get_dir_protection(mailbox)))&& (s = strrchr (s,'/')) && !s[1]) return T; return ret ? set_mbx_protections (mailbox,tmp) : NIL; } /* Dummy create path * Accepts: mail stream * path name to create * directory mode * Returns: T on success, NIL on failure */ long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN]; int fd; long ret = NIL; char *t = strrchr (path,'/'); int wantdir = t && !t[1]; int mask = umask (0); if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */ if (s = strrchr (path,'/')) { /* found superior to this name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (path,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,path,dirmode)) { umask (mask); /* restore mask */ return NIL; } *s = c; /* restore full name */ } if (wantdir) { /* want to create directory? */ ret = !mkdir (path,(int) dirmode); *t = '/'; /* restore directory delimiter */ } /* create file */ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL, (int) mail_parameters(NIL,GET_MBXPROTECTION,NIL))) >= 0) ret = !close (fd); if (!ret) { /* error? */ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,strerror (errno)); MM_LOG (tmp,ERROR); } umask (mask); /* restore mask */ return ret; /* return status */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { struct stat sbuf; char *s,tmp[MAILTMPLEN]; if (!(s = dummy_file (tmp,mailbox))) { sprintf (tmp,"Can't delete - invalid name: %.80s",s); MM_LOG (tmp,ERROR); } /* no trailing / (workaround BSD kernel bug) */ if ((s = strrchr (tmp,'/')) && !s[1]) *s = '\0'; if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? rmdir (tmp) : unlink (tmp)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } return T; /* return success */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; /* no trailing / allowed */ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || ((s = strrchr (s,'/')) && !s[1])) { sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); MM_LOG (mbx,ERROR); return NIL; } if (s) { /* found superior to destination name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create (stream,mbx)) return NIL; *s = c; /* restore full name */ } /* rename of non-ex INBOX creates dest */ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } return T; /* return success */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { int fd; char err[MAILTMPLEN],tmp[MAILTMPLEN]; struct stat sbuf; /* OP_PROTOTYPE call */ if (!stream) return &dummyproto; err[0] = '\0'; /* no error message yet */ /* can we open the file? */ if (!dummy_file (tmp,stream->mailbox)) sprintf (err,"Can't open this name: %.80s",stream->mailbox); else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { /* no, error unless INBOX */ if (compare_cstring (stream->mailbox,"INBOX")) sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox); } else { /* file had better be empty then */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); if ((sbuf.st_mode & S_IFMT) != S_IFREG) sprintf (err,"Can't open %.80s: not a selectable mailbox", stream->mailbox); else if (sbuf.st_size) /* bogus format if non-empty */ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format", stream->mailbox,tmp); } if (err[0]) { /* if an error happened */ MM_LOG (err,stream->silent ? WARN : ERROR); return NIL; } else if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); /* and certainly no recent ones! */ stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long dummy_ping (MAILSTREAM *stream) { MAILSTREAM *test; if (time (0) >= /* time to do another test? */ ((time_t) (stream->gensym + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) { /* has mailbox format changed? */ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) && (test->dtb != stream->dtb) && (test = mail_open (NIL,stream->mailbox,NIL))) { /* preserve some resources */ test->original_mailbox = stream->original_mailbox; stream->original_mailbox = NIL; test->sparep = stream->sparep; stream->sparep = NIL; test->sequence = stream->sequence; mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */ memcpy (fs_get (sizeof (MAILSTREAM)),stream, sizeof (MAILSTREAM))); /* swap the streams */ memcpy (stream,test,sizeof (MAILSTREAM)); fs_give ((void **) &test);/* flush test now that copied */ /* make sure application knows */ mail_exists (stream,stream->recent = stream->nmsgs); } /* still hasn't changed */ else stream->gensym = time (0); } return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd = -1; int e; char tmp[MAILTMPLEN]; MAILSTREAM *ts = default_proto (T); if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) { if ((e = errno) == ENOENT) /* failed, was it no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox); MM_LOG (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } if (fd >= 0) { /* found file? */ fstat (fd,&sbuf); /* get its size */ close (fd); /* toss out the fd */ if (sbuf.st_size) ts = NIL; /* non-empty file? */ } if (ts) return (*ts->dtb->append) (stream,mailbox,af,data); sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* Dummy mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *dummy_file (char *dst,char *name) { char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? strcpy (dst,sysinbox ()) : s; } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { if (ref) { /* preliminary reference check */ if (*ref == '{') return NIL;/* remote reference not allowed */ else if (!*ref) ref = NIL; /* treat empty reference as no reference */ } switch (*pat) { case '#': /* namespace name */ if (mailboxfile (tmp,pat)) strcpy (tmp,pat); else return NIL; /* unknown namespace */ break; case '{': /* remote names not allowed */ return NIL; case '/': /* rooted name */ case '~': /* home directory name */ if (!ref || (*ref != '#')) {/* non-namespace reference? */ strcpy (tmp,pat); /* yes, ignore */ break; } /* fall through */ default: /* apply reference for all other names */ if (!ref) strcpy (tmp,pat); /* just copy if no namespace */ else if ((*ref != '#') || mailboxfile (tmp,ref)) { /* wants root of name? */ if (*pat == '/') strcpy (strchr (strcpy (tmp,ref),'/'),pat); /* otherwise just append */ else sprintf (tmp,"%s%s",ref,pat); } else return NIL; /* unknown namespace */ } return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/dummy.h000066400000000000000000000021051137544547100231200ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/env_unix.c000066400000000000000000001545561137544547100236350ustar00rootroot00000000000000/* * Program: UNIX environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 13 September 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include /* c-client environment parameters */ static char *myUserName = NIL; /* user name */ static char *myHomeDir = NIL; /* home directory name */ static char *myMailboxDir = NIL;/* mailbox directory name */ static char *myLocalHost = NIL; /* local host name */ static char *myNewsrc = NIL; /* newsrc file name */ static char *mailsubdir = NIL; /* mail subdirectory name */ static char *sysInbox = NIL; /* system inbox name */ static char *newsActive = NIL; /* news active file */ static char *newsSpool = NIL; /* news spool */ /* anonymous home directory */ static char *anonymousHome = NIL; static char *ftpHome = NIL; /* ftp export home directory */ static char *publicHome = NIL; /* public home directory */ static char *sharedHome = NIL; /* shared home directory */ static char *blackBoxDir = NIL; /* black box directory name */ /* black box default home directory */ static char *blackBoxDefaultHome = NIL; static short anonymous = NIL; /* is anonymous */ static short blackBox = NIL; /* is a black box */ static short closedBox = NIL; /* is a closed box */ static short restrictBox = NIL; /* is a restricted box */ static short has_no_life = NIL; /* is a cretin with no life */ /* flock() emulator is a no-op */ static short disableFcntlLock = NIL; static short hideDotFiles = NIL;/* hide files whose names start with . */ /* advertise filesystem root */ static short advertisetheworld = NIL; /* only advertise own mailboxes and #shared */ static short limitedadvertise = NIL; /* disable automatic shared namespaces */ static short noautomaticsharedns = NIL; static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ static short netfsstatbug = NIL;/* compensate for broken stat() on network * filesystems (AFS and old NFS). Don't do * this unless you really have to! */ /* allow user config files */ static short allowuserconfig = NIL; /* 1 = disable plaintext, 2 = if not SSL */ static long disablePlaintext = NIL; static long list_max_level = 20;/* maximum level of list recursion */ /* default file protection */ static long mbx_protection = 0600; /* default directory protection */ static long dir_protection = 0700; /* default lock file protection */ static long lock_protection = MANDATORYLOCKPROT; /* default ftp file protection */ static long ftp_protection = 0644; static long ftp_dir_protection = 0755; /* default public file protection */ static long public_protection = 0666; static long public_dir_protection = 0777; /* default shared file protection */ static long shared_protection = 0660; static long shared_dir_protection = 0770; static long locktimeout = 5; /* default lock timeout */ /* default prototypes */ static MAILSTREAM *createProto = NIL; static MAILSTREAM *appendProto = NIL; /* default user flags */ static char *userFlags[NUSERFLAGS] = {NIL}; static NAMESPACE *nslist[3]; /* namespace list */ static int logtry = 3; /* number of server login tries */ /* block notification */ static blocknotify_t mailblocknotify = mm_blocknotify; /* logout function */ static logouthook_t maillogouthook = NIL; /* logout data */ static void *maillogoutdata = NIL; /* Note: setting disableLockWarning means that you assert that the * so-modified copy of this software will NEVER be used: * 1) in conjunction with any software which expects .lock files * 2) to access NFS-mounted files and directories * * Unless both of these conditions apply, then do not set this flag. * Instead, read the FAQ (item 7.10) and either use 1777 protection * on the mail spool, or install mlock. */ /* disable warning if can't make .lock file */ static short disableLockWarning = NIL; /* UNIX namespaces */ /* personal mh namespace */ static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL}; static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf}; /* home namespace */ static NAMESPACE nshome = {"",'/',NIL,&nsmh}; /* UNIX other user namespace */ static NAMESPACE nsunixother = {"~",'/',NIL,NIL}; /* black box other user namespace */ static NAMESPACE nsblackother = {"/",'/',NIL,NIL}; /* public (anonymous OK) namespace */ static NAMESPACE nspublic = {"#public/",'/',NIL,NIL}; /* netnews namespace */ static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic}; /* FTP export namespace */ static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews}; /* shared (no anonymous) namespace */ static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp}; /* world namespace */ static NAMESPACE nsworld = {"/",'/',NIL,&nsshared}; /* only shared and public namespaces */ static NAMESPACE nslimited = {"#shared/",'/',NIL,&nspublic}; #include "write.c" /* include safe writing routines */ #include "crexcl.c" /* include exclusive create */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Get all authenticators */ #include "auths.c" /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_NAMESPACE: ret = (void *) nslist; break; case SET_USERNAME: if (myUserName) fs_give ((void **) &myUserName); myUserName = cpystr ((char *) value); case GET_USERNAME: ret = (void *) myUserName; break; case SET_HOMEDIR: if (myHomeDir) fs_give ((void **) &myHomeDir); myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: if (myLocalHost) fs_give ((void **) &myLocalHost); myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: ret = (void *) myNewsrc; break; case SET_NEWSACTIVE: if (newsActive) fs_give ((void **) &newsActive); newsActive = cpystr ((char *) value); case GET_NEWSACTIVE: ret = (void *) newsActive; break; case SET_NEWSSPOOL: if (newsSpool) fs_give ((void **) &newsSpool); newsSpool = cpystr ((char *) value); case GET_NEWSSPOOL: ret = (void *) newsSpool; break; case SET_ANONYMOUSHOME: if (anonymousHome) fs_give ((void **) &anonymousHome); anonymousHome = cpystr ((char *) value); case GET_ANONYMOUSHOME: if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME); ret = (void *) anonymousHome; break; case SET_FTPHOME: if (ftpHome) fs_give ((void **) &ftpHome); ftpHome = cpystr ((char *) value); case GET_FTPHOME: ret = (void *) ftpHome; break; case SET_PUBLICHOME: if (publicHome) fs_give ((void **) &publicHome); publicHome = cpystr ((char *) value); case GET_PUBLICHOME: ret = (void *) publicHome; break; case SET_SHAREDHOME: if (sharedHome) fs_give ((void **) &sharedHome); sharedHome = cpystr ((char *) value); case GET_SHAREDHOME: ret = (void *) sharedHome; break; case SET_SYSINBOX: if (sysInbox) fs_give ((void **) &sysInbox); sysInbox = cpystr ((char *) value); case GET_SYSINBOX: ret = (void *) sysInbox; break; case SET_LISTMAXLEVEL: list_max_level = (long) value; case GET_LISTMAXLEVEL: ret = (void *) list_max_level; break; case SET_MBXPROTECTION: mbx_protection = (long) value; case GET_MBXPROTECTION: ret = (void *) mbx_protection; break; case SET_DIRPROTECTION: dir_protection = (long) value; case GET_DIRPROTECTION: ret = (void *) dir_protection; break; case SET_LOCKPROTECTION: lock_protection = (long) value; case GET_LOCKPROTECTION: ret = (void *) lock_protection; break; case SET_FTPPROTECTION: ftp_protection = (long) value; case GET_FTPPROTECTION: ret = (void *) ftp_protection; break; case SET_PUBLICPROTECTION: public_protection = (long) value; case GET_PUBLICPROTECTION: ret = (void *) public_protection; break; case SET_SHAREDPROTECTION: shared_protection = (long) value; case GET_SHAREDPROTECTION: ret = (void *) shared_protection; break; case SET_FTPDIRPROTECTION: ftp_dir_protection = (long) value; case GET_FTPDIRPROTECTION: ret = (void *) ftp_dir_protection; break; case SET_PUBLICDIRPROTECTION: public_dir_protection = (long) value; case GET_PUBLICDIRPROTECTION: ret = (void *) public_dir_protection; break; case SET_SHAREDDIRPROTECTION: shared_dir_protection = (long) value; case GET_SHAREDDIRPROTECTION: ret = (void *) shared_dir_protection; break; case SET_LOCKTIMEOUT: locktimeout = (long) value; case GET_LOCKTIMEOUT: ret = (void *) locktimeout; break; case SET_DISABLEFCNTLLOCK: disableFcntlLock = value ? T : NIL; case GET_DISABLEFCNTLLOCK: ret = (void *) (disableFcntlLock ? VOIDT : NIL); break; case SET_LOCKEACCESERROR: disableLockWarning = value ? NIL : T; case GET_LOCKEACCESERROR: ret = (void *) (disableLockWarning ? NIL : VOIDT); break; case SET_HIDEDOTFILES: hideDotFiles = value ? T : NIL; case GET_HIDEDOTFILES: ret = (void *) (hideDotFiles ? VOIDT : NIL); break; case SET_DISABLEPLAINTEXT: disablePlaintext = (long) value; case GET_DISABLEPLAINTEXT: ret = (void *) disablePlaintext; break; case SET_CHROOTSERVER: closedBox = value ? T : NIL; case GET_CHROOTSERVER: ret = (void *) (closedBox ? VOIDT : NIL); break; case SET_ADVERTISETHEWORLD: advertisetheworld = value ? T : NIL; case GET_ADVERTISETHEWORLD: ret = (void *) (advertisetheworld ? VOIDT : NIL); break; case SET_LIMITEDADVERTISE: limitedadvertise = value ? T : NIL; case GET_LIMITEDADVERTISE: ret = (void *) (limitedadvertise ? VOIDT : NIL); break; case SET_DISABLEAUTOSHAREDNS: noautomaticsharedns = value ? T : NIL; case GET_DISABLEAUTOSHAREDNS: ret = (void *) (noautomaticsharedns ? VOIDT : NIL); break; case SET_DISABLE822TZTEXT: no822tztext = value ? T : NIL; case GET_DISABLE822TZTEXT: ret = (void *) (no822tztext ? VOIDT : NIL); break; case SET_USERHASNOLIFE: has_no_life = value ? T : NIL; case GET_USERHASNOLIFE: ret = (void *) (has_no_life ? VOIDT : NIL); break; case SET_NETFSSTATBUG: netfsstatbug = value ? T : NIL; case GET_NETFSSTATBUG: ret = (void *) (netfsstatbug ? VOIDT : NIL); break; case SET_BLOCKNOTIFY: mailblocknotify = (blocknotify_t) value; case GET_BLOCKNOTIFY: ret = (void *) mailblocknotify; break; case SET_LOGOUTHOOK: maillogouthook = (logouthook_t) value; case GET_LOGOUTHOOK: ret = maillogouthook; break; case SET_LOGOUTDATA: maillogoutdata = (void *) value; case GET_LOGOUTDATA: ret = maillogoutdata; } return ret; } /* Write current time * Accepts: destination string * optional format of day-of-week prefix * format of date and time * flag whether to append symbolic timezone */ static void do_date (char *date,char *prefix,char *fmt,int suffix) { time_t tn = time (0); struct tm *t = gmtime (&tn); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&tn); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; if (prefix) { /* want day of week? */ sprintf (date,prefix,days[t->tm_wday]); date += strlen (date); /* make next sprintf append */ } /* output the date */ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); /* append timezone suffix if desired */ if (suffix) rfc822_timezone (date,(void *) t); } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d", no822tztext ? NIL : T); } /* Write current time in fixed-width RFC 822 format * Accepts: destination string */ void rfc822_fixed_date (char *date) { do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL); } /* Initialize server * Accepts: server name for syslog or NIL * /etc/services service name or NIL * alternate /etc/services service name or NIL * clock interrupt handler * kiss-of-death interrupt handler * hangup interrupt handler * termination interrupt handler */ void server_init (char *server,char *service,char *sslservice, void *clkint,void *kodint,void *hupint,void *trmint) { /* only do this if for init call */ if (server && service && sslservice) { int mask; long port; struct servent *sv; /* set server name in syslog */ openlog (server,LOG_PID,LOG_MAIL); fclose (stderr); /* possibly save a process ID */ dorc (NIL,NIL); /* do systemwide configuration */ /* Use SSL if SSL service, or if server starts with "s" and not service */ if (((port = tcp_serverport ()) >= 0)) { if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port))) syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ()); else if ((sv = getservbyname (sslservice,"tcp")) && (port == ntohs (sv->s_port))) { syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice, tcp_clientaddr ()); ssl_server_init (server); } else { /* not service or SSL service port */ syslog (LOG_DEBUG,"port %ld service init from %s",port, tcp_clientaddr ()); if (*server == 's') ssl_server_init (server); } } switch (mask = umask (022)){/* check old umask */ case 0: /* definitely unreasonable */ case 022: /* don't need to change it */ break; default: /* already was a reasonable value */ umask (mask); /* so change it back */ } } arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */ arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */ arm_signal (SIGHUP,hupint); /* prepare for hangup */ arm_signal (SIGTERM,trmint); /* prepare for termination */ } /* Wait for stdin input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long server_input_wait (long seconds) { fd_set rfd,efd; struct timeval tmo; FD_ZERO (&rfd); FD_ZERO (&efd); FD_SET (0,&rfd); FD_SET (0,&efd); tmo.tv_sec = seconds; tmo.tv_usec = 0; return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL; } /* Return UNIX password entry for user name * Accepts: user name string * Returns: password entry * * Tries all-lowercase form of user name if given user name fails */ static struct passwd *pwuser (unsigned char *user) { unsigned char *s; struct passwd *pw = getpwnam (user); if (!pw) { /* failed, see if any uppercase characters */ for (s = user; *s && !isupper (*s); s++); if (*s) { /* yes, try all lowercase form */ pw = getpwnam (s = lcase (cpystr (user))); fs_give ((void **) &s); } } return pw; } /* Validate password for user name * Accepts: user name string * password string * argument count * argument vector * Returns: password entry if validated * * Tries password+1 if password fails and starts with space */ static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[]) { char *s; struct passwd *pw; struct passwd *ret = NIL; if (auth_md5.server) { /* using CRAM-MD5 authentication? */ if (s = auth_md5_pwd (user)) { if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1))) ret = pwuser (user); /* validated, get passwd entry for user */ memset (s,0,strlen (s)); /* erase sensitive information */ fs_give ((void **) &s); } } else if (pw = pwuser (user)) {/* can get user? */ s = cpystr (pw->pw_name); /* copy returned name in case we need it */ if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) && (*pwd == ' ') && pwd[1] && (ret = pwuser (s))) ret = checkpw (pw,pwd+1,argc,argv); fs_give ((void **) &s); /* don't need copy of name any more */ } return ret; } /* Server log in * Accepts: user name string * password string * authenticating user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[]) { struct passwd *pw = NIL; int level = LOG_NOTICE; char *err = "failed"; /* cretins still haven't given up */ if ((strlen (user) >= NETMAXUSER) || (authuser && (strlen (authuser) >= NETMAXUSER))) { level = LOG_ALERT; /* escalate this alert */ err = "SYSTEM BREAK-IN ATTEMPT"; logtry = 0; /* render this session useless */ } else if (logtry-- <= 0) err = "excessive login failures"; else if (disablePlaintext) err = "disabled"; else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv); else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user); if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T; syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err, user,(authuser && *authuser) ? authuser : user,tcp_clienthost ()); sleep (3); /* slow down possible cracker */ return NIL; } /* Authenticated server log in * Accepts: user name string * authenticating user name string * argument count * argument vector * Returns: T if password validated, NIL otherwise */ long authserver_login (char *user,char *authuser,int argc,char *argv[]) { return pw_login (pwuser (user),authuser,user,NIL,argc,argv); } /* Log in as anonymous daemon * Accepts: argument count * argument vector * Returns: T if successful, NIL if error */ long anonymous_login (int argc,char *argv[]) { /* log in Mr. A. N. Onymous */ return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL, (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL), argc,argv); } /* Finish log in and environment initialization * Accepts: passwd struct for loginpw() * optional authentication user name * user name (NIL for anonymous) * home directory (NIL to use directory from passwd struct) * argument count * argument vector * Returns: T if successful, NIL if error */ long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc, char *argv[]) { struct group *gr; char **t; long ret = NIL; if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */ /* make safe copies of user and home */ if (user) user = cpystr (pw->pw_name); home = cpystr (home ? home : pw->pw_dir); /* authorization ID .NE. authentication ID? */ if (user && auser && *auser && compare_cstring (auser,user)) { /* scan list of mail administrators */ if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret) if (!compare_cstring (auser,*t++)) ret = pw_login (pw,NIL,user,home,argc,argv); syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s", ret ? "Admin" : "Failed",auser,user,tcp_clienthost ()); } else if (closedBox) { /* paranoid site, lock out other directories */ if (chdir (home) || chroot (home)) syslog (LOG_NOTICE|LOG_AUTH, "Login %s failed: unable to set chroot=%.80s host=%.80s", pw->pw_name,home,tcp_clienthost ()); else if (loginpw (pw,argc,argv)) ret = env_init (user,NIL); else fatal ("Login failed after chroot"); } /* normal login */ else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) && (ret = env_init (user,home))) chdir (myhomedir ()); fs_give ((void **) &home); /* clean up */ if (user) fs_give ((void **) &user); } endpwent (); /* in case shadow passwords in pw data */ return ret; /* return status */ } /* Initialize environment * Accepts: user name (NIL for anonymous) * home directory name * Returns: T, always */ long env_init (char *user,char *home) { extern MAILSTREAM CREATEPROTO; extern MAILSTREAM EMPTYPROTO; struct passwd *pw; struct stat sbuf; char tmp[MAILTMPLEN]; if (myUserName) fatal ("env_init called twice!"); /* initially nothing in namespace list */ nslist[0] = nslist[1] = nslist[2] = NIL; /* myUserName must be set before dorc() call */ myUserName = cpystr (user ? user : ANONYMOUSUSER); dorc (NIL,NIL); /* do systemwide configuration */ if (!home) { /* closed box server */ /* standard user can only reference home */ if (user) nslist[0] = &nshome; else { /* anonymous user */ nslist[0] = &nsblackother; /* set root */ anonymous = T; /* flag as anonymous */ } myHomeDir = cpystr (""); /* home directory is root */ sysInbox = cpystr ("INBOX");/* make system INBOX */ } else { /* open or black box */ closedBox = NIL; /* definitely not a closed box */ if (user) { /* remember user name and home directory */ if (blackBoxDir) { /* build black box directory name */ sprintf (tmp,"%s/%s",blackBoxDir,myUserName); /* must exist */ if (!((!stat (home = tmp,&sbuf) && (sbuf.st_mode & S_IFDIR)) || (blackBoxDefaultHome && !stat (home = blackBoxDefaultHome,&sbuf) && (sbuf.st_mode & S_IFDIR)))) fatal ("no home"); sysInbox = (char *) fs_get (strlen (home) + 7); /* set system INBOX */ sprintf (sysInbox,"%s/INBOX",home); blackBox = T; /* mark that it's a black box */ /* mbox meaningless if black box */ mail_parameters (NIL,DISABLE_DRIVER,(void *) "mbox"); } nslist[0] = &nshome; /* home namespace */ /* limited advertise namespaces */ if (limitedadvertise) nslist[2] = &nslimited; else if (blackBox) { /* black box namespaces */ nslist[1] = &nsblackother; nslist[2] = &nsshared; } else { /* open box namespaces */ nslist[1] = &nsunixother; nslist[2] = advertisetheworld ? &nsworld : &nsshared; } } else { nslist[2] = &nsftp; /* anonymous user */ sprintf (tmp,"%s/INBOX", home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL)); sysInbox = cpystr (tmp); /* make system INBOX */ anonymous = T; /* flag as anonymous */ } myHomeDir = cpystr (home); /* set home directory */ } if (allowuserconfig) { /* allow user config files */ dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T); dorc (strcat (strcpy (tmp,myHomeDir),"/.imaprc"),NIL); } if (!closedBox && !noautomaticsharedns) { /* #ftp namespace */ if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir); /* #public namespace */ if (!publicHome && (pw = getpwnam ("imappublic"))) publicHome = cpystr (pw->pw_dir); /* #shared namespace */ if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared"))) sharedHome = cpystr (pw->pw_dir); } if (!myLocalHost) mylocalhost (); if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc")); if (!newsActive) newsActive = cpystr (ACTIVEFILE); if (!newsSpool) newsSpool = cpystr (NEWSSPOOL); /* force default prototype to be set */ if (!createProto) createProto = &CREATEPROTO; if (!appendProto) appendProto = &EMPTYPROTO; /* re-do open action to get flags */ (*createProto->dtb->open) (NIL); endpwent (); /* close pw database */ return T; } /* Return my user name * Accepts: pointer to optional flags * Returns: my user name */ char *myusername_full (unsigned long *flags) { struct passwd *pw; struct stat sbuf; char *s; unsigned long euid; char *ret = UNLOGGEDUSER; /* no user name yet and not root? */ if (!myUserName && (euid = geteuid ())) { /* yes, look up getlogin() user name or EUID */ if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) && (pw = getpwnam (s)) && (pw->pw_uid == euid)) || (pw = getpwuid (euid))) env_init (pw->pw_name, ((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ? s : pw->pw_dir); else fatal ("Unable to look up user name"); } if (myUserName) { /* logged in? */ if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN; ret = myUserName; /* return user name */ } else if (flags) *flags = MU_NOTLOGGEDIN; return ret; } /* Return my local host name * Returns: my local host name */ char *mylocalhost () { char tmp[MAILTMPLEN]; if (!myLocalHost) { gethostname(tmp,MAILTMPLEN);/* get local host name */ myLocalHost = cpystr (tcp_canonical (tmp)); } return myLocalHost; } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { if (!myHomeDir) myusername ();/* initialize if first time */ return myHomeDir ? myHomeDir : ""; } /* Return my home mailbox name * Returns: my home directory name */ static char *mymailboxdir () { char *home = myhomedir (); /* initialize if first time */ if (!myMailboxDir && myHomeDir) { if (mailsubdir) { char tmp[MAILTMPLEN]; sprintf (tmp,"%s/%s",home,mailsubdir); myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */ } else myMailboxDir = cpystr (home); } return myMailboxDir ? myMailboxDir : ""; } /* Return system standard INBOX * Accepts: buffer string */ char *sysinbox () { char tmp[MAILTMPLEN]; if (!sysInbox) { /* initialize if first time */ sprintf (tmp,"%s/%s",MAILSPOOL,myusername ()); sysInbox = cpystr (tmp); /* system inbox is from mail spool */ } return sysInbox; } /* Return mailbox directory name * Accepts: destination buffer * directory prefix * name in directory * Returns: file name or NIL if error */ char *mailboxdir (char *dst,char *dir,char *name) { char tmp[MAILTMPLEN]; if (dir || name) { /* if either argument provided */ if (dir) { if (strlen (dir) > NETMAXMBX) return NIL; strcpy (tmp,dir); /* write directory prefix */ } else tmp[0] = '\0'; /* otherwise null string */ if (name) { if (strlen (name) > NETMAXMBX) return NIL; strcat (tmp,name); /* write name in directory */ } /* validate name, return its name */ if (!mailboxfile (dst,tmp)) return NIL; } /* no arguments, wants mailbox directory */ else strcpy (dst,mymailboxdir ()); return dst; /* return the name */ } /* Return mailbox file name * Accepts: destination buffer * mailbox name * Returns: file name or empty string for driver-selected INBOX or NIL if error */ char *mailboxfile (char *dst,char *name) { struct passwd *pw; char *s; if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) || ((anonymous || blackBox || restrictBox || (*name == '#')) && (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~")))) dst = NIL; /* invalid name */ else switch (*name) { /* determine mailbox type based upon name */ case '#': /* namespace name */ /* #ftp/ namespace */ if (((name[1] == 'f') || (name[1] == 'F')) && ((name[2] == 't') || (name[2] == 'T')) && ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5); /* #public/ and #shared/ namespaces */ else if ((((name[1] == 'p') || (name[1] == 'P')) && ((name[2] == 'u') || (name[2] == 'U')) && ((name[3] == 'b') || (name[3] == 'B')) && ((name[4] == 'l') || (name[4] == 'L')) && ((name[5] == 'i') || (name[5] == 'I')) && ((name[6] == 'c') || (name[6] == 'C')) && (name[7] == '/') && (s = publicHome)) || (!anonymous && ((name[1] == 's') || (name[1] == 'S')) && ((name[2] == 'h') || (name[2] == 'H')) && ((name[3] == 'a') || (name[3] == 'A')) && ((name[4] == 'r') || (name[4] == 'R')) && ((name[5] == 'e') || (name[5] == 'E')) && ((name[6] == 'd') || (name[6] == 'D')) && (name[7] == '/') && (s = sharedHome))) sprintf (dst,"%s/%s",s,compare_cstring (name+8,"INBOX") ? name+8 : "INBOX"); else dst = NIL; /* unknown namespace */ break; case '/': /* root access */ if (anonymous) dst = NIL; /* anonymous forbidden to do this */ else if (blackBox) { /* other user access if blackbox */ if (restrictBox & RESTRICTOTHERUSER) dst = NIL; /* see if other user INBOX */ else if ((s = strchr (name+1,'/')) && !compare_cstring (s+1,"INBOX")) { *s = '\0'; /* temporarily tie off string */ sprintf (dst,"%s/%s/INBOX",blackBoxDir,name+1); *s = '/'; /* in case caller cares */ } else sprintf (dst,"%s/%s",blackBoxDir,name+1); } else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ())) dst = NIL; /* restricted and not access to sysinbox */ else strcpy (dst,name); /* unrestricted, copy root name */ break; case '~': /* other user access */ /* bad syntax or anonymous can't win */ if (!*++name || anonymous) dst = NIL; /* ~/ equivalent to ordinary name */ else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1); /* other user forbidden if closed/restricted */ else if (closedBox || (restrictBox & RESTRICTOTHERUSER)) dst = NIL; else if (blackBox) { /* black box form of other user */ /* see if other user INBOX */ if ((s = strchr (name,'/')) && compare_cstring (s+1,"INBOX")) { *s = '\0'; /* temporarily tie off string */ sprintf (dst,"%s/%s/INBOX",blackBoxDir,name); *s = '/'; /* in case caller cares */ } else sprintf (dst,"%s/%s",blackBoxDir,name); } else { /* clear box other user */ /* copy user name */ for (s = dst; *name && (*name != '/'); *s++ = *name++); *s++ = '\0'; /* tie off user name, look up in passwd file */ if ((pw = getpwnam (dst)) && pw->pw_dir) { if (*name) name++; /* skip past the slash */ /* canonicalize case of INBOX */ if (!compare_cstring (name,"INBOX")) name = "INBOX"; /* remove trailing / from directory */ if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0'; /* don't allow ~root/ if restricted root */ if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL; /* build final name w/ subdir if needed */ else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name); else sprintf (dst,"%s/%s",pw->pw_dir,name); } else dst = NIL; /* no such user */ } break; case 'I': case 'i': /* possible INBOX */ if (!compare_cstring (name+1,"NBOX")) { /* if restricted, use INBOX in mailbox dir */ if (anonymous || blackBox || closedBox) sprintf (dst,"%s/INBOX",mymailboxdir ()); else *dst = '\0'; /* otherwise driver selects the name */ break; } /* drop into to ordinary name case */ default: /* ordinary name is easy */ sprintf (dst,"%s/%s",mymailboxdir (),name); break; } return dst; /* return final name */ } /* Dot-lock file locker * Accepts: file name to lock * destination buffer for lock file name * open file description on file name to lock * Returns: T if success, NIL if failure */ long dotlock_lock (char *file,DOTLOCK *base,int fd) { int i = locktimeout * 60; int j,mask,retry,pi[2],po[2]; char *s,tmp[MAILTMPLEN]; struct stat sb; /* flush absurd file name */ if (strlen (file) > 512) return NIL; /* build lock filename */ sprintf (base->lock,"%s.lock",file); /* assume no pipe */ base->pipei = base->pipeo = -1; do { /* make sure not symlink */ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL; /* time out if file older than 5 minutes */ if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0; /* try to create the lock */ switch (retry = crexcl (base->lock)) { case -1: /* OK to retry */ if (!(i%15)) { /* time to notify? */ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...", file,i); MM_LOG (tmp,WARN); } sleep (1); /* wait 1 second before next try */ break; case NIL: /* failure, can't retry */ i = 0; break; case T: /* success, make sure others can break lock */ chmod (base->lock,(int) lock_protection); return LONGT; } } while (i--); /* until out of retries */ if (retry < 0) { /* still returning retry after locktimeout? */ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL; if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) { sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock", (long) (time (0) - sb.st_ctime)); MM_LOG (tmp,WARN); } mask = umask (0); /* want our lock protection */ unlink (base->lock); /* try to remove the old file */ /* seize the lock */ if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) lock_protection)) >= 0) { close (i); /* don't need descriptor any more */ sprintf (tmp,"Mailbox %.80s lock overridden",file); MM_LOG (tmp,NIL); chmod (base->lock,(int) lock_protection); umask (mask); /* restore old umask */ return LONGT; } umask (mask); /* restore old umask */ } if (fd >= 0) switch (errno) { case EACCES: /* protection failure? */ /* make command pipes */ if (!closedBox && !stat (LOCKPGM,&sb) && (pipe (pi) >= 0)) { if (pipe (po) >= 0) { if (!(j = fork ())) { /* make inferior process */ if (!fork ()) { /* make grandchild so it's inherited by init */ char *argv[4]; /* prepare argument vector */ sprintf (tmp,"%d",fd); argv[0] = LOCKPGM; argv[1] = tmp; argv[2] = file; argv[3] = NIL; /* set parent's I/O to my O/I */ dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0); /* close all unnecessary descriptors */ for (j = max (20,max (max (pi[0],pi[1]),max(po[0],po[1]))); j >= 3; --j) if (j != fd) close (j); /* be our own process group */ setpgrp (0,getpid ()); /* now run it */ execv (argv[0],argv); } _exit (1); /* child is done */ } else if (j > 0) { /* reap child; grandchild now owned by init */ grim_pid_reap (j,NIL); /* read response from locking program */ if ((read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) { /* success, record pipes */ base->pipei = pi[0]; base->pipeo = po[1]; /* close child's side of the pipes */ close (pi[1]); close (po[0]); return LONGT; } } close (po[0]); close (po[1]); } close (pi[0]); close (pi[1]); } /* find directory/file delimiter */ if (s = strrchr (base->lock,'/')) { *s = '\0'; /* tie off at directory */ sprintf(tmp, /* generate default message */ "Mailbox vulnerable - directory %.80s must have 1777 protection", base->lock); /* definitely not 1777 if can't stat */ mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777); *s = '/'; /* restore lock name */ if (mask != 1777) { /* default warning if not 1777 */ if (!disableLockWarning) MM_LOG (tmp,WARN); break; } } default: sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s", base->lock,strerror (errno)); if (!disableLockWarning) MM_LOG (tmp,WARN); break; } base->lock[0] = '\0'; /* don't use lock files */ return NIL; } /* Dot-lock file unlocker * Accepts: lock file name * Returns: T if success, NIL if failure */ long dotlock_unlock (DOTLOCK *base) { long ret = LONGT; if (base && base->lock[0]) { if (base->pipei >= 0) { /* if running through a pipe unlocker */ ret = (write (base->pipeo,"+",1) == 1); /* nuke the pipes */ close (base->pipei); close (base->pipeo); } else ret = !unlink (base->lock); } return ret; } /* Lock file name * Accepts: scratch buffer * file name * type of locking operation (LOCK_SH or LOCK_EX) * pointer to return PID of locker * Returns: file descriptor of lock or negative if error */ int lockname (char *lock,char *fname,int op,long *pid) { struct stat sbuf; *pid = 0; /* no locker PID */ return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid); } /* Lock file descriptor * Accepts: file descriptor * lock file name buffer * type of locking operation (LOCK_SH or LOCK_EX) * Returns: file descriptor of lock or negative if error */ int lockfd (int fd,char *lock,int op) { struct stat sbuf; return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL); } /* Lock file name worker * Accepts: lock file name * pointer to stat() buffer * type of locking operation (LOCK_SH or LOCK_EX) * pointer to return PID of locker * Returns: file descriptor of lock or negative if error */ int lock_work (char *lock,void *sb,int op,long *pid) { struct stat lsb,fsb; struct stat *sbuf = (struct stat *) sb; char tmp[MAILTMPLEN]; long i; int fd; int mask = umask (0); if (pid) *pid = 0; /* initialize return PID */ /* make temporary lock file name */ sprintf (lock,"%s/.%lx.%lx",closedBox ? "" : "/tmp", (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino); while (T) { /* until get a good lock */ do switch ((int) chk_notsymlink (lock,&lsb)) { case 1: /* exists just once */ if (((fd = open (lock,O_RDWR,lock_protection)) >= 0) || (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break; case -1: /* name doesn't exist */ fd = open (lock,O_RDWR|O_CREAT|O_EXCL,lock_protection); break; default: /* multiple hard links */ MM_LOG ("hard link to lock name",ERROR); syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock); case 0: /* symlink (already did syslog) */ umask (mask); /* restore old mask */ return -1; /* fail: no lock file */ } while ((fd < 0) && (errno == EEXIST)); if (fd < 0) { /* failed to get file descriptor */ syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock, strerror (errno)); if (!closedBox) { /* more explicit snarl for bad configuration */ if (stat ("/tmp",&lsb)) syslog (LOG_CRIT,"SYSTEM ERROR: no /tmp: %s",strerror (errno)); else if ((lsb.st_mode & 01777) != 01777) MM_LOG ("Can't lock for write: /tmp must have 1777 protection",WARN); } umask (mask); /* restore old mask */ return -1; /* fail: can't open lock file */ } /* non-blocking form */ if (op & LOCK_NB) i = flock (fd,op); else { /* blocking form */ (*mailblocknotify) (BLOCK_FILELOCK,NIL); i = flock (fd,op); (*mailblocknotify) (BLOCK_NONE,NIL); } if (i) { /* failed, get other process' PID */ if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) && (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0)) *pid = i; close (fd); /* failed, give up on lock */ umask (mask); /* restore old mask */ return -1; /* fail: can't lock */ } /* make sure this lock is good for us */ if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) && !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) && (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break; close (fd); /* lock not right, drop fd and try again */ } /* make sure mode OK (don't use fchmod()) */ chmod (lock,(int) lock_protection); umask (mask); /* restore old mask */ return fd; /* success */ } /* Check to make sure not a symlink * Accepts: file name * stat buffer * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links */ long chk_notsymlink (char *name,void *sb) { struct stat *sbuf = (struct stat *) sb; /* name exists? */ if (lstat (name,sbuf)) return -1; /* forbid symbolic link */ if ((sbuf->st_mode & S_IFMT) == S_IFLNK) { MM_LOG ("symbolic link on lock name",ERROR); syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s", name); return NIL; } return (long) sbuf->st_nlink; /* return number of hard links */ } /* Unlock file descriptor * Accepts: file descriptor * lock file name from lockfd() */ void unlockfd (int fd,char *lock) { /* delete the file if no sharers */ if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock); flock (fd,LOCK_UN); /* unlock it */ close (fd); /* close it */ } /* Set proper file protection for mailbox * Accepts: mailbox name * actual file path name * Returns: T, always */ long set_mbx_protections (char *mailbox,char *path) { struct stat sbuf; int mode = (int) mbx_protection; if (*mailbox == '#') { /* possible namespace? */ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) && ((mailbox[2] == 't') || (mailbox[2] == 'T')) && ((mailbox[3] == 'p') || (mailbox[3] == 'P')) && (mailbox[4] == '/')) mode = (int) ftp_protection; else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) && ((mailbox[2] == 'u') || (mailbox[2] == 'U')) && ((mailbox[3] == 'b') || (mailbox[3] == 'B')) && ((mailbox[4] == 'l') || (mailbox[4] == 'L')) && ((mailbox[5] == 'i') || (mailbox[5] == 'I')) && ((mailbox[6] == 'c') || (mailbox[6] == 'C')) && (mailbox[7] == '/')) mode = (int) public_protection; else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) && ((mailbox[2] == 'h') || (mailbox[2] == 'H')) && ((mailbox[3] == 'a') || (mailbox[3] == 'A')) && ((mailbox[4] == 'r') || (mailbox[4] == 'R')) && ((mailbox[5] == 'e') || (mailbox[5] == 'E')) && ((mailbox[6] == 'd') || (mailbox[6] == 'D')) && (mailbox[7] == '/')) mode = (int) shared_protection; } /* if a directory */ if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) { /* set owner search if allow read or write */ if (mode & 0600) mode |= 0100; if (mode & 060) mode |= 010;/* set group search if allow read or write */ if (mode & 06) mode |= 01; /* set world search if allow read or write */ /* preserve directory SGID bit */ if (sbuf.st_mode & S_ISGID) mode |= S_ISGID; } chmod (path,mode); /* set the new protection, ignore failure */ return LONGT; } /* Get proper directory protection * Accepts: mailbox name * Returns: directory mode, always */ long get_dir_protection (char *mailbox) { if (*mailbox == '#') { /* possible namespace? */ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) && ((mailbox[2] == 't') || (mailbox[2] == 'T')) && ((mailbox[3] == 'p') || (mailbox[3] == 'P')) && (mailbox[4] == '/')) return ftp_dir_protection; else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) && ((mailbox[2] == 'u') || (mailbox[2] == 'U')) && ((mailbox[3] == 'b') || (mailbox[3] == 'B')) && ((mailbox[4] == 'l') || (mailbox[4] == 'L')) && ((mailbox[5] == 'i') || (mailbox[5] == 'I')) && ((mailbox[6] == 'c') || (mailbox[6] == 'C')) && (mailbox[7] == '/')) return public_dir_protection; else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) && ((mailbox[2] == 'h') || (mailbox[2] == 'H')) && ((mailbox[3] == 'a') || (mailbox[3] == 'A')) && ((mailbox[4] == 'r') || (mailbox[4] == 'R')) && ((mailbox[5] == 'e') || (mailbox[5] == 'E')) && ((mailbox[6] == 'd') || (mailbox[6] == 'D')) && (mailbox[7] == '/')) return shared_dir_protection; } return dir_protection; } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { myusername (); /* make sure initialized */ /* return default driver's prototype */ return type ? appendProto : createProto; } /* Set up user flags for stream * Accepts: MAIL stream * Returns: MAIL stream with user flags set up */ MAILSTREAM *user_flags (MAILSTREAM *stream) { int i; myusername (); /* make sure initialized */ for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i) if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]); return stream; } /* Return nth user flag * Accepts: user flag number * Returns: flag */ char *default_user_flag (unsigned long i) { myusername (); /* make sure initialized */ return userFlags[i]; } /* Process rc file * Accepts: file name * .mminit flag * Don't even think of using this feature. */ void dorc (char *file,long flag) { int i; char *s,*t,*k,tmp[MAILTMPLEN],tmpx[MAILTMPLEN]; extern MAILSTREAM CREATEPROTO; extern MAILSTREAM EMPTYPROTO; DRIVER *d; FILE *f; /* no file or ill-advised usage */ if ((f = fopen (file ? file : SYSCONFIG,"r")) && (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n')) && (flag || (!strncmp (s,RISKPHRASE,sizeof (RISKPHRASE)-1) && (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))))) do { *t++ = '\0'; /* tie off line, find second space */ if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) { *k++ = '\0'; /* tie off two words */ if (!compare_cstring (s,"set keywords") && !userFlags[0]) { k = strtok (k,", "); /* yes, get first keyword */ /* copy keyword list */ for (i = 0; k && i < NUSERFLAGS; ++i) if (strlen (k) <= MAXUSERFLAG) { if (userFlags[i]) fs_give ((void **) &userFlags[i]); userFlags[i] = cpystr (k); k = strtok (NIL,", "); } if (flag) break; /* found "set keywords" in .mminit */ } else if (!flag) { /* none of these valid in .mminit */ if (myUserName) { /* only valid if logged in */ if (!compare_cstring (s,"set new-folder-format")) { if (!compare_cstring (k,"same-as-inbox")) createProto = ((d = mail_valid (NIL,"INBOX",NIL)) && compare_cstring (d->name,"dummy")) ? ((*d->open) (NIL)) : &CREATEPROTO; else if (!compare_cstring (k,"system-standard")) createProto = &CREATEPROTO; else { /* see if a driver name */ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL); d && compare_cstring (d->name,k); d = d->next); if (d) createProto = (*d->open) (NIL); else { /* duh... */ sprintf (tmpx,"Unknown new folder format in %s: %s",file,k); MM_LOG (tmpx,WARN); } } } if (!compare_cstring (s,"set empty-folder-format")) { if (!compare_cstring (k,"same-as-inbox")) appendProto = ((d = mail_valid (NIL,"INBOX",NIL)) && compare_cstring (d->name,"dummy")) ? ((*d->open) (NIL)) : &EMPTYPROTO; else if (!compare_cstring (k,"system-standard")) appendProto = &EMPTYPROTO; else { /* see if a driver name */ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL); d && compare_cstring (d->name,k); d = d->next); if (d) appendProto = (*d->open) (NIL); else { /* duh... */ sprintf (tmpx,"Unknown empty folder format in %s: %s",file,k); MM_LOG (tmpx,WARN); } } } } if (!compare_cstring (s,"set local-host")) { fs_give ((void **) &myLocalHost); myLocalHost = cpystr (k); } else if (!compare_cstring (s,"set news-active-file")) { fs_give ((void **) &newsActive); newsActive = cpystr (k); } else if (!compare_cstring (s,"set news-spool-directory")) { fs_give ((void **) &newsSpool); newsSpool = cpystr (k); } else if (!compare_cstring (s,"set news-state-file")) { fs_give ((void **) &myNewsrc); myNewsrc = cpystr (k); } else if (!compare_cstring (s,"set ftp-export-directory")) { fs_give ((void **) &ftpHome); ftpHome = cpystr (k); } else if (!compare_cstring (s,"set public-home-directory")) { fs_give ((void **) &publicHome); publicHome = cpystr (k); } else if (!compare_cstring (s,"set shared-home-directory")) { fs_give ((void **) &sharedHome); sharedHome = cpystr (k); } else if (!compare_cstring (s,"set system-inbox")) { fs_give ((void **) &sysInbox); sysInbox = cpystr (k); } else if (!compare_cstring (s,"set mail-subdirectory")) { fs_give ((void **) &mailsubdir); mailsubdir = cpystr (k); } else if (!compare_cstring (s,"set from-widget")) mail_parameters (NIL,SET_FROMWIDGET, compare_cstring (k,"header-only") ? VOIDT : NIL); else if (!compare_cstring (s,"set rsh-command")) mail_parameters (NIL,SET_RSHCOMMAND,(void *) k); else if (!compare_cstring (s,"set rsh-path")) mail_parameters (NIL,SET_RSHPATH,(void *) k); else if (!compare_cstring (s,"set ssh-command")) mail_parameters (NIL,SET_SSHCOMMAND,(void *) k); else if (!compare_cstring (s,"set ssh-path")) mail_parameters (NIL,SET_SSHPATH,(void *) k); else if (!compare_cstring (s,"set tcp-open-timeout")) mail_parameters (NIL,SET_OPENTIMEOUT,(void *) atol (k)); else if (!compare_cstring (s,"set tcp-read-timeout")) mail_parameters (NIL,SET_READTIMEOUT,(void *) atol (k)); else if (!compare_cstring (s,"set tcp-write-timeout")) mail_parameters (NIL,SET_WRITETIMEOUT,(void *) atol (k)); else if (!compare_cstring (s,"set rsh-timeout")) mail_parameters (NIL,SET_RSHTIMEOUT,(void *) atol (k)); else if (!compare_cstring (s,"set ssh-timeout")) mail_parameters (NIL,SET_SSHTIMEOUT,(void *) atol (k)); else if (!compare_cstring (s,"set maximum-login-trials")) mail_parameters (NIL,SET_MAXLOGINTRIALS,(void *) atol (k)); else if (!compare_cstring (s,"set lookahead")) mail_parameters (NIL,SET_LOOKAHEAD,(void *) atol (k)); else if (!compare_cstring (s,"set prefetch")) mail_parameters (NIL,SET_PREFETCH,(void *) atol (k)); else if (!compare_cstring (s,"set close-on-error")) mail_parameters (NIL,SET_CLOSEONERROR,(void *) atol (k)); else if (!compare_cstring (s,"set imap-port")) mail_parameters (NIL,SET_IMAPPORT,(void *) atol (k)); else if (!compare_cstring (s,"set pop3-port")) mail_parameters (NIL,SET_POP3PORT,(void *) atol (k)); else if (!compare_cstring (s,"set uid-lookahead")) mail_parameters (NIL,SET_UIDLOOKAHEAD,(void *) atol (k)); else if (!compare_cstring (s,"set try-ssl-first")) mail_parameters (NIL,SET_TRYSSLFIRST,(void *) atol (k)); else if (!compare_cstring (s,"set mailbox-protection")) mbx_protection = atol (k); else if (!compare_cstring (s,"set directory-protection")) dir_protection = atol (k); else if (!compare_cstring (s,"set lock-protection")) lock_protection = atol (k); else if (!compare_cstring (s,"set ftp-protection")) ftp_protection = atol (k); else if (!compare_cstring (s,"set public-protection")) public_protection = atol (k); else if (!compare_cstring (s,"set shared-protection")) shared_protection = atol (k); else if (!compare_cstring (s,"set ftp-directory-protection")) ftp_dir_protection = atol (k); else if (!compare_cstring (s,"set public-directory-protection")) public_dir_protection = atol (k); else if (!compare_cstring (s,"set shared-directory-protection")) shared_dir_protection = atol (k); else if (!compare_cstring (s,"set dot-lock-file-timeout")) locktimeout = atoi (k); else if (!compare_cstring (s,"set disable-fcntl-locking")) disableFcntlLock = atoi (k); else if (!compare_cstring (s,"set disable-lock-warning")) disableLockWarning = atoi (k); else if (!compare_cstring (s,"set hide-dot-files")) hideDotFiles = atoi (k); else if (!compare_cstring (s,"set list-maximum-level")) list_max_level = atol (k); else if (!compare_cstring (s,"set trust-dns")) mail_parameters (NIL,SET_TRUSTDNS,(void *) atol (k)); else if (!compare_cstring (s,"set sasl-uses-ptr-name")) mail_parameters (NIL,SET_SASLUSESPTRNAME,(void *) atol (k)); else if (!compare_cstring (s,"set network-filesystem-stat-bug")) netfsstatbug = atoi (k); else if (!file) { /* only allowed in system init */ if (!compare_cstring (s,"set black-box-directory") && !blackBoxDir) blackBoxDir = cpystr (k); else if (!compare_cstring(s,"set black-box-default-home-directory")&& blackBoxDir && !blackBoxDefaultHome) blackBoxDefaultHome = cpystr (k); else if (!compare_cstring (s,"set anonymous-home-directory") && !anonymousHome) anonymousHome = cpystr (k); else if (!compare_cstring (s,"set disable-plaintext")) disablePlaintext = atoi (k); else if (!compare_cstring (s,"set allowed-login-attempts")) logtry = atoi (k); else if (!compare_cstring (s,"set chroot-server")) closedBox = atoi (k); else if (!compare_cstring (s,"set restrict-mailbox-access")) { for (k = strtok (k,", "); k; k = strtok (NIL,", ")) { if (!compare_cstring (k,"root")) restrictBox |= RESTRICTROOT; else if (!compare_cstring (k,"otherusers")) restrictBox |= RESTRICTOTHERUSER; else if (!compare_cstring (k,"all")) restrictBox = -1; } } else if (!compare_cstring (s,"set advertise-the-world")) advertisetheworld = atoi (k); else if (!compare_cstring (s,"set limited-advertise")) limitedadvertise = atoi (k); else if (!compare_cstring (s,"set disable-automatic-shared-namespaces")) noautomaticsharedns = atoi (k); else if (!compare_cstring (s,"set allow-user-config")) allowuserconfig = atoi (k); else if (!compare_cstring (s,"set allow-reverse-dns")) mail_parameters (NIL,SET_ALLOWREVERSEDNS,(void *) atol (k)); } } } } while ((s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))); if (f) fclose (f); /* flush the file */ } /* INBOX create function for tmail/dmail use only * Accepts: mail stream * path name buffer, preloaded with driver-dependent path * Returns: T on success, NIL on failure * * This routine is evil and a truly incredible kludge. It is private for * tmail/dmail and is not supported for any other application. */ long path_create (MAILSTREAM *stream,char *path) { long ret; short rsave = restrictBox; restrictBox = NIL; /* can't restrict */ if (blackBox) { /* if black box */ /* toss out driver dependent names */ printf (path,"%s/INBOX",mymailboxdir ()); blackBox = NIL; /* well that's evil - evil is going on */ ret = mail_create (stream,path); blackBox = T; /* restore the box */ } /* easy thing otherwise */ else ret = mail_create (stream,path); restrictBox = rsave; /* restore restrictions */ return ret; } /* Default block notify routine * Accepts: reason for calling * data * Returns: data */ void *mm_blocknotify (int reason,void *data) { void *ret = data; switch (reason) { case BLOCK_SENSITIVE: /* entering sensitive code */ ret = (void *) alarm (0); break; case BLOCK_NONSENSITIVE: /* exiting sensitive code */ if ((unsigned int) data) alarm ((unsigned int) data); break; default: /* ignore all other reasons */ break; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/env_unix.h000066400000000000000000000056731137544547100236350ustar00rootroot00000000000000/* * Program: UNIX environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 22 February 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ typedef struct dotlock_base { char lock[MAILTMPLEN]; int pipei; int pipeo; } DOTLOCK; /* Bits that can be set in restrictBox */ #define RESTRICTROOT 0x1 /* restricted box doesn't allow root */ #define RESTRICTOTHERUSER 0x2 /* restricted box doesn't allow other user */ /* Subscription definitions for UNIX */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s/.mailboxlist",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/.mlbxlsttmp",myhomedir ()) /* dorc() options */ #define RISKPHRASE "I accept the risk" #define SYSCONFIG "/etc/c-client.cf" /* Special users */ #define ANONYMOUSUSER "nobody" /* anonymous user */ #define UNLOGGEDUSER "root" /* unlogged-in user */ #define ADMINGROUP "mailadm" /* mail administrator group */ /* * Attention: all sorcerer's apprentices who think that 0666 is a mistake. * You are wrong. Read the FAQ. Do not meddle in the affairs of wizards, * for they are subtle and quick to anger. */ #define MANDATORYLOCKPROT 0666 /* don't change this */ /* Function prototypes */ #include "env.h" void rfc822_fixed_date (char *date); long env_init (char *user,char *home); char *myusername_full (unsigned long *flags); #define MU_LOGGEDIN 0 #define MU_NOTLOGGEDIN 1 #define MU_ANONYMOUS 2 #define myusername() \ myusername_full (NIL) char *sysinbox (); char *mailboxdir (char *dst,char *dir,char *name); long dotlock_lock (char *file,DOTLOCK *base,int fd); long dotlock_unlock (DOTLOCK *base); int lockname (char *lock,char *fname,int op,long *pid); int lockfd (int fd,char *lock,int op); int lock_work (char *lock,void *sbuf,int op,long *pid); long chk_notsymlink (char *name,void *sbuf); void unlockfd (int fd,char *lock); long set_mbx_protections (char *mailbox,char *path); long get_dir_protection (char *mailbox); MAILSTREAM *user_flags (MAILSTREAM *stream); char *default_user_flag (unsigned long i); void dorc (char *file,long flag); long path_create (MAILSTREAM *stream,char *mailbox); void grim_pid_reap_status (int pid,int killreq,void *status); #define grim_pid_reap(pid,killreq) \ grim_pid_reap_status (pid,killreq,NIL) long safe_write (int fd,char *buf,long nbytes); void *arm_signal (int sig,void *action); struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]); long loginpw (struct passwd *pw,int argc,char *argv[]); long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc, char *argv[]); void *mm_blocknotify (int reason,void *data); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/fdstring.c000066400000000000000000000050171137544547100236050ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "mail.h" #include "osdep.h" #include "misc.h" #include "fdstring.h" /* String driver for fd stringstructs */ static void fd_string_init (STRING *s,void *data,unsigned long size); static char fd_string_next (STRING *s); static void fd_string_setpos (STRING *s,unsigned long i); STRINGDRIVER fd_string = { fd_string_init, /* initialize string structure */ fd_string_next, /* get next byte in string structure */ fd_string_setpos /* set position in string structure */ }; /* Initialize string structure for fd stringstruct * Accepts: string structure * pointer to string * size of string */ static void fd_string_init (STRING *s,void *data,unsigned long size) { FDDATA *d = (FDDATA *) data; s->data = (void *) d->fd; /* note fd */ s->data1 = d->pos; /* note file offset */ s->size = size; /* note size */ s->curpos = s->chunk = d->chunk; s->chunksize = (unsigned long) d->chunksize; s->offset = 0; /* initial position */ /* and size of data */ s->cursize = min (s->chunksize,size); /* move to that position in the file */ lseek (d->fd,d->pos,L_SET); read (d->fd,s->chunk,(size_t) s->cursize); } /* Get next character from fd stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ static char fd_string_next (STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for fd stringstruct * Accepts: string structure * new position */ static void fd_string_setpos (STRING *s,unsigned long i) { if (i > s->size) i = s->size; /* don't permit setting beyond EOF */ s->offset = i; /* set new offset */ s->curpos = s->chunk; /* reset position */ /* set size of data */ if (s->cursize = min (s->chunksize,SIZE (s))) { /* move to that position in the file */ lseek ((int) s->data,s->data1 + s->offset,L_SET); read ((int) s->data,s->curpos,(size_t) s->cursize); } } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/fdstring.h000066400000000000000000000015131137544547100236070ustar00rootroot00000000000000/* * Program: File descriptor string routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Driver-dependent data passed to init method */ typedef struct fd_data { int fd; /* file descriptor */ unsigned long pos; /* initial position */ char *chunk; /* I/O buffer chunk */ unsigned long chunksize; /* I/O buffer chunk length */ } FDDATA; extern STRINGDRIVER fd_string; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/flockcyg.c000066400000000000000000000054301137544547100235650ustar00rootroot00000000000000/* * Program: flock emulation via fcntl() locking * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 2001 * Last Edited: 21 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Cygwin does not seem to have the design flaw in fcntl() locking that * most other systems do (see flocksim.c for details). If some cretin * decides to implement that design flaw, then Cygwin will have to use * flocksim. Also, we don't test NFS either. * * However, Cygwin does have the Windows misfeature (introduced in NT 4.0) * that you can not write to any segment which has a shared lock, and you * can't lock a zero-byte segment either. This screws up the shared-write * mailbox drivers (mbx, mtx, mx, and tenex). As a workaround, we'll only * lock the first byte of the file, meaning that you can't write that byte * shared. It's been suggested to lock the maximum off_t type, but that * risks having a future version of Windows (or Cygwin) deciding that this * also means "no lock". */ #undef flock /* name is used as a struct for fcntl */ /* Emulator for flock() call * Accepts: file descriptor * operation bitmask * Returns: 0 if successful, -1 if failure under BSD conditions */ int flocksim (int fd,int op) { char tmp[MAILTMPLEN]; int logged = 0; struct flock fl; /* lock one bytes at byte 0 */ fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; fl.l_pid = getpid (); /* shouldn't be necessary */ switch (op & ~LOCK_NB) { /* translate to fcntl() operation */ case LOCK_EX: /* exclusive */ fl.l_type = F_WRLCK; break; case LOCK_SH: /* shared */ fl.l_type = F_RDLCK; break; case LOCK_UN: /* unlock */ fl.l_type = F_UNLCK; break; default: /* default */ errno = EINVAL; return -1; } while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl)) if (errno != EINTR) { /* Can't use switch here because these error codes may resolve to the * same value on some systems. */ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) { sprintf (tmp,"Unexpected file locking failure: %s",strerror (errno)); /* give the user a warning of what happened */ MM_NOTIFY (NIL,tmp,WARN); if (!logged++) syslog (LOG_ERR,"%s",tmp); if (op & LOCK_NB) return -1; sleep (5); /* slow things down for loops */ } /* return failure for non-blocking lock */ else if (op & LOCK_NB) return -1; } return 0; /* success */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/flockcyg.h000066400000000000000000000021731137544547100235730ustar00rootroot00000000000000/* * Program: flock() emulation via fcntl() locking * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 2001 * Last Edited: 25 April 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Cygwin does not seem to have the design flaw in fcntl() locking that * most other systems do (see flocksim.c for details). If some cretin * decides to implement that design flaw, then Cygwin will have to use * flocksim. Also, we don't test NFS either */ #define flock flocksim /* use ours instead of theirs */ #undef LOCK_SH #define LOCK_SH 1 /* shared lock */ #undef LOCK_EX #define LOCK_EX 2 /* exclusive lock */ #undef LOCK_NB #define LOCK_NB 4 /* no blocking */ #undef LOCK_UN #define LOCK_UN 8 /* unlock */ /* Function prototypes */ int flocksim (int fd,int operation); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/flocklnx.c000066400000000000000000000040501137544547100236010ustar00rootroot00000000000000/* * Program: Safe File Lock for Linux * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 April 2005 * Last Edited: 26 April 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #undef flock #include #ifndef NFS_SUPER_MAGIC #define NFS_SUPER_MAGIC 0x6969 #endif int safe_flock (int fd,int op) { struct statfs sfbuf; char tmp[MAILTMPLEN]; int e; int logged = 0; /* Check for NFS because Linux 2.6 broke flock() on NFS. Instead of being * a no-op, flock() on NFS now returns ENOLCK. Read * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=123415 * for the gruesome details. */ /* check filesystem type */ while ((e = fstatfs (fd,&sfbuf)) && (errno == EINTR)); if (!e) switch (sfbuf.f_type) { case NFS_SUPER_MAGIC: /* always a fast no-op on NFS */ break; default: /* allow on other filesystem types */ /* do the lock */ while (flock (fd,op)) switch (errno) { case EINTR: /* interrupt */ break; case ENOLCK: /* lock table is full */ sprintf (tmp,"File locking failure: %s",strerror (errno)); mm_log (tmp,WARN); /* give the user a warning of what happened */ if (!logged++) syslog (LOG_ERR,tmp); /* return failure if non-blocking lock */ if (op & LOCK_NB) return -1; sleep (5); /* slow down in case it loops */ break; case EWOULDBLOCK: /* file is locked, LOCK_NB should be set */ if (op & LOCK_NB) return -1; case EBADF: /* not valid open file descriptor */ case EINVAL: /* invalid operator */ default: /* other error code? */ sprintf (tmp,"Unexpected file locking failure: %s",strerror (errno)); fatal (tmp); } break; } return 0; /* success */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/flocksim.c000066400000000000000000000611041137544547100235730ustar00rootroot00000000000000/* * Program: flock emulation via fcntl() locking * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 2001 * Last Edited: 21 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #undef flock /* name is used as a struct for fcntl */ #undef fork /* make damn sure that we don't use vfork!! */ #include "nfstest.c" /* get NFS tester */ #ifndef NSIG /* don't know if this can happen */ #define NSIG 32 /* a common maximum */ #endif /* Emulator for flock() call * Accepts: file descriptor * operation bitmask * Returns: 0 if successful, -1 if failure under BSD conditions */ int flocksim (int fd,int op) { char tmp[MAILTMPLEN]; int logged = 0; struct flock fl; /* lock zero bytes at byte 0 */ fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0; fl.l_pid = getpid (); /* shouldn't be necessary */ switch (op & ~LOCK_NB) { /* translate to fcntl() operation */ case LOCK_EX: /* exclusive */ fl.l_type = F_WRLCK; break; case LOCK_SH: /* shared */ fl.l_type = F_RDLCK; break; case LOCK_UN: /* unlock */ fl.l_type = F_UNLCK; break; default: /* default */ errno = EINVAL; return -1; } /* Make fcntl() locking of NFS files be a no-op the way it is with flock() * on BSD. This is because the rpc.statd/rpc.lockd daemons don't work very * well and cause cluster-wide hangs if you exercise them at all. The * result of this is that you lose the ability to detect shared mail_open() * on NFS-mounted files. If you are wise, you'll use IMAP instead of NFS * for mail files. * * Sun alleges that it doesn't matter, because they say they have fixed all * the rpc.statd/rpc.lockd bugs. This is absolutely not true; huge amounts * of user and support time have been wasted in cluster-wide hangs. */ if (test_nfs (fd) || mail_parameters (NIL,GET_DISABLEFCNTLLOCK,NIL)) return 0; /* fcntl() locking disabled, return success */ /* do the lock */ while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl)) if (errno != EINTR) { /* Can't use switch here because these error codes may resolve to the * same value on some systems. */ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) { sprintf (tmp,"Unexpected file locking failure: %s",strerror (errno)); /* give the user a warning of what happened */ MM_NOTIFY (NIL,tmp,WARN); if (!logged++) syslog (LOG_ERR,"%s",tmp); if (op & LOCK_NB) return -1; sleep (5); /* slow things down for loops */ } /* return failure for non-blocking lock */ else if (op & LOCK_NB) return -1; } return 0; /* success */ } /* Master/slave procedures for safe fcntl() locking. * * The purpose of this nonsense is to work around a bad bug in fcntl() * locking. The cretins who designed it decided that a close() should * release any locks made by that process on the file opened on that * file descriptor. Never mind that the lock wasn't made on that file * descriptor, but rather on some other file descriptor. * * This bug is on every implementation of fcntl() locking that I have * tested. Fortunately, on BSD systems, OSF/1, and Linux, we can use the * flock() system call which doesn't have this bug. * * Note that OSF/1, Linux, and some BSD systems have both broken fcntl() * locking and the working flock() locking. * * The program below can be used to demonstrate this problem. Be sure to * let it run long enough for all the sleep() calls to finish. */ #if 0 #include #include #include #include #include #include main () { struct flock fl; int fd,fd2; char *file = "a.a"; if ((fd = creat (file,0666)) < 0) perror ("TEST FAILED: can't create test file"),_exit (errno); close (fd); if (fork ()) { /* parent */ if ((fd = open (file,O_RDWR,0)) < 0) abort(); /* lock applies to entire file */ fl.l_whence = fl.l_start = fl.l_len = 0; fl.l_pid = getpid (); /* shouldn't be necessary */ fl.l_type = F_RDLCK; if (fcntl (fd,F_SETLKW,&fl) == -1) abort (); sleep (5); if ((fd2 = open (file,O_RDWR,0)) < 0) abort (); sleep (1); puts ("parent test ready -- will hang here if locking works correctly"); close (fd2); wait (0); puts ("OS BUG: child terminated"); _exit (0); } else { /* child */ sleep (2); if ((fd = open (file,O_RDWR,0666)) < 0) abort (); puts ("child test ready -- child will hang if no bug"); /* lock applies to entire file */ fl.l_whence = fl.l_start = fl.l_len = 0; fl.l_pid = getpid (); /* shouldn't be necessary */ fl.l_type = F_WRLCK; if (fcntl (fd,F_SETLKW,&fl) == -1) abort (); puts ("OS BUG: child got lock"); } } #endif /* Beware of systems such as AIX which offer flock() as a compatibility * function that is just a jacket into fcntl() locking. The program below * is a variant of the program above, only using flock(). It can be used * to test to see if your system has real flock() or just a jacket into * fcntl(). * * Be sure to let it run long enough for all the sleep() calls to finish. * If the program hangs, then flock() works and you can dispense with the * use of this module (you lucky person!). */ #if 0 #include #include #include #include main () { int fd,fd2; char *file = "a.a"; if ((fd = creat (file,0666)) < 0) perror ("TEST FAILED: can't create test file"),_exit (errno); close (fd); if (fork ()) { /* parent */ if ((fd = open (file,O_RDWR,0)) < 0) abort(); if (flock (fd,LOCK_SH) == -1) abort (); sleep (5); if ((fd2 = open (file,O_RDWR,0)) < 0) abort (); sleep (1); puts ("parent test ready -- will hang here if flock() works correctly"); close (fd2); wait (0); puts ("OS BUG: child terminated"); _exit (0); } else { /* child */ sleep (2); if ((fd = open (file,O_RDWR,0666)) < 0) abort (); puts ("child test ready -- child will hang if no bug"); if (flock (fd,LOCK_EX) == -1) abort (); puts ("OS BUG: child got lock"); } } #endif /* Master/slave details * * On broken systems, we invoke an inferior fork to execute any driver * dispatches which are likely to tickle this bug; specifically, any * dispatch which may fiddle with a mailbox that is already selected. As * of this writing, these are: delete, rename, status, scan, copy, and append. * * Delete and rename are pretty marginal, yet there are certain clients * (e.g. Outlook Express) that really want to delete or rename the selected * mailbox. The same is true of status, but there are people (such as the * authors of Entourage) who don't understand why status of the selected * mailbox is bad news. * * However, in copy and append it is reasonable to do this to a selected * mailbox. Although scanning the selected mailbox isn't particularly * sensible, it's hard to avoid due to wildcards. * * It is still possible for an application to trigger the bug by doing * mail_open() on the same mailbox twice. Don't do it. * * Once the slave is invoked, the master only has to read events from the * slave's output (see below for these events) and translate these events * to the appropriate c-client callback. When end of file occurs on the pipe, * the master reads the slave's exit status and uses that as the function * return. The append master is slightly more complicated because it has to * send data back to the slave (see below). * * The slave takes callback events from the driver which otherwise would * pass to the main program. Only those events which a slave can actually * encounter are covered here; for example mm_searched() and mm_list() are * not covered since a slave never does the operations that trigger these. * Certain other events (mm_exists(), mm_expunged(), mm_flags()) are discarded * by the slave since the master will generate these events for itself. * * The other events cause the slave to write a newline-terminated string to * its output. The first character of string indicates the event: S for * mm_status(), N for mm_notify(), L for mm_log(), C for mm_critical(), X for * mm_nocritical(), D for mm_diskerror(), F for mm_fatal(), and "A" for append * argument callback. Most of these events also carry data, which carried as * text space-delimited in the string. * * Append argument callback requires the master to provide the slave with * data in the slave's input. The first thing that the master provides is * either a "+" (master has data for the slave) or a "-" (master has no data). * If the master has data, it will then send the flags, internal date, and * message text, each as . */ /* It should be alright for lockslavep to be a global, since it will always * be zero in the master (which is where threads would be). The slave won't * ever thread, since any driver which threads in its methods probably can't * use fcntl() locking so won't have DR_LOCKING in its driver flags * * lockslavep can not be a static, since it's used by the dispatch macros. */ int lockslavep = 0; /* non-zero means slave process for locking */ static int lockproxycopy = 0; /* non-zero means redo copy as proxy */ FILE *slavein = NIL; /* slave input */ FILE *slaveout = NIL; /* slave output */ /* Common master * Accepts: permitted stream * append callback (append calls only, else NIL) * data for callback (append calls only, else NIL) * Returns: (master) T if slave succeeded, NIL if slave failed * (slave) NIL always, with lockslavep non-NIL */ static long master (MAILSTREAM *stream,append_t af,void *data) { MAILSTREAM *st; MAILSTATUS status; STRING *message; FILE *pi,*po; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); long ret = NIL; unsigned long i,j; int c,pid,pipei[2],pipeo[2]; char *s,*t,tmp[MAILTMPLEN]; lockproxycopy = NIL; /* not doing a lock proxycopy */ /* make pipe from slave */ if (pipe (pipei) < 0) mm_log ("Can't create input pipe",ERROR); else if (pipe (pipeo) < 0) { mm_log ("Can't create output pipe",ERROR); close (pipei[0]); close (pipei[1]); } else if ((pid = fork ()) < 0) {/* make slave */ mm_log ("Can't create execution process",ERROR); close (pipei[0]); close (pipei[1]); close (pipeo[0]); close (pipeo[1]); } else if (lockslavep = !pid) { /* are we slave or master? */ alarm (0); /* slave doesn't have alarms or signals */ for (c = 0; c < NSIG; c++) signal (c,SIG_DFL); if (!(slavein = fdopen (pipeo[0],"r")) || !(slaveout = fdopen (pipei[1],"w"))) fatal ("Can't do slave pipe buffered I/O"); close (pipei[0]); /* close parent's side of the pipes */ close (pipeo[1]); } else { /* master process */ void *blockdata = (*bn) (BLOCK_SENSITIVE,NIL); close (pipei[1]); /* close slave's side of the pipes */ close (pipeo[0]); if (!(pi = fdopen (pipei[0],"r")) || !(po = fdopen (pipeo[1],"w"))) fatal ("Can't do master pipe buffered I/O"); /* do slave events until EOF */ /* read event */ while (fgets (tmp,MAILTMPLEN,pi)) { /* tie off event at end of line */ if (s = strchr (tmp,'\n')) *s = '\0'; else { s = "Execution process event string too long: "; sprintf (t = (char *) fs_get (strlen (s) + strlen (tmp) + 1),"%s:%s", s,tmp); fatal (t); } switch (tmp[0]) { /* analyze event */ case 'A': /* append callback */ if ((*af) (NIL,data,&s,&t,&message)) { if (i = message ? SIZE (message) : 0) { if (!s) s = ""; /* default values */ if (!t) t = ""; } else s = t = ""; /* no flags or date if no message */ errno = NIL; /* reset last error */ /* build response */ sprintf (tmp,"+%lu %s%lu %s%lu ",strlen (s),s,strlen (t),t,i); if (fputs (tmp,po) == EOF) { sprintf (tmp,"Failed to pipe command \"+%lu %s%lu %s%lu \": %s", strlen (s),s,strlen (t),t,i,strerror (errno)); fatal (tmp); } /* write message text */ if (i) do if (putc (c = 0xff & SNX (message),po) == EOF) { sprintf (tmp,"Failed to pipe %lu bytes (of %lu), last=%u: %s", i,message->size,c,strerror (errno)); fatal (tmp); } while (--i); } else putc ('-',po); /* append error */ fflush (po); break; case '&': /* slave wants a proxycopy? */ lockproxycopy = T; break; case 'L': /* mm_log() */ i = strtoul (tmp+1,&s,10); if (!s || (*s++ != ' ')) { s = "Invalid log event arguments: "; sprintf (t = (char *) fs_get (strlen (s) + strlen (tmp) + 1),"%s:%s", s,tmp); fatal (t); } mm_log (s,i); break; case 'N': /* mm_notify() */ st = (MAILSTREAM *) strtoul (tmp+1,&s,16); if (s && (*s++ == ' ')) { i = strtoul (s,&s,10);/* get severity */ if (s && (*s++ == ' ')) { mm_notify ((st == stream) ? stream : NIL,s,i); break; } } s = "Invalid notify event arguments: "; sprintf (t = (char *) fs_get (strlen (s) + strlen (tmp) + 1),"%s:%s", s,tmp); fatal (t); case 'S': /* mm_status() */ st = (MAILSTREAM *) strtoul (tmp+1,&s,16); if (s && (*s++ == ' ')) { status.flags = strtoul (s,&s,10); if (s && (*s++ == ' ')) { status.messages = strtoul (s,&s,10); if (s && (*s++ == ' ')) { status.recent = strtoul (s,&s,10); if (s && (*s++ == ' ')) { status.unseen = strtoul (s,&s,10); if (s && (*s++ == ' ')) { status.uidnext = strtoul (s,&s,10); if (s && (*s++ == ' ')) { status.uidvalidity = strtoul (s,&s,10); if (s && (*s++ == ' ')) { mm_status ((st == stream) ? stream : NIL,s,&status); break; } } } } } } } s = "Invalid status event arguments: "; sprintf (t = (char *) fs_get (strlen (s) + strlen (tmp) + 1),"%s:%s", s,tmp); fatal (t); case 'C': /* mm_critical() */ st = (MAILSTREAM *) strtoul (tmp+1,&s,16); mm_critical ((st == stream) ? stream : NIL); break; case 'X': /* mm_nocritical() */ st = (MAILSTREAM *) strtoul (tmp+1,&s,16); mm_nocritical ((st == stream) ? stream : NIL); break; case 'D': /* mm_diskerror() */ st = (MAILSTREAM *) strtoul (tmp+1,&s,16); if (s && (*s++ == ' ')) { i = strtoul (s,&s,10); if (s && (*s++ == ' ')) { j = (long) strtoul (s,NIL,10); if (st == stream) /* let's hope it's on usable stream */ putc (mm_diskerror (stream,(long) i,j) ? '+' : '-',po); else if (j) { /* serious diskerror on slave-created stream */ mm_log ("Retrying disk write to avoid mailbox corruption!",WARN); sleep (5); /* give some time for it to clear up */ putc ('-',po); /* don't abort */ } else { /* recoverable on slave-created stream */ mm_log ("Error on disk write",ERROR); putc ('+',po); /* so abort it */ } fflush (po); /* force it out either way */ break; } } s = "Invalid diskerror event arguments: "; sprintf (t = (char *) fs_get (strlen (s) + strlen (tmp) + 1),"%s:%s", s,tmp); fatal (t); case 'F': /* mm_fatal() */ mm_fatal (tmp+1); break; default: /* random lossage */ s = "Unknown event from execution process: "; sprintf (t = (char *) fs_get (strlen (s) + strlen (tmp) + 1),"%s:%s", s,tmp); fatal (t); } } fclose (pi); fclose (po); /* done with the pipes */ /* get slave status */ grim_pid_reap_status (pid,NIL,&ret); if (ret & 0177) { /* signal or stopped */ sprintf (tmp,"Execution process terminated abnormally (%lx)",ret); mm_log (tmp,ERROR); ret = NIL; } else ret >>= 8; /* return exit code */ (*bn) (BLOCK_NONSENSITIVE,blockdata); } return ret; /* return status */ } /* Safe driver calls */ /* Safely delete mailbox * Accepts: driver to call under slave * MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx) { long ret = master (stream,NIL,NIL); if (lockslavep) exit ((*dtb->mbxdel) (stream,mbx)); return ret; } /* Safely rename mailbox * Accepts: driver to call under slave * MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname) { long ret = master (stream,NIL,NIL); if (lockslavep) exit ((*dtb->mbxren) (stream,old,newname)); return ret; } /* Safely get status of mailbox * Accepts: driver to call under slave * MAIL stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags) { long ret = master (stream,NIL,NIL); if (lockslavep) exit ((*dtb->status) (stream,mbx,flags)); return ret; } /* Scan file for contents * Accepts: file name * desired contents * Returns: NIL if contents not found, T if found */ long safe_scan_contents (char *name,char *contents,unsigned long csiz, unsigned long fsiz) { long ret = master (NIL,NIL,NIL); if (lockslavep) exit (dummy_scan_contents (name,contents,csiz,fsiz)); return ret; } /* Safely copy message to mailbox * Accepts: driver to call under slave * MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags) { mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); long ret = master (stream,NIL,NIL); if (lockslavep) { /* don't do proxycopy in slave */ if (pc) mail_parameters (stream,SET_MAILPROXYCOPY,(void *) slaveproxycopy); exit ((*dtb->copy) (stream,seq,mbx,flags)); } /* do any proxycopy in master */ if (lockproxycopy && pc) return (*pc) (stream,seq,mbx,flags); return ret; } /* Append package for slave */ typedef struct append_data { int first; /* flag indicating first message */ char *flags; /* message flags */ char *date; /* message date */ char *msg; /* message text */ STRING message; /* message stringstruct */ } APPENDDATA; /* Safely append message to mailbox * Accepts: driver to call under slave * MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af, void *data) { long ret = master (stream,af,data); if (lockslavep) { APPENDDATA ad; ad.first = T; /* initialize initial append package */ ad.flags = ad.date = ad.msg = NIL; exit ((*dtb->append) (stream,mbx,slave_append,&ad)); } return ret; } /* Slave callbacks */ /* Message exists (i.e. there are that many messages in the mailbox) * Accepts: MAIL stream * message number */ void slave_exists (MAILSTREAM *stream,unsigned long number) { /* this event never passed by slaves */ } /* Message expunged * Accepts: MAIL stream * message number */ void slave_expunged (MAILSTREAM *stream,unsigned long number) { /* this event never passed by slaves */ } /* Message status changed * Accepts: MAIL stream * message number */ void slave_flags (MAILSTREAM *stream,unsigned long number) { /* this event never passed by slaves */ } /* Mailbox status * Accepts: MAIL stream * mailbox name * mailbox status */ void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { fprintf (slaveout,"S%lx %lu %lu %lu %lu %lu %lu %s\n", (unsigned long) stream,status->flags,status->messages,status->recent, status->unseen,status->uidnext,status->uidvalidity,mailbox); fflush (slaveout); } /* Notification event * Accepts: MAIL stream * string to log * error flag */ void slave_notify (MAILSTREAM *stream,char *string,long errflg) { fprintf (slaveout,"N%lx %lu %s\n",(unsigned long) stream,errflg,string); fflush (slaveout); } /* Log an event for the user to see * Accepts: string to log * error flag */ void slave_log (char *string,long errflg) { fprintf (slaveout,"L%lu %s\n",errflg,string); fflush (slaveout); } /* About to enter critical code * Accepts: stream */ void slave_critical (MAILSTREAM *stream) { fprintf (slaveout,"C%lx\n",(unsigned long) stream); fflush (slaveout); } /* About to exit critical code * Accepts: stream */ void slave_nocritical (MAILSTREAM *stream) { fprintf (slaveout,"X%lx\n",(unsigned long) stream); fflush (slaveout); } /* Disk error found * Accepts: stream * system error code * flag indicating that mailbox may be clobbered * Returns: abort flag */ long slave_diskerror (MAILSTREAM *stream,long errcode,long serious) { int c; fprintf (slaveout,"D%lx %lu %lu\n",(unsigned long) stream,errcode,serious); fflush (slaveout); if ((c = getc (slavein)) == '+') return LONGT; if (c != '-') slave_fatal ("Unknown master response for diskerror"); return NIL; } /* Log a fatal error event * Accepts: string to log */ void slave_fatal (char *string) { syslog (LOG_ALERT,"IMAP toolkit slave process crash: %.100s",string); fprintf (slaveout,"F%s\n",string); fflush (slaveout); abort (); /* die */ } /* Append read buffer * Accepts: number of bytes to read * error message if fails * Returns: read-in string */ static char *slave_append_read (unsigned long n,char *error) { #if 0 unsigned long i; #endif int c; char *t,tmp[MAILTMPLEN]; char *s = (char *) fs_get (n + 1); s[n] = '\0'; #if 0 /* This doesn't work on Solaris with GCC. I think that it's a C library * bug, since the problem only shows up if the application does fread() * on some other file */ for (t = s; n && ((i = fread (t,1,n,slavein)); t += i,n -= i); #else for (t = s; n && ((c = getc (slavein)) != EOF); *t++ = c,--n); #endif if (n) { sprintf (tmp,"Error reading %s with %lu bytes remaining",error,n); slave_fatal (tmp); } return s; } /* Append message callback * Accepts: MAIL stream * append data package * pointer to return initial flags * pointer to return message internal date * pointer to return stringstruct of message or NIL to stop * Returns: T if success (have message or stop), NIL if error */ long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message) { char tmp[MAILTMPLEN]; unsigned long n; int c; APPENDDATA *ad = (APPENDDATA *) data; /* flush text of previous message */ if (ad->flags) fs_give ((void **) &ad->flags); if (ad->date) fs_give ((void **) &ad->date); if (ad->msg) fs_give ((void **) &ad->msg); *flags = *date = NIL; /* assume no flags or date */ fputs ("A\n",slaveout); /* tell master we're doing append callback */ fflush (slaveout); switch (c = getc (slavein)) { /* what did master say? */ case '+': /* have message, get size of flags */ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0')); if (c != ' ') { sprintf (tmp,"Missing delimiter after flag size %lu: %c",n,c); slave_fatal (tmp); } if (n) *flags = ad->flags = slave_append_read (n,"flags"); /* get size of date */ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0')); if (c != ' ') { sprintf (tmp,"Missing delimiter after date size %lu: %c",n,c); slave_fatal (tmp); } if (n) *date = ad->date = slave_append_read (n,"date"); /* get size of message */ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0')); if (c != ' ') { sprintf (tmp,"Missing delimiter after message size %lu: %c",n,c); slave_fatal (tmp); } if (n) { /* make buffer for message */ ad->msg = slave_append_read (n,"message"); /* initialize stringstruct */ INIT (&ad->message,mail_string,(void *) ad->msg,n); ad->first = NIL; /* no longer first message */ *message = &ad->message; /* return message */ } else *message = NIL; /* empty message */ return LONGT; case '-': /* error */ *message = NIL; /* set stop */ break; default: /* unknown event */ sprintf (tmp,"Unknown master response for append: %c",c); slave_fatal (tmp); } return NIL; /* return failure */ } /* Proxy copy across mailbox formats * Accepts: mail stream * sequence to copy on this stream * destination mailbox * option flags * Returns: T if success, else NIL */ long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox, long options) { fputs ("&\n",slaveout); /* redo copy as append */ fflush (slaveout); return NIL; /* failure for now */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/flocksim.h000066400000000000000000000077671137544547100236170ustar00rootroot00000000000000/* * Program: flock() emulation via fcntl() locking * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 2001 * Last Edited: 22 January 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define flock flocksim /* use ours instead of theirs */ #undef LOCK_SH #define LOCK_SH 1 /* shared lock */ #undef LOCK_EX #define LOCK_EX 2 /* exclusive lock */ #undef LOCK_NB #define LOCK_NB 4 /* no blocking */ #undef LOCK_UN #define LOCK_UN 8 /* unlock */ /* Safe locking definitions */ extern int lockslavep; /* non-zero means slave process */ #undef SAFE_DELETE #define SAFE_DELETE(dtb,stream,mbx) (dtb->flags & DR_LOCKING) ? \ safe_delete (dtb,stream,mbx) : (*dtb->mbxdel) (stream,mbx) #undef SAFE_RENAME #define SAFE_RENAME(dtb,stream,old,newname) (dtb->flags & DR_LOCKING) ? \ safe_rename (dtb,stream,old,newname) : (*dtb->mbxren) (stream,old,newname) #undef SAFE_STATUS #define SAFE_STATUS(dtb,stream,mbx,bits) (dtb->flags & DR_LOCKING) ? \ safe_status (dtb,stream,mbx,bits) : (*dtb->status) (stream,mbx,bits) #undef SAFE_SCAN_CONTENTS #define SAFE_SCAN_CONTENTS(dtb,name,contents,csiz,fsiz) \ (!dtb || (dtb->flags & DR_LOCKING)) ? \ safe_scan_contents (name,contents,csiz,fsiz) : \ dummy_scan_contents (name,contents,csiz,fsiz) #undef SAFE_COPY #define SAFE_COPY(dtb,stream,seq,mbx,bits) (dtb->flags & DR_LOCKING) ? \ safe_copy (dtb,stream,seq,mbx,bits) : (*dtb->copy) (stream,seq,mbx,bits) #undef SAFE_APPEND #define SAFE_APPEND(dtb,stream,mbx,af,data) (dtb->flags & DR_LOCKING) ? \ safe_append (dtb,stream,mbx,af,data) : (*dtb->append) (stream,mbx,af,data) #undef MM_EXISTS #define MM_EXISTS (lockslavep ? slave_exists : mm_exists) #undef MM_EXPUNGED #define MM_EXPUNGED (lockslavep ? slave_expunged : mm_expunged) #undef MM_FLAGS #define MM_FLAGS (lockslavep ? slave_flags : mm_flags) #undef MM_NOTIFY #define MM_NOTIFY (lockslavep ? slave_notify : mm_notify) #undef MM_STATUS #define MM_STATUS (lockslavep ? slave_status : mm_status) #undef MM_LOG #define MM_LOG (lockslavep ? slave_log : mm_log) #undef MM_CRITICAL #define MM_CRITICAL (lockslavep ? slave_critical : mm_critical) #undef MM_NOCRITICAL #define MM_NOCRITICAL (lockslavep ? slave_nocritical : mm_nocritical) #undef MM_DISKERROR #define MM_DISKERROR (lockslavep ? slave_diskerror : mm_diskerror) #undef MM_FATAL #define MM_FATAL (lockslavep ? slave_fatal : mm_fatal) #undef MM_APPEND #define MM_APPEND(af) (lockslavep ? slave_append : (*af)) /* Function prototypes */ int flocksim (int fd,int operation); long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx); long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname); long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags); long safe_scan_contents (char *name,char *contents,unsigned long csiz, unsigned long fsiz); long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags); long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af, void *data); void slave_exists (MAILSTREAM *stream,unsigned long number); void slave_expunged (MAILSTREAM *stream,unsigned long number); void slave_flags (MAILSTREAM *stream,unsigned long number); void slave_notify (MAILSTREAM *stream,char *string,long errflg); void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status); void slave_log (char *string,long errflg); void slave_critical (MAILSTREAM *stream); void slave_nocritical (MAILSTREAM *stream); long slave_diskerror (MAILSTREAM *stream,long errcode,long serious); void slave_fatal (char *string); long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message); long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox, long options); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/fs_unix.c000066400000000000000000000032211137544547100234330ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); void *block = ckalloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); (*bn) (BLOCK_NONSENSITIVE,data); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); if (!(*block = ckrealloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); (*bn) (BLOCK_NONSENSITIVE,data); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); ckfree (*block); *block = NIL; (*bn) (BLOCK_NONSENSITIVE,data); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/fsync.c000066400000000000000000000012521137544547100231040ustar00rootroot00000000000000/* * Program: File sync emulator * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for BSD fsync() call * Accepts: file name * Returns: 0 if successful, -1 if failure */ int fsync (int fd) { sync (); return 0; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ftl_unix.c000066400000000000000000000014021137544547100236070ustar00rootroot00000000000000/* * Program: UNIX crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 14 May 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { MM_FATAL (string); /* pass up the string */ syslog (LOG_ALERT,"IMAP toolkit crash: %.100s",string); abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/gethstid.c000066400000000000000000000013531137544547100235770ustar00rootroot00000000000000/* * Program: Get host ID emulator * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for BSD gethostid() call * Returns: unique identifier for this machine */ long gethostid (void) { /* No gethostid() here, so just fake it and hope things turn out okay. */ return 0xdeadface; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/getspnam.c000066400000000000000000000025201137544547100235770ustar00rootroot00000000000000/* * Program: 64-bit getsockname()/getpeername() emulator * * Author: Mark Crispin from code contributed by Chris Ross * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 5 November 2004 * Last Edited: 5 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Jacket into getpeername() * Accepts: socket * pointer to socket address * void pointer to len * Returns: 0 if success, -1 if error */ int Getpeername (int s,struct sockaddr *name,size_t *namelen) { int ret; socklen_t len = (socklen_t) *namelen; ret = getpeername (s,name,&len); *namelen = (size_t) len; return ret; } /* Jacket into getsockname() * Accepts: socket * pointer to socket address * void pointer to len * Returns: 0 if success, -1 if error */ int Getsockname (int s,struct sockaddr *name,size_t *namelen) { int ret; socklen_t len = (socklen_t) *namelen; ret = getsockname (s,name,&len); *namelen = (size_t) len; return ret; } #define getpeername Getpeername #define getsockname Getsockname tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/gr_wait.c000066400000000000000000000020601137544547100234140ustar00rootroot00000000000000/* * Program: UNIX Grim PID Reaper -- wait() version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 November 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Grim PID reaper * Accepts: process ID * kill request flag * status return value */ void grim_pid_reap_status (int pid,int killreq,void *status) { int r; if (killreq) { kill (pid,SIGHUP); /* kill if not already dead */ alarm (10); /* in case we get hosed */ while (((r = wait (NIL)) != pid) && ((r > 0) || ((errno != ECHILD) && (errno != EINTR)))); alarm (0); /* cancel the alarm */ } else while (((r = wait (status)) != pid) && ((r > 0) || (errno != ECHILD))); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/gr_wait4.c000066400000000000000000000015251137544547100235050ustar00rootroot00000000000000/* * Program: UNIX Grim PID Reaper -- wait4() version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 November 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Grim PID reaper * Accepts: process ID * kill request flag * status return value */ void grim_pid_reap_status (int pid,int killreq,void *status) { if (killreq) kill(pid,SIGHUP);/* kill if not already dead */ while ((wait4 (pid,status,NIL,NIL) < 0) && (errno != ECHILD)); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/gr_waitp.c000066400000000000000000000015251137544547100236010ustar00rootroot00000000000000/* * Program: UNIX Grim PID Reaper -- waitpid() version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 November 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Grim PID reaper * Accepts: process ID * kill request flag * status return value */ void grim_pid_reap_status (int pid,int killreq,void *status) { if (killreq) kill(pid,SIGHUP);/* kill if not already dead */ while ((waitpid (pid,status,NIL) < 0) && (errno != ECHILD)); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ip4_unix.c000066400000000000000000000116341137544547100235260ustar00rootroot00000000000000/* * Program: UNIX IPv4 routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 18 December 2003 * Last Edited: 26 May 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SADRLEN sizeof (struct sockaddr) #define SADR4(sadr) ((struct sockaddr_in *) sadr) #define SADR4LEN sizeof (struct sockaddr_in) #define SADR4ADR(sadr) SADR4 (sadr)->sin_addr #define ADR4LEN sizeof (struct in_addr) #define SADR4PORT(sadr) SADR4 (sadr)->sin_port /* IP abstraction layer */ char *ip_sockaddrtostring (struct sockaddr *sadr); long ip_sockaddrtoport (struct sockaddr *sadr); void *ip_stringtoaddr (char *text,size_t *len,int *family); struct sockaddr *ip_newsockaddr (size_t *len); struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len); char *ip_sockaddrtoname (struct sockaddr *sadr); void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next); /* Return IP address string from socket address * Accepts: socket address * Returns: IP address as name string */ char *ip_sockaddrtostring (struct sockaddr *sadr) { return (sadr->sa_family == PF_INET) ? inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4"; } /* Return port from socket address * Accepts: socket address * Returns: port number or -1 if can't determine it */ long ip_sockaddrtoport (struct sockaddr *sadr) { return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1; } /* Return IP address from string * Accepts: name string * pointer to returned length * pointer to returned address family * Returns: address if valid, length and family updated, or NIL */ void *ip_stringtoaddr (char *text,size_t *len,int *family) { unsigned long adr; struct in_addr *ret; /* get address */ if ((adr = inet_addr (text)) == -1) ret = NIL; else { /* make in_addr */ ret = (struct in_addr *) fs_get (*len = ADR4LEN); *family = AF_INET; /* IPv4 */ ret->s_addr = adr; /* set address */ } return (void *) ret; } /* Create a maximum-size socket address * Accepts: pointer to return maximum socket address length * Returns: new, empty socket address of maximum size */ struct sockaddr *ip_newsockaddr (size_t *len) { return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN); } /* Stuff a socket address * Accepts: address family * IPv4 address * length of address (always 4 in IPv4) * port number * pointer to return socket address length * Returns: socket address or NIL if error */ struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len) { struct sockaddr *sadr = ip_newsockaddr (len); switch (family) { /* build socket address based upon family */ case AF_INET: /* IPv4 */ sadr->sa_family = PF_INET; /* copy host address */ memcpy (&SADR4ADR (sadr),adr,adrlen); /* copy port number in network format */ SADR4PORT (sadr) = htons (port); *len = SADR4LEN; break; default: /* non-IP?? */ sadr->sa_family = PF_UNSPEC; break; } return sadr; } /* Return name from socket address * Accepts: socket address * Returns: canonical name for that address or NIL if none */ char *ip_sockaddrtoname (struct sockaddr *sadr) { struct hostent *he; return ((sadr->sa_family == PF_INET) && (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ? (char *) he->h_name : NIL; } /* Return address from name * Accepts: name or NIL to return next address * pointer to previous/returned length * pointer to previous/returned address family * pointer to previous/returned canonical name * pointer to previous/return state for next-address calls * Returns: address with length/family/canonical updated if needed, or NIL */ void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next) { char **adl,tmp[MAILTMPLEN]; struct hostent *he; if (name) { /* first lookup? */ /* yes, do case-independent lookup */ if ((strlen (name) < MAILTMPLEN) && (he = gethostbyname (lcase (strcpy (tmp,name))))) { adl = he->h_addr_list; if (len) *len = he->h_length; if (family) *family = he->h_addrtype; if (canonical) *canonical = (char *) he->h_name; if (next) *next = (void *) adl; } else { /* error */ adl = NIL; if (len) *len = 0; if (family) *family = 0; if (canonical) *canonical = NIL; if (next) *next = NIL; } } /* return next in series */ else if (next && (adl = (char **) *next)) *next = ++adl; else adl = NIL; /* failure */ return adl ? (void *) *adl : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ip6_unix.c000066400000000000000000000205041137544547100235240ustar00rootroot00000000000000/* * Program: UNIX IPv6 routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 18 December 2003 * Last Edited: 12 October 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* * There is some amazingly bad design in IPv6 sockets. * * Supposedly, the new getnameinfo() and getaddrinfo() functions create an * abstraction that is not dependent upon IPv4 or IPv6. However, the * definition of getnameinfo() requires that the caller pass the length of * the sockaddr instead of deriving it from sa_family. The man page says * that there's an sa_len member in the sockaddr, but actually there isn't. * This means that any caller to getnameinfo() and getaddrinfo() has to know * the size for the protocol family used by that sockaddr. * * The new sockaddr_in6 is bigger than the generic sockaddr (which is what * connect(), accept(), bind(), getpeername(), getsockname(), etc. expect). * Rather than increase the size of sockaddr, there's a new sockaddr_storage * which is only usable for allocating space. */ #define SADRLEN sizeof (struct sockaddr_storage) #define SADR4(sadr) ((struct sockaddr_in *) sadr) #define SADR4LEN sizeof (struct sockaddr_in) #define SADR4ADR(sadr) SADR4 (sadr)->sin_addr #define ADR4LEN sizeof (struct in_addr) #define SADR4PORT(sadr) SADR4 (sadr)->sin_port #define SADR6(sadr) ((struct sockaddr_in6 *) sadr) #define SADR6LEN sizeof (struct sockaddr_in6) #define SADR6ADR(sadr) SADR6 (sadr)->sin6_addr #define ADR6LEN sizeof (struct in6_addr) #define SADR6PORT(sadr) SADR6 (sadr)->sin6_port /* IP abstraction layer */ char *ip_sockaddrtostring (struct sockaddr *sadr); long ip_sockaddrtoport (struct sockaddr *sadr); void *ip_stringtoaddr (char *text,size_t *len,int *family); struct sockaddr *ip_newsockaddr (size_t *len); struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len); char *ip_sockaddrtoname (struct sockaddr *sadr); void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next); /* Return IP address string from socket address * Accepts: socket address * Returns: IP address as name string */ char *ip_sockaddrtostring (struct sockaddr *sadr) { static char tmp[NI_MAXHOST]; switch (sadr->sa_family) { case PF_INET: /* IPv4 */ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST)) return tmp; break; case PF_INET6: /* IPv6 */ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST)) return tmp; break; } return "NON-IP"; } /* Return port from socket address * Accepts: socket address * Returns: port number or -1 if can't determine it */ long ip_sockaddrtoport (struct sockaddr *sadr) { switch (sadr->sa_family) { case PF_INET: return ntohs (SADR4PORT (sadr)); case PF_INET6: return ntohs (SADR6PORT (sadr)); } return -1; } /* Return IP address from string * Accepts: name string * pointer to returned length * pointer to returned address family * Returns: address if valid, length and family updated, or NIL */ void *ip_stringtoaddr (char *text,size_t *len,int *family) { char tmp[MAILTMPLEN]; static struct addrinfo *hints; struct addrinfo *ai; void *adr = NIL; if (!hints) { /* hints set up yet? */ hints = (struct addrinfo *) /* one-time setup */ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo)); hints->ai_family = AF_UNSPEC;/* allow any address family */ hints->ai_socktype = SOCK_STREAM; /* numeric name only */ hints->ai_flags = AI_NUMERICHOST; } /* case-independent lookup */ if (text && (strlen (text) < MAILTMPLEN) && (!getaddrinfo (lcase (strcpy (tmp,text)),NIL,hints,&ai))) { switch (*family = ai->ai_family) { case AF_INET: /* IPv4 */ adr = fs_get (*len = ADR4LEN); memcpy (adr,(void *) &SADR4ADR (ai->ai_addr),*len); break; case AF_INET6: /* IPv6 */ adr = fs_get (*len = ADR6LEN); memcpy (adr,(void *) &SADR6ADR (ai->ai_addr),*len); break; } freeaddrinfo (ai); /* free addrinfo */ } return adr; } /* Create a maximum-size socket address * Accepts: pointer to return maximum socket address length * Returns: new, empty socket address of maximum size */ struct sockaddr *ip_newsockaddr (size_t *len) { return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN); } /* Stuff a socket address * Accepts: address family * IPv4 address * length of address * port number * pointer to return socket address length * Returns: socket address */ struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len) { struct sockaddr *sadr = ip_newsockaddr (len); switch (family) { /* build socket address based upon family */ case AF_INET: /* IPv4 */ sadr->sa_family = PF_INET; /* copy host address */ memcpy (&SADR4ADR (sadr),adr,adrlen); /* copy port number in network format */ SADR4PORT (sadr) = htons (port); *len = SADR4LEN; break; case AF_INET6: /* IPv6 */ sadr->sa_family = PF_INET6; /* copy host address */ memcpy (&SADR6ADR (sadr),adr,adrlen); /* copy port number in network format */ SADR6PORT (sadr) = htons (port); *len = SADR6LEN; break; default: /* non-IP?? */ sadr->sa_family = PF_UNSPEC; break; } return sadr; } /* Return name from socket address * Accepts: socket address * Returns: canonical name for that address or NIL if none */ char *ip_sockaddrtoname (struct sockaddr *sadr) { static char tmp[NI_MAXHOST]; switch (sadr->sa_family) { case PF_INET: /* IPv4 */ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD)) return tmp; break; case PF_INET6: /* IPv6 */ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD)) return tmp; break; } return NIL; } /* Return address from name * Accepts: name or NIL to return next address * pointer to previous/returned length * pointer to previous/returned address family * pointer to previous/returned canonical name * pointer to previous/return state for next-address calls * Returns: address with length/family/canonical updated if needed, or NIL */ void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next) { struct addrinfo *cur = NIL; static struct addrinfo *hints; static struct addrinfo *ai = NIL; static char lcname[MAILTMPLEN]; if (!hints) { /* hints set up yet? */ hints = (struct addrinfo *) /* one-time setup */ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo)); /* allow any address family */ hints->ai_family = AF_UNSPEC; hints->ai_socktype = SOCK_STREAM; /* need canonical name */ hints->ai_flags = AI_CANONNAME; } if (name) { /* name supplied? */ if (ai) { freeaddrinfo (ai); /* free old addrinfo */ ai = NIL; } /* case-independent lookup */ if ((strlen (name) < MAILTMPLEN) && (!getaddrinfo (lcase (strcpy (lcname,name)),NIL,hints,&ai))) { cur = ai; /* current block */ if (canonical) /* set canonical name */ *canonical = cur->ai_canonname ? cur->ai_canonname : lcname; /* remember as next block */ if (next) *next = (void *) ai; } else { /* error */ cur = NIL; if (len) *len = 0; if (family) *family = 0; if (canonical) *canonical = NIL; if (next) *next = NIL; } } /* return next in series */ else if (next && (cur = ((struct addrinfo *) *next)->ai_next)) { *next = cur; /* set as last address */ /* set canonical in case changed */ if (canonical && cur->ai_canonname) *canonical = cur->ai_canonname; } if (cur) { /* got data? */ if (family) *family = cur->ai_family; switch (cur->ai_family) { case AF_INET: if (len) *len = ADR4LEN; return (void *) &SADR4ADR (cur->ai_addr); case AF_INET6: if (len) *len = ADR6LEN; return (void *) &SADR6ADR (cur->ai_addr); } } if (len) *len = 0; /* error return */ return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ipo_unix.c000066400000000000000000000114751137544547100236240ustar00rootroot00000000000000/* * Program: UNIX IPv4 old routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 18 December 2003 * Last Edited: 26 May 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SADRLEN sizeof (struct sockaddr) #define SADR4(sadr) ((struct sockaddr_in *) sadr) #define SADR4LEN sizeof (struct sockaddr_in) #define SADR4ADR(sadr) SADR4 (sadr)->sin_addr #define ADR4LEN sizeof (struct in_addr) #define SADR4PORT(sadr) SADR4 (sadr)->sin_port /* IP abstraction layer */ char *ip_sockaddrtostring (struct sockaddr *sadr); long ip_sockaddrtoport (struct sockaddr *sadr); void *ip_stringtoaddr (char *text,size_t *len,int *family); struct sockaddr *ip_newsockaddr (size_t *len); struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len); char *ip_sockaddrtoname (struct sockaddr *sadr); void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next); /* Return IP address string from socket address * Accepts: socket address * Returns: IP address as name string */ char *ip_sockaddrtostring (struct sockaddr *sadr) { return (sadr->sa_family == PF_INET) ? inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4"; } /* Return port from socket address * Accepts: socket address * Returns: port number or -1 if can't determine it */ long ip_sockaddrtoport (struct sockaddr *sadr) { return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1; } /* Return IP address from string * Accepts: name string * pointer to returned length * pointer to returned address family * Returns: address if valid, length and family updated, or NIL */ void *ip_stringtoaddr (char *text,size_t *len,int *family) { unsigned long adr; struct in_addr *ret; /* get address */ if ((adr = inet_addr (text)) == -1) ret = NIL; else { /* make in_addr */ ret = (struct in_addr *) fs_get (*len = ADR4LEN); *family = AF_INET; /* IPv4 */ ret->s_addr = adr; /* set address */ } return (void *) ret; } /* Create a maximum-size socket address * Accepts: pointer to return maximum socket address length * Returns: new, empty socket address of maximum size */ struct sockaddr *ip_newsockaddr (size_t *len) { return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN); } /* Stuff a socket address * Accepts: address family * IPv4 address * length of address (always 4 in IPv4) * port number * pointer to return socket address length * Returns: socket address or NIL if error */ struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen, unsigned short port,size_t *len) { struct sockaddr *sadr = ip_newsockaddr (len); switch (family) { /* build socket address based upon family */ case AF_INET: /* IPv4 */ sadr->sa_family = PF_INET; /* copy host address */ memcpy (&SADR4ADR (sadr),adr,adrlen); /* copy port number in network format */ SADR4PORT (sadr) = htons (port); *len = SADR4LEN; break; default: /* non-IP?? */ sadr->sa_family = PF_UNSPEC; break; } return sadr; } /* Return name from socket address * Accepts: socket address * Returns: canonical name for that address or NIL if none */ char *ip_sockaddrtoname (struct sockaddr *sadr) { struct hostent *he; return ((sadr->sa_family == PF_INET) && (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ? (char *) he->h_name : NIL; } /* Return address from name * Accepts: name or NIL to return next address * pointer to previous/returned length * pointer to previous/returned address family * pointer to previous/returned canonical name * pointer to previous/return state for next-address calls * Returns: address with length/family/canonical updated if needed, or NIL */ void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical, void **next) { char tmp[MAILTMPLEN]; struct hostent *he; void *ret = NIL; if (name) { /* first lookup? */ /* yes, do case-independent lookup */ if ((strlen (name) < MAILTMPLEN) && (he = gethostbyname (lcase (strcpy (tmp,name))))) { if (len) *len = he->h_length; if (family) *family = he->h_addrtype; if (canonical) *canonical = (char *) he->h_name; if (next) *next = 0; ret = he->h_addr; /* set result to this one and only block */ } else { /* error */ if (len) *len = 0; if (family) *family = 0; if (canonical) *canonical = NIL; if (next) *next = NIL; } } return ret; /* return result */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/kerb_mit.c000066400000000000000000000051361137544547100235630ustar00rootroot00000000000000/* * Program: MIT Kerberos routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 4 March 2003 * Last Edited: 17 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define PROTOTYPE(x) x #include #include long kerberos_server_valid (void); long kerberos_try_kinit (OM_uint32 error); char *kerberos_login (char *user,char *authuser,int argc,char *argv[]); /* Kerberos server valid check * Returns: T if have keytab, NIL otherwise */ long kerberos_server_valid () { krb5_context ctx; krb5_keytab kt; krb5_kt_cursor csr; long ret = NIL; /* make a context */ if (!krb5_init_context (&ctx)) { /* get default keytab */ if (!krb5_kt_default (ctx,&kt)) { /* can do server if have good keytab */ if (!krb5_kt_start_seq_get (ctx,kt,&csr)) ret = LONGT; krb5_kt_close (ctx,kt); /* finished with keytab */ } krb5_free_context (ctx); /* finished with context */ } return ret; } /* Kerberos check for missing or expired credentials * Returns: T if should suggest running kinit, NIL otherwise */ long kerberos_try_kinit (OM_uint32 error) { switch (error) { case KRB5KRB_AP_ERR_TKT_EXPIRED: case KRB5_FCC_NOFILE: /* MIT */ case KRB5_CC_NOTFOUND: /* Heimdal */ return LONGT; } return NIL; } /* Kerberos server log in * Accepts: authorization ID as user name * authentication ID as Kerberos principal * argument count * argument vector * Returns: logged in user name if logged in, NIL otherwise */ char *kerberos_login (char *user,char *authuser,int argc,char *argv[]) { krb5_context ctx; krb5_principal prnc; char kuser[NETMAXUSER]; char *ret = NIL; /* make a context */ if (!krb5_init_context (&ctx)) { /* build principal */ if (!krb5_parse_name (ctx,authuser,&prnc)) { /* can get local name for this principal? */ if (!krb5_aname_to_localname (ctx,prnc,NETMAXUSER-1,kuser)) { /* yes, local name permitted login as user? */ if (authserver_login (user,kuser,argc,argv) || authserver_login (lcase (user),kuser,argc,argv)) ret = myusername (); /* yes, return user name */ } krb5_free_principal (ctx,prnc); } krb5_free_context (ctx); /* finished with context */ } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/log_bsi.c000066400000000000000000000025071137544547100234040ustar00rootroot00000000000000/* * Program: BSI login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { long ret = NIL; uid_t euid = geteuid (); login_cap_t *lc = login_getclass (pw->pw_class); /* have class and can become user? */ if (lc && !seteuid (pw->pw_uid)) { /* ask for approval */ if (auth_approve (lc,pw->pw_name, (char *) mail_parameters (NIL,GET_SERVICENAME,NIL)) <= 0) seteuid (euid); /* not approved, restore root euid */ else { /* approved */ seteuid (euid); /* restore former root euid first */ setsid (); /* ensure we are session leader */ /* log the guy in */ ret = !setusercontext (lc,pw,pw->pw_uid,LOGIN_SETALL&~LOGIN_SETPATH); } } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/log_cyg.c000066400000000000000000000020231137544547100234020ustar00rootroot00000000000000/* * Program: Cygwin login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 25 April 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { uid_t uid = pw->pw_uid; /* must be same user name as last checkpw() */ if (!(cyg_user && !strcmp (pw->pw_name,cyg_user))) return NIL; /* do the ImpersonateLoggedOnUser() */ cygwin_set_impersonation_token (cyg_hdl); return !(setgid (pw->pw_gid) || initgroups (cyg_user,pw->pw_gid) || setuid (uid)); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/log_old.c000066400000000000000000000014451137544547100234050ustar00rootroot00000000000000/* * Program: Standard login for very old UNIX systems * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { return !(setgid (pw->pw_gid) || setuid (pw->pw_uid)); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/log_os4.c000066400000000000000000000026301137544547100233310ustar00rootroot00000000000000/* * Program: OSF/1 (Digital UNIX) 4 login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { int i; char *s; char *name = cpystr (pw->pw_name); char *host = cpystr (tcp_clienthost ()); uid_t uid = pw->pw_uid; long ret = NIL; /* tie off address */ if (s = strchr (host,' ')) *s = '\0'; if (*host == '[') { /* convert [a.b.c.d] to a.b.c.d */ memmove (host,host+1,i = strlen (host + 2)); host[i] = '\0'; } if (sia_become_user (checkpw_collect,argc,argv,host,name,NIL,NIL,NIL,NIL, SIA_BEU_REALLOGIN|SIA_BEU_OKROOTDIR) != SIASUCCESS) setreuid (0,0); /* make sure have root again */ /* probable success, complete login */ else ret = (!setreuid (0,0) && !setuid (uid)); fs_give ((void **) &name); fs_give ((void **) &host); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/log_sec.c000066400000000000000000000016561137544547100234050ustar00rootroot00000000000000/* * Program: SecureWare login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 23 November 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { uid_t uid = pw->pw_uid; char *name = cpystr (pw->pw_name); long ret = !(setluid (uid) || setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) || setuid (uid)); fs_give ((void **) &name); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/log_std.c000066400000000000000000000016321137544547100234170ustar00rootroot00000000000000/* * Program: Standard login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { uid_t uid = pw->pw_uid; char *name = cpystr (pw->pw_name); long ret = !(setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) || setuid (uid)); fs_give ((void **) &name); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/log_sv4.c000066400000000000000000000016031137544547100233370ustar00rootroot00000000000000/* * Program: SVR4 login * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Log in * Accepts: login passwd struct * argument count * argument vector * Returns: T if success, NIL otherwise */ long loginpw (struct passwd *pw,int argc,char *argv[]) { uid_t uid = pw->pw_uid; char *name = cpystr (pw->pw_name); long ret = !(setgid (pw->pw_gid) || initgroups (name) || setuid (uid)); fs_give ((void **) &name); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mbx.c000066400000000000000000001646131137544547100225630ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "mbx.h" #include "misc.h" #include "dummy.h" /* Kludge to make Cygwin happy */ #ifndef O_BINARY #define O_BINARY 0 #endif /* MBX I/O stream local data */ typedef struct mbx_local { unsigned int flagcheck: 1; /* if ping should sweep for flags */ unsigned int expok: 1; /* if expunging OK in ping */ unsigned int expunged : 1; /* if one or more expunged messages */ int fd; /* file descriptor for I/O */ int ld; /* lock file descriptor */ int ffuserflag; /* first free user flag */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned long lastpid; /* PID of last writer */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ char lock[MAILTMPLEN]; /* buffer to write lock name */ } MBXLOCAL; /* Convenient access to local data */ #define LOCAL ((MBXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mbx_valid (char *name); int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp); void *mbx_parameters (long function,void *value); void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mbx_list (MAILSTREAM *stream,char *ref,char *pat); void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mbx_create (MAILSTREAM *stream,char *mailbox); long mbx_delete (MAILSTREAM *stream,char *mailbox); long mbx_rename (MAILSTREAM *stream,char *old,char *newname); long mbx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mbx_open (MAILSTREAM *stream); void mbx_close (MAILSTREAM *stream,long options); void mbx_abort (MAILSTREAM *stream); void mbx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mbx_ping (MAILSTREAM *stream); void mbx_check (MAILSTREAM *stream); void mbx_expunge (MAILSTREAM *stream); void mbx_snarf (MAILSTREAM *stream); long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); char *mbx_file (char *dst,char *name); long mbx_parse (MAILSTREAM *stream); MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok); unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mbx_update_header (MAILSTREAM *stream); void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags); unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr); unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags); long mbx_flaglock (MAILSTREAM *stream); /* MBX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mbxdriver = { "mbx", /* driver name */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING, /* driver flags */ (DRIVER *) NIL, /* next driver */ mbx_valid, /* mailbox is valid for us */ mbx_parameters, /* manipulate parameters */ mbx_scan, /* scan mailboxes */ mbx_list, /* list mailboxes */ mbx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mbx_create, /* create mailbox */ mbx_delete, /* delete mailbox */ mbx_rename, /* rename mailbox */ mbx_status, /* status of mailbox */ mbx_open, /* open mailbox */ mbx_close, /* close mailbox */ mbx_flags, /* fetch message "fast" attributes */ mbx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mbx_header, /* fetch message header */ mbx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mbx_flag, /* modify flags */ mbx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mbx_ping, /* ping mailbox to see if still alive */ mbx_check, /* check for new messages */ mbx_expunge, /* expunge deleted messages */ mbx_copy, /* copy messages to another mailbox */ mbx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mbxproto = {&mbxdriver}; /* MBX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mbx_valid (char *name) { char tmp[MAILTMPLEN]; return mbx_isvalid (NIL,name,tmp) ? &mbxdriver : NIL; } /* MBX mail test for valid mailbox * Accepts: returned stream with valid mailbox keywords * mailbox name * scratch buffer * Returns: T if valid, NIL otherwise */ int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp) { int fd; int ret = NIL; unsigned long i; unsigned char *s,*t,hdr[HDRSIZE]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) && ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) >= 0)) { errno = -1; /* bogus format */ /* I love cretinous C compilers -- don't you? */ if (read (fd,hdr,HDRSIZE) == HDRSIZE) if ((hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8])) if (isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (hdr[15]) && isxdigit (hdr[16])) if (isxdigit (hdr[17]) && isxdigit (hdr[18]) && isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) && isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) ret = T; if (stream) { /* stream specified? */ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0, sizeof (MAILSTREAM)); for (i = 0, s = hdr + 25; /* parse user flags */ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (strlen (s) <= MAXUSERFLAG) (*stream)->user_flags[i] = cpystr (s); } } close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (tmp,tp); /* set the times */ } } /* in case INBOX but not mbx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MBX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mbx_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = mbx_file ((char *) value,"INBOX"); break; case SET_ONETIMEEXPUNGEATPING: if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T; case GET_ONETIMEEXPUNGEATPING: if (value) ret = (void *) (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL); break; } return ret; } /* MBX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MBX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MBX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MBX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mbx_create (MAILSTREAM *stream,char *mailbox) { char *s,*t,mbx[MAILTMPLEN],tmp[HDRSIZE]; long ret = NIL; int i,fd; if (!(s = mbx_file (mbx,mailbox))) { sprintf (mbx,"Can't create %.80s: invalid name",mailbox); MM_LOG (mbx,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) { /* done if made directory */ if ((s = strrchr (s,'/')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY|O_BINARY, (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else { memset (tmp,'\0',HDRSIZE);/* initialize header */ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012", (unsigned long) time (0)); for (i = 0; i < NUSERFLAGS; ++i) { t = (stream && stream->user_flags[i]) ? stream->user_flags[i] : ((t = default_user_flag (i)) ? t : ""); sprintf (s += strlen (s),"%s\015\012",t); } if (write (fd,tmp,HDRSIZE) != HDRSIZE) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s", mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ close (fd); /* close file */ } } /* set proper protections */ return ret ? set_mbx_protections (mailbox,mbx) : NIL; } /* MBX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mbx_delete (MAILSTREAM *stream,char *mailbox) { return mbx_rename (stream,mailbox,NIL); } /* MBX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mbx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!mbx_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); MM_LOG (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); MM_LOG (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if (s = strrchr (tmp,'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* superior name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) ret = NIL; else *s = c; /* restore full name */ } /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } } else if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } flock (fd,LOCK_UN); /* release lock on the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ close (fd); /* close the file */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX"); return ret; /* return success */ } /* MBX Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long mbx_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; /* calculate post-snarf results */ if (!status.recent && stream->inbox && (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* MBX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mbx_open (MAILSTREAM *stream) { int fd,ld; short silent; char tmp[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mbxproto); if (stream->local) fatal ("mbx recycle stream"); /* canonicalize the mailbox name */ if (!mbx_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->ld = -1; /* no flaglock */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get parse/append permission */ if ((ld = lockfd (LOCAL->fd,tmp,LOCK_EX)) < 0) { MM_LOG ("Unable to lock open mailbox",ERROR); return NIL; } (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* lock the file */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */ /* time not set up yet */ LOCAL->lastsnarf = LOCAL->filetime = 0; LOCAL->expok = LOCAL->flagcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; silent = stream->silent; /* defer events */ stream->silent = T; if (mbx_ping (stream) && !stream->nmsgs) MM_LOG ("Mailbox is empty",(long) NIL); stream->silent = silent; /* now notify upper level */ mail_exists (stream,stream->nmsgs); mail_recent (stream,stream->recent); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T; /* can we create new user flags? */ return stream; /* return stream to caller */ } /* MBX mail close * Accepts: MAIL stream * close options */ void mbx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ /* do an expunge if requested */ if (options & CL_EXPUNGE) mbx_expunge (stream); else { /* otherwise do a checkpoint to purge */ LOCAL->expok = T; /* possible expunged messages */ mbx_ping (stream); } stream->silent = silent; /* restore previous status */ mbx_abort (stream); } } /* MBX mail abort stream * Accepts: MAIL stream */ void mbx_abort (MAILSTREAM *stream) { if (stream && LOCAL) { /* only if a file is open */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MBX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mbx_flags (MAILSTREAM *stream,char *sequence,long flags) { MESSAGECACHE *elt; unsigned long i; if (mbx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence && !elt->valid) mbx_elt (stream,i,NIL); } /* MBX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i; char *s; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get header position, possibly header */ i = mbx_hdrpos (stream,msgno,length,&s); if (!s) { /* mbx_hdrpos() returned header? */ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */ /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->buf,*length); } s[*length] = '\0'; /* tie off string */ return s; } /* MBX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; char *s = LOCAL->text.data; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = mbx_elt (stream,msgno,NIL); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mbx_update_status (stream,msgno,NIL); MM_FLAGS (stream,msgno); /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } if (!LOCAL) i = 0; /* mbx_flaglock() could have aborted */ /* in case previous text cached */ if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mbx_hdrpos (stream,msgno,&j,NIL); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,s = LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* MBX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { time_t tp[2]; struct stat sbuf; unsigned long oldpid = LOCAL->lastpid; /* make sure the update takes */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) { fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; /* we are the last flag updater */ LOCAL->lastpid = (unsigned long) getpid (); /* update header if needed */ if (((LOCAL->ffuserflag < NUSERFLAGS) && stream->user_flags[LOCAL->ffuserflag]) || (oldpid != LOCAL->lastpid)) mbx_update_header (stream); tp[0] = time (0); /* make sure read comes after all that */ utime (stream->mailbox,tp); unlockfd (LOCAL->ld,LOCAL->lock); LOCAL->ld = -1; } } /* MBX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL); } /* MBX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mbx_ping (MAILSTREAM *stream) { unsigned long i,pos; long ret = NIL; int ld; char lock[MAILTMPLEN]; MESSAGECACHE *elt; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ int snarf = stream->inbox && !stream->rdonly; ret = LONGT; /* assume OK */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ /* allow expunge if permitted at ping */ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T; /* if external modification */ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; /* upgrade to flag checking */ /* new mail or flagcheck handling needed? */ if (((i = (sbuf.st_size - LOCAL->filesize)) || LOCAL->flagcheck || !stream->nmsgs || snarf) && ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) { if (LOCAL->flagcheck) { /* sweep mailbox for changed message status */ if (ret = mbx_parse (stream)) { LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ) if (mbx_elt (stream,i,LOCAL->expok)) ++i; LOCAL->flagcheck =NIL;/* got all the updates */ } } else if (i) ret = mbx_parse (stream); if (ret && snarf) { /* snarf new messages if still OK */ mbx_snarf (stream); /* parse snarfed messages */ ret = mbx_parse (stream); } unlockfd (ld,lock); /* release shared parse/append permission */ } if (ret) { /* must still be alive */ if (!LOCAL->expunged) /* look for holes if none known yet */ for (i = 1, pos = HDRSIZE; !LOCAL->expunged && (i <= stream->nmsgs); i++, pos += elt->private.special.text.size + elt->rfc822_size) if ((elt = mail_elt (stream,i))->private.special.offset != pos) LOCAL->expunged = T;/* found a hole */ /* burp any holes */ if (LOCAL->expunged && !stream->rdonly) { if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check"); if (i) { /* any space reclaimed? */ LOCAL->expunged = NIL;/* no more pending expunge */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i); MM_LOG (LOCAL->buf,(long) NIL); } } LOCAL->expok = NIL; /* no more expok */ } } return ret; /* return result of the parse */ } /* MBX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mbx_check (MAILSTREAM *stream) { if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */ if (mbx_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* MBX mail expunge mailbox * Accepts: MAIL stream */ void mbx_expunge (MAILSTREAM *stream) { unsigned long nexp,reclaimed; if (!mbx_ping (stream)); /* do nothing if stream dead */ else if (stream->rdonly) /* won't do on readonly files! */ MM_LOG ("Expunge ignored on readonly mailbox",WARN); /* if expunged any messages */ else if (nexp = mbx_rewrite (stream,&reclaimed,T)) { sprintf (LOCAL->buf,"Expunged %lu messages",nexp); MM_LOG (LOCAL->buf,(long) NIL); } else if (reclaimed) { /* or if any prior expunged space reclaimed */ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed); MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); } /* MBX mail snarf messages from system inbox * Accepts: MAIL stream, already locked */ void mbx_snarf (MAILSTREAM *stream) { unsigned long i = 0; unsigned long j,r,hdrlen,txtlen; struct stat sbuf; char *hdr,*txt,tmp[MAILTMPLEN]; MESSAGECACHE *elt; MAILSTREAM *sysibx = NIL; /* give up if can't get exclusive permission */ if ((time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && strcmp (sysinbox (),stream->mailbox)) { MM_CRITICAL (stream); /* go critical */ /* sizes match and anything in sysinbox? */ if (!stat (sysinbox (),&sbuf) && sbuf.st_size && !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { /* yes, go to end of file in our mailbox */ lseek (LOCAL->fd,sbuf.st_size,L_SET); /* for each message in sysibx mailbox */ while (r && (++i <= sysibx->nmsgs)) { /* snarf message from system INBOX */ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL)); txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK); /* if have a message */ if (j = hdrlen + txtlen) { /* build header line */ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i)); sprintf (LOCAL->buf + strlen (LOCAL->buf), ",%lu;00000000%04x-00000000\015\012",j,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* copy message */ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) || (write (LOCAL->fd,hdr,hdrlen) < 0) || (write (LOCAL->fd,txt,txtlen) < 0)) r = 0; } fs_give ((void **) &hdr); } /* make sure all the updates take */ if (fsync (LOCAL->fd)) r = 0; if (r) { /* delete all the messages we copied */ if (r == 1) strcpy (tmp,"1"); else sprintf (tmp,"1:%lu",r); mail_setflag (sysibx,tmp,"\\Deleted"); mail_expunge (sysibx); /* now expunge all those messages */ } else { sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (LOCAL->fd,sbuf.st_size); } fstat (LOCAL->fd,&sbuf); /* yes, get current file size */ LOCAL->filetime = sbuf.st_mtime; } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ LOCAL->lastsnarf = time (0);/* note time of last snarf */ } } /* MBX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; time_t tp[2]; MESSAGECACHE *elt; unsigned long i,j,k,m; long ret = LONGT; int fd,ld; char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd = open (mbx_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock copy mailbox",ERROR); MM_NOCRITICAL (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); mail_date(LOCAL->buf,elt);/* build target header */ /* get target keyword mask */ for (j = elt->user_flags, k = 0; j; ) if (s = stream->user_flags[find_rightmost_bit (&j)]) for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++) if (!compare_cstring (s,t) && (k |= 1 << m)) break; sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-00000000\015\012", elt->rfc822_size,k,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* write target header */ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){ read (LOCAL->fd,LOCAL->buf,j); ret = write (fd,LOCAL->buf,j) >= 0; } } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) { for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) { /* mark message deleted */ mbx_elt (stream,i,NIL)->deleted = T; /* recalculate status */ mbx_update_status (stream,i,NIL); } /* update flags */ mbx_flag (stream,NIL,NIL,NIL); } return ret; } /* MBX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; time_t tp[2]; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = NIL; MAILSTREAM *dstream = NIL; /* make sure valid mailbox */ if (!mbx_isvalid (&dstream,mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mbx_create (dstream = stream ? stream : user_flags (&mbxproto),"INBOX"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (dstream,data,&flags,&date,&message)); /* open destination mailbox */ else if (((fd = open (mbx_file (file,mailbox), O_WRONLY|O_APPEND|O_CREAT|O_BINARY, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); } /* get parse/append permission */ else if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock append mailbox",ERROR); close (fd); } else { MM_CRITICAL (dstream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; for (ret = LONGT; ret && message; ) { if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (dstream,flags,&uf); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%08lx%04lx-00000000\015\012",tmp, i = SIZE (message),uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !MM_APPEND (af) (dstream,data,&flags,&date,&message)) ret = NIL; } } /* if error... */ if (!ret || (fflush (df) == EOF)) { /* revert file */ ftruncate (fd,sbuf.st_size); close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); } ret = NIL; } /* set atime to now-1 if successful copy */ if (ret) tp[0] = time (0) - 1; /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (dstream); /* release critical */ } if (dstream != stream) mail_close (dstream); return ret; } /* Internal routines */ /* MBX mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *mbx_file (char *dst,char *name) { char *s = mailboxfile (dst,name); return (s && !*s) ? mailboxfile (dst,"~/INBOX") : s; } /* MBX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mbx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j,k,m; off_t curpos = LOCAL->filesize; unsigned long nmsgs = stream->nmsgs; unsigned long recent = stream->recent; unsigned long lastuid = 0; short dirty = NIL; short added = NIL; short silent = stream->silent; short uidwarn = T; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } lseek (LOCAL->fd,0,L_SET); /* rewind file */ /* read internal header */ read (LOCAL->fd,LOCAL->buf,HDRSIZE); LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */ c = LOCAL->buf[15]; /* save first character of last UID */ LOCAL->buf[15] = '\0'; /* parse UID validity */ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16); LOCAL->buf[15] = c; /* restore first character of last UID */ /* parse last UID */ i = strtoul (LOCAL->buf + 15,NIL,16); stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i; /* parse user flags */ for (i = 0, s = LOCAL->buf + 25; (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s); i++, s = t + 2) { *t = '\0'; /* tie off flag */ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG)) stream->user_flags[i] = cpystr (s); } LOCAL->ffuserflag = (int) i; /* first free user flag */ /* get current last flag updater PID */ i = (isxdigit (LOCAL->buf[HDRSIZE-10]) && isxdigit (LOCAL->buf[HDRSIZE-9]) && isxdigit (LOCAL->buf[HDRSIZE-8]) && isxdigit (LOCAL->buf[HDRSIZE-7]) && isxdigit (LOCAL->buf[HDRSIZE-6]) && isxdigit (LOCAL->buf[HDRSIZE-5]) && isxdigit (LOCAL->buf[HDRSIZE-4]) && isxdigit (LOCAL->buf[HDRSIZE-3]) && (LOCAL->buf[HDRSIZE-2] == '\015') && (LOCAL->buf[HDRSIZE-1] == '\012'))? strtoul (LOCAL->buf + HDRSIZE - 8,NIL,16) : 0; /* set flagcheck if lastpid changed */ if (LOCAL->lastpid && (LOCAL->lastpid != i)) LOCAL->flagcheck = T; LOCAL->lastpid = i; /* set as last PID */ stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s", (unsigned long) curpos,i,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) && isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) && isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) && isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) { sprintf (tmp,"Unable to parse message flags at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } if ((t[13] != '-') || t[22] || !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) && isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) && isxdigit (t[20]) && isxdigit (t[21]))) { sprintf (tmp,"Unable to parse message UID at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } *s++ = '\0'; *t++ = '\0'; /* break up fields */ /* get message size */ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) { sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s", (unsigned long) curpos,(char *) LOCAL->buf,(char *) s, (char *) t); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } /* make sure didn't run off end of file */ if (((off_t) (curpos + i + j)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", (unsigned long) curpos,(unsigned long) (curpos + i + j), (unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } /* parse UID */ if ((m = strtoul (t+13,NIL,16)) && ((m <= lastuid) || (m > stream->uid_last))) { if (uidwarn) { sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs", m,nmsgs+1); MM_LOG (tmp,WARN); uidwarn = NIL; /* restart UID validity */ stream->uid_validity = time (0); } m = 0; /* lose this UID */ dirty = T; /* mark dirty, set new lastuid */ stream->uid_last = lastuid; } t[12] = '\0'; /* parse system flags */ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) { if (m) lastuid = m; /* expunge message, update last UID seen */ else { /* no UID assigned? */ lastuid = ++stream->uid_last; dirty = T; } } else { /* not expunged, swell the cache */ added = T; /* note that a new message was added */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; /* parse the date */ if (!mail_parse_date (elt,LOCAL->buf)) { sprintf (tmp,"Unable to parse message date at %lu: %.80s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mbx_abort (stream); return NIL; } /* note file offset of header */ elt->private.special.offset = curpos; /* and internal header size */ elt->private.special.text.size = i; /* header size not known yet */ elt->private.msg.header.text.size = 0; elt->rfc822_size = j; /* note message size */ /* calculate system flags */ if (k & fSEEN) elt->seen = T; if (k & fDELETED) elt->deleted = T; if (k & fFLAGGED) elt->flagged = T; if (k & fANSWERED) elt->answered = T; if (k & fDRAFT) elt->draft = T; t[8] = '\0'; /* get user flags value */ elt->user_flags = strtoul (t,NIL,16); /* UID already assigned? */ if (!(elt->private.uid = m)) { elt->recent = T; /* no, mark as recent */ ++recent; /* count up a new recent message */ dirty = T; /* and must rewrite header */ /* assign new UID */ elt->private.uid = ++stream->uid_last; mbx_update_status (stream,elt->msgno,NIL); } /* update last parsed UID */ lastuid = elt->private.uid; } curpos += i + j; /* update position */ } if (dirty && !stream->rdonly){/* update header */ mbx_update_header (stream); fsync (LOCAL->fd); /* make sure all the UID updates take */ } /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ time_t tp[2]; tp[0] = time (0); tp[1] = LOCAL->filetime; utime (stream->mailbox,tp); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MBX get cache element with status updating from file * Accepts: MAIL stream * message number * expunge OK flag * Returns: cache element */ MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; /* get new flags */ if (mbx_read_flags (stream,elt) && expok) { mail_expunged (stream,elt->msgno); return NIL; /* return this message was expunged */ } if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) MM_FLAGS (stream,msgno); /* let top level know */ return elt; } /* MBX read flags from file * Accepts: MAIL stream * cache element * Returns: non-NIL if message expunged */ unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i; struct stat sbuf; fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } LOCAL->buf[13] = '\0'; /* tie off buffer */ /* calculate system flags */ i = strtoul (LOCAL->buf+9,NIL,16); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->expunged |= i & fEXPUNGED ? T : NIL; LOCAL->buf[9] = '\0'; /* tie off flags */ /* get user flags value */ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16); elt->valid = T; /* have valid flags now */ return i & fEXPUNGED; } /* MBX update header * Accepts: MAIL stream */ #ifndef CYGKLUDGEOFFSET #define CYGKLUDGEOFFSET 0 #endif void mbx_update_header (MAILSTREAM *stream) { int i; char *s = LOCAL->buf; memset (s,'\0',HDRSIZE); /* initialize header */ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012", stream->uid_validity,stream->uid_last); for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]); LOCAL->ffuserflag = i; /* first free user flag */ /* can we create more user flags? */ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL; /* write reserved lines */ while (i++ < NUSERFLAGS) strcat (s,"\015\012"); sprintf (LOCAL->buf + HDRSIZE - 10,"%08lx\015\012",LOCAL->lastpid); while (T) { /* rewind file */ lseek (LOCAL->fd,CYGKLUDGEOFFSET,L_SET); /* write new header */ if (write (LOCAL->fd,LOCAL->buf + CYGKLUDGEOFFSET, HDRSIZE - CYGKLUDGEOFFSET) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } } /* MBX update status string * Accepts: MAIL stream * message number * flags */ void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags) { struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); /* readonly */ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt); else { /* readwrite */ fstat (LOCAL->fd,&sbuf); /* get status */ /* make sure file size is good */ if (sbuf.st_size < LOCAL->filesize) { sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); fatal (LOCAL->buf); } /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 24,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,14) < 0) { sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno)); fatal (LOCAL->buf); } if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) { LOCAL->buf[14] = '\0'; /* tie off buffer for error message */ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s", elt->msgno,elt->private.special.offset, elt->private.special.text.size,(char *) LOCAL->buf); fatal (LOCAL->buf+50); } /* print new flag string */ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned) (((elt->deleted && flags) ? fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft)),elt->private.uid); while (T) { /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 23,L_SET); /* write new flags and UID */ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } } } /* MBX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * pointer to possible returned header * Returns: position of header in file */ #define HDRBUFLEN 4096 /* good enough for most headers */ #define SLOP 4 /* CR LF CR LF */ unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size,char **hdr) { unsigned long siz,done; long i; unsigned char *s,*t,*te; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; if (hdr) *hdr = NIL; /* assume no header returned */ /* is header size known? */ if (*size = elt->private.msg.header.text.size) return ret; /* paranoia check */ if (LOCAL->buflen < (HDRBUFLEN + SLOP)) fatal ("LOCAL->buf smaller than HDRBUFLEN"); lseek (LOCAL->fd,ret,L_SET); /* get to header position */ /* read HDRBUFLEN chunks with 4 byte slop */ for (done = siz = 0, s = LOCAL->buf; (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) && (read (LOCAL->fd,s,i) == i); done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) { te = (t = s + i) - 12; /* calculate end of fast scan */ /* fast scan for CR */ for (s = LOCAL->buf; s < te;) if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } for (te = t - 3; (s < te);) /* final character-at-a-time scan */ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') && (*++s == '\012')) { *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf); if (hdr) *hdr = LOCAL->buf; return ret; } if (i <= SLOP) break; /* end of data */ /* slide over last 4 bytes */ memmove (LOCAL->buf,t - SLOP,SLOP); hdr = NIL; /* can't return header this way */ } /* not found: header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; if (hdr) *hdr = LOCAL->buf; /* possibly return header too */ return ret; } /* MBX mail rewrite mailbox * Accepts: MAIL stream * pointer to return reclaimed size * flags (non-NIL to do expunge) * Returns: number of expunged messages */ unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed, long flags) { time_t tp[2]; struct stat sbuf; off_t pos,ppos; int ld; unsigned long i,j,k,m,n,delta; unsigned long recent = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* The cretins who designed flock() created a window of vulnerability in * upgrading locks from shared to exclusive or downgrading from exclusive * to shared. Rather than maintain the lock at shared status at a minimum, * flock() actually *releases* the former lock. Obviously they never talked * to any database guys. Fortunately, we have the parse/append permission * lock. If we require this lock before going exclusive on the mailbox, * another process can not sneak in and steal the exclusive mailbox lock on * us, because it will block on trying to get parse/append permission first. */ /* get parse/append permission */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock mailbox for rewrite",ERROR); return *reclaimed = 0; } fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime && !LOCAL->flagcheck && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T; if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */ unlockfd (ld,lock); /* failed?? */ return *reclaimed = 0; } if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */ LOCAL->filetime = sbuf.st_mtime; for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL); LOCAL->flagcheck = NIL; } /* get exclusive access */ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { MM_CRITICAL (stream); /* go critical */ for (i = 1,n = delta = *reclaimed = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) { /* note if message not at predicted location */ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) { ppos = elt->private.special.offset; *reclaimed += m; /* note reclaimed message space */ delta += m; /* and as expunge delta */ } /* number of bytes to smash or preserve */ ppos += (k = elt->private.special.text.size + elt->rfc822_size); /* if deleted */ if (flags && elt->deleted) { delta += k; /* number of bytes to delete */ mail_expunged(stream,i);/* notify upper levels */ n++; /* count up one more expunged message */ } else { /* preserved message */ i++; /* count this message */ if (elt->recent) ++recent; if (delta) { /* moved, note first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages yet */ else pos = elt->private.special.offset + k; } } /* deltaed file size match position? */ if (m = (LOCAL->filesize -= delta) - pos) { *reclaimed += m; /* probably an fEXPUNGED msg */ LOCAL->filesize = pos; /* set correct size */ } /* truncate file after last message */ ftruncate (LOCAL->fd,LOCAL->filesize); fsync (LOCAL->fd); /* force disk update */ MM_NOCRITICAL (stream); /* release critical */ (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ } else { /* can't get exclusive */ (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ /* checkpoint while shared? */ if (!flags) n = *reclaimed = 0; /* no, do hide-expunge */ else for (i = 1,n = *reclaimed = 0; i <= stream->nmsgs; ) { if (elt = mbx_elt (stream,i,T)) { if (elt->deleted) { /* make deleted message invisible */ mbx_update_status (stream,elt->msgno,LONGT); /* notify upper levels */ mail_expunged (stream,i); n++; /* count up one more expunged message */ } else { i++; /* preserved message */ if (elt->recent) ++recent; } } else n++; /* count up one more expunged message */ } fsync (LOCAL->fd); /* force disk update */ } fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* reset atime to now */ utime (stream->mailbox,tp); /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); return n; /* return number of expunged messages */ } /* MBX mail lock for flag updating * Accepts: stream * Returns: T if successful, NIL if failure */ long mbx_flaglock (MAILSTREAM *stream) { struct stat sbuf; unsigned long i; int ld; char lock[MAILTMPLEN]; /* no-op if readonly or already locked */ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) { /* lock now */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) return NIL; if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */ if (LOCAL->filetime) { /* know previous time? */ fstat (LOCAL->fd,&sbuf);/* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } if (!mbx_parse (stream)) {/* parse mailbox */ unlockfd (ld,lock); /* shouldn't happen */ return NIL; } if (LOCAL->flagcheck) /* invalidate cache if flagcheck */ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL; } LOCAL->ld = ld; /* copy to stream for subseuent calls */ memcpy (LOCAL->lock,lock,MAILTMPLEN); } return LONGT; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mbx.h000066400000000000000000000012211137544547100225510ustar00rootroot00000000000000/* * Program: MBX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 October 1995 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define HDRSIZE 2048 /* Private driver flags, should be in mail.h? */ #define fEXPUNGED 32768 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mh.c000066400000000000000000001036431137544547100223750ustar00rootroot00000000000000/* * Program: MH mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 23 February 1992 * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "mh.h" #include "misc.h" #include "dummy.h" /* MH I/O stream local data */ typedef struct mh_local { char *dir; /* spool directory name */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long cachedtexts; /* total size of all cached texts */ time_t scantime; /* last time directory scanned */ } MHLOCAL; /* Convenient access to local data */ #define LOCAL ((MHLOCAL *) stream->local) /* Function prototypes */ DRIVER *mh_valid (char *name); int mh_isvalid (char *name,char *tmp,long synonly); void *mh_parameters (long function,void *value); void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mh_list (MAILSTREAM *stream,char *ref,char *pat); void mh_lsub (MAILSTREAM *stream,char *ref,char *pat); void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level); long mh_subscribe (MAILSTREAM *stream,char *mailbox); long mh_unsubscribe (MAILSTREAM *stream,char *mailbox); long mh_create (MAILSTREAM *stream,char *mailbox); long mh_delete (MAILSTREAM *stream,char *mailbox); long mh_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *mh_open (MAILSTREAM *stream); void mh_close (MAILSTREAM *stream,long options); void mh_fast (MAILSTREAM *stream,char *sequence,long flags); char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); long mh_ping (MAILSTREAM *stream); void mh_check (MAILSTREAM *stream); void mh_expunge (MAILSTREAM *stream); long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long options); long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int mh_select (struct direct *name); int mh_numsort (const void *d1,const void *d2); char *mh_file (char *dst,char *name); long mh_canonicalize (char *pattern,char *ref,char *pat); void mh_setdate (char *file,MESSAGECACHE *elt); /* MH mail routines */ /* Driver dispatch used by MAIL */ DRIVER mhdriver = { "mh", /* driver name */ /* driver flags */ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ mh_valid, /* mailbox is valid for us */ mh_parameters, /* manipulate parameters */ mh_scan, /* scan mailboxes */ mh_list, /* find mailboxes */ mh_lsub, /* find subscribed mailboxes */ mh_subscribe, /* subscribe to mailbox */ mh_unsubscribe, /* unsubscribe from mailbox */ mh_create, /* create mailbox */ mh_delete, /* delete mailbox */ mh_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mh_open, /* open mailbox */ mh_close, /* close mailbox */ mh_fast, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mh_header, /* fetch message header */ mh_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mh_ping, /* ping mailbox to see if still alive */ mh_check, /* check for new messages */ mh_expunge, /* expunge deleted messages */ mh_copy, /* copy messages to another mailbox */ mh_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mhproto = {&mhdriver}; /* MH mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mh_valid (char *name) { char tmp[MAILTMPLEN]; return mh_isvalid (name,tmp,T) ? &mhdriver : NIL; } /* MH mail test for valid mailbox * Accepts: mailbox name * temporary buffer to use * syntax only test flag * Returns: T if valid, NIL otherwise */ static char *mh_profile = NIL; /* holds MH profile */ static char *mh_path = NIL; /* holds MH path name */ static long mh_once = 0; /* already through this code */ int mh_isvalid (char *name,char *tmp,long synonly) { struct stat sbuf; /* name must be #MHINBOX or #mh/... */ if ((name[0] != '#') || ((name[1] != 'm') && name[1] != 'M') || ((name[2] != 'h') && name[2] != 'H') || ((name[3] != '/') && compare_cstring (name+3,"INBOX"))) { errno = EINVAL; /* bogus name */ return NIL; } if (!mh_path) { /* have MH path yet? */ char *s,*t,*v; int fd; if (mh_once++) return NIL; /* only do this code once */ if (!mh_profile) { /* have MH profile? */ sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE); mh_profile = cpystr (tmp); } if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { strcat (tmp," not found, mh format names disabled"); mm_log (tmp,WARN); return NIL; } fstat (fd,&sbuf); /* yes, get size and read file */ read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size); close (fd); /* don't need the file any more */ t[sbuf.st_size] = '\0'; /* tie it off */ /* parse profile file */ for (s = strtok (t,"\r\n"); s && *s; s = strtok (NIL,"\r\n")) { /* found space in line? */ if (v = strpbrk (s," \t")) { *v++ = '\0'; /* tie off, is keyword "Path:"? */ if (!strcmp (lcase (s),"path:")) { /* skip whitespace */ while ((*v == ' ') || (*v == '\t')) ++v; if (*v == '/') s = v; /* absolute path? */ else sprintf (s = tmp,"%s/%s",myhomedir (),v); mh_path = cpystr (s); /* copy name */ break; /* don't need to look at rest of file */ } } } fs_give ((void **) &t); /* flush profile text */ if (!mh_path) { /* default path if not in the profile */ sprintf (tmp,"%s/%s",myhomedir (),MHPATH); mh_path = cpystr (tmp); } } if (synonly) return T; /* all done if syntax only check */ errno = NIL; /* zap error */ /* validate name as directory */ return ((stat (mh_file (tmp,name),&sbuf) == 0) && (sbuf.st_mode & S_IFMT) == S_IFDIR); } /* MH manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mh_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_MHPROFILE: if (mh_profile) fs_give ((void **) &mh_profile); mh_profile = cpystr ((char *) value); case GET_MHPROFILE: ret = (void *) mh_profile; break; case SET_MHPATH: if (mh_path) fs_give ((void **) &mh_path); mh_path = cpystr ((char *) value); case GET_MHPATH: ret = (void *) mh_path; break; } return ret; } /* MH scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char tmp[MAILTMPLEN]; if (mh_canonicalize (tmp,ref,pat)) mm_log ("Scan not valid for mh mailboxes",ERROR); } /* MH list mailboxes * Accepts: mail stream * reference * pattern to search */ void mh_list (MAILSTREAM *stream,char *ref,char *pat) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i = 0; if (!pat || !*pat) { /* empty pattern? */ if (mh_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'/')) *++s = '\0'; else test[0] = '\0'; mm_list (stream,'/',test,LATT_NOSELECT); } } /* get canonical form of name */ else if (mh_canonicalize (test,ref,pat)) { if (test[3] == '/') { /* looking down levels? */ /* yes, found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test+4,i = s - (test+4)); file[i] = '\0'; /* tie off */ } else strcpy (file,test+4);/* use just that name then */ /* find directory name */ if (s = strrchr (file,'/')) { *s = '\0'; /* found, tie off at that point */ s = file; } /* do the work */ mh_list_work (stream,s,test,0); } /* always an INBOX */ if (!compare_cstring (test,"#MHINBOX")) mm_list (stream,NIL,"#MHINBOX",LATT_NOINFERIORS); } } /* MH list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mh_lsub (MAILSTREAM *stream,char *ref,char *pat) { void *sdb = NIL; char *s,test[MAILTMPLEN]; /* get canonical form of name */ if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) { do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL); while (s = sm_read (&sdb)); /* until no more subscriptions */ } } /* MH list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * search level */ void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) { DIR *dp; struct direct *d; struct stat sbuf; char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN]; /* build MH name to search */ if (dir) sprintf (name,"#mh/%s/",dir); else strcpy (name,"#mh/"); /* make directory name, punt if bogus */ if (!mh_file (curdir,name)) return; cp = curdir + strlen (curdir);/* end of directory name */ np = name + strlen (name); /* end of MH name */ if (dp = opendir (curdir)) { /* open directory */ while (d = readdir (dp)) /* scan, ignore . and numeric names */ if ((d->d_name[0] != '.') && !mh_select (d)) { strcpy (cp,d->d_name); /* make directory name */ if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) { strcpy (np,d->d_name);/* make mh name of directory name */ /* yes, an MH name if full match */ if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL); /* check if should recurse */ if (dmatch (name,pat,'/') && (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) mh_list_work (stream,name+4,pat,level+1); } } closedir (dp); /* all done, flush directory */ } } /* MH mail subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long mh_subscribe (MAILSTREAM *stream,char *mailbox) { return sm_subscribe (mailbox); } /* MH mail unsubscribe to mailbox * Accepts: mail stream * mailbox to delete from subscription list * Returns: T on success, NIL on failure */ long mh_unsubscribe (MAILSTREAM *stream,char *mailbox) { return sm_unsubscribe (mailbox); } /* MH mail create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long mh_create (MAILSTREAM *stream,char *mailbox) { char *s,tmp[MAILTMPLEN]; /* assume error */ sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox); if (mailbox[0] == '#' && (mailbox[1] == 'm' || mailbox[1] == 'M') && (mailbox[2] == 'h' || mailbox[2] == 'H') && mailbox[3] == '/') /* make sure valid name */ for (s = mailbox + 4; s && *s;) { if (isdigit (*s)) s++; /* digit, check this node further... */ /* all digit node, barf */ else if (*s == '/') s = NIL; /* non-digit in node, skip to next node */ else if (s = strchr (s+1,'/')) s++; else tmp[0] = NIL; /* no more nodes, good name */ } if (tmp[0]) { /* was there an error in the name? */ mm_log (tmp,ERROR); /* yes, log it */ return NIL; } /* must not already exist */ if (mh_isvalid (mailbox,tmp,NIL)) { sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox); mm_log (tmp,ERROR); return NIL; } if (!mh_path) return NIL; /* sorry */ if (!(mh_file (tmp,mailbox) &&/* try to make it */ dummy_create_path (stream,strcat (tmp,"/"), get_dir_protection (mailbox)))) { sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno)); mm_log (tmp,ERROR); return NIL; } return T; /* return success */ } /* MH mail delete mailbox * mailbox name to delete * Returns: T on success, NIL on failure */ long mh_delete (MAILSTREAM *stream,char *mailbox) { DIR *dirp; struct direct *d; int i; char tmp[MAILTMPLEN]; if (!(mailbox[0] == '#' && (mailbox[1] == 'm' || mailbox[1] == 'M') && (mailbox[2] == 'h' || mailbox[2] == 'H') && mailbox[3] == '/')) { sprintf (tmp,"Can't delete mailbox %.80s: invalid MH-format name",mailbox); mm_log (tmp,ERROR); return NIL; } /* is mailbox valid? */ if (!mh_isvalid (mailbox,tmp,NIL)){ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox); mm_log (tmp,ERROR); return NIL; } /* get name of directory */ i = strlen (mh_file (tmp,mailbox)); if (dirp = opendir (tmp)) { /* open directory */ tmp[i++] = '/'; /* now apply trailing delimiter */ while (d = readdir (dirp)) /* massacre all numeric or comma files */ if (mh_select (d) || (*d->d_name == ',') || !strcmp (d->d_name,MHSEQUENCE)) { strcpy (tmp + i,d->d_name); unlink (tmp); /* sayonara */ } closedir (dirp); /* flush directory */ } /* try to remove the directory */ if (rmdir (mh_file (tmp,mailbox))) { sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno)); mm_log (tmp,WARN); } return T; /* return success */ } /* MH mail rename mailbox * Accepts: MH mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long mh_rename (MAILSTREAM *stream,char *old,char *newname) { char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN]; struct stat sbuf; if (!(old[0] == '#' && (old[1] == 'm' || old[1] == 'M') && (old[2] == 'h' || old[2] == 'H') && old[3] == '/')) sprintf (tmp,"Can't delete mailbox %.80s: invalid MH-format name",old); /* old mailbox name must be valid */ else if (!mh_isvalid (old,tmp,NIL)) sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old); else if (!(newname[0] == '#' && (newname[1] == 'm' || newname[1] == 'M') && (newname[2] == 'h' || newname[2] == 'H') && newname[3] == '/')) sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name", newname); /* new mailbox name must not be valid */ else if (mh_isvalid (newname,tmp,NIL)) sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists", newname); /* success if can rename the directory */ else { /* found superior to destination name? */ if (s = strrchr (mh_file (tmp1,newname),'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp1,get_dir_protection (newname))) return NIL; *s = c; /* restore full name */ } if (!rename (mh_file (tmp,old),tmp1)) return T; sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s", old,newname,strerror (errno)); } mm_log (tmp,ERROR); /* something failed */ return NIL; } /* MH mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mh_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */ if (stream->local) fatal ("mh recycle stream"); stream->local = fs_get (sizeof (MHLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"#MHINBOX"); mh_file (tmp,stream->mailbox);/* get directory name */ LOCAL->dir = cpystr (tmp); /* copy directory name for later */ /* make temporary buffer */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); LOCAL->scantime = 0; /* not scanned yet */ LOCAL->cachedtexts = 0; /* no cached texts */ stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (!mh_ping (stream)) return NIL; if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",(long) NIL); return stream; /* return stream to caller */ } /* MH mail close * Accepts: MAIL stream * close options */ void mh_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mh_expunge (stream); if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); /* free local scratch buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ stream->silent = silent; /* reset silent state */ } } /* MH mail fetch fast information * Accepts: MAIL stream * sequence * option flags */ void mh_fast (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i,j; /* ugly and slow */ if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) mh_header (stream,i,&j,NIL); } /* MH mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i,hdrsize; int fd; unsigned char *t; struct stat sbuf; struct tm *tm; MESSAGECACHE *elt; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get elt */ if (!elt->private.msg.header.text.data) { /* purge cache if too big */ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) { mail_gc (stream,GC_TEXTS);/* just can't keep that much */ LOCAL->cachedtexts = 0; } /* build message file name */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return ""; fstat (fd,&sbuf); /* get size of message */ /* make plausible IMAPish date string */ tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ /* find end of header */ for (i = 0,t = LOCAL->buf; *t && !(i && (*t == '\n')); i = (*t++ == '\n')); /* number of header bytes */ hdrsize = (*t ? ++t : t) - LOCAL->buf; elt->rfc822_size = /* size of entire message in CRLF form */ (elt->private.msg.header.text.size = strcrlfcpy (&elt->private.msg.header.text.data,&i,LOCAL->buf, hdrsize)) + (elt->private.msg.text.text.size = strcrlfcpy (&elt->private.msg.text.text.data,&i,t, sbuf.st_size - hdrsize)); /* add to cached size */ LOCAL->cachedtexts += elt->rfc822_size; } *length = elt->private.msg.header.text.size; return (char *) elt->private.msg.header.text.data; } /* MH mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL on failure */ long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get elt */ /* snarf message if don't have it yet */ if (!elt->private.msg.text.text.data) { mh_header (stream,msgno,&i,flags); if (!elt->private.msg.text.text.data) return NIL; } if (!(flags & FT_PEEK)) { /* mark as seen */ mail_elt (stream,msgno)->seen = T; mm_flags (stream,msgno); } if (!elt->private.msg.text.text.data) return NIL; INIT (bs,mail_string,elt->private.msg.text.text.data, elt->private.msg.text.text.size); return T; } /* MH mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long mh_ping (MAILSTREAM *stream) { MAILSTREAM *sysibx = NIL; MESSAGECACHE *elt,*selt; struct stat sbuf; char *s,tmp[MAILTMPLEN]; int fd; unsigned long i,j,r,old; long nmsgs = stream->nmsgs; long recent = stream->recent; int silent = stream->silent; if (stat (LOCAL->dir,&sbuf)) { /* directory exists? */ if (stream->inbox) return T; sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox); mm_log (tmp,ERROR); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ if (sbuf.st_ctime != LOCAL->scantime) { struct direct **names = NIL; long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort); if (nfiles < 0) nfiles = 0; /* in case error */ old = stream->uid_last; /* note scanned now */ LOCAL->scantime = sbuf.st_ctime; /* scan directory */ for (i = 0; i < nfiles; ++i) { /* if newly seen, add to list */ if ((j = atoi (names[i]->d_name)) > old) { mail_exists (stream,++nmsgs); stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j; elt->valid = T; /* note valid flags */ if (old) { /* other than the first pass? */ elt->recent = T; /* yup, mark as recent */ recent++; /* bump recent count */ } else { /* see if already read */ sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name); stat (tmp,&sbuf); /* get inode poop */ if (sbuf.st_atime > sbuf.st_mtime) elt->seen = T; } } /* TkRat: This memory comes from the system malloc and should not be handled by the tcl ck* routines */ free ((void *) names[i]); } /* TkRat: This memory comes from the system malloc and should not be handled by the tcl ck* routines */ /* free directory */ if (s = (void *) names) free ((void *) s); } /* if INBOX, snarf from system INBOX */ if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) { old = stream->uid_last; mm_critical (stream); /* go critical */ stat (sysinbox (),&sbuf); /* see if anything there */ /* can get sysinbox mailbox? */ if (sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */ /* build file name we will use */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old); /* snarf message from Berkeley mailbox */ selt = mail_elt (sysibx,i); if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL, S_IREAD|S_IWRITE)) >= 0) && (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) && (write (fd,s,j) == j) && (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) && (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) { /* swell the cache */ mail_exists (stream,++nmsgs); stream->uid_last = /* create new elt, note its file number */ (elt = mail_elt (stream,nmsgs))->private.uid = old; recent++; /* bump recent count */ /* set up initial flags and date */ elt->valid = elt->recent = T; elt->seen = selt->seen; elt->deleted = selt->deleted; elt->flagged = selt->flagged; elt->answered = selt->answered; elt->draft = selt->draft; elt->day = selt->day;elt->month = selt->month;elt->year = selt->year; elt->hours = selt->hours;elt->minutes = selt->minutes; elt->seconds = selt->seconds; elt->zhours = selt->zhours; elt->zminutes = selt->zminutes; mh_setdate (LOCAL->buf,elt); } else { /* failed to snarf */ if (fd) { /* did it ever get opened? */ mm_log ("Message copy to MH mailbox failed",ERROR); close (fd); /* close descriptor */ unlink (LOCAL->buf);/* flush this file */ } else { sprintf (tmp,"Can't add message: %s",strerror (errno)); mm_log (tmp,ERROR); } stream->silent = silent; return NIL; /* note that something is badly wrong */ } sprintf (tmp,"%lu",i); /* delete it from the sysinbox */ mail_flag (sysibx,tmp,"\\Deleted",ST_SET); } stat (LOCAL->dir,&sbuf); /* update scan time */ LOCAL->scantime = sbuf.st_ctime; mail_expunge (sysibx); /* now expunge all those messages */ } if (sysibx) mail_close (sysibx); mm_nocritical (stream); /* release critical */ } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */ mail_recent (stream,recent); return T; /* return that we are alive */ } /* MH mail check mailbox * Accepts: MAIL stream */ void mh_check (MAILSTREAM *stream) { /* Perhaps in the future this will preserve flags */ if (mh_ping (stream)) mm_log ("Check completed",(long) NIL); } /* MH mail expunge mailbox * Accepts: MAIL stream */ void mh_expunge (MAILSTREAM *stream) { MESSAGECACHE *elt; unsigned long i = 1; unsigned long n = 0; unsigned long recent = stream->recent; mm_critical (stream); /* go critical */ while (i <= stream->nmsgs) { /* for each message */ /* if deleted, need to trash it */ if ((elt = mail_elt (stream,i))->deleted) { sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if (unlink (LOCAL->buf)) {/* try to delete the message */ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i, strerror (errno)); mm_log (LOCAL->buf,(long) NIL); break; } /* note uncached */ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ? elt->private.msg.header.text.size : 0) + (elt->private.msg.text.text.data ? elt->private.msg.text.text.size : 0)); mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS); if (elt->recent) --recent;/* if recent, note one less recent message */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else i++; /* otherwise try next message */ } if (n) { /* output the news if any expunged */ sprintf (LOCAL->buf,"Expunged %lu messages",n); mm_log (LOCAL->buf,(long) NIL); } else mm_log ("No messages deleted, so no update needed",(long) NIL); mm_nocritical (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); } /* MH mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { STRING st; MESSAGECACHE *elt; struct stat sbuf; int fd; unsigned long i; char flags[MAILTMPLEN],date[MAILTMPLEN]; /* copy the messages */ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL; fstat (fd,&sbuf); /* get size of message */ if (!elt->day) { /* make plausible IMAPish date string */ struct tm *tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; } /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size); /* init flag string */ flags[0] = flags[1] = '\0'; if (elt->seen) strcat (flags," \\Seen"); if (elt->deleted) strcat (flags," \\Deleted"); if (elt->flagged) strcat (flags," \\Flagged"); if (elt->answered) strcat (flags," \\Answered"); if (elt->draft) strcat (flags," \\Draft"); flags[0] = '('; /* open list */ strcat (flags,")"); /* close list */ mail_date (date,elt); /* generate internal date */ if (!mail_append_full (NIL,mailbox,flags,date,&st)) return NIL; if (options & CP_MOVE) elt->deleted = T; } return T; /* return success */ } /* MH mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct direct **names; int fd; char c,*flags,*date,*s,tmp[MAILTMPLEN]; STRING *message; MESSAGECACHE elt; long i,size,last,nfiles; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = &mhproto; /* make sure valid mailbox */ if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"#mhinbox")) mh_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; /* TkRat: This memory comes from the system malloc and should not be handled by the tcl ck* routines */ if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) { /* largest number */ last = atoi (names[nfiles-1]->d_name); for (i = 0; i < nfiles; ++i) /* free directory */ free((void *)names[i]); } else last = 0; /* no messages here yet */ if (s = (void *) names) free ((void*)s); mm_critical (stream); /* go critical */ do { if (!SIZE (message)) { /* guard against zero-length */ mm_log ("Append of zero-length message",ERROR); ret = NIL; break; } if (date) { /* want to preserve date? */ /* yes, parse date into an elt */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); ret = NIL; break; } } mh_file (tmp,mailbox); /* build file name we will use */ sprintf (tmp + strlen (tmp),"/%ld",++last); if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { sprintf (tmp,"Can't open append message: %s",strerror (errno)); mm_log (tmp,ERROR); ret = NIL; break; } /* copy the data w/o CR's */ for (size = 0,i = SIZE (message),s = (char *) fs_get (i + 1); i; --i) if ((c = SNX (message)) != '\015') s[size++] = c; /* write the data */ if ((write (fd,s,size) < 0) || fsync (fd)) { unlink (tmp); /* delete message */ sprintf (tmp,"Message append failed: %s",strerror (errno)); mm_log (tmp,ERROR); ret = NIL; } fs_give ((void **) &s); /* flush the buffer */ close (fd); /* close the file */ if (ret) { /* set the date for this message */ if (date) mh_setdate (tmp,&elt); /* get next message */ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); mm_nocritical (stream); /* release critical */ return ret; } /* Internal routines */ /* MH file name selection test * Accepts: candidate directory entry * Returns: T to use file name, NIL to skip it */ int mh_select (struct direct *name) { char c; char *s = name->d_name; while (c = *s++) if (!isdigit (c)) return NIL; return T; } /* MH file name comparision * Accepts: first candidate directory entry * second candidate directory entry * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2 */ int mh_numsort (const void *d1,const void *d2) { return atoi ((*(struct direct **) d1)->d_name) - atoi ((*(struct direct **) d2)->d_name); } /* MH mail build file name * Accepts: destination string * source * Returns: destination */ char *mh_file (char *dst,char *name) { char *s; /* build composite name */ sprintf (dst,"%s/%.900s",mh_path, compare_cstring (name,"#MHINBOX") ? name + 4 : "inbox"); /* tie off unnecessary trailing / */ if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0'; return dst; } /* MH canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long mh_canonicalize (char *pattern,char *ref,char *pat) { char tmp[MAILTMPLEN]; if (ref && *ref) { /* have a reference */ strcpy (pattern,ref); /* copy reference to pattern */ /* # overrides mailbox field in reference */ if (*pat == '#') strcpy (pattern,pat); /* pattern starts, reference ends, with / */ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/')) strcat (pattern,pat + 1); /* append, omitting one of the period */ else strcat (pattern,pat); /* anything else is just appended */ } else strcpy (pattern,pat); /* just have basic name */ return (mh_isvalid (pattern,tmp,T)); } /* Set date for message * Accepts: file name * elt containing date */ void mh_setdate (char *file,MESSAGECACHE *elt) { time_t tp[2]; tp[0] = time (0); /* atime is now */ tp[1] = mail_longdate (elt); /* modification time */ utime (file,tp); /* set the times */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mh.h000066400000000000000000000012141137544547100223710ustar00rootroot00000000000000/* * Program: MH mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 23 February 1992 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define MHPROFILE ".mh_profile" #define MHSEQUENCE ".mh_sequence" #define MHPATH "Mail" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mkauths000077500000000000000000000017411137544547100232230ustar00rootroot00000000000000#!/bin/sh # # Program: Authenticator Linkage Generator # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 5 December 1995 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. # Erase old authenticators list rm -f auths.c touch auths.c # Now define the new list for authenticator do if [ -f Makefile."$authenticator" ]; then make -f Makefile."$authenticator" `cat SPECIALS` fi echo "extern AUTHENTICATOR auth_"$authenticator";" >> linkage.h echo " auth_link (&auth_"$authenticator"); /* link in the $authenticator authenticator */" | cat >> linkage.c echo "#include \"auth_"$authenticator".c\"" >> auths.c done tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mmdf.c000066400000000000000000002423271137544547100227170ustar00rootroot00000000000000/* * Program: MMDF mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include #include "mail.h" #include "osdep.h" #include #include #include "pseudo.h" #include "fdstring.h" #include "misc.h" #include "dummy.h" /* Supposedly, this page has everything the MMDF driver needs to know about * the MMDF delimiter. By changing these macros, the MMDF driver should * change with it. Note that if you change the length of MMDFHDRTXT you * also need to change the ISMMDF and RETIFMMDFWRD macros to reflect the new * size. */ /* Useful MMDF constants */ #define MMDFCHR '\01' /* MMDF character */ #define MMDFCHRS 0x01010101 /* MMDF header character spread in a word */ /* MMDF header text */ #define MMDFHDRTXT "\01\01\01\01\n" /* length of MMDF header text */ #define MMDFHDRLEN (sizeof (MMDFHDRTXT) - 1) /* Validate MMDF header * Accepts: pointer to candidate string to validate as an MMDF header * Returns: T if valid; else NIL */ #define ISMMDF(s) \ ((*(s) == MMDFCHR) && ((s)[1] == MMDFCHR) && ((s)[2] == MMDFCHR) && \ ((s)[3] == MMDFCHR) && ((s)[4] == '\n')) /* Return if a 32-bit word has the start of an MMDF header * Accepts: pointer to word of four bytes to validate as an MMDF header * Returns: pointer to MMDF header, else proceeds */ #define RETIFMMDFWRD(s) { \ if (s[3] == MMDFCHR) { \ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == MMDFCHR) && \ (s[7] == '\n')) return s + 3; \ else if (s[2] == MMDFCHR) { \ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == '\n')) \ return s + 2; \ else if (s[1] == MMDFCHR) { \ if ((s[4] == MMDFCHR) && (s[5] == '\n')) return s + 1; \ else if ((*s == MMDFCHR) && (s[4] == '\n')) return s; \ } \ } \ } \ } /* Validate line * Accepts: pointer to candidate string to validate as a From header * return pointer to end of date/time field * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') * return pointer to offset from t of time zone (if non-zero) * Returns: t,ti,zn set if valid From string, else ti is NIL */ #define VALID(s,x,ti,zn) { \ ti = 0; \ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ (s[4] == ' ')) { \ for (x = s + 5; *x && *x != '\n'; x++); \ if (*x) { \ if (x - s >= 41) { \ for (zn = -1; x[zn] != ' '; zn--); \ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ x += zn - 12; \ } \ if (x - s >= 27) { \ if (x[-5] == ' ') { \ if (x[-8] == ':') zn = 0,ti = -5; \ else if (x[-9] == ' ') ti = zn = -9; \ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ ti = zn = -11; \ } \ else if (x[-4] == ' ') { \ if (x[-9] == ' ') zn = -4,ti = -9; \ } \ else if (x[-6] == ' ') { \ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ zn = -6,ti = -11; \ } \ if (ti && !((x[ti - 3] == ':') && \ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ (x[ti - 11] == ' '))) ti = 0; \ } \ } \ } \ } /* You are not expected to understand this macro, but read the next page if * you are not faint of heart. * * Known formats to the VALID macro are: * From user Wed Dec 2 05:53 1992 * BSD From user Wed Dec 2 05:53:22 1992 * SysV From user Wed Dec 2 05:53 PST 1992 * rn From user Wed Dec 2 05:53:22 PST 1992 * From user Wed Dec 2 05:53 -0700 1992 * emacs From user Wed Dec 2 05:53:22 -0700 1992 * From user Wed Dec 2 05:53 1992 PST * From user Wed Dec 2 05:53:22 1992 PST * From user Wed Dec 2 05:53 1992 -0700 * Solaris From user Wed Dec 2 05:53:22 1992 -0700 * * Plus all of the above with `` remote from xxx'' after it. Thank you very * much, smail and Solaris, for making my life considerably more complicated. */ /* * What? You want to understand the VALID macro anyway? Alright, since you * insist. Actually, it isn't really all that difficult, provided that you * take it step by step. * * Line 1 Initializes the return ti value to failure (0); * Lines 2-3 Validates that the 1st-5th characters are ``From ''. * Lines 4-5 Validates that there is an end of line and points x at it. * Lines 6-13 First checks to see if the line is at least 41 characters long. * If so, it scans backwards to find the rightmost space. From * that point, it scans backwards to see if the string matches * `` remote from''. If so, it sets x to point to the space at * the start of the string. * Line 14 Makes sure that there are at least 27 characters in the line. * Lines 15-20 Checks if the date/time ends with the year (there is a space * five characters back). If there is a colon three characters * further back, there is no timezone field, so zn is set to 0 * and ti is set in front of the year. Otherwise, there must * either to be a space four characters back for a three-letter * timezone, or a space six characters back followed by a + or - * for a numeric timezone; in either case, zn and ti become the * offset of the space immediately before it. * Lines 21-23 Are the failure case for line 14. If there is a space four * characters back, it is a three-letter timezone; there must be a * space for the year nine characters back. zn is the zone * offset; ti is the offset of the space. * Lines 24-27 Are the failure case for line 20. If there is a space six * characters back, it is a numeric timezone; there must be a * space eleven characters back and a + or - five characters back. * zn is the zone offset; ti is the offset of the space. * Line 28-31 If ti is valid, make sure that the string before ti is of the * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise * invalidate ti. There must be a colon three characters back * and a space six or nine characters back (depending upon * whether or not the character six characters back is a colon). * There must be a space three characters further back (in front * of the day), one seven characters back (in front of the month), * and one eleven characters back (in front of the day of week). * ti is set to be the offset of the space before the time. * * Why a macro? It gets invoked a *lot* in a tight loop. On some of the * newer pipelined machines it is faster being open-coded than it would be if * subroutines are called. * * Why does it scan backwards from the end of the line, instead of doing the * much easier forward scan? There is no deterministic way to parse the * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to * see if unquoted spaces were possible. They are, and I've encountered enough * evil mail to be totally unwilling to trust that ``it will never happen''. */ /* Build parameters */ #define KODRETRY 15 /* kiss-of-death retry in seconds */ #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define CHUNK 16384 /* read-in chunk size */ /* MMDF I/O stream local data */ typedef struct mmdf_local { unsigned int dirty : 1; /* disk copy needs updating */ unsigned int pseudo : 1; /* uses a pseudo message */ int fd; /* mailbox file descriptor */ int ld; /* lock file descriptor */ char *lname; /* lock file name */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ unsigned long textlen; /* current text length */ char *line; /* returned line */ } MMDFLOCAL; /* Convenient access to local data */ #define LOCAL ((MMDFLOCAL *) stream->local) /* MMDF protected file structure */ typedef struct mmdf_file { MAILSTREAM *stream; /* current stream */ off_t curpos; /* current file position */ off_t protect; /* protected position */ off_t filepos; /* current last written file position */ char *buf; /* overflow buffer */ size_t buflen; /* current overflow buffer length */ char *bufpos; /* current buffer position */ } MMDFFILE; /* Function prototypes */ DRIVER *mmdf_valid (char *name); long mmdf_isvalid (char *name,char *tmp); long mmdf_isvalid_fd (int fd,char *tmp); void *mmdf_parameters (long function,void *value); void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mmdf_list (MAILSTREAM *stream,char *ref,char *pat); void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat); long mmdf_create (MAILSTREAM *stream,char *mailbox); long mmdf_delete (MAILSTREAM *stream,char *mailbox); long mmdf_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *mmdf_open (MAILSTREAM *stream); void mmdf_close (MAILSTREAM *stream,long options); char *mmdf_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mmdf_ping (MAILSTREAM *stream); void mmdf_check (MAILSTREAM *stream); void mmdf_check (MAILSTREAM *stream); void mmdf_expunge (MAILSTREAM *stream); long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int mmdf_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void mmdf_abort (MAILSTREAM *stream); char *mmdf_file (char *dst,char *name); int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op); void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock); int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op); char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr); unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag); long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock); long mmdf_extend (MAILSTREAM *stream,unsigned long size); void mmdf_write (MMDFFILE *f,char *s,unsigned long i); void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size); /* MMDF mail routines */ /* Driver dispatch used by MAIL */ DRIVER mmdfdriver = { "mmdf", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ mmdf_valid, /* mailbox is valid for us */ mmdf_parameters, /* manipulate parameters */ mmdf_scan, /* scan mailboxes */ mmdf_list, /* list mailboxes */ mmdf_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mmdf_create, /* create mailbox */ mmdf_delete, /* delete mailbox */ mmdf_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mmdf_open, /* open mailbox */ mmdf_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mmdf_header, /* fetch message header */ mmdf_text, /* fetch message text */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ mmdf_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mmdf_ping, /* ping mailbox to see if still alive */ mmdf_check, /* check for new messages */ mmdf_expunge, /* expunge deleted messages */ mmdf_copy, /* copy messages to another mailbox */ mmdf_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mmdfproto = {&mmdfdriver}; char *mmdfhdr = MMDFHDRTXT; /* MMDF header */ /* MMDF mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mmdf_valid (char *name) { char tmp[MAILTMPLEN]; return mmdf_isvalid (name,tmp) ? &mmdfdriver : NIL; } /* MMDF mail test for valid mailbox name * Accepts: mailbox name * scratch buffer * Returns: T if valid, NIL otherwise */ long mmdf_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *t,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* must be non-empty file */ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { /* error -1 for invalid format */ if (!(ret = mmdf_isvalid_fd (fd,tmp))) errno = -1; close (fd); /* close the file */ /* \Marked status? */ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } return ret; /* return what we should */ } /* MMDF mail test for valid mailbox * Accepts: file descriptor * scratch buffer * Returns: T if valid, NIL otherwise */ long mmdf_isvalid_fd (int fd,char *tmp) { int ret = NIL; memset (tmp,'\0',MAILTMPLEN); if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = ISMMDF (tmp) ? T : NIL; return ret; /* return what we should */ } /* MMDF manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mmdf_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = dummy_file ((char *) value,"INBOX"); break; } return ret; } /* MMDF mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MMDF mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mmdf_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MMDF mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MMDF mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mmdf_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN]; long ret = NIL; int i,fd; time_t ti = time (0); if (!(s = dummy_file (mbx,mailbox))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); MM_LOG (tmp,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) { /* done if made directory */ if ((s = strrchr (s,'/')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY, (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } /* in case a whiner with no life */ else if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T; else { /* initialize header */ memset (tmp,'\0',MAILTMPLEN); sprintf (tmp,"%sFrom %s %sDate: ",mmdfhdr,pseudo_from,ctime (&ti)); rfc822_date (s = tmp + strlen (tmp)); sprintf (s += strlen (s), /* write the pseudo-header */ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000", pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) ti); for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i)) sprintf (s += strlen (s)," %s",default_user_flag (i)); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr); if ((write (fd,tmp,strlen (tmp)) < 0) || close (fd)) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx, strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ } close (fd); /* close file, set proper protections */ } return ret ? set_mbx_protections (mailbox,mbx) : NIL; } /* MMDF mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mmdf_delete (MAILSTREAM *stream,char *mailbox) { return mmdf_rename (stream,mailbox,NIL); } /* MMDF mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mmdf_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = NIL; char c,*s = NIL; char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; DOTLOCK lockx; int fd,ld; long i; struct stat sbuf; MM_CRITICAL (stream); /* get the c-client lock */ if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); /* lock out other c-clients */ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0) sprintf (tmp,"Mailbox %.80s is in use by another process",old); else { if ((fd = mmdf_lock (file,O_RDWR,S_IREAD|S_IWRITE,&lockx,LOCK_EX)) < 0) sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno)); else { if (newname) { /* want rename? */ /* found superior to destination name? */ if (s = strrchr (s,'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) { mmdf_unlock (fd,NIL,&lockx); mmdf_unlock (ld,NIL,NIL); unlink (lock); MM_NOCRITICAL (stream); return ret; /* return success or failure */ } *s = c; /* restore full name */ } if (rename (file,tmp)) sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); else ret = T; /* set success */ } else if (unlink (file)) sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); else ret = T; /* set success */ mmdf_unlock (fd,NIL,&lockx); } mmdf_unlock (ld,NIL,NIL); /* flush the lock */ unlink (lock); } MM_NOCRITICAL (stream); /* no longer critical */ if (!ret) MM_LOG (tmp,ERROR); /* log error */ return ret; /* return success or failure */ } /* MMDF mail open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *mmdf_open (MAILSTREAM *stream) { long i; int fd; char tmp[MAILTMPLEN]; DOTLOCK lock; long retry; /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mmdfproto); retry = stream->silent ? 1 : KODRETRY; if (stream->local) fatal ("mmdf recycle stream"); stream->local = memset (fs_get (sizeof (MMDFLOCAL)),0,sizeof (MMDFLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); /* canonicalize the stream mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); MM_LOG (tmp,ERROR); return NIL; } /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); stream->sequence++; /* bump sequence number */ /* make lock for read/write access */ if (!stream->rdonly) while (retry) { /* try to lock file */ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) { if (retry-- == KODRETRY) {/* no, first time through? */ if (i) { /* learned the other guy's PID? */ kill ((int) i,SIGUSR2); sprintf (tmp,"Trying to get mailbox lock from process %ld",i); MM_LOG (tmp,WARN); } else retry = 0; /* give up */ } if (!stream->silent) { /* nothing if silent stream */ if (retry) sleep (1); /* wait a second before trying again */ else MM_LOG ("Mailbox is open by another process, access is readonly", WARN); } } else { /* got the lock, nobody else can alter state */ LOCAL->ld = fd; /* note lock's fd and name */ LOCAL->lname = cpystr (tmp); /* make sure mode OK (don't use fchmod()) */ chmod (LOCAL->lname,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL)); if (stream->silent) i = 0;/* silent streams won't accept KOD */ else { /* note our PID in the lock */ sprintf (tmp,"%d",getpid ()); write (fd,tmp,(i = strlen (tmp))+1); } ftruncate (fd,i); /* make sure tied off */ fsync (fd); /* make sure it's available */ retry = 0; /* no more need to try */ } } /* parse mailbox */ stream->nmsgs = stream->recent = 0; /* will we be able to get write access? */ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) { MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); flock (LOCAL->ld,LOCK_UN); /* release the lock */ close (LOCAL->ld); /* close the lock file */ LOCAL->ld = -1; /* no more lock fd */ unlink (LOCAL->lname); /* delete it */ } /* reset UID validity */ stream->uid_validity = stream->uid_last = 0; if (stream->silent && !stream->rdonly && (LOCAL->ld < 0)) mmdf_abort (stream); /* abort if can't get RW silent stream */ /* parse mailbox */ else if (mmdf_parse (stream,&lock,LOCK_SH)) { mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ } if (!LOCAL) return NIL; /* failure if stream died */ /* make sure upper level knows readonly */ stream->rdonly = (LOCAL->ld < 0); /* notify about empty mailbox */ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL); if (!stream->rdonly) { /* flags stick if readwrite */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = T; if (!stream->uid_nosticky) {/* users with lives get permanent keywords */ stream->perm_user_flags = 0xffffffff; /* and maybe can create them too! */ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T; } } return stream; /* return stream alive to caller */ } /* MMDF mail close * Accepts: MAIL stream * close options */ void mmdf_close (MAILSTREAM *stream,long options) { int silent = stream->silent; stream->silent = T; /* go silent */ /* expunge if requested */ if (options & CL_EXPUNGE) mmdf_expunge (stream); /* else dump final checkpoint */ else if (LOCAL->dirty) mmdf_check (stream); stream->silent = silent; /* restore old silence state */ mmdf_abort (stream); /* now punt the file and local data */ } /* MMDF mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ /* lines to filter from header */ static STRINGLIST *mmdf_hlines = NIL; char *mmdf_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { MESSAGECACHE *elt; unsigned char *s,*t,*tl; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get cache */ if (!mmdf_hlines) { /* once only code */ STRINGLIST *lines = mmdf_hlines = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Keywords")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-UID")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAP")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAPbase")); } /* go to header position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.header.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.header.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.header.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; } else { /* need to make a CRLF version */ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1), elt->private.msg.header.text.size); /* tie off string, and convert to CRLF */ s[elt->private.msg.header.text.size] = '\0'; *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, elt->private.msg.header.text.size); fs_give ((void **) &s); /* free readin buffer */ } *length = mail_filter (LOCAL->buf,*length,mmdf_hlines,FT_NOT); return LOCAL->buf; /* return processed copy */ } /* MMDF mail fetch message text * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL if failure */ long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get cache element */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { /* mark message seen and dirty */ elt->seen = elt->private.dirty = LOCAL->dirty = T; MM_FLAGS (stream,msgno); } s = mmdf_text_work (stream,elt,&i,flags); INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* MMDF mail fetch message text worker routine * Accepts: MAIL stream * message cache element * pointer to returned header text length * option flags */ char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags) { FDDATA d; STRING bs; unsigned char *s,*t,*tl,tmp[CHUNK]; /* go to text position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.text.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.text.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.text.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; return LOCAL->buf; } /* have it cached already? */ if (elt->private.uid != LOCAL->uid) { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->text.size) { /* excessively conservative, but the right thing is too hard to do */ fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); } d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = elt->private.special.offset + elt->private.msg.text.offset; d.chunk = tmp; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) { case '\r': /* carriage return seen */ *s++ = SNX (&bs); /* copy it and any succeeding LF */ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); break; case '\n': *s++ = '\r'; /* insert a CR */ default: *s++ = SNX (&bs); /* copy characters */ } *s = '\0'; /* tie off buffer */ /* calculate length of cached data */ LOCAL->textlen = s - LOCAL->text.data; } *length = LOCAL->textlen; /* return from cache */ return (char *) LOCAL->text.data; } /* MMDF per-message modify flag * Accepts: MAIL stream * message cache element */ void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { /* only after finishing */ if (elt->valid) elt->private.dirty = LOCAL->dirty = T; } /* MMDF mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long mmdf_ping (MAILSTREAM *stream) { DOTLOCK lock; struct stat sbuf; long reparse; /* big no-op if not readwrite */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) { if (stream->rdonly) { /* does he want to give up readwrite? */ /* checkpoint if we changed something */ if (LOCAL->dirty) mmdf_check (stream); flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ close (LOCAL->ld); /* close the readwrite lock file */ LOCAL->ld = -1; /* no more readwrite lock fd */ unlink (LOCAL->lname); /* delete the readwrite lock file */ } else { /* see if need to reparse */ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) { /* get current mailbox size */ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); else stat (stream->mailbox,&sbuf); reparse = (sbuf.st_size != LOCAL->filesize); } /* parse if mailbox changed */ if (reparse && mmdf_parse (stream,&lock,LOCK_SH)) { /* unlock mailbox */ mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* and stream */ /* done with critical */ MM_NOCRITICAL (stream); } } } return LOCAL ? LONGT : NIL; /* return if still alive */ } /* MMDF mail check mailbox * Accepts: MAIL stream */ void mmdf_check (MAILSTREAM *stream) { DOTLOCK lock; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && mmdf_parse (stream,&lock,LOCK_EX)) { /* any unsaved changes? */ if (LOCAL->dirty && mmdf_rewrite (stream,NIL,&lock)) { if (!stream->silent) MM_LOG ("Checkpoint completed",NIL); } /* no checkpoint needed, just unlock */ else mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ } } /* MMDF mail expunge mailbox * Accepts: MAIL stream */ void mmdf_expunge (MAILSTREAM *stream) { unsigned long i; DOTLOCK lock; char *msg = NIL; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && mmdf_parse (stream,&lock,LOCK_EX)) { /* count expunged messages if not dirty */ if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; if (!LOCAL->dirty) { /* not dirty and no expunged messages */ mmdf_unlock (LOCAL->fd,stream,&lock); msg = "No messages deleted, so no update needed"; } else if (mmdf_rewrite (stream,&i,&lock)) { if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); else msg = "Mailbox checkpointed, but no messages expunged"; } /* rewrite failed */ else mmdf_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ if (msg && !stream->silent) MM_LOG (msg,NIL); } else if (!stream->silent) MM_LOG ("Expunge ignored on readonly mailbox",WARN); } /* MMDF mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; int fd; char *s,file[MAILTMPLEN]; DOTLOCK lock; time_t tp[2]; unsigned long i,j; MESSAGECACHE *elt; long ret = T; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* make sure valid mailbox */ if (!mmdf_isvalid (mailbox,file)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { if (pc) return (*pc) (stream,sequence,mailbox,options); mmdf_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MMDF-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MMDF-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } LOCAL->buf[0] = '\0'; MM_CRITICAL (stream); /* go critical */ if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); /* log the error */ return NIL; /* failed */ } fstat (fd,&sbuf); /* get current file size */ /* write all requested messages to mailbox */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL; else { /* internal header succeeded */ s = mmdf_header (stream,i,&j,FT_INTERNAL); /* header size, sans trailing newline */ if (j && (s[j - 2] == '\n')) j--; if (write (fd,s,j) < 0) ret = NIL; else { /* message header succeeded */ j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* message status succeeded */ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL); if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0)) ret = NIL; } } } } if (!ret || fsync (fd)) { /* force out the update */ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); ftruncate (fd,sbuf.st_size); ret = NIL; } tp[1] = time (0); /* set mtime to now */ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ else tp[0] = /* else preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; utime (file,tp); /* set the times */ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ MM_NOCRITICAL (stream); /* release critical */ /* log the error */ if (!ret) MM_LOG (LOCAL->buf,ERROR); /* delete if requested message */ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = elt->private.dirty = LOCAL->dirty = T; return ret; } /* MMDF mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ #define BUFLEN 8*MAILTMPLEN long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN]; time_t tp[2]; FILE *sf,*df; MESSAGECACHE elt; DOTLOCK lock; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) { /* stream specified? */ stream = &mmdfproto; /* no, default stream to prototype */ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) fs_give ((void **) &stream->user_flags[i]); stream->kwd_create = T; /* allow new flags */ } /* make sure valid mailbox */ if (!mmdf_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { mmdf_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } unlink (tmp); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR); else if (!mmdf_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); } /* get next message */ else if (MM_APPEND (af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ MM_CRITICAL (stream); /* go critical */ if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ rewind (sf); for (; i && ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) && (fwrite (buf,1,j,df) == j)); i -= j); fclose (sf); /* done with scratch file */ tp[1] = time (0); /* set mtime to now */ /* make sure append wins, fsync() necessary */ if (i || (fflush (df) == EOF) || fsync (fd)) { sprintf (buf,"Message append failed: %s",strerror (errno)); MM_LOG (buf,ERROR); ftruncate (fd,sbuf.st_size); tp[0] = /* preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; ret = NIL; /* return error */ } else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ utime (file,tp); /* set the times */ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ fclose (df); MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */ int mmdf_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg) { unsigned long i,uf; int c; char tmp[MAILTMPLEN]; int hdrp = T; long f = mail_parse_flags (stream,flags,&uf); /* build initial header */ if ((fprintf (sf,"%sFrom %s@%s %sStatus: ", mmdfhdr,myusername (),mylocalhost (),date) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if (putc ('\n',sf) == EOF) return NIL; while (SIZE (msg)) { /* copy text to scratch file */ /* disregard CRs */ while ((c = (SIZE (msg)) ? (c = 0xff & SNX (msg)) : '\n') == '\r'); /* see if line needs special treatment */ if (hdrp && ((c == 'S') || (c == 'X'))) { /* copy line to buffer */ for (i = 1,tmp[0] = c; (c != '\n') && (i < MAILTMPLEN); ) if ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') != '\r') tmp[i++] = c; /* insert X- before metadata header */ if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && (tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && (tmp[5] == 's') && (tmp[6] == ':')) || (((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && (tmp[5] == ':')) || ((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && (tmp[4] == 'A') && (tmp[5] == 'P') && ((tmp[6] == ':') || ((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && (tmp[8] == 's') && (tmp[9] == 'e') && (tmp[10] == ':')))) || ((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && (tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && (tmp[7] == 's') && (tmp[8] == ':')) || ((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && (tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && (tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && (tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF)) return NIL; /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; /* set up to copy remainder of line */ if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); else continue; /* end of line or end of message */ } /* check for end of header */ else if (hdrp && (c == '\n')) hdrp = NIL; do switch (c) { /* copy line */ case MMDFCHR: /* flush CTRL/A */ case '\r': /* and CR */ break; default: /* any other character */ if (putc (c,sf) == EOF) return NIL; } while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* write trailer and return */ return (fputs (mmdfhdr,sf) == EOF) ? NIL : T; } /* Internal routines */ /* MMDF mail abort stream * Accepts: MAIL stream */ void mmdf_abort (MAILSTREAM *stream) { if (LOCAL) { /* only if a file is open */ if (LOCAL->fd >= 0) close (LOCAL->fd); if (LOCAL->ld >= 0) { /* have a mailbox lock? */ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ close (LOCAL->ld); /* close the lock file */ unlink (LOCAL->lname); /* and delete it */ } if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); /* free local text buffers */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MMDF open and lock mailbox * Accepts: file name to open/lock * file open mode * destination buffer for lock file name * type of locking operation (LOCK_SH or LOCK_EX) */ int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op) { int fd; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_FILELOCK,NIL); /* try locking the easy way */ if (dotlock_lock (file,lock,-1)) { /* got dotlock file, easy open */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } /* no dot lock file, open file now */ else if ((fd = open (file,flags,mode)) >= 0) { /* try paranoid way to make a dot lock file */ if (dotlock_lock (file,lock,fd)) { close (fd); /* get fresh fd in case of timing race */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } else flock (fd,op); /* paranoid way failed, just flock() it */ } (*bn) (BLOCK_NONE,NIL); return fd; } /* MMDF unlock and close mailbox * Accepts: file descriptor * (optional) mailbox stream to check atime/mtime * (optional) lock file name */ void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock) { if (stream) { /* need to muck with times? */ struct stat sbuf; time_t tp[2]; time_t now = time (0); fstat (fd,&sbuf); /* get file times */ if (LOCAL->ld >= 0) { /* yes, readwrite session? */ tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else if (stream->recent) { /* readonly with recent messages */ if ((sbuf.st_atime >= sbuf.st_mtime) || (sbuf.st_atime >= sbuf.st_ctime)) /* keep past mtime, whack back atime */ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; else now = 0; /* no time change needed */ } /* readonly with no recent messages */ else if ((sbuf.st_atime < sbuf.st_mtime) || (sbuf.st_atime < sbuf.st_ctime)) { tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else now = 0; /* no time change needed */ /* set the times, note change */ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; } flock (fd,LOCK_UN); /* release flock'ers */ if (!stream) close (fd); /* close the file if no stream */ dotlock_unlock (lock); /* flush the lock file if any */ } /* MMDF mail parse and lock mailbox * Accepts: MAIL stream * space to write lock file name * type of locking operation * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure */ int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op) { int ti,zn,m; unsigned long i,j,k; unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; int retain = T; unsigned long nmsgs = stream->nmsgs; unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; unsigned long recent = stream->recent; unsigned long oldnmsgs = stream->nmsgs; short silent = stream->silent; short pseudoseen = NIL; struct stat sbuf; STRING bs; FDDATA d; MESSAGECACHE *elt; mail_lock (stream); /* guard against recursion or pingers */ /* toss out previous descriptor */ if (LOCAL->fd >= 0) close (LOCAL->fd); MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */ if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ? O_RDWR : O_RDONLY,NIL,lock,op)) < 0) { sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (tmp,ERROR); mmdf_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } fstat (LOCAL->fd,&sbuf); /* get status */ /* validate change in size */ if (sbuf.st_size < LOCAL->filesize) { sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); /* this is pretty bad */ mmdf_unlock (LOCAL->fd,stream,lock); mmdf_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } /* new data? */ else if (i = sbuf.st_size - LOCAL->filesize) { d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = LOCAL->filesize; /* get to that position in the file */ d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ /* skip leading whitespace for broken MTAs */ while (((c = CHR (&bs)) == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) SNX (&bs); if (SIZE (&bs)) { /* read new data */ /* remember internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = mmdf_mbxline (stream,&bs,&i); stream->silent = T; /* quell main program new message events */ do { /* read MMDF header */ if (!(i && ISMMDF (s))){/* see if valid MMDF header */ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", (char *) s); /* see if we can back up to a line */ if (i && (j > MMDFHDRLEN)) { SETPOS (&bs,j -= MMDFHDRLEN); /* read previous line */ s = mmdf_mbxline (stream,&bs,&i); /* kill the error if it looks good */ if (i && ISMMDF (s)) tmp[0] = '\0'; } if (tmp[0]) { MM_LOG (tmp,ERROR); mmdf_unlock (LOCAL->fd,stream,lock); mmdf_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); return NIL; } } /* instantiate first new message */ mail_exists (stream,++nmsgs); (elt = mail_elt (stream,nmsgs))->valid = T; recent++; /* assume recent by default */ elt->recent = T; /* note position/size of internal header */ elt->private.special.offset = j; elt->private.special.text.size = i; s = mmdf_mbxline (stream,&bs,&i); ti = 0; /* assume not a valid date */ zn = 0,t = NIL; if (i) VALID (s,t,ti,zn); if (ti) { /* generate plausible IMAPish date string */ /* this is also part of header */ elt->private.special.text.size += i; date[2] = date[6] = date[20] = '-'; date[11] = ' '; date[14] = date[17] = ':'; /* dd */ date[0] = t[ti - 2]; date[1] = t[ti - 1]; /* mmm */ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; /* hh */ date[12] = t[ti + 1]; date[13] = t[ti + 2]; /* mm */ date[15] = t[ti + 4]; date[16] = t[ti + 5]; if (t[ti += 6]==':'){ /* ss */ date[18] = t[++ti]; date[19] = t[++ti]; ti++; /* move to space */ } else date[18] = date[19] = '0'; /* yy -- advance over timezone if necessary */ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4); date[7] = t[ti + 1]; date[8] = t[ti + 2]; date[9] = t[ti + 3]; date[10] = t[ti + 4]; /* zzz */ t = zn ? (t + zn + 1) : (unsigned char *) "LCL"; date[21] = *t++; date[22] = *t++; date[23] = *t++; if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0'; else { /* numeric time zone */ date[24] = *t++; date[25] = *t++; date[26] = '\0'; date[20] = ' '; } /* set internal date */ if (!mail_parse_date (elt,date)) { sprintf (tmp,"Unable to parse internal date: %s",(char *) date); MM_LOG (tmp,WARN); } } else { /* make date from file date */ struct tm *tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; t = NIL; /* suppress line read */ } /* header starts here */ elt->private.msg.header.offset = elt->private.special.text.size; do { /* look for message body */ if (t) s = t = mmdf_mbxline (stream,&bs,&i); else t = s; /* this line read was suppressed */ if (ISMMDF (s)) break; /* this line is part of header */ elt->private.msg.header.text.size += i; if (i) switch (*s) { /* check header lines */ case 'X': /* possible X-???: line */ if (s[1] == '-') { /* must be immediately followed by hyphen */ /* X-Status: becomes Status: in S case */ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' && s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2; /* possible X-Keywords */ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' && s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && s[8] == 'd' && s[9] == 's' && s[10] == ':') { SIZEDTEXT uf; retain = NIL; /* don't retain continuation */ s += 11; /* flush leading whitespace */ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){ while (*s == ' ') s++; /* find end of keyword */ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); /* got a keyword? */ if ((k = (u - s)) && (k < MAXUSERFLAG)) { uf.data = (unsigned char *) s; uf.size = k; for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j) if (!compare_csizedtext (stream->user_flags[j],&uf)) { elt->user_flags |= ((long) 1) << j; break; } } s = u; /* advance to next keyword */ } break; } /* possible X-IMAP */ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') && (s[5] == 'P') && ((m = (s[6] == ':')) || ((s[6] == 'b') && (s[7] == 'a') && (s[8] == 's') && (s[9] == 'e') && (s[10] == ':')))) { retain = NIL; /* don't retain continuation */ if ((nmsgs == 1) && !stream->uid_validity) { /* advance to data */ s += m ? 7 : 11; /* flush whitespace */ while (*s == ' ') s++; j = 0; /* slurp UID validity */ /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush whitespace */ while (*s == ' ') s++; /* must have valid UID validity and UID last */ if (j && isdigit (*s)) { /* pseudo-header seen if X-IMAP */ if (m) pseudoseen = LOCAL->pseudo = T; /* save UID validity */ stream->uid_validity = j; j = 0; /* slurp UID last */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* save UID last */ stream->uid_last = j; /* process keywords */ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n')); s = u,j++) { /* flush leading whitespace */ while (*s == ' ') s++; u = strpbrk (s," \n\r"); /* got a keyword? */ if ((k = (u - s)) && j < NUSERFLAGS) { if (stream->user_flags[j]) fs_give ((void **) &stream->user_flags[j]); stream->user_flags[j] = (char *) fs_get (k + 1); strncpy (stream->user_flags[j],s,k); stream->user_flags[j][k] = '\0'; } } } } break; } /* possible X-UID */ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' && s[5] == ':') { retain = NIL; /* don't retain continuation */ /* only believe if have a UID validity */ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) { s += 6; /* advance to UID value */ /* flush whitespace */ while (*s == ' ') s++; j = 0; /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush remainder of line */ while (*s != '\n') s++; /* make sure not duplicated */ if (elt->private.uid) sprintf (tmp,"Message %lu UID %lu already has UID %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,elt->private.uid); /* make sure UID doesn't go backwards */ else if (j <= prevuid) sprintf (tmp,"Message %lu UID %lu less than %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,prevuid + 1); /* or skip by mailbox's recorded last */ else if (j > stream->uid_last) sprintf (tmp,"Message %lu UID %lu greater than last %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,stream->uid_last); else { /* normal UID case */ prevuid = elt->private.uid = j; break; /* exit this cruft */ } MM_LOG (tmp,WARN); /* invalidate UID validity */ stream->uid_validity = 0; elt->private.uid = 0; } break; } } /* otherwise fall into S case */ case 'S': /* possible Status: line */ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' && s[5] == 's' && s[6] == ':') { retain = NIL; /* don't retain continuation */ s += 6; /* advance to status flags */ do switch (*s++) {/* parse flags */ case 'R': /* message read */ elt->seen = T; break; case 'O': /* message old */ if (elt->recent) { elt->recent = NIL; recent--; /* it really wasn't recent */ } break; case 'D': /* message deleted */ elt->deleted = T; break; case 'F': /* message flagged */ elt->flagged = T; break; case 'A': /* message answered */ elt->answered = T; break; case 'T': /* message is a draft */ elt->draft = T; break; default: /* some other crap */ break; } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))); break; /* all done */ } /* otherwise fall into default case */ default: /* ordinary header line */ if ((*s == 'S') || (*s == 's') || (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) { unsigned char *e,*v; /* must match what mail_filter() does */ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1); (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') && ((c > ' ') || ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))); *v++ = *u++); *v = '\0'; /* tie off */ /* matches internal header? */ if (!compare_cstring (tmp,"STATUS") || !compare_cstring (tmp,"X-STATUS") || !compare_cstring (tmp,"X-KEYWORDS") || !compare_cstring (tmp,"X-UID") || !compare_cstring (tmp,"X-IMAP") || !compare_cstring (tmp,"X-IMAPBASE")) { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus %s header in message %lu", (char *) tmp,elt->msgno); MM_LOG (err,WARN); retain = NIL; /* don't retain continuation */ break; /* different case or something */ } } /* retain or non-continuation? */ if (retain || ((*s != ' ') && (*s != '\t'))) { retain = T; /* retaining continuation now */ /* line length in LF format newline */ k = i - (((i >= 2) && (s[i - 2] == '\r')) ? 1 : 0); /* "internal" header size */ elt->private.data += k; /* message size */ elt->rfc822_size += k + 1; } else { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus continuation in msg %lu: %.80s", elt->msgno,(char *) s); if (u = strpbrk (err,"\r\n")) *u = '\0'; MM_LOG (err,WARN); break; /* different case or something */ } break; } } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); /* "internal" header sans trailing newline */ if (i) elt->private.data--; /* assign a UID if none found */ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { prevuid = elt->private.uid = ++stream->uid_last; elt->private.dirty = T; } else elt->private.dirty = elt->recent; /* note size of header, location of text */ elt->private.msg.header.text.size = (elt->private.msg.text.offset = (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - elt->private.special.text.size; /* note current position */ j = LOCAL->filesize + GETPOS (&bs); if (i) do { /* look for next message */ s = mmdf_mbxline (stream,&bs,&i); if (i) { /* got new data? */ if (ISMMDF (s)) break; else { elt->rfc822_size += i + (((i < 2) || s[i - 2] != '\r') ? 1 : 0); /* update current position */ j = LOCAL->filesize + GETPOS (&bs); } } } while (i); /* until found a header */ elt->private.msg.text.text.size = j - (elt->private.special.offset + elt->private.msg.text.offset); if (i) { /* get next header line */ /* remember first internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = mmdf_mbxline (stream,&bs,&i); } } while (i); /* until end of buffer */ if (pseudoseen) { /* flush pseudo-message if present */ /* decrement recent count */ if (mail_elt (stream,1)->recent) recent--; /* and the exists count */ mail_exists (stream,nmsgs--); mail_expunged(stream,1);/* fake an expunge of that message */ } /* need to start a new UID validity? */ if (!stream->uid_validity) { stream->uid_validity = time (0); /* in case a whiner with no life */ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) stream->uid_nosticky = T; else if (nmsgs) { /* don't bother if empty file */ LOCAL->dirty = T; /* make dirty to restart UID epoch */ /* need to rewrite msg 1 if not pseudo */ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; MM_LOG ("Assigning new unique identifiers to all messages",NIL); } } stream->nmsgs = oldnmsgs; /* whack it back down */ stream->silent = silent; /* restore old silent setting */ /* notify upper level of new mailbox sizes */ mail_exists (stream,nmsgs); mail_recent (stream,recent); /* mark dirty so O flags are set */ if (recent) LOCAL->dirty = T; } } /* no change, don't babble if never got time */ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime) MM_LOG ("New mailbox modification time but apparently no changes",WARN); /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; LOCAL->filetime = sbuf.st_mtime; return T; /* return the winnage */ } /* MMDF read line from mailbox * Accepts: mail stream * stringstruct * pointer to line size * Returns: pointer to input line */ char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) { unsigned long i,j,k,m; char *s,*t,*te,p1[CHUNK]; char *ret = ""; /* flush old buffer */ if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* if buffer needs refreshing */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) { /* find newline */ /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* difficult case if line spans buffer */ if ((i = s - bs->curpos) == bs->cursize) { memcpy (p1,bs->curpos,i); /* remember what we have so far */ /* load next buffer */ SETPOS (bs,k = GETPOS (bs) + i); /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; /* fast scan in overlap buffer */ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* huge line? */ if ((j = s - bs->curpos) == bs->cursize) { SETPOS (bs,GETPOS (bs) + j); /* look for end of line (s-l-o-w!!) */ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); SETPOS (bs,k); /* go back to where it started */ } /* got size of data, make buffer for return */ ret = LOCAL->line = (char *) fs_get (i + j + 2); memcpy (ret,p1,i); /* copy first chunk */ while (j) { /* copy remainder */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); i += k; /* account for this much read in */ j -= k; bs->curpos += k; /* increment new position */ bs->cursize -= k; /* eat that many bytes */ } if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */ ret[i++] = '\n'; /* make sure newline at end */ ret[i] = '\0'; /* makes debugging easier */ } else { /* this is easy */ ret = bs->curpos; /* string it at this position */ bs->curpos += ++i; /* increment new position */ bs->cursize -= i; /* eat that many bytes */ } *size = i; /* return that to user */ } else *size = 0; /* end of data, return empty */ /* embedded MMDF header at end of line? */ if ((*size > sizeof (MMDFHDRTXT)) && (s = ret + *size - (i = sizeof (MMDFHDRTXT) - 1)) && ISMMDF (s)) { SETPOS (bs,GETPOS (bs) - i);/* back up to start of MMDF header */ *size -= i; /* reduce length of line */ ret[*size - 1] = '\n'; /* force newline at end */ } return ret; } /* MMDF make pseudo-header * Accepts: MAIL stream * buffer to write pseudo-header * Returns: length of pseudo-header */ unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr) { int i; char *s,tmp[MAILTMPLEN]; time_t now = time (0); rfc822_fixed_date (tmp); sprintf (hdr,"%sFrom %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu", mmdfhdr,pseudo_from,ctime (&now), tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) now,mylocalhost (),stream->uid_validity, stream->uid_last); for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) sprintf (s += strlen (s)," %s",stream->user_flags[i]); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr); return strlen (hdr); } /* MMDF make status string * Accepts: MAIL stream * destination string to write * message cache entry * non-zero flag to write UID (.LT. 0 to write UID base info too) * Returns: length of string */ unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag) { char *t,stack[64]; char *s = status; unsigned long n; int pad = 50; /* This used to use sprintf(), but thanks to certain cretinous C libraries with horribly slow implementations of sprintf() I had to change it to this mess. At least it should be fast. */ /* need to write X-IMAPbase: header? */ if ((flag < 0) && !stream->uid_nosticky) { *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P'; *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' '; t = stack; n = stream->uid_validity; /* push UID validity digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID validity digits from stack */ while (t > stack) *s++ = *--t; *s++ = ' '; n = stream->uid_last; /* push UID last digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID last digits from stack */ while (t > stack) *s++ = *--t; for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n]) for (*s++ = ' '; *t; *s++ = *t++); *s++ = '\n'; pad += 30; /* increased padding if have IMAPbase */ } *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->seen) *s++ = 'R'; if (flag) *s++ = 'O'; /* only write O if have a UID */ *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->deleted) *s++ = 'D'; if (elt->flagged) *s++ = 'F'; if (elt->answered) *s++ = 'A'; if (elt->draft) *s++ = 'T'; *s++ = '\n'; if (!stream->uid_nosticky) { /* cretins with no life can't use this */ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w'; *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':'; if (n = elt->user_flags) do { *s++ = ' '; for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); } while (n); n = s - status; /* get size of stuff so far */ /* pad X-Keywords to make size constant */ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; *s++ = '\n'; if (flag) { /* want to include UID? */ t = stack; n = elt->private.uid; /* push UID digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; *s++ = ' '; /* pop UID from stack */ while (t > stack) *s++ = *--t; *s++ = '\n'; } } *s++ = '\n'; *s = '\0'; /* end of extended message status */ return s - status; /* return size of resulting string */ } /* Rewrite mailbox file * Accepts: MAIL stream, must be critical and locked * return pointer to number of expunged messages if want expunge * lock file name * Returns: T if success and mailbox unlocked, NIL if failure */ #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock) { MESSAGECACHE *elt; MMDFFILE f; char *s; time_t tp[2]; long ret,flag; unsigned long i,j; unsigned long recent = stream->recent; unsigned long size = LOCAL->pseudo ? mmdf_pseudo (stream,LOCAL->buf) : 0; if (nexp) *nexp = 0; /* initially nothing expunged */ /* calculate size of mailbox after rewrite */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) if (!(elt = mail_elt (stream,i))->deleted || !nexp) { /* add RFC822 size of this message */ size += elt->private.special.text.size + elt->private.data + mmdf_xstatus (stream,LOCAL->buf,elt,flag) + elt->private.msg.text.text.size + MMDFHDRLEN; flag = 1; /* only count X-IMAPbase once */ } /* no messages, has a life, and no pseudo */ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) { LOCAL->pseudo = T; /* so make a pseudo-message now */ size = mmdf_pseudo (stream,LOCAL->buf); } /* extend the file as necessary */ if (ret = mmdf_extend (stream,size)) { /* Set up buffered I/O file structure * curpos current position being written through buffering * filepos current position being written physically to the disk * bufpos current position being written in the buffer * protect current maximum position that can be written to the disk * before buffering is forced * The code tries to buffer so that that disk is written in multiples of * OVERBLOWBUFLEN bytes. */ f.stream = stream; /* note mail stream */ f.curpos = f.filepos = 0; /* start of file */ f.protect = stream->nmsgs ? /* initial protection pointer */ mail_elt (stream,1)->private.special.offset : 8192; f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); if (LOCAL->pseudo) /* update pseudo-header */ mmdf_write (&f,LOCAL->buf,mmdf_pseudo (stream,LOCAL->buf)); /* loop through all messages */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { elt = mail_elt (stream,i);/* get cache */ if (nexp && elt->deleted){/* expunge this message? */ /* one less recent message */ if (elt->recent) --recent; mail_expunged(stream,i);/* notify upper levels */ ++*nexp; /* count up one more expunged message */ } else { /* preserve this message */ i++; /* advance to next message */ if ((flag < 0) || /* need to rewrite message? */ elt->private.dirty || (f.curpos != elt->private.special.offset) || (elt->private.msg.header.text.size != (elt->private.data + mmdf_xstatus (stream,LOCAL->buf,elt,flag)))) { unsigned long newoffset = f.curpos; /* yes, seek to internal header */ lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); /* see if need to squeeze out a CR */ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') { LOCAL->buf[--elt->private.special.text.size - 1] = '\n'; --size; /* squeezed out a CR from PC */ } /* protection pointer moves to RFC822 header */ f.protect = elt->private.special.offset + elt->private.msg.header.offset; /* write internal header */ mmdf_write (&f,LOCAL->buf,elt->private.special.text.size); /* get RFC822 header */ s = mmdf_header (stream,elt->msgno,&j,FT_INTERNAL); /* in case this got decremented */ elt->private.msg.header.offset = elt->private.special.text.size; /* header size, sans trailing newline */ if ((j < 2) || (s[j - 2] == '\n')) j--; if (j != elt->private.data) fatal ("header size inconsistent"); /* protection pointer moves to RFC822 text */ f.protect = elt->private.special.offset + elt->private.msg.text.offset; mmdf_write (&f,s,j); /* write RFC822 header */ /* write status and UID */ mmdf_write (&f,LOCAL->buf, j = mmdf_xstatus (stream,LOCAL->buf,elt,flag)); flag = 1; /* only write X-IMAPbase once */ /* new file header size */ elt->private.msg.header.text.size = elt->private.data + j; /* did text move? */ if (f.curpos != f.protect) { /* get message text */ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL); /* this can happen if CRs were squeezed */ if (j < elt->private.msg.text.text.size) { /* so fix up counts */ size -= elt->private.msg.text.text.size - j; elt->private.msg.text.text.size = j; } /* can't happen it says here */ else if (j > elt->private.msg.text.text.size) fatal ("text size inconsistent"); /* new text offset, status/UID may change it */ elt->private.msg.text.offset = f.curpos - newoffset; /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : (f.curpos + j + MMDFHDRLEN); mmdf_write (&f,s,j);/* write text */ /* write trailing newline */ mmdf_write (&f,mmdfhdr,MMDFHDRLEN); } else { /* tie off header and status */ mmdf_write (&f,NIL,NIL); f.curpos = f.protect =/* restart everything at end of message */ f.filepos += elt->private.msg.text.text.size + MMDFHDRLEN; } /* new internal header offset */ elt->private.special.offset = newoffset; elt->private.dirty =NIL;/* message is now clean */ } else { /* no need to rewrite this message */ /* tie off previous message if needed */ mmdf_write (&f,NIL,NIL); f.curpos = f.protect =/* restart everything at end of message */ f.filepos += elt->private.special.text.size + elt->private.msg.header.text.size + elt->private.msg.text.text.size + MMDFHDRLEN; } } } mmdf_write (&f,NIL,NIL); /* tie off final message */ if (size != f.filepos) fatal ("file size inconsistent"); fs_give ((void **) &f.buf); /* free buffer */ /* make sure tied off */ ftruncate (LOCAL->fd,LOCAL->filesize = size); fsync (LOCAL->fd); /* make sure the updates take */ if (size && (flag < 0)) fatal ("lost UID base information"); LOCAL->dirty = NIL; /* no longer dirty */ /* notify upper level of new mailbox sizes */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); /* set atime to now, mtime a second earlier */ tp[1] = (tp[0] = time (0)) - 1; /* set the times, note change */ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; close (LOCAL->fd); /* close and reopen file */ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,NIL)) < 0) { sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); mmdf_abort (stream); } dotlock_unlock (lock); /* flush the lock file */ } return ret; /* return state from algorithm */ } /* Extend MMDF mailbox file * Accepts: MAIL stream * new desired size * Return: T if success, else NIL */ long mmdf_extend (MAILSTREAM *stream,unsigned long size) { unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0; if (i) { /* does the mailbox need to grow? */ if (i > LOCAL->buflen) { /* make sure have enough space */ /* this user won the lottery all right */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1); } memset (LOCAL->buf,'\0',i); /* get a block of nulls */ while (T) { /* until write successful or punt */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break; else { long e = errno; /* note error before doing ftruncate */ ftruncate (LOCAL->fd,LOCAL->filesize); if (MM_DISKERROR (stream,e,NIL)) { fsync (LOCAL->fd); /* user chose to punt */ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e)); if (!stream->silent) MM_LOG (LOCAL->buf,ERROR); return NIL; } } } } return LONGT; } /* Write data to buffered file * Accepts: buffered file pointer * file data or NIL to indicate "flush buffer" * date size (ignored for "flush buffer") * Does not return until success */ void mmdf_write (MMDFFILE *f,char *buf,unsigned long size) { unsigned long i,j,k; if (buf) { /* doing buffered write? */ i = f->bufpos - f->buf; /* yes, get size of current buffer data */ /* yes, have space in current buffer chunk? */ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) { /* yes, fill up buffer as much as we can */ memcpy (f->bufpos,buf,k = min (j,size)); f->bufpos += k; /* new buffer position */ f->curpos += k; /* new current position */ if (j -= k) return; /* all done if still have buffer free space */ buf += k; /* full, get new unwritten data pointer */ size -= k; /* new data size */ i += k; /* new buffer data size */ } /* This chunk of the buffer is full. See if can make some space by * writing to the disk, if there's enough unprotected space to do so. * Try to fill out any unaligned chunk, along with any subsequent full * chunks that will fit in unprotected space. */ /* any unprotected space we can write to? */ if (j = min (i,f->protect - f->filepos)) { /* yes, filepos not at chunk boundary? */ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j)) j -= k; /* yes, and can write out partial chunk */ else k = 0; /* no partial chunk to write */ /* if at least a chunk free, write that too */ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN); if (k) { /* write data if there is anything we can */ mmdf_phys_write (f,f->buf,k); /* slide buffer */ if (i -= k) memmove (f->buf,f->buf + k,i); f->bufpos = f->buf + i; /* new end of buffer */ } } /* Have flushed the buffer as best as possible. All done if no more * data to write. Otherwise, if the buffer is empty AND if the unwritten * data is larger than a chunk AND the unprotected space is also larger * than a chunk, then write as many chunks as we can directly from the * data. Buffer the rest, expanding the buffer as needed. */ if (size) { /* have more data that we need to buffer? */ /* can write any of it to disk instead? */ if ((f->bufpos == f->buf) && ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) { /* write as much as we can right now */ mmdf_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN)); buf += j; /* new data pointer */ size -= j; /* new data size */ f->curpos += j; /* advance current pointer */ } if (size) { /* still have data that we need to buffer? */ /* yes, need to expand the buffer? */ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) { /* note current position in buffer */ j = f->bufpos - f->buf; i += OVERFLOWBUFLEN; /* yes, grow another chunk */ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN)); /* in case buffer relocated */ f->bufpos = f->buf + j; } /* buffer remaining data */ memcpy (f->bufpos,buf,size); f->bufpos += size; /* new end of buffer */ f->curpos += size; /* advance current pointer */ } } } else { /* flush buffer to disk */ mmdf_phys_write (f,f->buf,i = f->bufpos - f->buf); f->bufpos = f->buf; /* reset buffer */ /* update positions */ f->curpos = f->protect = f->filepos; } } /* Physical disk write * Accepts: buffered file pointer * buffer address * buffer size * Does not return until success */ void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size) { MAILSTREAM *stream = f->stream; /* write data at desired position */ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) || (write (LOCAL->fd,buf,size) < 0))) { int e; char tmp[MAILTMPLEN]; sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno)); MM_LOG (tmp,ERROR); MM_DISKERROR (NIL,e,T); /* serious problem, must retry */ } f->filepos += size; /* update file position */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mtx.c000066400000000000000000001310671137544547100226020ustar00rootroot00000000000000/* * Program: MTX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "misc.h" #include "dummy.h" /* MTX I/O stream local data */ typedef struct mtx_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } MTXLOCAL; /* Convenient access to local data */ #define LOCAL ((MTXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mtx_valid (char *name); int mtx_isvalid (char *name,char *tmp); void *mtx_parameters (long function,void *value); void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mtx_list (MAILSTREAM *stream,char *ref,char *pat); void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat); long mtx_create (MAILSTREAM *stream,char *mailbox); long mtx_delete (MAILSTREAM *stream,char *mailbox); long mtx_rename (MAILSTREAM *stream,char *old,char *newname); long mtx_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mtx_open (MAILSTREAM *stream); void mtx_close (MAILSTREAM *stream,long options); void mtx_flags (MAILSTREAM *stream,char *sequence,long flags); char *mtx_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mtx_ping (MAILSTREAM *stream); void mtx_check (MAILSTREAM *stream); void mtx_snarf (MAILSTREAM *stream); void mtx_expunge (MAILSTREAM *stream); long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); char *mtx_file (char *dst,char *name); long mtx_parse (MAILSTREAM *stream); MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno); void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag); unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* MTX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mtxdriver = { "mtx", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY|DR_LOCKING, (DRIVER *) NIL, /* next driver */ mtx_valid, /* mailbox is valid for us */ mtx_parameters, /* manipulate parameters */ mtx_scan, /* scan mailboxes */ mtx_list, /* list mailboxes */ mtx_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ mtx_delete, /* delete mailbox */ mtx_rename, /* rename mailbox */ mtx_status, /* status of mailbox */ mtx_open, /* open mailbox */ mtx_close, /* close mailbox */ mtx_flags, /* fetch message "fast" attributes */ mtx_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mtx_header, /* fetch message header */ mtx_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ mtx_flag, /* modify flags */ mtx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mtx_ping, /* ping mailbox to see if still alive */ mtx_check, /* check for new messages */ mtx_expunge, /* expunge deleted messages */ mtx_copy, /* copy messages to another mailbox */ mtx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mtxproto = {&mtxdriver}; /* MTX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mtx_valid (char *name) { char tmp[MAILTMPLEN]; return mtx_isvalid (name,tmp) ? &mtxdriver : NIL; } /* MTX mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int mtx_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = mtx_file (file,name)) && !stat (s,&sbuf)) { if (!sbuf.st_size) { /* allow empty file if INBOX */ if ((s = mailboxfile (tmp,name)) && !*s) ret = T; else errno = 0; /* empty file */ } else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) && (s[1] == '\012')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } /* in case INBOX but not mtx format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* MTX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mtx_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = mtx_file ((char *) value,"INBOX"); break; } return ret; } /* MTX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* MTX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* MTX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MTX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mtx_delete (MAILSTREAM *stream,char *mailbox) { return mtx_rename (stream,mailbox,NIL); } /* MTX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mtx_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = T; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!mtx_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); MM_LOG (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); MM_LOG (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) ret = NIL; else *s = c; /* restore full name */ } /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } } else if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"INBOX.MTX"); return ret; /* return success */ } /* Mtx Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long mtx_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; /* calculate post-snarf results */ if (!status.recent && stream->inbox && (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* MTX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mtx_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mtxproto); if (stream->local) fatal ("mtx recycle stream"); user_flags (stream); /* set up user flags */ /* canonicalize the mailbox name */ if (!mtx_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (MTXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) { MM_LOG ("Unable to lock open mailbox",ERROR); return NIL; } (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* lock the file */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ /* time not set up yet */ LOCAL->lastsnarf = LOCAL->filetime = 0; LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (mtx_ping (stream) && !stream->nmsgs) MM_LOG ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* MTX mail close * Accepts: MAIL stream * close options */ void mtx_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mtx_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* MTX mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to see if some other process changed the flags */ void mtx_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if (mtx_ping (stream) && /* ping mailbox, get new status for messages */ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) mtx_elt (stream,i); } /* MTX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET); /* is buffer big enough? */ if (*length > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); } LOCAL->buf[*length] = '\0'; /* tie off string */ /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length); return LOCAL->buf; } /* MTX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: T, always */ long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mtx_elt (stream,msgno); /* get message status */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ mtx_update_status (stream,msgno,T); MM_FLAGS (stream,msgno); } /* in case previous text cached */ if (elt->private.uid == LOCAL->uid) i = elt->rfc822_size - elt->private.msg.header.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = mtx_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* is buffer big enough? */ if ((i = elt->rfc822_size - j) > LOCAL->text.size) { fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = i) + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->text.data,i); LOCAL->text.data[i] = '\0'; /* tie off string */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); return T; /* success */ } /* MTX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { time_t tp[2]; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read comes after all that */ utime (stream->mailbox,tp); } } /* MTX mail per-message modify flags * Accepts: MAIL stream * message cache element */ void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ mtx_update_status (stream,elt->msgno,NIL); } /* MTX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long mtx_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) mtx_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (mtx_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } if (LOCAL) { /* stream must still be alive */ /* snarf if this is a read-write inbox */ if (stream->inbox && !stream->rdonly) { mtx_snarf (stream); fstat (LOCAL->fd,&sbuf);/* see if file changed now */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (mtx_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } } } return r; /* return result of the parse */ } /* MTX mail check mailbox (reparses status too) * Accepts: MAIL stream */ void mtx_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (mtx_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* MTX mail snarf messages from system inbox * Accepts: MAIL stream */ void mtx_snarf (MAILSTREAM *stream) { unsigned long i = 0; unsigned long j,r,hdrlen,txtlen; struct stat sbuf; char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN]; MESSAGECACHE *elt; MAILSTREAM *sysibx = NIL; int ld; /* give up if can't get exclusive permission */ if ((time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && strcmp (sysinbox (),stream->mailbox) && ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) { MM_CRITICAL (stream); /* go critical */ /* sizes match and anything in sysinbox? */ if (!stat (sysinbox (),&sbuf) && sbuf.st_size && !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { /* yes, go to end of file in our mailbox */ lseek (LOCAL->fd,sbuf.st_size,L_SET); /* for each message in sysibx mailbox */ while (r && (++i <= sysibx->nmsgs)) { /* snarf message from system INBOX */ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL)); txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK); /* if have a message */ if (j = hdrlen + txtlen) { /* calculate header line */ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i)); sprintf (LOCAL->buf + strlen (LOCAL->buf), ",%lu;0000000000%02o\015\012",j,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* copy message */ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) || (write (LOCAL->fd,hdr,hdrlen) < 0) || (write (LOCAL->fd,txt,txtlen) < 0)) r = 0; } fs_give ((void **) &hdr); } /* make sure all the updates take */ if (fsync (LOCAL->fd)) r = 0; if (r) { /* delete all the messages we copied */ if (r == 1) strcpy (tmp,"1"); else sprintf (tmp,"1:%lu",r); mail_flag (sysibx,tmp,"\\Deleted",ST_SET); mail_expunge (sysibx); /* now expunge all those messages */ } else { sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (LOCAL->fd,sbuf.st_size); } fstat (LOCAL->fd,&sbuf); /* yes, get current file size */ LOCAL->filetime = sbuf.st_mtime; } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ unlockfd (ld,lock); /* release exclusive parse/append permission */ LOCAL->lastsnarf = time (0);/* note time of last snarf */ } } /* MTX mail expunge mailbox * Accepts: MAIL stream */ void mtx_expunge (MAILSTREAM *stream) { time_t tp[2]; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* do nothing if stream dead */ if (!mtx_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ MM_LOG ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* The cretins who designed flock() created a window of vulnerability in * upgrading locks from shared to exclusive or downgrading from exclusive * to shared. Rather than maintain the lock at shared status at a minimum, * flock() actually *releases* the former lock. Obviously they never talked * to any database guys. Fortunately, we have the parse/append permission * lock. If we require this lock before going exclusive on the mailbox, * another process can not sneak in and steal the exclusive mailbox lock on * us, because it will block on trying to get parse/append permission first. */ /* get exclusive parse/append permission */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!mtx_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ (*bn) (BLOCK_NONE,NIL); MM_LOG("Can't expunge because mailbox is in use by another process",ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } MM_CRITICAL (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = mtx_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + elt->rfc822_size; if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ lseek (LOCAL->fd,pos,L_SET); while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); MM_LOG (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* reset atime to now */ utime (stream->mailbox,tp); MM_NOCRITICAL (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* MTX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; time_t tp[2]; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd= open (mtx_file (file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0){ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock copy mailbox",ERROR); MM_NOCRITICAL (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + elt->rfc822_size; do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = mtx_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ mtx_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure atime remains greater */ utime (stream->mailbox,tp); } } return ret; } /* MTX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; time_t tp[2]; FILE *df; MESSAGECACHE elt; long f; unsigned long i,uf; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = user_flags (&mtxproto); /* make sure valid mailbox */ if (!mtx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"INBOX.MTX"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open (mtx_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } MM_CRITICAL (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf, (unsigned long) f) < 0) ret = NIL; else { /* write message */ if (i) do c = 0xff & SNX (message); while ((putc (c,df) != EOF) && --i); /* get next message */ if (i || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); } ret = NIL; } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Internal routines */ /* MTX mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *mtx_file (char *dst,char *name) { char tmp[MAILTMPLEN]; char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX",tmp) ? "~/INBOX" : "INBOX.MTX") : s; } /* MTX mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long mtx_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 2) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); mtx_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ mtx_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ time_t tp[2]; tp[0] = time (0); tp[1] = LOCAL->filetime; utime (stream->mailbox,tp); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* MTX get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; mtx_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) MM_FLAGS (stream,msgno); /* let top level know */ return elt; } /* MTX read flags from file * Accepts: MAIL stream * Returns: cache element */ void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* MTX update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { time_t tp[2]; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 14,L_SET); /* write new flags */ write (LOCAL->fd,LOCAL->buf,12); if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read is later */ utime (stream->mailbox,tp); } } } /* MTX locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; int q = 0; char *s,tmp[MAILTMPLEN]; MESSAGECACHE *elt = mtx_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for CRLF CRLF */ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) { /* read another buffer as necessary */ if ((--i <= 0) && /* buffer empty? */ (read (LOCAL->fd,s = tmp, i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)) return ret; /* I/O error? */ switch (q) { /* sniff at buffer */ case 0: /* first character */ q = (*s++ == '\015') ? 1 : 0; break; case 1: /* second character */ q = (*s++ == '\012') ? 2 : 0; break; case 2: /* third character */ q = (*s++ == '\015') ? 3 : 0; break; case 3: /* fourth character */ if (*s++ == '\012') { /* have the sequence? */ /* yes, note for later */ elt->private.msg.header.text.size = *size = siz; return ret; } q = 0; /* lost... */ break; } } /* header consumes entire message */ elt->private.msg.header.text.size = *size = elt->rfc822_size; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mx.c000066400000000000000000001055641137544547100224210ustar00rootroot00000000000000/* * Program: MX mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1996 * Last Edited: 4 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include #include "mx.h" #include "misc.h" #include "dummy.h" /* MX I/O stream local data */ typedef struct mx_local { int fd; /* file descriptor of open index */ char *dir; /* spool directory name */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long cachedtexts; /* total size of all cached texts */ time_t scantime; /* last time directory scanned */ } MXLOCAL; /* Convenient access to local data */ #define LOCAL ((MXLOCAL *) stream->local) /* Function prototypes */ DRIVER *mx_valid (char *name); int mx_isvalid (char *name,char *tmp); void *mx_parameters (long function,void *value); void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mx_list (MAILSTREAM *stream,char *ref,char *pat); void mx_lsub (MAILSTREAM *stream,char *ref,char *pat); void mx_list_work (MAILSTREAM *stream,char *dir,char *pat,long level); long mx_subscribe (MAILSTREAM *stream,char *mailbox); long mx_unsubscribe (MAILSTREAM *stream,char *mailbox); long mx_create (MAILSTREAM *stream,char *mailbox); long mx_delete (MAILSTREAM *stream,char *mailbox); long mx_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *mx_open (MAILSTREAM *stream); void mx_close (MAILSTREAM *stream,long options); void mx_fast (MAILSTREAM *stream,char *sequence,long flags); char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt); char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags); long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long mx_ping (MAILSTREAM *stream); void mx_check (MAILSTREAM *stream); void mx_expunge (MAILSTREAM *stream); long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long options); long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int mx_numsort (const void *d1,const void *d2); char *mx_file (char *dst,char *name); long mx_lockindex (MAILSTREAM *stream); void mx_unlockindex (MAILSTREAM *stream); void mx_setdate (char *file,MESSAGECACHE *elt); /* MX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mxdriver = { "mx", /* driver name */ /* driver flags */ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING, (DRIVER *) NIL, /* next driver */ mx_valid, /* mailbox is valid for us */ mx_parameters, /* manipulate parameters */ mx_scan, /* scan mailboxes */ mx_list, /* find mailboxes */ mx_lsub, /* find subscribed mailboxes */ mx_subscribe, /* subscribe to mailbox */ mx_unsubscribe, /* unsubscribe from mailbox */ mx_create, /* create mailbox */ mx_delete, /* delete mailbox */ mx_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mx_open, /* open mailbox */ mx_close, /* close mailbox */ mx_fast, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mx_header, /* fetch message header only */ mx_text, /* fetch message body only */ NIL, /* fetch partial message test */ NIL, /* unique identifier */ NIL, /* message number */ mx_flag, /* modify flags */ mx_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mx_ping, /* ping mailbox to see if still alive */ mx_check, /* check for new messages */ mx_expunge, /* expunge deleted messages */ mx_copy, /* copy messages to another mailbox */ mx_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mxproto = {&mxdriver}; /* MX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mx_valid (char *name) { char tmp[MAILTMPLEN]; return mx_isvalid (name,tmp) ? &mxdriver : NIL; } /* MX mail test for valid mailbox * Accepts: mailbox name * temporary buffer to use * Returns: T if valid, NIL otherwise */ int mx_isvalid (char *name,char *tmp) { struct stat sbuf; errno = NIL; /* zap error */ /* validate name as directory */ return (!stat (MXINDEX (tmp,name),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)); } /* MX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *mx_parameters (long function,void *value) { return NIL; } /* MX scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) MM_LOG ("Scan not valid for mx mailboxes",ERROR); } /* MX list mailboxes * Accepts: mail stream * reference * pattern to search */ void mx_list (MAILSTREAM *stream,char *ref,char *pat) { char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i = 0; /* get canonical form of name */ if (stream && dummy_canonicalize (test,ref,pat)) { /* found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test,i = s - test); file[i] = '\0'; /* tie off */ } else strcpy (file,test); /* use just that name then */ /* find directory name */ if (s = strrchr (file,'/')) { *s = '\0'; /* found, tie off at that point */ s = file; } /* do the work */ mx_list_work (stream,s,test,0); } } /* MX list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void mx_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* MX list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * search level */ void mx_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) { DIR *dp; struct direct *d; struct stat sbuf; char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN]; if (dir && *dir) { /* make mailbox and directory names */ sprintf (name,"%s/",dir); /* print name starts at directory */ mx_file (curdir,dir); /* path starts from mx_file() name */ } else { /* no directory, use mailbox home dir */ mx_file (curdir,mailboxdir (name,NIL,NIL)); name[0] = '\0'; /* dummy name */ } if (dp = opendir (curdir)) { /* open directory */ np = name + strlen (name); cp = curdir + strlen (strcat (curdir,"/")); while (d = readdir (dp)) { /* scan, ignore . and numeric names */ if ((d->d_name[0] != '.') && !mx_select (d)) { if (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)) { strcpy (cp,d->d_name);/* make directory name */ strcpy (np,d->d_name);/* make mx name of directory name */ if (dmatch (name,pat,'/') && !stat (curdir,&sbuf) && ((sbuf.st_mode &= S_IFMT) == S_IFDIR)) mx_list_work (stream,name,pat,level+1); } } else if (!strcmp (d->d_name,MXINDEXNAME+1) && pmatch_full (dir,pat,'/')) mm_list (stream,'/',dir,NIL); } closedir (dp); /* all done, flush directory */ } } /* MX mail subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long mx_subscribe (MAILSTREAM *stream,char *mailbox) { return sm_subscribe (mailbox); } /* MX mail unsubscribe to mailbox * Accepts: mail stream * mailbox to delete from subscription list * Returns: T on success, NIL on failure */ long mx_unsubscribe (MAILSTREAM *stream,char *mailbox) { return sm_unsubscribe (mailbox); } /* MX mail create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long mx_create (MAILSTREAM *stream,char *mailbox) { int fd; char *s,tmp[MAILTMPLEN],mbx[MAILTMPLEN]; /* assume error */ sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox); /* make sure valid name */ for (s = mailbox; s && *s;) { if (isdigit (*s)) s++; /* digit, check this node further... */ /* all digit node, barf */ else if (*s == '/') s = NIL; /* non-digit in node, skip to next node */ else if (s = strchr (s+1,'/')) s++; else tmp[0] = NIL; /* no more nodes, good name */ } if (tmp[0]); /* was there an error in the name? */ /* must not already exist */ else if (mx_isvalid (mailbox,tmp)) sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox); /* create directory */ else if (!dummy_create_path (stream,strcat (mx_file (mbx,mailbox),"/"), get_dir_protection (mailbox))) sprintf (tmp,"Can't create mailbox leaf %.80s: %s", mailbox,strerror (errno)); else { /* create index file */ int mask = umask (0); if (((fd = open (MXINDEX (tmp,mailbox),O_WRONLY|O_CREAT|O_EXCL, (int) mail_parameters (NIL,GET_MBXPROTECTION,mailbox)))<0) || close (fd)) sprintf (tmp,"Can't create mailbox index %.80s: %s", mailbox,strerror (errno)); else { /* success */ set_mbx_protections (mailbox,mbx); set_mbx_protections (mailbox,tmp); tmp[0] = NIL; } umask (mask); /* restore mask */ } if (!tmp[0]) return LONGT; /* success */ MM_LOG (tmp,ERROR); /* some error */ return NIL; } /* MX mail delete mailbox * mailbox name to delete * Returns: T on success, NIL on failure */ long mx_delete (MAILSTREAM *stream,char *mailbox) { DIR *dirp; struct direct *d; char *s; char tmp[MAILTMPLEN]; if (!mx_isvalid (mailbox,tmp)) sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox); /* delete index */ else if (unlink (MXINDEX (tmp,mailbox))) sprintf (tmp,"Can't delete mailbox %.80s index: %s", mailbox,strerror (errno)); else { /* get directory name */ *(s = strrchr (tmp,'/')) = '\0'; if (dirp = opendir (tmp)) { /* open directory */ *s++ = '/'; /* restore delimiter */ /* massacre messages */ while (d = readdir (dirp)) if (mx_select (d)) { strcpy (s,d->d_name); /* make path */ unlink (tmp); /* sayonara */ } closedir (dirp); /* flush directory */ } /* try to remove the directory */ if (rmdir (mx_file (tmp,mailbox))) { sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno)); mm_log (tmp,WARN); } return T; /* always success */ } MM_LOG (tmp,ERROR); /* something failed */ return NIL; } /* MX mail rename mailbox * Accepts: MX mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long mx_rename (MAILSTREAM *stream,char *old,char *newname) { char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN]; struct stat sbuf; if (!mx_isvalid (old,tmp)) sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old); /* new mailbox name must not be valid */ else if (mx_isvalid (newname,tmp)) sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists", newname); /* success if can rename the directory */ else { /* found superior to destination name? */ if (s = strrchr (mx_file (tmp1,newname),'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp1,get_dir_protection (newname))) return NIL; *s = c; /* restore full name */ } if (!rename (mx_file (tmp,old),tmp1)) { /* recreate file if renamed INBOX */ if (!compare_cstring (old,"INBOX")) mx_create (NIL,"INBOX"); return T; } sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s", old,newname,strerror (errno)); } MM_LOG (tmp,ERROR); /* something failed */ return NIL; } /* MX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mx_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&mxproto); if (stream->local) fatal ("mx recycle stream"); stream->local = fs_get (sizeof (MXLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); mx_file (tmp,stream->mailbox);/* get directory name */ LOCAL->dir = cpystr (tmp); /* copy directory name for later */ /* make temporary buffer */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); LOCAL->scantime = 0; /* not scanned yet */ LOCAL->fd = -1; /* no index yet */ LOCAL->cachedtexts = 0; /* no cached texts */ stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (mx_ping (stream) && !(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",(long) NIL); stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T; /* can we create new user flags? */ return stream; /* return stream to caller */ } /* MX mail close * Accepts: MAIL stream * close options */ void mx_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) mx_expunge (stream); if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); /* free local scratch buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ stream->silent = silent; /* reset silent state */ } } /* MX mail fetch fast information * Accepts: MAIL stream * sequence * option flags */ void mx_fast (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; MESSAGECACHE *elt; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) mx_fast_work (stream,elt); } /* MX mail fetch fast information * Accepts: MAIL stream * message cache element * Returns: name of message file */ char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; struct tm *tm; /* build message file name */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if (!elt->rfc822_size) { /* have size yet? */ stat (LOCAL->buf,&sbuf); /* get size of message */ /* make plausible IMAPish date string */ tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0; elt->rfc822_size = sbuf.st_size; } return LOCAL->buf; /* return file name */ } /* MX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags) { unsigned long i; int fd; MESSAGECACHE *elt; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get elt */ if (!elt->private.msg.header.text.data) { /* purge cache if too big */ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) { mail_gc (stream,GC_TEXTS);/* just can't keep that much */ LOCAL->cachedtexts = 0; } if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL)) < 0) return ""; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,elt->rfc822_size); /* tie off file */ LOCAL->buf[elt->rfc822_size] = '\0'; close (fd); /* flush message file */ /* find end of header */ if (elt->rfc822_size < 4) i = 0; else for (i = 4; (i < elt->rfc822_size) && !((LOCAL->buf[i - 4] == '\015') && (LOCAL->buf[i - 3] == '\012') && (LOCAL->buf[i - 2] == '\015') && (LOCAL->buf[i - 1] == '\012')); i++); /* copy header */ cpytxt (&elt->private.msg.header.text,LOCAL->buf,i); cpytxt (&elt->private.msg.text.text,LOCAL->buf+i,elt->rfc822_size - i); /* add to cached size */ LOCAL->cachedtexts += elt->rfc822_size; } *length = elt->private.msg.header.text.size; return (char *) elt->private.msg.header.text.data; } /* MX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL on failure */ long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno); /* snarf message if don't have it yet */ if (!elt->private.msg.text.text.data) { mx_header (stream,msgno,&i,flags); if (!elt->private.msg.text.text.data) return NIL; } /* mark as seen */ if (!(flags & FT_PEEK) && mx_lockindex (stream)) { elt->seen = T; mx_unlockindex (stream); MM_FLAGS (stream,msgno); } INIT (bs,mail_string,elt->private.msg.text.text.data, elt->private.msg.text.text.size); return T; } /* MX mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { mx_unlockindex (stream); /* finished with index */ } /* MX per-message modify flags * Accepts: MAIL stream * message cache element */ void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { mx_lockindex (stream); /* lock index if not already locked */ } /* MX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long mx_ping (MAILSTREAM *stream) { MAILSTREAM *sysibx = NIL; MESSAGECACHE *elt,*selt; struct stat sbuf; char *s,tmp[MAILTMPLEN]; int fd; unsigned long i,j,r,old; long nmsgs = stream->nmsgs; long recent = stream->recent; int silent = stream->silent; if (stat (LOCAL->dir,&sbuf)) return NIL; stream->silent = T; /* don't pass up mm_exists() events yet */ if (sbuf.st_ctime != LOCAL->scantime) { struct direct **names = NIL; long nfiles = scandir (LOCAL->dir,&names,mx_select,mx_numsort); if (nfiles < 0) nfiles = 0; /* in case error */ old = stream->uid_last; /* note scanned now */ LOCAL->scantime = sbuf.st_ctime; /* scan directory */ for (i = 0; i < nfiles; ++i) { /* if newly seen, add to list */ if ((j = atoi (names[i]->d_name)) > old) { /* swell the cache */ mail_exists (stream,++nmsgs); stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j; elt->valid = T; /* note valid flags */ if (old) { /* other than the first pass? */ elt->recent = T; /* yup, mark as recent */ recent++; /* bump recent count */ } } fs_give ((void **) &names[i]); } /* free directory */ if (s = (void *) names) fs_give ((void **) &s); } stream->nmsgs = nmsgs; /* don't upset mail_uid() */ /* if INBOX, snarf from system INBOX */ if (mx_lockindex (stream) && stream->inbox) { old = stream->uid_last; /* paranoia check */ if (!strcmp (sysinbox (),stream->mailbox)) { stream->silent = silent; return NIL; } MM_CRITICAL (stream); /* go critical */ stat (sysinbox (),&sbuf); /* see if anything there */ /* can get sysinbox mailbox? */ if (sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */ /* build file name we will use */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old); /* snarf message from Berkeley mailbox */ selt = mail_elt (sysibx,i); if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL, S_IREAD|S_IWRITE)) >= 0) && (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) && (write (fd,s,j) == j) && (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) && (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) { /* swell the cache */ mail_exists (stream,++nmsgs); stream->uid_last = /* create new elt, note its file number */ (elt = mail_elt (stream,nmsgs))->private.uid = old; recent++; /* bump recent count */ /* set up initial flags and date */ elt->valid = elt->recent = T; elt->seen = selt->seen; elt->deleted = selt->deleted; elt->flagged = selt->flagged; elt->answered = selt->answered; elt->draft = selt->draft; elt->day = selt->day;elt->month = selt->month;elt->year = selt->year; elt->hours = selt->hours;elt->minutes = selt->minutes; elt->seconds = selt->seconds; elt->zhours = selt->zhours; elt->zminutes = selt->zminutes; elt->zoccident = selt->zoccident; mx_setdate (LOCAL->buf,elt); } else { /* failed to snarf */ if (fd) { /* did it ever get opened? */ close (fd); /* close descriptor */ unlink (LOCAL->buf);/* flush this file */ } stream->silent = silent; return NIL; /* note that something is badly wrong */ } sprintf (tmp,"%lu",i); /* delete it from the sysinbox */ mail_flag (sysibx,tmp,"\\Deleted",ST_SET); } stat (LOCAL->dir,&sbuf); /* update scan time */ LOCAL->scantime = sbuf.st_ctime; mail_expunge (sysibx); /* now expunge all those messages */ } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ } mx_unlockindex (stream); /* done with index */ stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */ mail_recent (stream,recent); return T; /* return that we are alive */ } /* MX mail check mailbox * Accepts: MAIL stream */ void mx_check (MAILSTREAM *stream) { if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* MX mail expunge mailbox * Accepts: MAIL stream */ void mx_expunge (MAILSTREAM *stream) { MESSAGECACHE *elt; unsigned long i = 1; unsigned long n = 0; unsigned long recent = stream->recent; if (mx_lockindex (stream)) { /* lock the index */ MM_CRITICAL (stream); /* go critical */ while (i <= stream->nmsgs) {/* for each message */ /* if deleted, need to trash it */ if ((elt = mail_elt (stream,i))->deleted) { sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if (unlink (LOCAL->buf)) {/* try to delete the message */ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i, strerror (errno)); MM_LOG (LOCAL->buf,(long) NIL); break; } /* note uncached */ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ? elt->private.msg.header.text.size : 0) + (elt->private.msg.text.text.data ? elt->private.msg.text.text.size : 0)); mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS); if(elt->recent)--recent;/* if recent, note one less recent message */ mail_expunged(stream,i);/* notify upper levels */ n++; /* count up one more expunged message */ } else i++; /* otherwise try next message */ } if (n) { /* output the news if any expunged */ sprintf (LOCAL->buf,"Expunged %lu messages",n); MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); MM_NOCRITICAL (stream); /* release critical */ mx_unlockindex (stream); /* finished with index */ } /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); } /* MX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { STRING st; MESSAGECACHE *elt; struct stat sbuf; int fd; unsigned long i,j; char *t,flags[MAILTMPLEN],date[MAILTMPLEN]; /* copy the messages */ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))<0) return NIL; fstat (fd,&sbuf); /* get size of message */ /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size); /* init flag string */ flags[0] = flags[1] = '\0'; if (j = elt->user_flags) do if (t = stream->user_flags[find_rightmost_bit (&j)]) strcat (strcat (flags," "),t); while (j); if (elt->seen) strcat (flags," \\Seen"); if (elt->deleted) strcat (flags," \\Deleted"); if (elt->flagged) strcat (flags," \\Flagged"); if (elt->answered) strcat (flags," \\Answered"); if (elt->draft) strcat (flags," \\Draft"); flags[0] = '('; /* open list */ strcat (flags,")"); /* close list */ mail_date (date,elt); /* generate internal date */ if (!mail_append_full (NIL,mailbox,flags,date,&st)) return NIL; if (options & CP_MOVE) elt->deleted = T; } return T; /* return success */ } /* MX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { MESSAGECACHE *elt,selt; MAILSTREAM *astream; int fd; char *flags,*date,*s,tmp[MAILTMPLEN]; STRING *message; long f,i,size; unsigned long uf; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = user_flags (&mxproto); /* N.B.: can't use LOCAL->buf for tmp */ /* make sure valid mailbox */ if (!mx_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* lock the index */ if (mx_lockindex (astream)) do { if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } /* parse flags */ f = mail_parse_flags (astream,flags,&uf); if (date) { /* want to preserve date? */ /* yes, parse date into an elt */ if (!mail_parse_date (&selt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; break; } } mx_file (tmp,mailbox); /* make message name */ sprintf (tmp + strlen (tmp),"/%lu",++astream->uid_last); if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { sprintf (tmp,"Can't create append message: %s",strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; break; } /* copy message */ s = (char *) fs_get (size = SIZE (message)); for (i = 0; i < size; s[i++] = SNX (message)); /* write the data */ if ((write (fd,s,size) < 0) || fsync (fd)) { unlink (tmp); /* delete mailbox */ sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; } fs_give ((void **) &s); /* flush the buffer */ close (fd); /* close the file */ if (ret) { /* set the date for this message */ if (date) mx_setdate (tmp,&selt); /* swell the cache */ mail_exists (astream,++astream->nmsgs); /* copy flags */ (elt = mail_elt (astream,astream->nmsgs))->private.uid=astream->uid_last; if (f&fSEEN) elt->seen = T; if (f&fDELETED) elt->deleted = T; if (f&fFLAGGED) elt->flagged = T; if (f&fANSWERED) elt->answered = T; if (f&fDRAFT) elt->draft = T; elt->user_flags |= uf; /* get next message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); else { MM_LOG ("Message append failed: unable to lock index",ERROR); ret = NIL; } mx_unlockindex (astream); /* unlock index */ MM_NOCRITICAL (stream); /* release critical */ mail_close (astream); return ret; } /* Internal routines */ /* MX file name selection test * Accepts: candidate directory entry * Returns: T to use file name, NIL to skip it */ int mx_select (struct direct *name) { char c; char *s = name->d_name; while (c = *s++) if (!isdigit (c)) return NIL; return T; } /* MX file name comparision * Accepts: first candidate directory entry * second candidate directory entry * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2 */ int mx_numsort (const void *d1,const void *d2) { return atoi ((*(struct direct **) d1)->d_name) - atoi ((*(struct direct **) d2)->d_name); } /* MX mail build file name * Accepts: destination string * source * Returns: destination */ char *mx_file (char *dst,char *name) { char *s; if (!(mailboxfile (dst,name) && *dst)) return mailboxfile (dst,"~/INBOX"); /* tie off unnecessary trailing / */ if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0'; return dst; } /* MX read and lock index * Accepts: MAIL stream * Returns: T if success, NIL if failure */ long mx_lockindex (MAILSTREAM *stream) { unsigned long uf,sf,uid; int k = 0; unsigned long msgno = 1; struct stat sbuf; char *s,*t,*idx,tmp[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */ (LOCAL->fd = open (strcat (strcpy (tmp,LOCAL->dir),MXINDEXNAME), O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) { (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */ (*bn) (BLOCK_NONE,NIL); fstat (LOCAL->fd,&sbuf); /* get size of index */ /* slurp index */ read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size); idx[sbuf.st_size] = '\0'; /* tie off index */ /* parse index */ if (sbuf.st_size) while (s && *s) switch (*s) { case 'V': /* UID validity record */ stream->uid_validity = strtoul (s+1,&s,16); break; case 'L': /* UID last record */ stream->uid_last = strtoul (s+1,&s,16); break; case 'K': /* keyword */ /* find end of keyword */ if (s = strchr (t = ++s,'\n')) { *s++ = '\0'; /* tie off keyword */ /* copy keyword */ if ((k < NUSERFLAGS) && !stream->user_flags[k] && (strlen (t) <= MAXUSERFLAG)) stream->user_flags[k] = cpystr (t); k++; /* one more keyword */ } break; case 'M': /* message status record */ uid = strtoul (s+1,&s,16);/* get UID for this message */ if (*s == ';') { /* get user flags */ uf = strtoul (s+1,&s,16); if (*s == '.') { /* get system flags */ sf = strtoul (s+1,&s,16); while ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) < uid)) msgno++; /* find message number for this UID */ if ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) == uid)) { (elt = mail_elt (stream,msgno))->valid = T; elt->user_flags=uf; /* set user and system flags in elt */ if (sf & fSEEN) elt->seen = T; if (sf & fDELETED) elt->deleted = T; if (sf & fFLAGGED) elt->flagged = T; if (sf & fANSWERED) elt->answered = T; if (sf & fDRAFT) elt->draft = T; } break; } } default: /* bad news */ sprintf (tmp,"Error in index: %.80s",s); MM_LOG (tmp,ERROR); *s = NIL; /* ignore remainder of index */ } else { /* new index */ stream->uid_validity = time (0); user_flags (stream); /* init stream with default user flags */ } fs_give ((void **) &idx); /* flush index */ } return (LOCAL->fd >= 0) ? T : NIL; } /* MX write and unlock index * Accepts: MAIL stream */ void mx_unlockindex (MAILSTREAM *stream) { unsigned long i,j; off_t size = 0; char *s,tmp[MAILTMPLEN + 64]; MESSAGECACHE *elt; if (LOCAL->fd >= 0) { lseek (LOCAL->fd,0,L_SET); /* rewind file */ /* write header */ sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last); for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]); /* write messages */ for (i = 1; i <= stream->nmsgs; i++) { /* filled buffer? */ if (((s += strlen (s)) - tmp) > MAILTMPLEN) { write (LOCAL->fd,tmp,j = s - tmp); size += j; *(s = tmp) = '\0'; /* dump out and restart buffer */ } elt = mail_elt (stream,i); sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); } /* write tail end of buffer */ if ((s += strlen (s)) != tmp) { write (LOCAL->fd,tmp,j = s - tmp); size += j; } ftruncate (LOCAL->fd,size); flock (LOCAL->fd,LOCK_UN); /* unlock the index */ close (LOCAL->fd); /* finished with file */ LOCAL->fd = -1; /* no index now */ } } /* Set date for message * Accepts: file name * elt containing date */ void mx_setdate (char *file,MESSAGECACHE *elt) { time_t tp[2]; tp[0] = time (0); /* atime is now */ tp[1] = mail_longdate (elt); /* modification time */ utime (file,tp); /* set the times */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/mx.h000066400000000000000000000013101137544547100224060ustar00rootroot00000000000000/* * Program: MX mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1996 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Build parameters */ #define MXINDEXNAME "/.mxindex" #define MXINDEX(d,s) strcat (mx_file (d,s),MXINDEXNAME) /* Function prototypes */ int mx_select (struct direct *name); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/news.c000066400000000000000000000452501137544547100227440ustar00rootroot00000000000000/* * Program: News routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 4 September 1991 * Last Edited: 8 July 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include #include "misc.h" #include "newsrc.h" /* NEWS I/O stream local data */ typedef struct news_local { unsigned int dirty : 1; /* disk copy of .newsrc needs updating */ char *dir; /* spool directory name */ char *name; /* local mailbox name */ unsigned char *buf; /* scratch buffer */ unsigned long buflen; /* current size of scratch buffer */ unsigned long cachedtexts; /* total size of all cached texts */ } NEWSLOCAL; /* Convenient access to local data */ #define LOCAL ((NEWSLOCAL *) stream->local) /* Function prototypes */ DRIVER *news_valid (char *name); DRIVER *news_isvalid (char *name,char *mbx); void *news_parameters (long function,void *value); void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void news_list (MAILSTREAM *stream,char *ref,char *pat); void news_lsub (MAILSTREAM *stream,char *ref,char *pat); long news_canonicalize (char *ref,char *pat,char *pattern); long news_subscribe (MAILSTREAM *stream,char *mailbox); long news_unsubscribe (MAILSTREAM *stream,char *mailbox); long news_create (MAILSTREAM *stream,char *mailbox); long news_delete (MAILSTREAM *stream,char *mailbox); long news_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *news_open (MAILSTREAM *stream); int news_select (struct direct *name); int news_numsort (const void *d1,const void *d2); void news_close (MAILSTREAM *stream,long options); void news_fast (MAILSTREAM *stream,char *sequence,long flags); void news_flags (MAILSTREAM *stream,char *sequence,long flags); char *news_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long news_ping (MAILSTREAM *stream); void news_check (MAILSTREAM *stream); void news_expunge (MAILSTREAM *stream); long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* News routines */ /* Driver dispatch used by MAIL */ DRIVER newsdriver = { "news", /* driver name */ /* driver flags */ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL, (DRIVER *) NIL, /* next driver */ news_valid, /* mailbox is valid for us */ news_parameters, /* manipulate parameters */ news_scan, /* scan mailboxes */ news_list, /* find mailboxes */ news_lsub, /* find subscribed mailboxes */ news_subscribe, /* subscribe to mailbox */ news_unsubscribe, /* unsubscribe from mailbox */ news_create, /* create mailbox */ news_delete, /* delete mailbox */ news_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ news_open, /* open mailbox */ news_close, /* close mailbox */ news_fast, /* fetch message "fast" attributes */ news_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ news_header, /* fetch message header */ news_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ news_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ news_ping, /* ping mailbox to see if still alive */ news_check, /* check for new messages */ news_expunge, /* expunge deleted messages */ news_copy, /* copy messages to another mailbox */ news_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM newsproto = {&newsdriver}; /* News validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *news_valid (char *name) { int fd; char *s,*t,*u; struct stat sbuf; if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') && (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') && !strchr (name,'/') && !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) && ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY, NIL)) >= 0)) { fstat (fd,&sbuf); /* get size of active file */ /* slurp in active file */ read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size); s[sbuf.st_size] = '\0'; /* tie off file */ close (fd); /* flush file */ while (*t && (u = strchr (t,' '))) { *u++ = '\0'; /* tie off at end of name */ if (!strcmp (name+6,t)) { fs_give ((void **) &s); /* flush data */ return &newsdriver; } t = 1 + strchr (u,'\n'); /* next line */ } fs_give ((void **) &s); /* flush data */ } return NIL; /* return status */ } /* News manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *news_parameters (long function,void *value) { return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL; } /* News scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { char tmp[MAILTMPLEN]; if (news_canonicalize (ref,pat,tmp)) mm_log ("Scan not valid for news mailboxes",ERROR); } /* News find list of newsgroups * Accepts: mail stream * reference * pattern to search */ void news_list (MAILSTREAM *stream,char *ref,char *pat) { int fd; int i; char *s,*t,*u,pattern[MAILTMPLEN],name[MAILTMPLEN]; struct stat sbuf; if (!pat || !*pat) { /* empty pattern? */ if (news_canonicalize (ref,"*",pattern)) { /* tie off name at root */ if (s = strchr (pattern,'.')) *++s = '\0'; else pattern[0] = '\0'; mm_list (stream,'.',pattern,LATT_NOSELECT); } } if (news_canonicalize (ref,pat,pattern) && !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) && ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY, NIL)) >= 0)) { fstat (fd,&sbuf); /* get file size and read data */ read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size); close (fd); /* close file */ s[sbuf.st_size] = '\0'; /* tie off string */ strcpy (name,"#news."); /* write initial prefix */ i = strlen (pattern); /* length of pattern */ if (pattern[--i] != '%') i = 0; if (t = strtok (s,"\n")) do if (u = strchr (t,' ')) { *u = '\0'; /* tie off at end of name */ strcpy (name + 6,t); /* make full form of name */ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL); else if (i && (u = strchr (name + i,'.'))) { *u = '\0'; /* tie off at delimiter, see if matches */ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,LATT_NOSELECT); } } while (t = strtok (NIL,"\n")); fs_give ((void **) &s); } } /* News find list of subscribed newsgroups * Accepts: mail stream * reference * pattern to search */ void news_lsub (MAILSTREAM *stream,char *ref,char *pat) { char pattern[MAILTMPLEN]; /* return data from newsrc */ if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern); } /* News canonicalize newsgroup name * Accepts: reference * pattern * returned single pattern * Returns: T on success, NIL on failure */ long news_canonicalize (char *ref,char *pat,char *pattern) { if (ref && *ref) { /* have a reference */ strcpy (pattern,ref); /* copy reference to pattern */ /* # overrides mailbox field in reference */ if (*pat == '#') strcpy (pattern,pat); /* pattern starts, reference ends, with . */ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.')) strcat (pattern,pat + 1); /* append, omitting one of the period */ else strcat (pattern,pat); /* anything else is just appended */ } else strcpy (pattern,pat); /* just have basic name */ return ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') && (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') && !strchr (pattern,'/')) ? T : NIL; } /* News subscribe to mailbox * Accepts: mail stream * mailbox to add to subscription list * Returns: T on success, NIL on failure */ long news_subscribe (MAILSTREAM *stream,char *mailbox) { return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL; } /* NEWS unsubscribe to mailbox * Accepts: mail stream * mailbox to delete from subscription list * Returns: T on success, NIL on failure */ long news_unsubscribe (MAILSTREAM *stream,char *mailbox) { return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL; } /* News create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long news_create (MAILSTREAM *stream,char *mailbox) { return NIL; /* never valid for News */ } /* News delete mailbox * mailbox name to delete * Returns: T on success, NIL on failure */ long news_delete (MAILSTREAM *stream,char *mailbox) { return NIL; /* never valid for News */ } /* News rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long news_rename (MAILSTREAM *stream,char *old,char *newname) { return NIL; /* never valid for News */ } /* News open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *news_open (MAILSTREAM *stream) { long i,nmsgs; char *s,tmp[MAILTMPLEN]; struct direct **names; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &newsproto; if (stream->local) fatal ("news recycle stream"); /* build directory name */ sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL), stream->mailbox + 6); while (s = strchr (s,'.')) *s = '/'; /* scan directory */ if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) { mail_exists (stream,nmsgs); /* notify upper level that messages exist */ stream->local = fs_get (sizeof (NEWSLOCAL)); LOCAL->dirty = NIL; /* no update to .newsrc needed yet */ LOCAL->dir = cpystr (tmp); /* copy directory name for later */ /* make temporary buffer */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); LOCAL->name = cpystr (stream->mailbox + 6); for (i = 0; i < nmsgs; ++i) { stream->uid_last = mail_elt (stream,i+1)->private.uid = atoi (names[i]->d_name); fs_give ((void **) &names[i]); } s = (void *) names; /* stupid language */ fs_give ((void **) &s); /* free directory */ LOCAL->cachedtexts = 0; /* no cached texts */ stream->sequence++; /* bump sequence number */ stream->rdonly = stream->perm_deleted = T; /* UIDs are always valid */ stream->uid_validity = 0xbeefface; /* read .newsrc entries */ mail_recent (stream,newsrc_read (LOCAL->name,stream)); /* notify if empty newsgroup */ if (!(stream->nmsgs || stream->silent)) { sprintf (tmp,"Newsgroup %s is empty",LOCAL->name); mm_log (tmp,WARN); } } else mm_log ("Unable to scan newsgroup spool directory",ERROR); return LOCAL ? stream : NIL; /* if stream is alive, return to caller */ } /* News file name selection test * Accepts: candidate directory entry * Returns: T to use file name, NIL to skip it */ int news_select (struct direct *name) { char c; char *s = name->d_name; while (c = *s++) if (!isdigit (c)) return NIL; return T; } /* News file name comparision * Accepts: first candidate directory entry * second candidate directory entry * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2 */ int news_numsort (const void *d1,const void *d2) { return atoi ((*(struct direct **) d1)->d_name) - atoi ((*(struct direct **) d2)->d_name); } /* News close * Accepts: MAIL stream * option flags */ void news_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ news_check (stream); /* dump final checkpoint */ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); /* free local scratch buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->name) fs_give ((void **) &LOCAL->name); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* News fetch fast information * Accepts: MAIL stream * sequence * option flags */ void news_fast (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i,j; /* ugly and slow */ if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) news_header (stream,i,&j,NIL); } /* News fetch flags * Accepts: MAIL stream * sequence * option flags */ void news_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if ((flags & FT_UID) ? /* validate all elts */ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T; } /* News fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *news_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { unsigned long i,hdrsize; int fd; unsigned char *t; struct stat sbuf; struct tm *tm; MESSAGECACHE *elt; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get elt */ (elt = mail_elt (stream,msgno))->valid = T; if (!elt->private.msg.header.text.data) { /* purge cache if too big */ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) { mail_gc (stream,GC_TEXTS);/* just can't keep that much */ LOCAL->cachedtexts = 0; } /* build message file name */ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return ""; fstat (fd,&sbuf); /* get size of message */ /* make plausible IMAPish date string */ tm = gmtime (&sbuf.st_mtime); elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->year = tm->tm_year + 1900 - BASEYEAR; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->seconds = tm->tm_sec; elt->zhours = 0; elt->zminutes = 0; /* is buffer big enough? */ if (sbuf.st_size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); } /* slurp message */ read (fd,LOCAL->buf,sbuf.st_size); /* tie off file */ LOCAL->buf[sbuf.st_size] = '\0'; close (fd); /* flush message file */ /* find end of header */ for (i = 0,t = LOCAL->buf; *t && !(i && (*t == '\n')); i = (*t++ == '\n')); /* number of header bytes */ hdrsize = (*t ? ++t : t) - LOCAL->buf; elt->rfc822_size = /* size of entire message in CRLF form */ (elt->private.msg.header.text.size = strcrlfcpy (&elt->private.msg.header.text.data,&i,LOCAL->buf, hdrsize)) + (elt->private.msg.text.text.size = strcrlfcpy (&elt->private.msg.text.text.data,&i,t, sbuf.st_size - hdrsize)); /* add to cached size */ LOCAL->cachedtexts += elt->rfc822_size; } *length = elt->private.msg.header.text.size; return (char *) elt->private.msg.header.text.data; } /* News fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL on failure */ long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get elt */ /* snarf message if don't have it yet */ if (!elt->private.msg.text.text.data) { news_header (stream,msgno,&i,flags); if (!elt->private.msg.text.text.data) return NIL; } if (!(flags & FT_PEEK)) { /* mark as seen */ mail_elt (stream,msgno)->seen = T; mm_flags (stream,msgno); } if (!elt->private.msg.text.text.data) return NIL; INIT (bs,mail_string,elt->private.msg.text.text.data, elt->private.msg.text.text.size); return T; } /* News per-message modify flag * Accepts: MAIL stream * message cache element */ void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { if (!LOCAL->dirty) { /* only bother checking if not dirty yet */ if (elt->valid) { /* if done, see if deleted changed */ if (elt->sequence != elt->deleted) LOCAL->dirty = T; elt->sequence = T; /* leave the sequence set */ } /* note current setting of deleted flag */ else elt->sequence = elt->deleted; } } /* News ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long news_ping (MAILSTREAM *stream) { return T; /* always alive */ } /* News check mailbox * Accepts: MAIL stream */ void news_check (MAILSTREAM *stream) { /* never do if no updates */ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream); LOCAL->dirty = NIL; } /* News expunge mailbox * Accepts: MAIL stream */ void news_expunge (MAILSTREAM *stream) { if (!stream->silent) mm_log ("Expunge ignored on news",NIL); } /* News copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * option flags * Returns: T if copy successful, else NIL */ long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (pc) return (*pc) (stream,sequence,mailbox,options); mm_log ("Copy not valid for News",ERROR); return NIL; } /* News append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback function * data for callback * Returns: T if append successful, else NIL */ long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { mm_log ("Append not valid for news",ERROR); return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/nfstnew.c000066400000000000000000000021421137544547100234450ustar00rootroot00000000000000/* * Program: Test for NFS file * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 2001 * Last Edited: 12 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include /* Test for NFS * Accepts: file descriptor * Returns: T if NFS file, NIL otherwise */ long test_nfs (int fd) { struct stat sbuf; struct ustat usbuf; struct statvfs vsbuf; /* Any base type that begins with "nfs" or "afs" is considered to be a * network filesystem. */ return ((!fstat (fd,&sbuf) && !ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) || (!fstatvfs (fd,&vsbuf) && (vsbuf.f_basetype[1] == 'f') && (vsbuf.f_basetype[2] == 's') && ((vsbuf.f_basetype[0] == 'n') || (vsbuf.f_basetype[0] == 'a')))) ? LONGT : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/nfstold.c000066400000000000000000000014461137544547100234400ustar00rootroot00000000000000/* * Program: Test for NFS file -- old version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 2001 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Test for NFS * Accepts: file descriptor * Returns: T if NFS file, NIL otherwise */ long test_nfs (int fd) { struct stat sbuf; struct ustat usbuf; return (!fstat (fd,&sbuf) && !ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) ? LONGT : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/nl_unix.c000066400000000000000000000045311137544547100234410ustar00rootroot00000000000000/* * Program: UNIX/VMS newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { long i = srcl * 2,j; unsigned char c,*d = src; if (*dst) { /* candidate destination provided? */ /* count NLs if doesn't fit worst-case */ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++; /* still too small, must reset destination */ if (i > *dstl) fs_give ((void **) dst); } /* make a new buffer if needed */ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1); d = *dst; /* destination string */ if (srcl) do { /* main copy loop */ if ((c = *src++) < '\016') { /* prepend CR to LF */ if (c == '\012') *d++ = '\015'; /* unlikely CR */ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) { *d++ = c; /* copy the CR */ c = *src++; /* grab the LF */ --srcl; /* adjust the count */ } } *d++ = c; /* copy character */ } while (--srcl); *d = '\0'; /* tie off destination */ return d - *dst; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { unsigned long pos = GETPOS (s); unsigned long i = SIZE (s); unsigned long j = i; while (j--) switch (SNX (s)) {/* search for newlines */ case '\015': /* unlikely carriage return */ if (j && (CHR (s) == '\012')) { SNX (s); /* eat the line feed */ j--; } break; case '\012': /* line feed? */ i++; default: /* ordinary chararacter */ break; } SETPOS (s,pos); /* restore old position */ return i; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/opendir.c000066400000000000000000000033431137544547100234250ustar00rootroot00000000000000/* * Program: Read directories * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 16 December 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for BSD opendir() call * Accepts: directory name * Returns: directory structure pointer */ DIR *opendir (char *name) { DIR *d = NIL; struct stat sbuf; int fd = open (name,O_RDONLY,NIL); errno = ENOTDIR; /* default error is bogus directory */ if ((fd >= 0) && !(fstat (fd,&sbuf)) && ((sbuf.st_mode&S_IFMT) == S_IFDIR)) { d = (DIR *) fs_get (sizeof (DIR)); /* initialize structure */ d->dd_loc = 0; read (fd,d->dd_buf = (char *) fs_get (sbuf.st_size), d->dd_size = sbuf.st_size); } else if (d) fs_give ((void **) &d); if (fd >= 0) close (fd); return d; } /* Emulator for BSD closedir() call * Accepts: directory structure pointer */ int closedir (DIR *d) { /* free storage */ fs_give ((void **) &(d->dd_buf)); fs_give ((void **) &d); return NIL; /* return */ } /* Emulator for BSD readdir() call * Accepts: directory structure pointer */ struct direct *readdir (DIR *d) { /* loop through directory */ while (d->dd_loc < d->dd_size) { struct direct *dp = (struct direct *) (d->dd_buf + d->dd_loc); d->dd_loc += sizeof (struct direct); if (dp->d_ino) return dp; /* if have a good entry return it */ } return NIL; /* all done */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_a32.c000066400000000000000000000023341137544547100230520ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX 3.2 on RS 6000 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include char *crypt (char *key,char *salt); extern int sys_nerr; extern char *sys_errlist[]; #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" #include "flocksim.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_a32.h000066400000000000000000000017501137544547100230600ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX on RS6000 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include /* for struct tm */ #include #include #include #include #include #include #define utime portable_utime int portable_utime (char *file,time_t timep[2]); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_a41.c000066400000000000000000000024731137544547100230560ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX 4.1 on RS 6000 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include #include /* needed for authenticate() */ int authenticate (char *UserName,char *Response,int *Reenter,char **Message); extern int sys_nerr; extern char *sys_errlist[]; #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" #include "flocksim.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_a41.h000066400000000000000000000017501137544547100230600ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX on RS6000 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include /* for struct tm */ #include #include #include #include #include #include #define utime portable_utime int portable_utime (char *file,time_t timep[2]); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_aix.c000066400000000000000000000024301137544547100232430ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" char *crypt (char *key,char *salt); extern long timezone; extern int daylight; extern char *tzname[2]; extern int sys_nerr; extern char *sys_errlist[]; #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "memmove.c" #include "strerror.c" #include "tz_sv4.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_aix.h000066400000000000000000000015761137544547100232620ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #define direct dirent char *strerror (int n); void *memmove (void *s,void *ct,size_t n); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_aos.c000066400000000000000000000024301137544547100232440ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- 4.3BSD version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') extern int sys_nerr; extern char *sys_errlist[]; #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_wait.c" #include "memmove.c" #include "strerror.c" #include "strstr.c" #include "strtoul.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_aos.h000066400000000000000000000020501137544547100232470ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AOS version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include char *getenv (char *name); char *strstr (char *cs,char *ct); char *strerror (int n); void *memmove (void *s,void *ct,size_t n); unsigned long strtoul (char *s,char **endp,int base); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_art.c000066400000000000000000000036331137544547100232560ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX/RT version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include extern int errno; #include #include #include #define KERNEL #include #undef KERNEL #include "misc.h" #define DIR_SIZE(d) sizeof (DIR) extern int sys_nerr; extern char *sys_errlist[]; #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define NBBY 8 /* number of bits in a byte */ #define FD_SETSIZE 256 typedef long fd_mask; #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ #define howmany(x, y) (((x)+((y)-1))/(y)) typedef struct fd_set { fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; } fd_set; #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \ (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \ ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \ (1 << ((n) % NFDBITS))) #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait.c" #include "flocksim.c" #include "memmove2.c" #include "strerror.c" #include "tz_sv4.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_art.h000066400000000000000000000034011137544547100232540ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- AIX/RT version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #define direct dirent #include #include #include #include #include /* Different names between BSD and SVR4 */ #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define random lrand48 #define SIGSTOP SIGQUIT /* For setitimer() emulation */ #define ITIMER_REAL 0 struct passwd *getpwent (void); struct passwd *getpwuid (int uid); struct passwd *getpwnam (char *name); char *getenv (char *name); long gethostid (void); void *memmove (void *s,void *ct,size_t n); char *strstr (char *cs,char *ct); char *strerror (int n); unsigned long strtoul (char *s,char **endp,int base); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); int openlog (ident,logopt,facility); int syslog (priority,message,parameters ...); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_asv.c000066400000000000000000000027501137544547100232600ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Altos System V version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define DIR_SIZE(d) d->d_reclen #define pid_t short /* may not be known on all ASV systems */ #include "strstr.c" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "strerror.c" #include "flocksim.c" #include "scandir.c" #include "strtoul.c" #include "tz_sv4.c" #include "gethstid.c" #include "memmove.c" #include "fsync.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_asv.h000066400000000000000000000032341137544547100232630ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Altos System V version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include /* Different names, equivalent things in BSD and SysV */ #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define direct dirent #define ftruncate chsize #define random lrand48 struct passwd *getpwent (void); struct passwd *getpwuid (int uid); struct passwd *getpwnam (char *name); char *getenv (char *name); long gethostid (void); void *memmove (void *s,void *ct,size_t n); char *strstr (char *cs,char *ct); char *strerror (int n); unsigned long strtoul (char *s,char **endp,int base); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_aux.c000066400000000000000000000024451137544547100232650ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- A/UX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') extern char *sys_errlist[]; extern int sys_nerr; #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait.c" #include "flocksim.c" #include "strerror.c" #include "strtoul.c" #include "strpbrk.c" /* the A/UX version is bogus! */ #include "memmove.c" #include "tz_sv4.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_aux.h000066400000000000000000000017271137544547100232740ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- A/UX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include extern int errno; char *strerror (int n); unsigned long strtoul (char *s,char **endp,int base); void *memmove (void *s,void *ct,size_t n); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_bsd.c000066400000000000000000000024301137544547100232320ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- 4.3BSD version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') extern int sys_nerr; extern char *sys_errlist[]; #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_wait.c" #include "memmove.c" #include "strerror.c" #include "strstr.c" #include "strtoul.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_bsd.h000066400000000000000000000021531137544547100232410ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- 4.3BSD version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include /* needed for htons() prototypes */ char *getenv (char *name); char *strstr (char *cs,char *ct); char *strerror (int n); void *memmove (void *s,void *ct,size_t n); unsigned long strtoul (char *s,char **endp,int base); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_bsf.c000066400000000000000000000021661137544547100232420ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- BSDI BSD/386 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 5 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "getspnam.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_bsf.h000066400000000000000000000015211137544547100232410ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- FreeBSD version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 5 March 1993 * Last Edited: 31 January 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #define direct dirent #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_bsi.c000066400000000000000000000021661137544547100232450ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- BSDI BSD/386 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 5 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "getspnam.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_bsi.h000066400000000000000000000014771137544547100232560ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- BSDI BSD/386 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 5 March 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_cvx.c000066400000000000000000000022451137544547100232660ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Convex version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') #include #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_wait.c" #include "tz_nul.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_cvx.h000066400000000000000000000015651137544547100232770ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Convex version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include void *malloc (size_t byteSize); void *realloc (void *oldptr,size_t newsize); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_cyg.c000066400000000000000000000026221137544547100232470ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Cygwin version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 25 April 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include #include "misc.h" #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait.c" #include "tz_nul.c" #include "flockcyg.c" #include "gethstid.c" /* Emulator for geteuid() call * Returns: effective UID */ #undef geteuid uid_t Geteuid (void) { uid_t ret = geteuid (); return (ret == SYSTEMUID) ? 0 : ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_cyg.h000066400000000000000000000026011137544547100232510ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Cygwin version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 19 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #define direct dirent #define CYGKLUDGEOFFSET 1 /* don't write 1st byte of shared-lock files */ /* Cygwin gets this wrong */ #define setpgrp setpgid #define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */ #define geteuid Geteuid uid_t Geteuid (void); /* Now Cygwin has reportedly joined this madness. Use ifndef in case it shares the SVR4 silliness too */ #ifndef L_SET #define L_SET SEEK_SET #endif #ifndef L_INCR #define L_INCR SEEK_CUR #endif #ifndef L_XTND #define L_XTND SEEK_END #endif #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flockcyg.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_d-g.c000066400000000000000000000021461137544547100231350ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- D-G version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" #include "flocksim.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_d-g.h000066400000000000000000000021751137544547100231440ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- D-G version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include /* for struct tm */ #include #define _USEC_UTIME_FLAVOR /* break it for compatibility with */ #include /* the incompatible past */ #include #include /* D-G gets this wrong */ #define setpgrp setpgrp2 #define utime portable_utime int portable_utime (char *file,time_t timep[2]); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_do4.c000066400000000000000000000022661137544547100231570ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Apollo Domain/OS sr10.4 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') extern int sys_nerr; extern char *sys_errlist[]; #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_do4.h000066400000000000000000000020111137544547100231500ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Apollo Domain/OS sr10.4 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #ifndef htons #include /* needed for htons() prototypes */ #endif extern int daylight; /* local timzone uses daylight savings time */ extern long altzone; /* seconds west of UTC during daylight time */ #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_drs.c000066400000000000000000000022541137544547100232560ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- ICL DRS/NX * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" char *crypt (char *key,char *salt); #define DIR_SIZE(d) d->d_reclen #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" #include "flocksim.c" #include "scandir.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_drs.h000066400000000000000000000022371137544547100232640ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- ICL DRS/NX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include /* for struct tm */ #include #include #include #include #include #define random rand #define direct dirent long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_dyn.c000066400000000000000000000024661137544547100232650ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Dynix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_wait.c" #include "memmove.c" #include "strerror.c" #include "strpbrk.c" #include "strstr.c" #include "strtoul.c" #include "strtok.c" #include "tz_nul.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_dyn.h000066400000000000000000000023521137544547100232640ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Dynix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include typedef unsigned long size_t; char *strtok (char *s,char *ct); char *strstr (char *cs,char *ct); char *strpbrk (char *cs,char *ct); char *strerror (int n); void *memmove (void *s,void *ct,size_t n); void *memset (void *s,int c,size_t n); unsigned long strtoul (char *s,char **endp,int base); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); int errno; #define memcpy memmove #define strchr index #define strrchr rindex #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_hpp.c000066400000000000000000000032561137544547100232600ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- HP/UX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') #include #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ extern char *sys_errlist[]; extern int sys_nerr; #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "flocksim.c" #include "tz_sv4.c" #undef setpgrp #include "setpgrp.c" #include "utime.c" /* Emulator for BSD gethostid() call * Returns: a unique identifier for the system. * Even though HP/UX has an undocumented gethostid() system call, * it does not work (at least for non-privileged users). */ long gethostid (void) { struct utsname udata; return (uname (&udata)) ? 0xfeedface : atol (udata.__idnumber); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_hpp.h000066400000000000000000000021511137544547100232560ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- HP/UX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include #define direct dirent #define random lrand48 /* Many versions of SysV get this wrong */ #define setpgrp(a,b) Setpgrp(a,b) #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_isc.c000066400000000000000000000026641137544547100232510ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- ISC version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define DIR_SIZE(d) d->d_reclen #define pid_t short /* may not be known on all ISC systems */ #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "strerror.c" #include "flocksim.c" #include "scandir.c" #include "tz_sv4.c" #include "fsync.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_isc.h000066400000000000000000000026561137544547100232570ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- ISC version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include /* Different names, equivalent things in BSD and SysV */ /* L_SET is defined for some strange reason in on SVR4. */ #ifndef L_SET #define L_SET SEEK_SET #endif #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define direct dirent #define ftruncate chsize #define random lrand48 long gethostid (void); void *memmove (void *s,void *ct,size_t n); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_lnx.c000066400000000000000000000021551137544547100232670ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Linux version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1993 * Last Edited: 20 April 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" #include "flocklnx.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_lnx.h000066400000000000000000000023501137544547100232710ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Linux version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 September 1993 * Last Edited: 20 April 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* *** These lines are claimed to be necessary to build on Debian Linux on an *** Alpha. */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 1 #endif /* _XOPEN_SOURCE */ #ifndef _BSD_SOURCE #define _BSD_SOURCE 1 #endif /* _BSD_SOURCE */ /* end Debian Linux on Alpha strangeness */ #include #include #include #include #include #include /* for struct tm */ #include #include #include /* Linux gets this wrong */ #define setpgrp setpgid #define direct dirent #define flock safe_flock #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_lyn.c000066400000000000000000000021471137544547100232710ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- LynxOS version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" char *crypt (char *key,char *salt); #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_nul.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_lyn.h000066400000000000000000000014761137544547100233020ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- LynxOS version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 5 March 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #define gethostid clock #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_mct.c000066400000000000000000000021021137544547100232410ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MachTen version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait4.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_mct.h000066400000000000000000000014731137544547100232600ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MachTen version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_mnt.c000066400000000000000000000021231137544547100232570ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Mint version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_nul.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_mnt.h000066400000000000000000000017211137544547100232670ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Mint version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 September 1993 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #define EAGAIN EWOULDBLOCK #define FNDELAY O_NDELAY /* MiNT gets this wrong */ #define setpgrp setpgid #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_nto.c000066400000000000000000000031721137544547100232660ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- QNX Neutrino RTP version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1993 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include #include #include "misc.h" #define DIR_SIZE(d) d->d_reclen #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait.c" #include "tz_sv4.c" #include "gethstid.c" #include "flocksim.c" #include "utime.c" /* QNX local readdir() * Accepts: directory structure * Returns: direct struct or NIL if failed */ #undef readdir struct direct *Readdir (DIR *dirp) { static struct direct dc; struct dirent *de = readdir (dirp); if (!de) return NIL; /* end of data */ dc.d_fileno = 0; /* could get from de->stat.st_ino */ dc.d_namlen = strlen (strcpy (dc.d_name,de->d_name)); dc.d_reclen = sizeof (dc); return &dc; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_nto.h000066400000000000000000000026521137544547100232750ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- QNX Neutrino RTP version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 September 1993 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include /* QNX gets these wrong */ #define setpgrp setpgid #define readdir Readdir #define FNDELAY O_NONBLOCK #define d_ino d_fileno /* Different names, equivalent things in BSD and SysV */ #ifndef L_SET #define L_SET SEEK_SET #endif #ifndef L_INCR #define L_INCR SEEK_CUR #endif #ifndef L_XTND #define L_XTND SEEK_END #endif #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); struct direct *Readdir (DIR *dirp); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_nxt.c000066400000000000000000000021221137544547100232710ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- NeXT version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_wait4.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_nxt.h000066400000000000000000000014661137544547100233100ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- NeXT version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_os4.c000066400000000000000000000022471137544547100231750ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64 4 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 21 June 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include #include #include #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "gr_waitp.c" #include "tcp_unix.c" #include "tz_bsd.c" #undef flock #include "flocksim.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_os4.h000066400000000000000000000017401137544547100231770ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 15 June 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include /* for struct tm */ #include #include #include #include /* OSF/1 gets this wrong */ #define setpgrp setpgid #define direct dirent #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_osf.c000066400000000000000000000022021137544547100232460ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 15 June 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "gr_waitp.c" #include "tcp_unix.c" #include "tz_bsd.c" #undef flock #include "flocksim.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_osf.h000066400000000000000000000017151137544547100232630ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 15 June 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include /* for struct tm */ #include #include #include /* OSF/1 gets this wrong */ #define setpgrp setpgid #define direct dirent #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_osx.c000066400000000000000000000021021137544547100232670ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MachTen version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait4.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_osx.h000066400000000000000000000014731137544547100233060ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- MachTen version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_ptx.c000066400000000000000000000052441137544547100233030ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- PTX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define DIR_SIZE(d) d->d_reclen #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #define env_init ENV_INIT #include "env_unix.c" #undef env_init #define getpeername Getpeername #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "flocksim.c" #include "scandir.c" #include "tz_sv4.c" #include "utime.c" /* Jacket around env_init() to work around PTX inetd braindamage */ static char may_need_server_init = T; long env_init (char *user,char *home) { if (may_need_server_init) { /* maybe need to do server init cruft? */ may_need_server_init = NIL; /* not any more we don't */ if (!getuid ()) { /* if root, we're most likely a server */ t_sync (0); /* PTX inetd is stupid, stupid, stupid */ ioctl (0,I_PUSH,"tirdwr");/* it needs this cruft, else servers won't */ dup2 (0,1); /* work. How obnoxious!!! */ } } ENV_INIT (user,home); /* call the real routine */ } /* Emulator for BSD gethostid() call * Returns: unique identifier for this machine */ long gethostid (void) { struct sockaddr_in sin; int inet = t_open (TLI_TCP, O_RDWR, 0); if (inet < 0) return 0; getmyinaddr (inet,&sin,sizeof (sin)); close (inet); return sin.sin_addr.s_addr; } /* Replaced version of getpeername() that jackets into getpeerinaddr() * Accepts: file descriptor * pointer to Internet socket addr * length * Returns: zero if success, data in socket addr */ int Getpeername (int s,struct sockaddr *name,int *namelen) { return getpeerinaddr (s,(struct sockaddr_in *) name,*namelen); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_ptx.h000066400000000000000000000026761137544547100233160ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- PTX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include /* needed in daemons */ #include #include #include /* Different names, equivalent things in BSD and SysV */ #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define direct dirent #define random lrand48 #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); long ENV_INIT (char *user,char *home); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_pyr.c000066400000000000000000000024161137544547100233000ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Pyramid OSx 4.4c version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait.c" #include "memmove.c" #include "memset.c" #include "strerror.c" #include "strpbrk.c" #include "strstr.c" #include "strtok.c" #include "tz_nul.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_pyr.h000066400000000000000000000022401137544547100233000ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Pyramid OSx 4.4c version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include char *strtok (char *s,char *ct); char *strstr (char *cs,char *ct); char *strpbrk (char *cs,char *ct); char *strerror (int n); void *memmove (void *s,void *ct,size_t n); void *memset (void *s,int c,size_t n); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); int errno; #define memcpy memmove #define strchr index #define strrchr rindex #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_qnx.c000066400000000000000000000031331137544547100232710ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- QNX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1993 * Last Edited: 2 January 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include #include #include "misc.h" #define DIR_SIZE(d) d->d_reclen #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" #include "gethstid.c" #include "scandir.c" /* QNX local readdir() * Accepts: directory structure * Returns: direct struct or NIL if failed */ #undef readdir struct direct *Readdir (DIR *dirp) { static struct direct dc; struct dirent *de = readdir (dirp); if (!de) return NIL; /* end of data */ dc.d_fileno = 0; /* could get from de->stat.st_ino */ dc.d_namlen = strlen (strcpy (dc.d_name,de->d_name)); dc.d_reclen = sizeof (dc); return &dc; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_qnx.h000066400000000000000000000023741137544547100233040ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- QNX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 September 1993 * Last Edited: 2 January 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include /* QNX gets these wrong */ #define setpgrp setpgid #define readdir Readdir #define FNDELAY O_NONBLOCK #define d_ino d_fileno typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" long gethostid(void); struct direct *Readdir (DIR *dirp); extern char *crypt (const char *pw, const char *salt); extern long random (void); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_s40.c000066400000000000000000000025631137544547100230770ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SUN-OS 4.0 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_wait4.c" #include "memmove.c" #include "strerror.c" #define strstr Strstr /* override SUN's broken version */ #include "strstr.c" #include "strtoul.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_s40.h000066400000000000000000000013311137544547100230740ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SUN-OS 4.0 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); #include "os_sun.h" /* now use regular SUN-OS file */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sc5.c000066400000000000000000000025261137544547100231620ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SCO Unix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include /* must be before osdep.h */ #include "mail.h" #include #include "osdep.h" #include #include #include #include #include #include #include #include "misc.h" #define SecureWare /* protected subsystem */ #include #include #include #include #define DIR_SIZE(d) d->d_reclen #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "flocksim.c" #include "scandir.c" #include "tz_sv4.c" #include "gethstid.c" #undef setpgrp #include "setpgrp.c" #include "rename.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sc5.h000066400000000000000000000026661137544547100231740ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SCO Unix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include /* SCO gets this wrong */ #define setpgrp Setpgrp #define rename Rename /* Different names, equivalent things in BSD and SysV */ #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define direct dirent #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); int fsync (int fd); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sco.c000066400000000000000000000026221137544547100232510ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SCO Unix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include /* must be before osdep.h */ #include "mail.h" #include #include "osdep.h" #include #include #include #include #include #include #include #include "misc.h" #define SecureWare /* protected subsystem */ #include #include #include #include char *bigcrypt (char *key,char *salt); #define DIR_SIZE(d) d->d_reclen #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "flocksim.c" #include "scandir.c" #include "tz_sv4.c" #include "gethstid.c" #include "fsync.c" #undef setpgrp #include "setpgrp.c" #include "rename.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sco.h000066400000000000000000000027441137544547100232630ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SCO Unix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include /* SCO gets this wrong */ #define setpgrp Setpgrp #define rename Rename /* Different names, equivalent things in BSD and SysV */ #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define direct dirent #define utime portable_utime int portable_utime (char *file,time_t timep[2]); #define ftruncate chsize #define random rand long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); int fsync (int fd); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sgi.c000066400000000000000000000022371137544547100232510ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SGI version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_nul.c" #include "flocksim.c" #undef setpgrp #include "setpgrp.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sgi.h000066400000000000000000000021541137544547100232540ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SGI version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 14 June 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include /* for struct tm */ #include #include #include #include #include /* Many versions of SysV get this wrong */ #define setpgrp(a,b) Setpgrp(a,b) #define direct dirent #define fatal cclient_fatal #define utime portable_utime int portable_utime (char *file,time_t timep[2]); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_shp.c000066400000000000000000000033301137544547100232540ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- HP/UX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') #include #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ extern char *sys_errlist[]; extern int sys_nerr; #include #include #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "flocksim.c" #include "tz_sv4.c" #undef setpgrp #include "setpgrp.c" #include "utime.c" /* Emulator for BSD gethostid() call * Returns: a unique identifier for the system. * Even though HP/UX has an undocumented gethostid() system call, * it does not work (at least for non-privileged users). */ long gethostid (void) { struct utsname udata; return (uname (&udata)) ? 0xfeedface : atol (udata.__idnumber); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_shp.h000066400000000000000000000021511137544547100232610ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- HP/UX version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include #define direct dirent #define random lrand48 /* Many versions of SysV get this wrong */ #define setpgrp(a,b) Setpgrp(a,b) #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_slx.c000066400000000000000000000022101137544547100232640ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Secure Linux version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1993 * Last Edited: 20 April 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_sv4.c" #include "flocklnx.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_slx.h000066400000000000000000000023501137544547100232760ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Linux version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 September 1993 * Last Edited: 20 April 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* *** These lines are claimed to be necessary to build on Debian Linux on an *** Alpha. */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 1 #endif /* _XOPEN_SOURCE */ #ifndef _BSD_SOURCE #define _BSD_SOURCE 1 #endif /* _BSD_SOURCE */ /* end Debian Linux on Alpha strangeness */ #include #include #include #include #include #include /* for struct tm */ #include #include #include /* Linux gets this wrong */ #define setpgrp setpgid #define direct dirent #define flock safe_flock #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sol.c000066400000000000000000000026331137544547100232640ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Solaris version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define DIR_SIZE(d) d->d_reclen #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "flocksim.c" #include "scandir.c" #include "tz_sv4.c" #include "gethstid.c" #undef setpgrp #include "setpgrp.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_soln.h000066400000000000000000000032001137544547100234360ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Solaris version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 28 September 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include /* Many versions of SysV get this wrong */ #define setpgrp(a,b) Setpgrp(a,b) /* Different names, equivalent things in BSD and SysV */ /* L_SET is defined for some strange reason in on SVR4. */ #ifndef L_SET #define L_SET SEEK_SET #endif #ifndef L_INCR #define L_INCR SEEK_CUR #endif #ifndef L_XTND #define L_XTND SEEK_END #endif #define direct dirent #define random lrand48 #define scandir Scandir #define getpass getpassphrase #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (const void *d1,const void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_solo.h000066400000000000000000000031401137544547100234420ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Solaris version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 28 September 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include /* Many versions of SysV get this wrong */ #define setpgrp(a,b) Setpgrp(a,b) /* Different names, equivalent things in BSD and SysV */ /* L_SET is defined for some strange reason in on SVR4. */ #ifndef L_SET #define L_SET SEEK_SET #endif #ifndef L_INCR #define L_INCR SEEK_CUR #endif #ifndef L_XTND #define L_XTND SEEK_END #endif #define direct dirent #define random lrand48 #define scandir Scandir #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (const void *d1,const void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sos.c000066400000000000000000000022601137544547100232670ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- S OSF/Digital UNIX/Tru64 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 21 June 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include #include #include #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_bsd.c" #undef flock #include "flocksim.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sos.h000066400000000000000000000017401137544547100232760ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64 * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 15 June 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include /* for struct tm */ #include #include #include #include /* OSF/1 gets this wrong */ #define setpgrp setpgid #define direct dirent #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sun.c000066400000000000000000000025571137544547100233010ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SUN-OS version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "memmove.c" #include "strerror.c" #define strstr Strstr /* override SUN's broken version */ #include "strstr.c" #include "strtoul.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sun.h000066400000000000000000000020671137544547100233020ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SUN-OS version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #define strstr Strstr /* override system definition */ #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" char *Strstr (char *cs,char *ct); char *strerror (int n); unsigned long strtoul (char *s,char **endp,int base); #define memcpy memmove void *memmove (void *s,void *ct,size_t n); void *memset (void *s,int c,size_t n); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sv2.c000066400000000000000000000051461137544547100232030ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SVR2 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include extern int errno; #include #include #include #define KERNEL #include #undef KERNEL #include "misc.h" #define DIR_SIZE(d) sizeof (DIR) extern int sys_nerr; extern char *sys_errlist[]; #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define NBBY 8 /* number of bits in a byte */ #define FD_SETSIZE 256 typedef long fd_mask; #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ #define howmany(x, y) (((x)+((y)-1))/(y)) typedef struct fd_set { fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; } fd_set; #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \ (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \ ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \ (1 << ((n) % NFDBITS))) #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_wait.c" #include "flocksim.c" #include "opendir.c" #include "scandir.c" #include "memmove2.c" #include "strstr.c" #include "strerror.c" #include "strtoul.c" #include "tz_sv4.c" #include "gethstid.c" #include "fsync.c" #undef setpgrp #include "setpgrp.c" /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ int syslog (int priority,char *message,char *parameters) { /* nothing here for now */ } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ int openlog (char *ident,int logopt,int facility) { /* nothing here for now */ } /* Emulator for BSD ftruncate() routine * Accepts: file descriptor * length */ int ftruncate (int fd,unsigned long length) { return -1; /* gotta figure out how to do this */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sv2.h000066400000000000000000000051711137544547100232060ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SVR2 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #define char void #include #undef char #include #include #include #include #include #include /* Many versions of SysV get this wrong */ #define setpgrp(a,b) Setpgrp(a,b) /* Different names between BSD and SVR4 */ #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define lstat stat #define random lrand48 #define SIGSTOP SIGQUIT #define S_IFLNK 0120000 /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ /* For setitimer() emulation */ #define ITIMER_REAL 0 /* For opendir() emulation */ typedef struct _dirdesc { int dd_fd; long dd_loc; long dd_size; char *dd_buf; } DIR; struct passwd *getpwent (void); struct passwd *getpwuid (int uid); struct passwd *getpwnam (char *name); struct group *getgrnam (char *name); char *getenv (char *name); long gethostid (void); void *memmove (void *s,void *ct,size_t n); char *strstr (char *cs,char *ct); char *strerror (int n); unsigned long strtoul (char *s,char **endp,int base); DIR *opendir (char * name); int closedir (DIR *d); struct direct *readdir (DIR *d); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); int fsync (int fd); int openlog (ident,logopt,facility); int syslog (priority,message,parameters ...); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sv4.c000066400000000000000000000026051137544547100232020ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SVR4 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" extern int sys_nerr; extern char *sys_errlist[]; #define DIR_SIZE(d) d->d_reclen #define toint(c) ((c)-'0') #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #include "tcp_unix.c" #include "gr_waitp.c" #include "flocksim.c" #include "scandir.c" #include "tz_sv4.c" #include "gethstid.c" #undef setpgrp #include "setpgrp.c" #include "utime.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_sv4.h000066400000000000000000000030041137544547100232010ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- SVR4 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 April 1992 * Last Edited: 10 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include #include #include #include /* Many versions of SysV get this wrong */ #define setpgrp(a,b) Setpgrp(a,b) /* Different names, equivalent things in BSD and SysV */ /* L_SET is defined for some strange reason in on SVR4. */ #ifndef L_SET #define L_SET SEEK_SET #endif #define L_INCR SEEK_CUR #define L_XTND SEEK_END #define direct dirent #define random lrand48 #define utime portable_utime int portable_utime (char *file,time_t timep[2]); long gethostid (void); typedef int (*select_t) (struct direct *name); typedef int (*compar_t) (void *d1,void *d2); int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #include "flocksim.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_ult.c000066400000000000000000000020551137544547100232710ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Ultrix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_waitp.c" #include "tz_bsd.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_ult.h000066400000000000000000000014501137544547100232740ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- Ultrix version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_vu2.c000066400000000000000000000034531137544547100232040ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- VAX Ultrix 2.3 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_unix.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include #include "misc.h" #define NFDBITS (sizeof(long) * 8) #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \ (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \ ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \ (1 << ((n) % NFDBITS))) #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) /* Old Ultrix has its own wierd inet_addr() that returns a in_addr struct. */ /* Portable inet_addr () that returns a u_long * Accepts: dotted host string * Returns: u_long */ u_long portable_inet_addr (char *hostname) { struct in_addr *in = &inet_addr (hostname); return in->s_addr; } #define inet_addr portable_inet_addr #include "fs_unix.c" #include "ftl_unix.c" #include "nl_unix.c" #include "env_unix.c" #define fork vfork #include "tcp_unix.c" #include "gr_wait.c" #include "memmove.c" #include "strerror.c" #include "strstr.c" #include "strtoul.c" #include "tz_nul.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/os_vu2.h000066400000000000000000000041701137544547100232060ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- VAX Ultrix 2.3 version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #include /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ #define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067)) #define toint(c) ((c)-'0') extern int sys_nerr; extern char *sys_errlist[]; char *getenv (char *name); char *strstr (char *cs,char *ct); char *strerror (int n); void *memmove (void *s,void *ct,size_t n); unsigned long strtoul (char *s,char **endp,int base); void *malloc (size_t byteSize); void free (void *ptr); void *realloc (void *oldptr,size_t newsize); u_long portable_inet_addr (char *hostname); #include "env_unix.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/phile.c000066400000000000000000000375331137544547100230760ustar00rootroot00000000000000/* * Program: File routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 25 August 1993 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include #include "mail.h" #include "osdep.h" #include #include #include #include "rfc822.h" #include "misc.h" #include "dummy.h" /* Types returned from phile_type() */ #define PTYPEBINARY 0 /* binary data */ #define PTYPETEXT 1 /* textual data */ #define PTYPECRTEXT 2 /* textual data with CR */ #define PTYPE8 4 /* textual 8bit data */ #define PTYPEISO2022JP 8 /* textual Japanese */ #define PTYPEISO2022KR 16 /* textual Korean */ #define PTYPEISO2022CN 32 /* textual Chinese */ /* PHILE I/O stream local data */ typedef struct phile_local { ENVELOPE *env; /* file envelope */ BODY *body; /* file body */ char tmp[MAILTMPLEN]; /* temporary buffer */ } PHILELOCAL; /* Convenient access to local data */ #define LOCAL ((PHILELOCAL *) stream->local) /* Function prototypes */ DRIVER *phile_valid (char *name); int phile_isvalid (char *name,char *tmp); void *phile_parameters (long function,void *value); void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void phile_list (MAILSTREAM *stream,char *ref,char *pat); void phile_lsub (MAILSTREAM *stream,char *ref,char *pat); long phile_create (MAILSTREAM *stream,char *mailbox); long phile_delete (MAILSTREAM *stream,char *mailbox); long phile_rename (MAILSTREAM *stream,char *old,char *newname); long phile_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *phile_open (MAILSTREAM *stream); int phile_type (unsigned char *s,unsigned long i,unsigned long *j); void phile_close (MAILSTREAM *stream,long options); ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body, long flags); char *phile_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); long phile_ping (MAILSTREAM *stream); void phile_check (MAILSTREAM *stream); void phile_expunge (MAILSTREAM *stream); long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* File routines */ /* Driver dispatch used by MAIL */ DRIVER philedriver = { "phile", /* driver name */ /* driver flags */ DR_LOCAL|DR_READONLY|DR_NOSTICKY, (DRIVER *) NIL, /* next driver */ phile_valid, /* mailbox is valid for us */ phile_parameters, /* manipulate parameters */ phile_scan, /* scan mailboxes */ phile_list, /* list mailboxes */ phile_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ phile_status, /* status of mailbox */ phile_open, /* open mailbox */ phile_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ phile_structure, /* fetch message envelopes */ phile_header, /* fetch message header only */ phile_text, /* fetch message body only */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ phile_ping, /* ping mailbox to see if still alive */ phile_check, /* check for new messages */ phile_expunge, /* expunge deleted messages */ phile_copy, /* copy messages to another mailbox */ phile_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM phileproto = {&philedriver}; /* File validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *phile_valid (char *name) { char tmp[MAILTMPLEN]; return phile_isvalid (name,tmp) ? &philedriver : NIL; } /* File test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int phile_isvalid (char *name,char *tmp) { struct stat sbuf; char *s; /* INBOX never accepted, any other name is */ return ((s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) && !(sbuf.st_mode & S_IFDIR) && /* only allow empty files if #ftp */ (sbuf.st_size || ((*name == '#') && ((name[1] == 'f') || (name[1] == 'F')) && ((name[2] == 't') || (name[2] == 'T')) && ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/')))); } /* File manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *phile_parameters (long function,void *value) { return NIL; } /* File mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* File list mailboxes * Accepts: mail stream * reference * pattern to search */ void phile_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* File list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void phile_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* File status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long phile_status (MAILSTREAM *stream,char *mbx,long flags) { char *s,tmp[MAILTMPLEN]; MAILSTATUS status; struct stat sbuf; long ret = NIL; if ((s = mailboxfile (tmp,mbx)) && *s && !stat (s,&sbuf)) { status.flags = flags; /* return status values */ status.unseen = (stream && mail_elt (stream,1)->seen) ? 0 : 1; status.messages = status.recent = status.uidnext = 1; status.uidvalidity = sbuf.st_mtime; /* pass status to main program */ mm_status (stream,mbx,&status); ret = LONGT; /* success */ } return ret; } /* File open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *phile_open (MAILSTREAM *stream) { int i,k,fd; unsigned long j,m; char *s,tmp[MAILTMPLEN]; struct passwd *pw; struct stat sbuf; struct tm *t; MESSAGECACHE *elt; SIZEDTEXT *buf; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &phileproto; if (stream->local) fatal ("phile recycle stream"); /* open associated file */ if (!mailboxfile (tmp,stream->mailbox) || !tmp[0] || stat (tmp,&sbuf) || (fd = open (tmp,O_RDONLY,NIL)) < 0) { sprintf (tmp,"Unable to open file %s",stream->mailbox); mm_log (tmp,ERROR); return NIL; } fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); stream->local = fs_get (sizeof (PHILELOCAL)); mail_exists (stream,1); /* make sure upper level knows */ mail_recent (stream,1); elt = mail_elt (stream,1); /* instantiate cache element */ elt->valid = elt->recent = T; /* mark valid flags */ stream->sequence++; /* bump sequence number */ stream->rdonly = T; /* make sure upper level knows readonly */ /* instantiate a new envelope and body */ LOCAL->env = mail_newenvelope (); LOCAL->body = mail_newbody (); t = gmtime (&sbuf.st_mtime); /* get UTC time and Julian day */ i = t->tm_hour * 60 + t->tm_min; k = t->tm_yday; t = localtime(&sbuf.st_mtime);/* get local time */ /* calculate time delta */ i = t->tm_hour * 60 + t->tm_min - i; if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60; k = abs (i); /* time from UTC either way */ elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec; elt->day = t->tm_mday; elt->month = t->tm_mon + 1; elt->year = t->tm_year - (BASEYEAR - 1900); elt->zoccident = (k == i) ? 0 : 1; elt->zhours = k/60; elt->zminutes = k % 60; sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d", days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+', elt->zhours,elt->zminutes); /* set up Date field */ LOCAL->env->date = cpystr (tmp); /* fill in From field from file owner */ LOCAL->env->from = mail_newaddr (); if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name); else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid); LOCAL->env->from->mailbox = cpystr (tmp); LOCAL->env->from->host = cpystr (mylocalhost ()); /* set subject to be mailbox name */ LOCAL->env->subject = cpystr (stream->mailbox); /* slurp the data */ (buf = &elt->private.special.text)->size = sbuf.st_size; read (fd,buf->data = (unsigned char *) fs_get (buf->size + 1),buf->size); buf->data[buf->size] = '\0'; close (fd); /* close the file */ /* analyze data type */ if (i = phile_type (buf->data,buf->size,&j)) { LOCAL->body->type = TYPETEXT; LOCAL->body->subtype = cpystr ("PLAIN"); if (!(i & PTYPECRTEXT)) { /* change Internet newline format as needed */ s = (char *) buf->data; /* make copy of UNIX-format string */ buf->data = NIL; /* zap the buffer */ buf->size = strcrlfcpy (&buf->data,&m,s,buf->size); fs_give ((void **) &s); /* flush original UNIX-format string */ } LOCAL->body->parameter = mail_newbody_parameter (); LOCAL->body->parameter->attribute = cpystr ("charset"); LOCAL->body->parameter->value = cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" : (i & PTYPEISO2022KR) ? "ISO-2022-KR" : (i & PTYPEISO2022CN) ? "ISO-2022-CN" : (i & PTYPE8) ? "X-UNKNOWN" : "US-ASCII"); LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT; LOCAL->body->size.lines = j; } else { /* binary data */ LOCAL->body->type = TYPEAPPLICATION; LOCAL->body->subtype = cpystr ("OCTET-STREAM"); LOCAL->body->parameter = mail_newbody_parameter (); LOCAL->body->parameter->attribute = cpystr ("name"); LOCAL->body->parameter->value = cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox); LOCAL->body->encoding = ENCBASE64; buf->data = rfc822_binary (s = (char *) buf->data,buf->size,&buf->size); fs_give ((void **) &s); /* flush originary binary contents */ } phile_header (stream,1,&j,NIL); LOCAL->body->size.bytes = LOCAL->body->contents.text.size = buf->size; elt->rfc822_size = j + buf->size; /* only one message ever... */ stream->uid_validity = sbuf.st_mtime; stream->uid_last = elt->private.uid = 1; return stream; /* return stream alive to caller */ } /* File determine data type * Accepts: data to examine * size of data * pointer to line count return * Returns: PTYPE mask of data type */ int phile_type (unsigned char *s,unsigned long i,unsigned long *j) { int ret = PTYPETEXT; char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; *j = 0; /* no lines */ /* check type of every character */ while (i--) switch (charvec[*s++]) { case 'A': ret |= PTYPE8; /* 8bit character */ break; case 'a': break; /* ASCII character */ case 'b': return PTYPEBINARY; /* binary byte seen, stop immediately */ case 'c': ret |= PTYPECRTEXT; /* CR indicates Internet text */ break; case 'e': /* ESC */ if (*s == '$') { /* ISO-2022 sequence? */ switch (s[1]) { case 'B': case '@': ret |= PTYPEISO2022JP; break; case ')': switch (s[2]) { case 'A': case 'E': case 'G': ret |= PTYPEISO2022CN; break; case 'C': ret |= PTYPEISO2022KR; break; } case '*': switch (s[2]) { case 'H': ret |= PTYPEISO2022CN; break; } case '+': switch (s[2]) { case 'I': case 'J': case 'K': case 'L': case 'M': ret |= PTYPEISO2022CN; break; } } } break; case 'l': /* newline */ (*j)++; break; } return ret; /* return type of data */ } /* File close * Accepts: MAIL stream * close options */ void phile_close (MAILSTREAM *stream,long options) { if (LOCAL) { /* only if a file is open */ fs_give ((void **) &mail_elt (stream,1)->private.special.text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* File fetch structure * Accepts: MAIL stream * message # to fetch * pointer to return body * option flags * Returns: envelope of this message, body returned in body value * * Fetches the "fast" information as well */ ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body, long flags) { if (body) *body = LOCAL->body; return LOCAL->env; /* return the envelope */ } /* File fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *phile_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { rfc822_header (LOCAL->tmp,LOCAL->env,LOCAL->body); *length = strlen (LOCAL->tmp); return LOCAL->tmp; } /* File fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T, always */ long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { SIZEDTEXT *buf = &mail_elt (stream,msgno)->private.special.text; if (!(flags &FT_PEEK)) { /* mark message as seen */ mail_elt (stream,msgno)->seen = T; mm_flags (stream,msgno); } INIT (bs,mail_string,buf->data,buf->size); return T; } /* File ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ long phile_ping (MAILSTREAM *stream) { return T; } /* File check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void phile_check (MAILSTREAM *stream) { mm_log ("Check completed",NIL); } /* File expunge mailbox * Accepts: MAIL stream */ void phile_expunge (MAILSTREAM *stream) { mm_log ("Expunge ignored on readonly mailbox",NIL); } /* File copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { char tmp[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (tmp,"Can't copy - file \"%s\" is not in valid mailbox format", stream->mailbox); mm_log (tmp,ERROR); return NIL; } /* File append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback function * data for callback * Returns: T if append successful, else NIL */ long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { char tmp[MAILTMPLEN],file[MAILTMPLEN]; char *s = mailboxfile (file,mailbox); if (s && *s) sprintf (tmp,"Can't append - not in valid mailbox format: %.80s",s); else sprintf (tmp,"Can't append - invalid name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/pmatch.c000066400000000000000000000050631137544547100232420ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-dependent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return (*pat == *s) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return (*pat == *s) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/pseudo.c000066400000000000000000000020221137544547100232550ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Local sites may wish to alter this text */ char *pseudo_from = "MAILER-DAEMON"; char *pseudo_name = "Mail System Internal Data"; char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA"; char *pseudo_msg = "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values." ; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/pseudo.h000066400000000000000000000011421137544547100232640ustar00rootroot00000000000000/* * Program: Pseudo Header Strings * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 September 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/rename.c000066400000000000000000000016261137544547100232360ustar00rootroot00000000000000/* * Program: Rename file * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 May 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for working Unix rename() call * Accepts: old file name * new file name * Returns: 0 if success, -1 if error with error in errno */ int Rename (char *oldname,char *newname) { int ret; unlink (newname); /* make sure the old name doesn't exist */ /* link to new name, unlink old name */ if (!(ret = link (oldname,newname))) unlink (oldname); return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/scandir.c000066400000000000000000000041051137544547100234050ustar00rootroot00000000000000/* * Program: Scan directories * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for BSD scandir() call * Accepts: directory name * destination pointer of names array * selection function * comparison function * Returns: number of elements in the array or -1 if error */ int scandir (char *dirname,struct direct ***namelist,select_t select, compar_t compar) { struct direct *p,*d,**names; int nitems; struct stat stb; long nlmax; DIR *dirp = opendir (dirname);/* open directory and get status poop */ if ((!dirp) || (fstat (dirp->dd_fd,&stb) < 0)) return -1; nlmax = stb.st_size / 24; /* guesstimate at number of files */ names = (struct direct **) malloc (nlmax * sizeof (struct direct *)); nitems = 0; /* initially none found */ while (d = readdir (dirp)) { /* read directory item */ /* matches select criterion? */ if (select && !(*select) (d)) continue; /* get size of direct record for this file */ p = (struct direct *) malloc (DIR_SIZE (d)); p->d_ino = d->d_ino; /* copy the poop */ strcpy (p->d_name,d->d_name); if (++nitems >= nlmax) { /* if out of space, try bigger guesstimate */ void *s = (void *) names; /* stupid language */ nlmax *= 2; /* double it */ realloc ((void **) &s,nlmax * sizeof (struct direct *)); names = (struct direct **) s; } names[nitems - 1] = p; /* store this file there */ } closedir (dirp); /* done with directory */ /* sort if necessary */ if (nitems && compar) qsort (names,nitems,sizeof (struct direct *),compar); *namelist = names; /* return directory */ return nitems; /* and size */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/setpgrp.c000066400000000000000000000013161137544547100234470ustar00rootroot00000000000000/* * Program: Set process group emulator * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 3 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for BSD setpgrp() call * Accepts: process ID * group ID * Returns: 0 if successful, -1 if failure */ int Setpgrp (int pid,int gid) { return setpgrp (); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/sig_bsd.c000066400000000000000000000013261137544547100233760ustar00rootroot00000000000000/* * Program: BSD Signals * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 29 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include /* Arm a signal * Accepts: signal number * desired action * Returns: old action */ void *arm_signal (int sig,void *action) { return (void *) signal (sig,action); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/sig_psx.c000066400000000000000000000021151137544547100234350ustar00rootroot00000000000000/* * Program: POSIX Signals * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 29 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #ifndef SA_RESTART #define SA_RESTART 0 #endif /* Arm a signal * Accepts: signal number * desired action * Returns: old action */ void *arm_signal (int sig,void *action) { struct sigaction nact,oact; memset (&nact,0,sizeof (struct sigaction)); sigemptyset (&nact.sa_mask); /* no signals blocked */ nact.sa_handler = action; /* set signal handler */ nact.sa_flags = SA_RESTART; /* needed on Linux, nice on SVR4 */ sigaction (sig,&nact,&oact); /* do the signal action */ return (void *) oact.sa_handler; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/sig_sv4.c000066400000000000000000000013271137544547100233430ustar00rootroot00000000000000/* * Program: SVR4 Signals * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 29 April 1997 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include /* Arm a signal * Accepts: signal number * desired action * Returns: old action */ void *arm_signal (int sig,void *action) { return (void *) sigset (sig,action); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ssl_none.c000066400000000000000000000045401137544547100236050ustar00rootroot00000000000000/* * Program: Dummy (no SSL) authentication/encryption module * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 7 February 2001 * Last Edited: 6 April 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Init server for SSL * Accepts: server name */ void ssl_server_init (char *server) { syslog (LOG_ERR,"This server does not support SSL"); exit (1); /* punt this program too */ } /* Start TLS * Accepts: /etc/services service name * Returns: cpystr'd error string if TLS failed, else NIL for success */ char *ssl_start_tls (char *server) { return cpystr ("This server does not support TLS"); } /* Get character * Returns: character or EOF */ int PBIN (void) { return getchar (); } /* Get string * Accepts: destination string pointer * number of bytes available * Returns: destination string pointer or NIL if EOF */ char *PSIN (char *s,int n) { return fgets (s,n,stdin); } /* Get record * Accepts: destination string pointer * number of bytes to read * Returns: T if success, NIL otherwise */ long PSINR (char *s,unsigned long n) { unsigned long i; while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i; return n ? NIL : LONGT; } /* Wait for input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long INWAIT (long seconds) { return server_input_wait (seconds); } /* Put character * Accepts: character * Returns: character written or EOF */ int PBOUT (int c) { return putchar (c); } /* Put string * Accepts: source string pointer * Returns: 0 or EOF if error */ int PSOUT (char *s) { return fputs (s,stdout); } /* Put record * Accepts: source sized text * Returns: 0 or EOF if error */ int PSOUTR (SIZEDTEXT *s) { unsigned char *t; unsigned long i,j; for (t = s->data,i = s->size; (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR))); t += j,i -= j); return i ? EOF : NIL; } /* Flush output * Returns: 0 or EOF if error */ int PFLUSH (void) { return fflush (stdout); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ssl_unix.c000066400000000000000000000533331137544547100236350ustar00rootroot00000000000000/* * Program: SSL authentication/encryption module * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 September 1998 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define crypt ssl_private_crypt #include #include #include #include #include #include #include #include #undef crypt #define SSLBUFLEN 8192 #define SSLCIPHERLIST "ALL:!LOW" /* SSL I/O stream */ typedef struct ssl_stream { TCPSTREAM *tcpstream; /* TCP stream */ SSL_CTX *context; /* SSL context */ SSL *con; /* SSL connection */ int ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[SSLBUFLEN]; /* input buffer */ } SSLSTREAM; #include "sslio.h" /* Function prototypes */ static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags); static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags); static int ssl_open_verify (int ok,X509_STORE_CTX *ctx); static char *ssl_extract_cn (char *name); static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat); static long ssl_abort (SSLSTREAM *stream); static RSA *ssl_genkey (SSL *con,int export,int keylength); /* Secure Sockets Layer network driver dispatch */ static struct ssl_driver ssldriver = { ssl_open, /* open connection */ ssl_aopen, /* open preauthenticated connection */ ssl_getline, /* get a line */ ssl_getbuffer, /* get a buffer */ ssl_soutr, /* output pushed data */ ssl_sout, /* output string */ ssl_close, /* close connection */ ssl_host, /* return host name */ ssl_remotehost, /* return remote host name */ ssl_port, /* return port number */ ssl_localhost /* return local host name */ }; /* non-NIL if doing SSL primary I/O */ static SSLSTDIOSTREAM *sslstdio = NIL; static char *start_tls = NIL; /* non-NIL if start TLS requested */ /* One-time SSL initialization */ static int sslonceonly = 0; void ssl_onceonlyinit (void) { if (!sslonceonly++) { /* only need to call it once */ int fd; unsigned long i; char tmp[MAILTMPLEN]; struct stat sbuf; /* if system doesn't have /dev/urandom */ if (stat ("/dev/urandom",&sbuf)) { if ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT,0600)) < 0) i = (unsigned long) tmp; else { unlink (tmp); /* don't need the file */ fstat (fd,&sbuf); /* get information about the file */ i = sbuf.st_ino; /* remember its inode */ close (fd); /* or its descriptor */ } /* not great but it'll have to do */ sprintf (tmp + strlen (tmp),"%.80s%lx%lx%lx", tcp_serverhost (),i, (unsigned long) (time (0) ^ gethostid ()), (unsigned long) getpid ()); RAND_seed (tmp,strlen (tmp)); } /* apply runtime linkage */ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver); mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start); SSL_library_init (); /* add all algorithms */ } } /* SSL open * Accepts: host name * contact service name * contact port number * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = tcp_open (host,service,port); return stream ? ssl_start (stream,host,port) : NIL; } /* SSL authenticated open * Accepts: host name * service name * returned user name buffer * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* don't use this mechanism with SSL */ } /* Start SSL/TLS negotiations * Accepts: open TCP stream of session * user's host name * flags * Returns: SSL stream if success else NIL */ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags) { char *reason,tmp[MAILTMPLEN]; sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); stream->tcpstream = tstream; /* bind TCP stream */ /* do the work */ reason = ssl_start_work (stream,host,flags); (*bn) (BLOCK_NONSENSITIVE,data); if (reason) { /* failed? */ ssl_close (stream); /* failed to do SSL */ stream = NIL; /* no stream returned */ switch (*reason) { /* analyze reason */ case '*': /* certificate failure */ ++reason; /* skip over certificate failure indication */ /* pass to error callback */ if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } case '\0': /* user answered no to certificate callback */ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); break; default: /* non-certificate failure */ if (flags & NET_TRYSSL); /* no error output if tryssl */ /* pass to error callback */ else if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } break; } } return stream; } /* Start SSL/TLS negotiations worker routine * Accepts: SSL stream * user's host name * flags * Returns: NIL if success, else error reason */ /* evil but I had no choice */ static char *ssl_last_error = NIL; static char *ssl_last_host = NIL; static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags) { BIO *bio; X509 *cert; char *s,*err,tmp[MAILTMPLEN]; sslcertificatequery_t scq = (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); if (ssl_last_error) fs_give ((void **) &ssl_last_error); ssl_last_host = host; if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ? TLSv1_client_method () : SSLv23_client_method ()))) return "SSL context failed"; SSL_CTX_set_options (stream->context,0); /* disable certificate validation? */ if (flags & NET_NOVALIDATECERT) SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL); else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify); /* set default paths to CAs */ SSL_CTX_set_default_verify_paths (stream->context); /* create connection */ if (!(stream->con = (SSL *) SSL_new (stream->context))) return "SSL connection failed"; bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE); SSL_set_bio (stream->con,bio,bio); SSL_set_connect_state (stream->con); if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con); /* now negotiate SSL */ if (SSL_write (stream->con,"",0) < 0) return ssl_last_error ? ssl_last_error : "SSL negotiation failed"; /* need to validate host names? */ if (!(flags & NET_NOVALIDATECERT)) { /* get certificate */ if (!(cert = SSL_get_peer_certificate (stream->con))) err = "No certificate from server"; /* locate Common Name */ else if (!(s = ssl_extract_cn (cert->name))) err = "Unable to locate common name in certificate"; /* wildcard match it with user's name */ else err = ssl_compare_hostnames (host,s) ? NIL : "Server name does not match certificate"; if (err) { /* got an error? */ /* application callback */ if (scq) return (*scq) (err,host,cert->name) ? NIL : ""; /* error message to return via mm_log() */ sprintf (tmp,"*%.128s: %.255s",err,cert->name); return ssl_last_error = cpystr (tmp); } } return NIL; } /* SSL certificate verification callback * Accepts: error flag * X509 context * Returns: error flag */ static int ssl_open_verify (int ok,X509_STORE_CTX *ctx) { char *err,cert[256],tmp[MAILTMPLEN]; sslcertificatequery_t scq = (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); if (!ok) { /* in case failure */ err = (char *) X509_verify_cert_error_string (X509_STORE_CTX_get_error (ctx)); X509_NAME_oneline (X509_get_subject_name (X509_STORE_CTX_get_current_cert (ctx)),cert,255); if (!scq) { /* mm_log() error message if no callback */ sprintf (tmp,"*%.128s: %.255s",err,cert); ssl_last_error = cpystr (tmp); } /* ignore error if application says to */ else if ((*scq) (err,ssl_last_host,cert)) ok = T; /* application wants punt */ else ssl_last_error = cpystr (""); } return ok; } /* SSL extract Common Name * Accepts: name * Returns: common name, or NIL if failure */ static char *ssl_extract_cn (char *name) { char *s; if (name && (name = strstr (name,"/CN=")) && (s = strchr (name += 4,'/'))) *s = '\0'; return name; } /* Case-independent wildcard pattern match * Accepts: base string * pattern string * Returns: T if pattern matches base, else NIL */ static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat) { if (*pat != '*') /* non-wildcard */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? (*pat ? ssl_compare_hostnames (s+1,pat+1) : T) : NIL; if (pat[1]) { /* scan remainder of string until delimiter */ do if (ssl_compare_hostnames (s,pat+1)) return T; while ((*s != '.') && *s++); } return NIL; /* wildcard ran off end of string */ } /* SSL receive line * Accepts: SSL stream * Returns: text line string or NIL if failure */ char *ssl_getline (SSLSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!ssl_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!ssl_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = ssl_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* SSL receive buffer * Accepts: SSL stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; while (size > 0) { /* until request satisfied */ if (!ssl_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (buffer,stream->iptr,n); buffer += n; /* update pointer */ stream->iptr += n; size -= n; /* update # of bytes to do */ stream->ictr -= n; } buffer[0] = '\0'; /* tie off string */ return T; } /* SSL receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long ssl_getdata (SSLSTREAM *stream) { int i,sock; fd_set fds,efds; struct timeval tmo; tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL); long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL); time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL; (*bn) (BLOCK_TCPREAD,NIL); while (stream->ictr < 1) { /* if nothing in the buffer */ if (!SSL_pending (stream->con)) { time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (sock,&fds); /* set bit in selection vector */ FD_SET (sock,&efds); /* set bit in error selection vector */ errno = NIL; /* block and read */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i <= 0) { /* failed, timeout with continue? */ if (!i && tmoh && ((*tmoh) (now - t,now - tl))) continue; /* error or timeout no-continue */ else return ssl_abort (stream); } } while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) && ((errno == EINTR) || (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ))); if (i < 1) return ssl_abort (stream); stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ } (*bn) (BLOCK_NONE,NIL); return T; } /* SSL send string as record * Accepts: SSL stream * string pointer * Returns: T if success else NIL */ long ssl_soutr (SSLSTREAM *stream,char *string) { return ssl_sout (stream,string,(unsigned long) strlen (string)); } /* SSL send string * Accepts: SSL stream * string pointer * byte count * Returns: T if success else NIL */ long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size) { long i; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!stream->con) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); /* until request satisfied */ for (i = 0; size > 0; string += i,size -= i) /* write as much as we can */ if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) return ssl_abort (stream);/* write failed */ (*bn) (BLOCK_NONE,NIL); return LONGT; /* all done */ } /* SSL close * Accepts: SSL stream */ void ssl_close (SSLSTREAM *stream) { ssl_abort (stream); /* nuke the stream */ fs_give ((void **) &stream); /* flush the stream */ } /* SSL abort stream * Accepts: SSL stream * Returns: NIL always */ static long ssl_abort (SSLSTREAM *stream) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->con) { /* close SSL connection */ SSL_shutdown (stream->con); SSL_free (stream->con); stream->con = NIL; } if (stream->context) { /* clean up context */ SSL_CTX_free (stream->context); stream->context = NIL; } if (stream->tcpstream) { /* close TCP stream */ tcp_close (stream->tcpstream); stream->tcpstream = NIL; } (*bn) (BLOCK_NONE,NIL); return NIL; } /* SSL get host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_host (SSLSTREAM *stream) { return tcp_host (stream->tcpstream); } /* SSL get remote host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_remotehost (SSLSTREAM *stream) { return tcp_remotehost (stream->tcpstream); } /* SSL return port for this stream * Accepts: SSL stream * Returns: port number for this stream */ unsigned long ssl_port (SSLSTREAM *stream) { return tcp_port (stream->tcpstream); } /* SSL get local host name * Accepts: SSL stream * Returns: local host name */ char *ssl_localhost (SSLSTREAM *stream) { return tcp_localhost (stream->tcpstream); } /* Start TLS * Accepts: /etc/services service name * Returns: cpystr'd error string if TLS failed, else NIL for success */ char *ssl_start_tls (char *server) { char tmp[MAILTMPLEN]; struct stat sbuf; if (sslstdio) return cpystr ("Already in an SSL session"); if (start_tls) return cpystr ("TLS already started"); if (server) { /* build specific certificate/key file name */ sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ()); if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */ sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server); if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed"); } start_tls = server; /* switch to STARTTLS mode */ } return NIL; } /* Init server for SSL * Accepts: server name */ void ssl_server_init (char *server) { char cert[MAILTMPLEN],key[MAILTMPLEN]; unsigned long i; struct stat sbuf; SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); ssl_onceonlyinit (); /* make sure algorithms added */ ERR_load_crypto_strings (); SSL_load_error_strings (); /* build specific certificate/key file names */ sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ()); sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ()); /* use non-specific name if no specific cert */ if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server); if (stat (key,&sbuf)) { /* use non-specific name if no specific key */ sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server); /* use cert file as fallback for key */ if (stat (key,&sbuf)) strcpy (key,cert); } /* create context */ if (!(stream->context = SSL_CTX_new (start_tls ? TLSv1_server_method () : SSLv23_server_method ()))) syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s", tcp_clienthost ()); else { /* set context options */ SSL_CTX_set_options (stream->context,SSL_OP_ALL); /* set cipher list */ if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST)) syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s", SSLCIPHERLIST,tcp_clienthost ()); /* load certificate */ else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert)) syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s", cert,tcp_clienthost ()); /* load key */ else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key, SSL_FILETYPE_PEM))) syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s", key,tcp_clienthost ()); else { /* generate key if needed */ if (SSL_CTX_need_tmp_RSA (stream->context)) SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey); /* create new SSL connection */ if (!(stream->con = SSL_new (stream->context))) syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s", tcp_clienthost ()); else { /* set file descriptor */ SSL_set_fd (stream->con,0); /* all OK if accepted */ if (SSL_accept (stream->con) < 0) syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s", tcp_clienthost ()); else { /* server set up */ sslstdio = (SSLSTDIOSTREAM *) memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM)); sslstdio->sslstream = stream; /* available space in output buffer */ sslstdio->octr = SSLBUFLEN; /* current output buffer pointer */ sslstdio->optr = sslstdio->obuf; /* allow PLAIN authenticator */ auth_pla.server = auth_plain_server; if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1) mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL); return; } } } } while (i = ERR_get_error ()) /* SSL failure */ syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL)); ssl_close (stream); /* punt stream */ exit (1); /* punt this program too */ } /* Generate one-time key for server * Accepts: SSL connection * export flag * keylength * Returns: generated key, always */ static RSA *ssl_genkey (SSL *con,int export,int keylength) { unsigned long i; static RSA *key = NIL; if (!key) { /* if don't have a key already */ /* generate key */ if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) { syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s", tcp_clienthost ()); while (i = ERR_get_error ()) syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL)); exit (1); } } return key; } /* Wait for stdin input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long ssl_server_input_wait (long seconds) { int i,sock; fd_set fds,efd; struct timeval tmo; SSLSTREAM *stream; if (!sslstdio) return server_input_wait (seconds); /* input available in buffer */ if (((stream = sslstdio->sslstream)->ictr > 0) || !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT; /* input available from SSL */ if (SSL_pending (stream->con) && ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) { stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ return LONGT; } FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efd); /* initialize selection vector */ FD_SET (sock,&fds); /* set bit in selection vector */ FD_SET (sock,&efd); /* set bit in selection vector */ tmo.tv_sec = seconds; tmo.tv_usec = 0; /* see if input available from the socket */ return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL; } #include "sslstdio.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/ssl_unix.new000066400000000000000000000602111137544547100241750ustar00rootroot00000000000000/* * Program: SSL authentication/encryption module * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 September 1998 * Last Edited: 16 August 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define crypt ssl_private_crypt #include #include #include #include #include #include #include #include #undef crypt #define SSLBUFLEN 8192 #define SSLCIPHERLIST "ALL:!LOW" /* SSL I/O stream */ typedef struct ssl_stream { TCPSTREAM *tcpstream; /* TCP stream */ SSL_CTX *context; /* SSL context */ SSL *con; /* SSL connection */ int ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[SSLBUFLEN]; /* input buffer */ } SSLSTREAM; #include "sslio.h" /* Function prototypes */ static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags); static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags); static int ssl_open_verify (int ok,X509_STORE_CTX *ctx); static char *ssl_validate_cert (X509 *cert,char *host); static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat); static long ssl_abort (SSLSTREAM *stream); static RSA *ssl_genkey (SSL *con,int export,int keylength); /* Secure Sockets Layer network driver dispatch */ static struct ssl_driver ssldriver = { ssl_open, /* open connection */ ssl_aopen, /* open preauthenticated connection */ ssl_getline, /* get a line */ ssl_getbuffer, /* get a buffer */ ssl_soutr, /* output pushed data */ ssl_sout, /* output string */ ssl_close, /* close connection */ ssl_host, /* return host name */ ssl_remotehost, /* return remote host name */ ssl_port, /* return port number */ ssl_localhost /* return local host name */ }; /* non-NIL if doing SSL primary I/O */ static SSLSTDIOSTREAM *sslstdio = NIL; static char *start_tls = NIL; /* non-NIL if start TLS requested */ /* One-time SSL initialization */ static int sslonceonly = 0; void ssl_onceonlyinit (void) { if (!sslonceonly++) { /* only need to call it once */ int fd; unsigned long i; char tmp[MAILTMPLEN]; struct stat sbuf; /* if system doesn't have /dev/urandom */ if (stat ("/dev/urandom",&sbuf)) { if ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT,0600)) < 0) i = (unsigned long) tmp; else { unlink (tmp); /* don't need the file */ fstat (fd,&sbuf); /* get information about the file */ i = sbuf.st_ino; /* remember its inode */ close (fd); /* or its descriptor */ } /* not great but it'll have to do */ sprintf (tmp + strlen (tmp),"%.80s%lx%lx%lx", tcp_serverhost (),i, (unsigned long) (time (0) ^ gethostid ()), (unsigned long) getpid ()); RAND_seed (tmp,strlen (tmp)); } /* apply runtime linkage */ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver); mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start); SSL_library_init (); /* add all algorithms */ } } /* SSL open * Accepts: host name * contact service name * contact port number * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = tcp_open (host,service,port); return stream ? ssl_start (stream,host,port) : NIL; } /* SSL authenticated open * Accepts: host name * service name * returned user name buffer * Returns: SSL stream if success else NIL */ SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* don't use this mechanism with SSL */ } /* Start SSL/TLS negotiations * Accepts: open TCP stream of session * user's host name * flags * Returns: SSL stream if success else NIL */ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags) { char *reason,tmp[MAILTMPLEN]; sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data = (*bn) (BLOCK_SENSITIVE,NIL); SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); stream->tcpstream = tstream; /* bind TCP stream */ /* do the work */ reason = ssl_start_work (stream,host,flags); (*bn) (BLOCK_NONSENSITIVE,data); if (reason) { /* failed? */ ssl_close (stream); /* failed to do SSL */ stream = NIL; /* no stream returned */ switch (*reason) { /* analyze reason */ case '*': /* certificate failure */ ++reason; /* skip over certificate failure indication */ /* pass to error callback */ if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } case '\0': /* user answered no to certificate callback */ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); break; default: /* non-certificate failure */ if (flags & NET_TRYSSL); /* no error output if tryssl */ /* pass to error callback */ else if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } break; } } return stream; } /* Start SSL/TLS negotiations worker routine * Accepts: SSL stream * user's host name * flags * Returns: NIL if success, else error reason */ /* evil but I had no choice */ static char *ssl_last_error = NIL; static char *ssl_last_host = NIL; static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags) { BIO *bio; X509 *cert; unsigned long sl,tl; char *s,*t,*err,tmp[MAILTMPLEN]; sslcertificatequery_t scq = (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); sslclientcert_t scc = (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL); sslclientkey_t sck = (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL); if (ssl_last_error) fs_give ((void **) &ssl_last_error); ssl_last_host = host; if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ? TLSv1_client_method () : SSLv23_client_method ()))) return "SSL context failed"; SSL_CTX_set_options (stream->context,0); /* disable certificate validation? */ if (flags & NET_NOVALIDATECERT) SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL); else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify); /* set default paths to CAs */ SSL_CTX_set_default_verify_paths (stream->context); /* want to send client certificate? */ if (scc && (s = (*scc) ()) && (sl = strlen (s))) { if (cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) { SSL_CTX_use_certificate (stream->context,cert); X509_free (cert); } BIO_free (bio); if (!cert) return "SSL client certificate failed"; /* want to supply private key? */ if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) { EVP_PKEY *key; if (key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl), NIL,NIL,"")) { SSL_CTX_use_PrivateKey (stream->context,key); EVP_PKEY_free (key); } BIO_free (bio); memset (t,0,tl); /* erase key */ } if (s != t) memset (s,0,sl);/* erase certificate if different from key */ } /* create connection */ if (!(stream->con = (SSL *) SSL_new (stream->context))) return "SSL connection failed"; bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE); SSL_set_bio (stream->con,bio,bio); SSL_set_connect_state (stream->con); if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con); /* now negotiate SSL */ if (SSL_write (stream->con,"",0) < 0) return ssl_last_error ? ssl_last_error : "SSL negotiation failed"; /* need to validate host names? */ if (!(flags & NET_NOVALIDATECERT) && (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con), host))) { /* application callback */ if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : ""; /* error message to return via mm_log() */ sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???"); return ssl_last_error = cpystr (tmp); } return NIL; } /* SSL certificate verification callback * Accepts: error flag * X509 context * Returns: error flag */ static int ssl_open_verify (int ok,X509_STORE_CTX *ctx) { char *err,cert[256],tmp[MAILTMPLEN]; sslcertificatequery_t scq = (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); if (!ok) { /* in case failure */ err = (char *) X509_verify_cert_error_string (X509_STORE_CTX_get_error (ctx)); X509_NAME_oneline (X509_get_subject_name (X509_STORE_CTX_get_current_cert (ctx)),cert,255); if (!scq) { /* mm_log() error message if no callback */ sprintf (tmp,"*%.128s: %.255s",err,cert); ssl_last_error = cpystr (tmp); } /* ignore error if application says to */ else if ((*scq) (err,ssl_last_host,cert)) ok = T; /* application wants punt */ else ssl_last_error = cpystr (""); } return ok; } /* SSL validate certificate * Accepts: certificate * host to validate against * Returns: NIL if validated, else string of error message */ static char *ssl_validate_cert (X509 *cert,char *host) { int i,n; char *s,*t,*ret; void *ext; GENERAL_NAME *name; /* make sure have a certificate */ if (!cert) ret = "No certificate from server"; /* and that it has a name */ else if (!cert->name) ret = "No name in certificate"; /* locate CN */ else if (s = strstr (cert->name,"/CN=")) { if (t = strchr (s += 4,'/')) *t = '\0'; /* host name matches pattern? */ ret = ssl_compare_hostnames (host,s) ? NIL : "Server name does not match certificate"; if (t) *t = '/'; /* restore smashed delimiter */ /* if mismatch, see if in extensions */ if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) && (n = sk_GENERAL_NAME_num (ext))) /* older versions of OpenSSL use "ia5" instead of dNSName */ for (i = 0; ret && (i < n); i++) if ((name = sk_GENERAL_NAME_value (ext,i)) && (name->type = GEN_DNS) && (s = name->d.ia5->data) && ssl_compare_hostnames (host,s)) ret = NIL; } else ret = "Unable to locate common name in certificate"; return ret; } /* Case-independent wildcard pattern match * Accepts: base string * pattern string * Returns: T if pattern matches base, else NIL */ static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat) { if (*pat != '*') /* non-wildcard */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? (*pat ? ssl_compare_hostnames (s+1,pat+1) : T) : NIL; if (pat[1]) { /* scan remainder of string until delimiter */ do if (ssl_compare_hostnames (s,pat+1)) return T; while ((*s != '.') && *s++); } return NIL; /* wildcard ran off end of string */ } /* SSL receive line * Accepts: SSL stream * Returns: text line string or NIL if failure */ char *ssl_getline (SSLSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!ssl_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!ssl_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = ssl_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* SSL receive buffer * Accepts: SSL stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; while (size > 0) { /* until request satisfied */ if (!ssl_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (buffer,stream->iptr,n); buffer += n; /* update pointer */ stream->iptr += n; size -= n; /* update # of bytes to do */ stream->ictr -= n; } buffer[0] = '\0'; /* tie off string */ return T; } /* SSL receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long ssl_getdata (SSLSTREAM *stream) { int i,sock; fd_set fds,efds; struct timeval tmo; tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL); long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL); time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL; (*bn) (BLOCK_TCPREAD,NIL); while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; if (SSL_pending (stream->con)) i = 1; else { if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (sock,&fds); /* set bit in selection vector */ FD_SET (sock,&efds); /* set bit in error selection vector */ errno = NIL; /* block and read */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); } if (i) { /* non-timeout result from select? */ errno = 0; /* just in case */ if (i > 0) /* read what we can */ while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) && ((errno == EINTR) || (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ))); if (i <= 0) { /* error seen? */ if (tcpdebug) { char *s,tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d", errno,SSL_get_error (stream->con,i)); else s = "SSL data read end of file"; mm_log (s,TCPDEBUG); } return ssl_abort (stream); } stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG); } /* timeout, punt unless told not to */ else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG); return ssl_abort (stream); } } (*bn) (BLOCK_NONE,NIL); return T; } /* SSL send string as record * Accepts: SSL stream * string pointer * Returns: T if success else NIL */ long ssl_soutr (SSLSTREAM *stream,char *string) { return ssl_sout (stream,string,(unsigned long) strlen (string)); } /* SSL send string * Accepts: SSL stream * string pointer * byte count * Returns: T if success else NIL */ long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size) { long i; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!stream->con) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG); /* until request satisfied */ for (i = 0; size > 0; string += i,size -= i) /* write as much as we can */ if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) { if (tcpdebug) { char tmp[MAILTMPLEN]; sprintf (tmp,"SSL data write I/O error %d SSL error %d", errno,SSL_get_error (stream->con,i)); mm_log (tmp,TCPDEBUG); } return ssl_abort (stream);/* write failed */ } if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); (*bn) (BLOCK_NONE,NIL); return LONGT; /* all done */ } /* SSL close * Accepts: SSL stream */ void ssl_close (SSLSTREAM *stream) { ssl_abort (stream); /* nuke the stream */ fs_give ((void **) &stream); /* flush the stream */ } /* SSL abort stream * Accepts: SSL stream * Returns: NIL always */ static long ssl_abort (SSLSTREAM *stream) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->con) { /* close SSL connection */ SSL_shutdown (stream->con); SSL_free (stream->con); stream->con = NIL; } if (stream->context) { /* clean up context */ SSL_CTX_free (stream->context); stream->context = NIL; } if (stream->tcpstream) { /* close TCP stream */ tcp_close (stream->tcpstream); stream->tcpstream = NIL; } (*bn) (BLOCK_NONE,NIL); return NIL; } /* SSL get host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_host (SSLSTREAM *stream) { return tcp_host (stream->tcpstream); } /* SSL get remote host name * Accepts: SSL stream * Returns: host name for this stream */ char *ssl_remotehost (SSLSTREAM *stream) { return tcp_remotehost (stream->tcpstream); } /* SSL return port for this stream * Accepts: SSL stream * Returns: port number for this stream */ unsigned long ssl_port (SSLSTREAM *stream) { return tcp_port (stream->tcpstream); } /* SSL get local host name * Accepts: SSL stream * Returns: local host name */ char *ssl_localhost (SSLSTREAM *stream) { return tcp_localhost (stream->tcpstream); } /* Start TLS * Accepts: /etc/services service name * Returns: cpystr'd error string if TLS failed, else NIL for success */ char *ssl_start_tls (char *server) { char tmp[MAILTMPLEN]; struct stat sbuf; if (sslstdio) return cpystr ("Already in an SSL session"); if (start_tls) return cpystr ("TLS already started"); if (server) { /* build specific certificate/key file name */ sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ()); if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */ sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server); if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed"); } start_tls = server; /* switch to STARTTLS mode */ } return NIL; } /* Init server for SSL * Accepts: server name */ void ssl_server_init (char *server) { char cert[MAILTMPLEN],key[MAILTMPLEN]; unsigned long i; struct stat sbuf; SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); ssl_onceonlyinit (); /* make sure algorithms added */ ERR_load_crypto_strings (); SSL_load_error_strings (); /* build specific certificate/key file names */ sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ()); sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ()); /* use non-specific name if no specific cert */ if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server); if (stat (key,&sbuf)) { /* use non-specific name if no specific key */ sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server); /* use cert file as fallback for key */ if (stat (key,&sbuf)) strcpy (key,cert); } /* create context */ if (!(stream->context = SSL_CTX_new (start_tls ? TLSv1_server_method () : SSLv23_server_method ()))) syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s", tcp_clienthost ()); else { /* set context options */ SSL_CTX_set_options (stream->context,SSL_OP_ALL); /* set cipher list */ if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST)) syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s", SSLCIPHERLIST,tcp_clienthost ()); /* load certificate */ else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert)) syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s", cert,tcp_clienthost ()); /* load key */ else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key, SSL_FILETYPE_PEM))) syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s", key,tcp_clienthost ()); else { /* generate key if needed */ if (SSL_CTX_need_tmp_RSA (stream->context)) SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey); /* create new SSL connection */ if (!(stream->con = SSL_new (stream->context))) syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s", tcp_clienthost ()); else { /* set file descriptor */ SSL_set_fd (stream->con,0); /* all OK if accepted */ if (SSL_accept (stream->con) < 0) syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s", tcp_clienthost ()); else { /* server set up */ sslstdio = (SSLSTDIOSTREAM *) memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM)); sslstdio->sslstream = stream; /* available space in output buffer */ sslstdio->octr = SSLBUFLEN; /* current output buffer pointer */ sslstdio->optr = sslstdio->obuf; /* allow PLAIN authenticator */ auth_pla.server = auth_plain_server; if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1) mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL); return; } } } } while (i = ERR_get_error ()) /* SSL failure */ syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL)); ssl_close (stream); /* punt stream */ exit (1); /* punt this program too */ } /* Generate one-time key for server * Accepts: SSL connection * export flag * keylength * Returns: generated key, always */ static RSA *ssl_genkey (SSL *con,int export,int keylength) { unsigned long i; static RSA *key = NIL; if (!key) { /* if don't have a key already */ /* generate key */ if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) { syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s", tcp_clienthost ()); while (i = ERR_get_error ()) syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL)); exit (1); } } return key; } /* Wait for stdin input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long ssl_server_input_wait (long seconds) { int i,sock; fd_set fds,efd; struct timeval tmo; SSLSTREAM *stream; if (!sslstdio) return server_input_wait (seconds); /* input available in buffer */ if (((stream = sslstdio->sslstream)->ictr > 0) || !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT; /* input available from SSL */ if (SSL_pending (stream->con) && ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) { stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ return LONGT; } FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efd); /* initialize selection vector */ FD_SET (sock,&fds); /* set bit in selection vector */ FD_SET (sock,&efd); /* set bit in selection vector */ tmo.tv_sec = seconds; tmo.tv_usec = 0; /* see if input available from the socket */ return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL; } #include "sslstdio.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/sslstdio.c000066400000000000000000000076621137544547100236410ustar00rootroot00000000000000/* * Program: SSL standard I/O routines for server use * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 September 1998 * Last Edited: 29 March 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get character * Returns: character or EOF */ int PBIN (void) { if (!sslstdio) return getchar (); if (!ssl_getdata (sslstdio->sslstream)) return EOF; /* one last byte available */ sslstdio->sslstream->ictr--; return (int) *(sslstdio->sslstream->iptr)++; } /* Get string * Accepts: destination string pointer * number of bytes available * Returns: destination string pointer or NIL if EOF */ char *PSIN (char *s,int n) { int i,c; if (start_tls) { /* doing a start TLS? */ ssl_server_init (start_tls);/* enter the mode */ start_tls = NIL; /* don't do this again */ } if (!sslstdio) return fgets (s,n,stdin); for (i = c = 0, n-- ; (c != '\n') && (i < n); sslstdio->sslstream->ictr--) { if ((sslstdio->sslstream->ictr <= 0) && !ssl_getdata (sslstdio->sslstream)) return NIL; /* read error */ c = s[i++] = *(sslstdio->sslstream->iptr)++; } s[i] = '\0'; /* tie off string */ return s; } /* Get record * Accepts: destination string pointer * number of bytes to read * Returns: T if success, NIL otherwise */ long PSINR (char *s,unsigned long n) { unsigned long i; if (start_tls) { /* doing a start TLS? */ ssl_server_init (start_tls);/* enter the mode */ start_tls = NIL; /* don't do this again */ } if (sslstdio) return ssl_getbuffer (sslstdio->sslstream,n,s); /* non-SSL case */ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i; return n ? NIL : LONGT; } /* Wait for stdin input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */ long INWAIT (long seconds) { return (sslstdio ? ssl_server_input_wait : server_input_wait) (seconds); } /* Put character * Accepts: character * Returns: character written or EOF */ int PBOUT (int c) { if (!sslstdio) return putchar (c); /* flush buffer if full */ if (!sslstdio->octr && PFLUSH ()) return EOF; sslstdio->octr--; /* count down one character */ *sslstdio->optr++ = c; /* write character */ return c; /* return that character */ } /* Put string * Accepts: destination string pointer * Returns: 0 or EOF if error */ int PSOUT (char *s) { if (!sslstdio) return fputs (s,stdout); while (*s) { /* flush buffer if full */ if (!sslstdio->octr && PFLUSH ()) return EOF; *sslstdio->optr++ = *s++; /* write one more character */ sslstdio->octr--; /* count down one character */ } return 0; /* success */ } /* Put record * Accepts: source sized text * Returns: 0 or EOF if error */ int PSOUTR (SIZEDTEXT *s) { unsigned char *t = s->data; unsigned long i = s->size; unsigned long j; if (sslstdio) while (i) { /* until request satisfied */ /* flush buffer if full */ if (!sslstdio->octr && PFLUSH ()) break; /* blat as big a chucnk as we can */ memcpy (sslstdio->optr,t,j = min (i,sslstdio->octr)); sslstdio->optr += j; /* account for chunk */ sslstdio->octr -= j; t += j; i -= j; } else while (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR))) t += j,i -= j; return i ? EOF : NIL; } /* Flush output * Returns: 0 or EOF if error */ int PFLUSH (void) { if (!sslstdio) return fflush (stdout); /* force out buffer */ if (!ssl_sout (sslstdio->sslstream,sslstdio->obuf, SSLBUFLEN - sslstdio->octr)) return EOF; /* renew output buffer */ sslstdio->optr = sslstdio->obuf; sslstdio->octr = SSLBUFLEN; return 0; /* success */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/strerror.c000066400000000000000000000013611137544547100236450ustar00rootroot00000000000000/* * Program: Error number to string * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Return implementation-defined string corresponding to error * Accepts: error number * Returns: string for that error */ char *strerror (int n) { return (n >= 0 && n < sys_nerr) ? sys_errlist[n] : NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/tcp_unix.c000066400000000000000000000715011137544547100236170ustar00rootroot00000000000000/* * Program: UNIX TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 8 August 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "ip_unix.c" #undef write /* don't use redefined write() */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_open = 0; /* TCP timeouts, in seconds */ static long ttmo_read = 0; static long ttmo_write = 0; static long rshtimeout = 15; /* rsh timeout */ static char *rshcommand = NIL; /* rsh command */ static char *rshpath = NIL; /* rsh path */ static long sshtimeout = 15; /* ssh timeout */ static char *sshcommand = NIL; /* ssh command */ static char *sshpath = NIL; /* ssh path */ static long allowreversedns = T;/* allow reverse DNS lookup */ static long tcpdebug = NIL; /* extra TCP debugging telemetry */ extern long maxposint; /* get this from write.c */ /* Local function prototypes */ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port, char *tmp,int *ctr,char *hst); long tcp_abort (TCPSTREAM *stream); char *tcp_name (struct sockaddr *sadr,long flag); char *tcp_name_valid (char *s); /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_OPENTIMEOUT: ttmo_open = (long) value; case GET_OPENTIMEOUT: ret = (void *) ttmo_open; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; case SET_ALLOWREVERSEDNS: allowreversedns = (long) value; case GET_ALLOWREVERSEDNS: ret = (void *) allowreversedns; break; case SET_TCPDEBUG: tcpdebug = (long) value; case GET_TCPDEBUG: ret = (void *) tcpdebug; break; case SET_RSHTIMEOUT: rshtimeout = (long) value; case GET_RSHTIMEOUT: ret = (void *) rshtimeout; break; case SET_RSHCOMMAND: if (rshcommand) fs_give ((void **) &rshcommand); rshcommand = cpystr ((char *) value); case GET_RSHCOMMAND: ret = (void *) rshcommand; break; case SET_RSHPATH: if (rshpath) fs_give ((void **) &rshpath); rshpath = cpystr ((char *) value); case GET_RSHPATH: ret = (void *) rshpath; break; case SET_SSHTIMEOUT: sshtimeout = (long) value; case GET_SSHTIMEOUT: ret = (void *) sshtimeout; break; case SET_SSHCOMMAND: if (sshcommand) fs_give ((void **) &sshcommand); sshcommand = cpystr ((char *) value); case GET_SSHCOMMAND: ret = (void *) sshcommand; break; case SET_SSHPATH: if (sshpath) fs_give ((void **) &sshpath); sshpath = cpystr ((char *) value); case GET_SSHPATH: ret = (void *) sshpath; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number and optional silent flag * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; int family; int sock = -1; int ctr = 0; int silent = (port & NET_SILENT) ? T : NIL; int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr; char *s,*hostname,tmp[MAILTMPLEN]; void *adr; size_t adrlen; struct servent *sv = NIL; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data,*next; port &= 0xffff; /* erase flags */ /* lookup service */ if (service && (sv = getservbyname (service,"tcp"))) port = ntohs (sv->s_port); /* The domain literal form is used (rather than simply the dotted decimal as with other Unix programs) because it has to be a valid "host name" in mailsystem terminology. */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (tmp,host+1); /* yes, copy number part */ tmp[(strlen (tmp))-1] = '\0'; if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) { (*bn) (BLOCK_TCPOPEN,NIL); /* get an open socket for this system */ sock = tcp_socket_open (family,adr,adrlen,port,tmp,ctrp,hostname = host); (*bn) (BLOCK_NONE,NIL); fs_give ((void **) &adr); } else sprintf (tmp,"Bad format domain-literal: %.80s",host); } else { /* lookup host name */ if (tcpdebug) { sprintf (tmp,"DNS resolution %.80s",host); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next))) sprintf (tmp,"No such host as %.80s",host); (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); if (s) { /* DNS resolution won? */ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG); do { (*bn) (BLOCK_TCPOPEN,NIL); if (((sock = tcp_socket_open (family,s,adrlen,port,tmp,ctrp, hostname)) < 0) && (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) && !silent) mm_log (tmp,WARN); (*bn) (BLOCK_NONE,NIL); } while ((sock < 0) && s);/* repeat until success or no more addreses */ } } if (sock >= 0) { /* won */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); stream->port = port; /* port number */ /* init sockets */ stream->tcpsi = stream->tcpso = sock; /* stash in the snuck-in byte */ if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0]; /* copy official host name */ stream->host = cpystr (hostname); if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG); } else if (!silent) mm_log (tmp,ERROR); return stream; /* return success */ } /* Open a TCP socket * Accepts: protocol family * address to connect to * address length * port * scratch buffer * pointer to "first byte read in" storage or NIL * host name for error message * Returns: socket if success, else -1 with error string in scratch buffer */ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port, char *tmp,int *ctr,char *hst) { int i,ti,sock,flgs; size_t len; time_t now; struct protoent *pt = getprotobyname ("tcp"); fd_set fds,efds; struct timeval tmo; struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* fetid Solaris */ void *data = (*bn) (BLOCK_SENSITIVE,NIL); sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr)); mm_log (tmp,NIL); /* make a socket */ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) { sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno)); (*bn) (BLOCK_NONSENSITIVE,data); } else { /* get current socket flags */ flgs = fcntl (sock,F_GETFL,0); /* set non-blocking if want open timeout */ if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY); /* open connection */ while ((i = connect (sock,sadr,len)) < 0 && (errno == EINTR)); (*bn) (BLOCK_NONSENSITIVE,data); if (i < 0) switch (errno) { /* failed? */ case EAGAIN: /* DG brain damage */ case EINPROGRESS: /* what we expect to happen */ case EALREADY: /* or another form of it */ case EISCONN: /* restart after interrupt? */ case EADDRINUSE: /* restart after interrupt? */ break; /* well, not really, it was interrupted */ default: sprintf (tmp,"Can't connect to %.80s,%u: %s",hst,(unsigned int) port, strerror (errno)); close (sock); /* flush socket */ sock = -1; } if ((sock >= 0) && ctr) { /* want open timeout? */ now = time (0); /* open timeout */ ti = ttmo_open ? now + ttmo_open : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (sock,&fds); /* block for error or readable */ FD_SET (sock,&efds); do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (sock+1,&fds,NIL,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i > 0) { /* success, make sure really connected */ /* restore blocking status */ fcntl (sock,F_SETFL,flgs); /* This used to be a zero-byte read(), but that crashes Solaris */ /* get socket status */ while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR)); } if (i <= 0) { /* timeout or error? */ i = i ? errno : ETIMEDOUT;/* determine error code */ close (sock); /* flush socket */ sock = -1; errno = i; /* return error code */ sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst, (unsigned long) port,strerror (errno)); } } } fs_give ((void **) &sadr); return sock; /* return the socket */ } /* TCP/IP authenticated open * Accepts: host name * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ #define MAXARGV 20 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { TCPSTREAM *stream = NIL; void *adr; char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1]; int i,ti,pipei[2],pipeo[2]; size_t len; time_t now; struct timeval tmo; fd_set fds,efds; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (*service == '*') { /* want ssh? */ /* return immediately if ssh disabled */ if (!(sshpath && (ti = sshtimeout))) return NIL; /* ssh command prototype defined yet? */ if (!sshcommand) sshcommand = cpystr ("%s %s -l %s exec /etc/r%sd"); } else if (ti = rshtimeout) { /* set rsh timeout */ /* rsh path/command prototypes defined yet? */ if (!rshpath) rshpath = cpystr (RSHPATH); if (!rshcommand) rshcommand = cpystr ("%s %s -l %s exec /etc/r%sd"); } else return NIL; /* rsh disabled */ /* look like domain literal? */ if (mb->host[0] == '[' && mb->host[i = (strlen (mb->host))-1] == ']') { strcpy (host,mb->host+1); /* yes, copy without brackets */ host[i-1] = '\0'; /* validate domain literal */ if (adr = ip_stringtoaddr (host,&len,&i)) fs_give ((void **) &adr); else { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else strcpy (host,tcp_canonical (mb->host)); if (*service == '*') /* build ssh command */ sprintf (tmp,sshcommand,sshpath,host, mb->user[0] ? mb->user : myusername (),service + 1); else sprintf (tmp,rshcommand,rshpath,host, mb->user[0] ? mb->user : myusername (),service); if (tcpdebug) { char msg[MAILTMPLEN]; sprintf (msg,"Trying %.100s",tmp); mm_log (msg,TCPDEBUG); } /* parse command into argv */ for (i = 1,path = argv[0] = strtok (tmp," "); (i < MAXARGV) && (argv[i] = strtok (NIL," ")); i++); argv[i] = NIL; /* make sure argv tied off */ /* make command pipes */ if (pipe (pipei) < 0) return NIL; if (pipe (pipeo) < 0) { close (pipei[0]); close (pipei[1]); return NIL; } (*bn) (BLOCK_TCPOPEN,NIL); /* quell alarm up here for NeXT */ if ((i = fork ()) < 0) { /* make inferior process */ close (pipei[0]); close (pipei[1]); close (pipeo[0]); close (pipeo[1]); return NIL; } if (!i) { /* if child */ alarm (0); /* never have alarms in children */ if (!fork ()) { /* make grandchild so it's inherited by init */ int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1]))); dup2 (pipei[1],1); /* parent's input is my output */ dup2 (pipei[1],2); /* parent's input is my error output too */ dup2 (pipeo[0],0); /* parent's output is my input */ /* close all unnecessary descriptors */ for (i = 3; i <= maxfd; i++) close (i); setpgrp (0,getpid ()); /* be our own process group */ execv (path,argv); /* now run it */ } _exit (1); /* child is done */ } grim_pid_reap (i,NIL); /* reap child; grandchild now owned by init */ close (pipei[1]); /* close child's side of the pipes */ close (pipeo[0]); /* create TCP/IP stream */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); /* copy remote host name from argument */ stream->remotehost = cpystr (stream->host = cpystr (host)); stream->tcpsi = pipei[0]; /* init sockets */ stream->tcpso = pipeo[1]; stream->ictr = 0; /* init input counter */ stream->port = 0xffffffff; /* no port number */ ti += now = time (0); /* open timeout */ tmo.tv_usec = 0; /* initialize usec timeout */ FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpsi,&fds); /* set bit in selection vector */ FD_SET (stream->tcpsi,&efds); /* set bit in error selection vector */ FD_SET (stream->tcpso,&efds); /* set bit in error selection vector */ do { /* block under timeout */ tmo.tv_sec = ti - now; i = select (max (stream->tcpsi,stream->tcpso)+1,&fds,NIL,&efds,&tmo); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i <= 0) { /* timeout or error? */ sprintf (tmp,i ? "error in %s to IMAP server" : "%s to IMAP server timed out",(*service == '*') ? "ssh" : "rsh"); mm_log (tmp,WARN); tcp_close (stream); /* punt stream */ stream = NIL; } (*bn) (BLOCK_NONE,NIL); /* return user name */ strcpy (usrbuf,mb->user[0] ? mb->user : myusername ()); return stream; /* return success */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s) { unsigned long n; /* make sure socket still alive */ if (stream->tcpsi < 0) return NIL; /* can transfer bytes from buffer? */ if (n = min (size,stream->ictr)) { memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */ s += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } if (size) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_TCPREAD,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ /* set bit in selection vectors */ FD_SET (stream->tcpsi,&fds); FD_SET (stream->tcpsi,&efds); errno = NIL; /* initially no error */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i) { /* non-timeout result from select? */ if (i > 0) /* read what we can */ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) && (errno == EINTR)); if (i <= 0) { /* error seen? */ if (tcpdebug) { char tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno); else s = "TCP buffer read end of file"; mm_log (s,TCPDEBUG); } return tcp_abort (stream); } s += i; /* success, point at new place to write */ size -= i; /* reduce byte count */ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } /* timeout, punt unless told not to */ else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (stream); } } (*bn) (BLOCK_NONE,NIL); } *s = '\0'; /* tie off string */ return LONGT; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->tcpsi < 0) return NIL; (*bn) (BLOCK_TCPREAD,NIL); while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpsi,&fds);/* set bit in selection vectors */ FD_SET (stream->tcpsi,&efds); errno = NIL; /* initially no error */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i) { /* non-timeout result from select? */ /* read what we can */ if (i > 0) while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); if (i <= 0) { /* error seen? */ if (tcpdebug) { char *s,tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno); else s = "TCP data read end of file"; mm_log (s,TCPDEBUG); } return tcp_abort (stream); } stream->ictr = i; /* success, set new count and pointer */ stream->iptr = stream->ibuf; if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG); } /* timeout, punt unless told not to */ else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG); return tcp_abort (stream);/* error or timeout no-continue */ } } (*bn) (BLOCK_NONE,NIL); return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->tcpso < 0) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_write ? now + ttmo_write : 0; if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */ FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */ errno = NIL; /* block and write */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i) { /* non-timeout result from select? */ /* write what we can */ if (i > 0) while (((i = write (stream->tcpso,string,size)) < 0) && (errno == EINTR)); if (i <= 0) { /* error seen? */ if (tcpdebug) { char tmp[MAILTMPLEN]; sprintf (tmp,"TCP write I/O error %d",errno); mm_log (tmp,TCPDEBUG); } return tcp_abort (stream); } string += i; /* how much we sent */ size -= i; /* count this size */ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); } /* timeout, punt unless told not to */ else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG); return tcp_abort (stream); } } (*bn) (BLOCK_NONE,NIL); return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (stream); /* nuke the stream */ /* flush host names */ if (stream->host) fs_give ((void **) &stream->host); if (stream->remotehost) fs_give ((void **) &stream->remotehost); if (stream->localhost) fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->tcpsi >= 0) { /* no-op if no socket */ (*bn) (BLOCK_TCPCLOSE,NIL); close (stream->tcpsi); /* nuke the socket */ if (stream->tcpsi != stream->tcpso) close (stream->tcpso); stream->tcpsi = stream->tcpso = -1; } (*bn) (BLOCK_NONE,NIL); return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* use tcp_remotehost() if want guarantees */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { if (!stream->remotehost) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); stream->remotehost = /* get socket's peer name */ getpeername (stream->tcpsi,sadr,(void *) &sadrlen) ? cpystr (stream->host) : tcp_name (sadr,NIL); fs_give ((void **) &sadr); } return stream->remotehost; } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { if (!stream->localhost) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); stream->localhost = /* get socket's name */ ((stream->port & 0xffff000) || getsockname (stream->tcpsi,sadr,(void *) &sadrlen)) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL); fs_give ((void **) &sadr); } return stream->localhost; /* return local host name */ } /* TCP/IP get client host address (server calls only) * Returns: client host address */ static char *myClientAddr = NIL; char *tcp_clientaddr () { if (!myClientAddr) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); myClientAddr = /* get stdin's peer name */ cpystr (getpeername (0,sadr,(void *) &sadrlen) ? "UNKNOWN" : ip_sockaddrtostring (sadr)); fs_give ((void **) &sadr); } return myClientAddr; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ static char *myClientHost = NIL; char *tcp_clienthost () { if (!myClientHost) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); myClientHost = /* get stdin's peer name */ getpeername (0,sadr,(void *) &sadrlen) ? cpystr ("UNKNOWN") : tcp_name (sadr,T); fs_give ((void **) &sadr); } return myClientHost; } /* TCP/IP get server host address (server calls only) * Returns: server host address */ static char *myServerAddr = NIL; char *tcp_serveraddr () { if (!myServerAddr) { size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); myServerAddr = /* get stdin's peer name */ cpystr (getsockname (0,sadr,(void *) &sadrlen) ? "UNKNOWN" : ip_sockaddrtostring (sadr)); fs_give ((void **) &sadr); } return myServerAddr; } /* TCP/IP get server host name (server calls only) * Returns: server host name */ static char *myServerHost = NIL; static long myServerPort = -1; char *tcp_serverhost () { if (!myServerHost) { /* once-only */ size_t sadrlen; struct sockaddr *sadr = ip_newsockaddr (&sadrlen); /* get stdin's name */ myServerHost = (getsockname (0,sadr,(void *) &sadrlen) || ((myServerPort = ip_sockaddrtoport (sadr)) < 0)) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL); fs_give ((void **) &sadr); } return myServerHost; } /* TCP/IP get server port number (server calls only) * Returns: server port number */ long tcp_serverport () { if (!myServerHost) tcp_serverhost (); return myServerPort; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { char *ret,host[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data; /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name; (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); if (tcpdebug) { sprintf (host,"DNS canonicalization %.80s",name); mm_log (host,TCPDEBUG); } /* get canonical name */ if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name; (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG); return ret; } /* TCP/IP return name from socket * Accepts: socket * verbose flag * Returns: cpystr name */ char *tcp_name (struct sockaddr *sadr,long flag) { char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN]; sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr)); if (allowreversedns) { blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL); void *data; if (tcpdebug) { sprintf (tmp,"Reverse DNS resolution %s",adr); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); /* translate address to name */ if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) { /* produce verbose form if needed */ if (flag) sprintf (ret = tmp,"%s %s",t,adr); else ret = t; } (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG); } return cpystr (ret); } /* Validate name * Accepts: domain name * Returns: name if valid, NIL otherwise */ char *tcp_name_valid (char *s) { int c; char *ret,*tail; /* must be non-empty and not too long */ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) { /* must be alnum, dot, or hyphen */ while ((c = *s++) && (s <= tail) && (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.'))); if (c) ret = NIL; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/tcp_unix.h000066400000000000000000000017441137544547100236260ustar00rootroot00000000000000/* * Program: UNIX TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer */ #define BUFLEN 8192 /* TCP I/O stream */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ char *remotehost; /* remote host name */ int tcpsi; /* input socket */ int tcpso; /* output socket */ int ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/tenex.c000066400000000000000000001367221137544547100231200ustar00rootroot00000000000000/* * Program: Tenex mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 8 March 2005 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2005 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. * * TEXT SIZE SEMANTICS * * Most of the text sizes are in internal (LF-only) form, except for the * msg.text size. Beware. */ #include #include #include extern int errno; /* just in case */ #include "mail.h" #include "osdep.h" #include #include "misc.h" #include "dummy.h" /* TENEX I/O stream local data */ typedef struct tenex_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* local snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ } TENEXLOCAL; /* Convenient access to local data */ #define LOCAL ((TENEXLOCAL *) stream->local) /* Function prototypes */ DRIVER *tenex_valid (char *name); int tenex_isvalid (char *name,char *tmp); void *tenex_parameters (long function,void *value); void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void tenex_list (MAILSTREAM *stream,char *ref,char *pat); void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat); long tenex_create (MAILSTREAM *stream,char *mailbox); long tenex_delete (MAILSTREAM *stream,char *mailbox); long tenex_rename (MAILSTREAM *stream,char *old,char *newname); long tenex_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *tenex_open (MAILSTREAM *stream); void tenex_close (MAILSTREAM *stream,long options); void tenex_fast (MAILSTREAM *stream,char *sequence,long flags); void tenex_flags (MAILSTREAM *stream,char *sequence,long flags); char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long tenex_ping (MAILSTREAM *stream); void tenex_check (MAILSTREAM *stream); void tenex_snarf (MAILSTREAM *stream); void tenex_expunge (MAILSTREAM *stream); long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); unsigned long tenex_size (MAILSTREAM *stream,unsigned long m); char *tenex_file (char *dst,char *name); long tenex_parse (MAILSTREAM *stream); MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno); void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); void tenex_update_status (MAILSTREAM *stream,unsigned long msgno, long syncflag); unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size); /* Tenex mail routines */ /* Driver dispatch used by MAIL */ DRIVER tenexdriver = { "tenex", /* driver name */ DR_LOCAL|DR_MAIL|DR_NOSTICKY|DR_LOCKING, /* driver flags */ (DRIVER *) NIL, /* next driver */ tenex_valid, /* mailbox is valid for us */ tenex_parameters, /* manipulate parameters */ tenex_scan, /* scan mailboxes */ tenex_list, /* list mailboxes */ tenex_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ tenex_delete, /* delete mailbox */ tenex_rename, /* rename mailbox */ tenex_status, /* status of mailbox */ tenex_open, /* open mailbox */ tenex_close, /* close mailbox */ tenex_fast, /* fetch message "fast" attributes */ tenex_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ tenex_header, /* fetch message header */ tenex_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ tenex_flag, /* modify flags */ tenex_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ tenex_ping, /* ping mailbox to see if still alive */ tenex_check, /* check for new messages */ tenex_expunge, /* expunge deleted messages */ tenex_copy, /* copy messages to another mailbox */ tenex_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM tenexproto = {&tenexdriver}; /* Tenex mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *tenex_valid (char *name) { char tmp[MAILTMPLEN]; return tenex_isvalid (name,tmp) ? &tenexdriver : NIL; } /* Tenex mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */ int tenex_isvalid (char *name,char *tmp) { int fd; int ret = NIL; char *s,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = tenex_file (file,name)) && !stat (s,&sbuf)) { if (!sbuf.st_size) { /* allow empty file if INBOX */ if ((s = mailboxfile (tmp,name)) && !*s) ret = T; else errno = 0; /* empty file */ } else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) && (s[-1] != '\015')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { tp[0] = sbuf.st_atime; /* preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } /* in case INBOX but not tenex format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */ } /* Tenex manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tenex_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = tenex_file ((char *) value,"INBOX"); break; } return ret; } /* Tenex mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* Tenex mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* Tenex mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* Tenex mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long tenex_delete (MAILSTREAM *stream,char *mailbox) { return tenex_rename (stream,mailbox,NIL); } /* Tenex mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long tenex_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = T; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } else if ((fd = open (file,O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); MM_LOG (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */ c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) ret = NIL; else *s = c; /* restore full name */ } /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } } else if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; /* set failure */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ /* recreate file if renamed INBOX */ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"mail.txt"); return ret; /* return success */ } /* Tenex Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long tenex_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; /* calculate post-snarf results */ if (!status.recent && stream->inbox && (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* Tenex mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *tenex_open (MAILSTREAM *stream) { int fd,ld; char tmp[MAILTMPLEN]; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&tenexproto); if (stream->local) fatal ("tenex recycle stream"); user_flags (stream); /* set up user flags */ /* canonicalize the mailbox name */ if (!tenex_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (TENEXLOCAL)); LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1); LOCAL->buflen = MAXMESSAGESIZE; LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); LOCAL->fd = fd; /* bind the file */ /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) { MM_LOG ("Unable to lock open mailbox",ERROR); return NIL; } (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* lock the file */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ /* time not set up yet */ LOCAL->lastsnarf = LOCAL->filetime = 0; LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (tenex_ping (stream) && !stream->nmsgs) MM_LOG ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */ } /* Tenex mail close * Accepts: MAIL stream * close options */ void tenex_close (MAILSTREAM *stream,long options) { if (stream && LOCAL) { /* only if a file is open */ int silent = stream->silent; stream->silent = T; /* note this stream is dying */ if (options & CL_EXPUNGE) tenex_expunge (stream); stream->silent = silent; /* restore previous status */ flock (LOCAL->fd,LOCK_UN); /* unlock local file */ close (LOCAL->fd); /* close the local file */ /* free local text buffer */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* Tenex mail fetch fast data * Accepts: MAIL stream * sequence * option flags */ void tenex_fast (MAILSTREAM *stream,char *sequence,long flags) { STRING bs; MESSAGECACHE *elt; unsigned long i; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { if (!elt->rfc822_size) { /* have header size yet? */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.special.text.size,L_SET); /* resize bigbuf if necessary */ if (LOCAL->buflen < elt->private.msg.full.text.size) { fs_give ((void **) &LOCAL->buf); LOCAL->buflen = elt->private.msg.full.text.size; LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1); } /* tie off string */ LOCAL->buf[elt->private.msg.full.text.size] = '\0'; /* read in the message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size); INIT (&bs,mail_string,(void *) LOCAL->buf, elt->private.msg.full.text.size); /* calculate its CRLF size */ elt->rfc822_size = strcrlflen (&bs); } tenex_elt (stream,i); /* get current flags from file */ } } /* Tenex mail fetch flags * Accepts: MAIL stream * sequence * option flags * Sniffs at file to get flags */ void tenex_flags (MAILSTREAM *stream,char *sequence,long flags) { unsigned long i; if (stream && LOCAL && ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) tenex_elt (stream,i); } /* TENEX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { char *s; unsigned long i; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ /* get to header position */ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET); if (flags & FT_INTERNAL) { if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* slurp the data */ read (LOCAL->fd,LOCAL->buf,*length = i); } else { s = (char *) fs_get (i + 1);/* get readin buffer */ s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i); fs_give ((void **) &s); /* free readin buffer */ } return LOCAL->buf; } /* TENEX mail fetch message text (body only) * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T, always */ long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i,j; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; /* get message status */ elt = tenex_elt (stream,msgno); /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { elt->seen = T; /* mark message as seen */ /* recalculate status */ tenex_update_status (stream,msgno,T); MM_FLAGS (stream,msgno); } if (flags & FT_INTERNAL) { /* if internal representation wanted */ /* find header position */ i = tenex_hdrpos (stream,msgno,&j); if (i > LOCAL->buflen) { /* resize if not enough space */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); } /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); /* slurp the data */ read (LOCAL->fd,LOCAL->buf,i); /* set up stringstruct for internal */ INIT (bs,mail_string,LOCAL->buf,i); } else { /* normal form, previous text cached? */ if (elt->private.uid == LOCAL->uid) i = elt->private.msg.text.text.size; else { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* find header position */ i = tenex_hdrpos (stream,msgno,&j); /* go to text position */ lseek (LOCAL->fd,i + j,L_SET); s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1); s[i] = '\0'; /* tie off string */ read (LOCAL->fd,s,i); /* slurp the data */ /* make CRLF copy of string */ i = elt->private.msg.text.text.size = strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i); fs_give ((void **) &s); /* free readin buffer */ } /* set up stringstruct */ INIT (bs,mail_string,LOCAL->text.data,i); } return T; /* success */ } /* Tenex mail modify flags * Accepts: MAIL stream * sequence * flag(s) * option flags */ void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) { time_t tp[2]; struct stat sbuf; if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read comes after all that */ utime (stream->mailbox,tp); } } /* Tenex mail per-message modify flags * Accepts: MAIL stream * message cache element */ void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { struct stat sbuf; /* maybe need to do a checkpoint? */ if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; LOCAL->filetime = 0; /* don't do this test for any other messages */ } /* recalculate status */ tenex_update_status (stream,elt->msgno,NIL); } /* Tenex mail ping mailbox * Accepts: MAIL stream * Returns: T if stream still alive, NIL if not */ long tenex_ping (MAILSTREAM *stream) { unsigned long i = 1; long r = T; int ld; char lock[MAILTMPLEN]; struct stat sbuf; if (stream && LOCAL) { /* only if stream already open */ fstat (LOCAL->fd,&sbuf); /* get current file poop */ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; /* check for changed message status */ if (LOCAL->mustcheck || LOCAL->shouldcheck) { LOCAL->filetime = sbuf.st_mtime; if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL); while (i <= stream->nmsgs) tenex_elt (stream,i++); LOCAL->mustcheck = LOCAL->shouldcheck = NIL; } /* get shared parse/append permission */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (tenex_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } if (LOCAL) { /* stream must still be alive */ /* snarf if this is a read-write inbox */ if (stream->inbox && !stream->rdonly) { tenex_snarf (stream); fstat (LOCAL->fd,&sbuf);/* see if file changed now */ if ((sbuf.st_size != LOCAL->filesize) && ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { /* parse resulting mailbox */ r = (tenex_parse (stream)) ? T : NIL; unlockfd (ld,lock); /* release shared parse/append permission */ } } } } return r; /* return result of the parse */ } /* Tenex mail check mailbox (reparses status too) * Accepts: MAIL stream */ void tenex_check (MAILSTREAM *stream) { /* mark that a check is desired */ if (LOCAL) LOCAL->mustcheck = T; if (tenex_ping (stream)) MM_LOG ("Check completed",(long) NIL); } /* Tenex mail snarf messages from system inbox * Accepts: MAIL stream */ void tenex_snarf (MAILSTREAM *stream) { unsigned long i = 0; unsigned long j,r,hdrlen,txtlen; struct stat sbuf; char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN]; MESSAGECACHE *elt; MAILSTREAM *sysibx = NIL; int ld; /* give up if can't get exclusive permission */ if ((time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && strcmp (sysinbox (),stream->mailbox) && ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) { MM_CRITICAL (stream); /* go critical */ /* sizes match and anything in sysinbox? */ if (!stat (sysinbox (),&sbuf) && sbuf.st_size && !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && (!sysibx->rdonly) && (r = sysibx->nmsgs)) { /* yes, go to end of file in our mailbox */ lseek (LOCAL->fd,sbuf.st_size,L_SET); /* for each message in sysibx mailbox */ while (r && (++i <= sysibx->nmsgs)) { /* snarf message from system INBOX */ hdr = cpystr (mail_fetchheader_full(sysibx,i,NIL,&hdrlen,FT_INTERNAL)); txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_INTERNAL|FT_PEEK); /* if have a message */ if (j = hdrlen + txtlen) { /* calculate header line */ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i)); sprintf (LOCAL->buf + strlen (LOCAL->buf), ",%lu;0000000000%02o\n",j,(unsigned) ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* copy message */ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) || (write (LOCAL->fd,hdr,hdrlen) < 0) || (write (LOCAL->fd,txt,txtlen) < 0)) r = 0; } fs_give ((void **) &hdr); } /* make sure all the updates take */ if (fsync (LOCAL->fd)) r = 0; if (r) { /* delete all the messages we copied */ if (r == 1) strcpy (tmp,"1"); else sprintf (tmp,"1:%lu",r); mail_flag (sysibx,tmp,"\\Deleted",ST_SET); mail_expunge (sysibx); /* now expunge all those messages */ } else { sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (LOCAL->fd,sbuf.st_size); } fstat (LOCAL->fd,&sbuf); /* yes, get current file size */ LOCAL->filetime = sbuf.st_mtime; } if (sysibx) mail_close (sysibx); MM_NOCRITICAL (stream); /* release critical */ unlockfd (ld,lock); /* release exclusive parse/append permission */ LOCAL->lastsnarf = time (0);/* note time of last snarf */ } } /* Tenex mail expunge mailbox * Accepts: MAIL stream */ void tenex_expunge (MAILSTREAM *stream) { time_t tp[2]; struct stat sbuf; off_t pos = 0; int ld; unsigned long i = 1; unsigned long j,k,m,recent; unsigned long n = 0; unsigned long delta = 0; char lock[MAILTMPLEN]; MESSAGECACHE *elt; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* do nothing if stream dead */ if (!tenex_ping (stream)) return; if (stream->rdonly) { /* won't do on readonly files! */ MM_LOG ("Expunge ignored on readonly mailbox",WARN); return; } if (LOCAL->filetime && !LOCAL->shouldcheck) { fstat (LOCAL->fd,&sbuf); /* get current write time */ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; } /* The cretins who designed flock() created a window of vulnerability in * upgrading locks from shared to exclusive or downgrading from exclusive * to shared. Rather than maintain the lock at shared status at a minimum, * flock() actually *releases* the former lock. Obviously they never talked * to any database guys. Fortunately, we have the parse/append permission * lock. If we require this lock before going exclusive on the mailbox, * another process can not sneak in and steal the exclusive mailbox lock on * us, because it will block on trying to get parse/append permission first. */ /* get exclusive parse/append permission */ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock expunge mailbox",ERROR); return; } /* make sure see any newly-arrived messages */ if (!tenex_parse (stream)) return; /* get exclusive access */ if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* recover previous lock */ (*bn) (BLOCK_NONE,NIL); MM_LOG ("Can't expunge because mailbox is in use by another process", ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return; } MM_CRITICAL (stream); /* go critical */ recent = stream->recent; /* get recent now that pinged and locked */ while (i <= stream->nmsgs) { /* for each message */ elt = tenex_elt (stream,i); /* get cache element */ /* number of bytes to smash or preserve */ k = elt->private.special.text.size + tenex_size (stream,i); if (elt->deleted) { /* if deleted */ if (elt->recent) --recent;/* if recent, note one less recent message */ delta += k; /* number of bytes to delete */ mail_expunged (stream,i); /* notify upper levels */ n++; /* count up one more expunged message */ } else if (i++ && delta) { /* preserved message */ /* first byte to preserve */ j = elt->private.special.offset; do { /* read from source position */ m = min (k,LOCAL->buflen); lseek (LOCAL->fd,j,L_SET); read (LOCAL->fd,LOCAL->buf,m); pos = j - delta; /* write to destination position */ lseek (LOCAL->fd,pos,L_SET); while (T) { lseek (LOCAL->fd,pos,L_SET); if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } pos += m; /* new position */ j += m; /* next chunk, perhaps */ } while (k -= m); /* until done */ /* note the new address of this text */ elt->private.special.offset -= delta; } /* preserved but no deleted messages */ else pos = elt->private.special.offset + k; } if (n) { /* truncate file after last message */ if (pos != (LOCAL->filesize -= delta)) { sprintf (LOCAL->buf,"Calculated size mismatch %lu != %lu, delta = %lu", (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); MM_LOG (LOCAL->buf,WARN); LOCAL->filesize = pos; /* fix it then */ } ftruncate (LOCAL->fd,LOCAL->filesize); sprintf (LOCAL->buf,"Expunged %lu messages",n); /* output the news */ MM_LOG (LOCAL->buf,(long) NIL); } else MM_LOG ("No messages deleted, so no update needed",(long) NIL); fsync (LOCAL->fd); /* force disk update */ fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* reset atime to now */ utime (stream->mailbox,tp); MM_NOCRITICAL (stream); /* release critical */ /* notify upper level of new mailbox size */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); (*bn) (BLOCK_FILELOCK,NIL); flock (LOCAL->fd,LOCK_SH); /* allow sharers again */ (*bn) (BLOCK_NONE,NIL); unlockfd (ld,lock); /* release exclusive parse/append permission */ } /* Tenex mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if success, NIL if failed */ long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; time_t tp[2]; MESSAGECACHE *elt; unsigned long i,j,k; long ret = LONGT; int fd,ld; char file[MAILTMPLEN],lock[MAILTMPLEN]; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) { case ENOENT: /* no such file? */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* got file? */ if ((fd=open(tenex_file(file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0) { sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); return NIL; } MM_CRITICAL (stream); /* go critical */ /* get exclusive parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock copy mailbox",ERROR); MM_NOCRITICAL (stream); return NIL; } fstat (fd,&sbuf); /* get current file size */ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ /* for each requested message */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); /* number of bytes to copy */ k = elt->private.special.text.size + tenex_size (stream,i); do { /* read from source position */ j = min (k,LOCAL->buflen); read (LOCAL->fd,LOCAL->buf,j); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; } while (ret && (k -= j));/* until done */ } /* make sure all the updates take */ if (!(ret && (ret = !fsync (fd)))) { sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); ftruncate (fd,sbuf.st_size); } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ close (fd); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ /* delete all requested messages */ if (ret && (options & CP_MOVE)) { for (i = 1; i <= stream->nmsgs; i++) if ((elt = tenex_elt (stream,i))->sequence) { elt->deleted = T; /* mark message deleted */ /* recalculate status */ tenex_update_status (stream,i,NIL); } if (!stream->rdonly) { /* make sure the update takes */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get current write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure atime remains greater */ utime (stream->mailbox,tp); } } return ret; } /* Tenex mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd,ld,c; char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; time_t tp[2]; FILE *df; MESSAGECACHE elt; long f; unsigned long i,j,uf,size; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = user_flags (&tenexproto); /* make sure valid mailbox */ if (!tenex_isvalid (mailbox,tmp)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"mail.txt"); else { MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; /* open destination mailbox */ if (((fd = open (tenex_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) { sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } /* get parse/append permission */ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { MM_LOG ("Unable to lock append mailbox",ERROR); close (fd); return NIL; } MM_CRITICAL (stream); /* go critical */ fstat (fd,&sbuf); /* get current file size */ errno = 0; do { /* parse flags */ if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Append of zero-length message",ERROR); ret = NIL; break; } f = mail_parse_flags (stream,flags,&i); /* reverse bits (dontcha wish we had CIRC?) */ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); if (date) { /* parse date if given */ if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); ret = NIL; /* mark failure */ break; } mail_date (tmp,&elt); /* write preseved date */ } else internal_date (tmp); /* get current date in IMAP format */ i = GETPOS (message); /* remember current position */ for (j = SIZE (message), size = 0; j; --j) if (SNX (message) != '\015') ++size; SETPOS (message,i); /* restore position */ /* write header */ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0) ret = NIL; else { /* write message */ while (size) if ((c = 0xff & SNX (message)) != '\015') { if (putc (c,df) != EOF) --size; else break; } /* get next message */ if (size || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL; } } while (ret && message); /* if error... */ if (!ret || (fflush (df) == EOF)) { ftruncate (fd,sbuf.st_size);/* revert file */ close (fd); /* make sure fclose() doesn't corrupt us */ if (errno) { sprintf (tmp,"Message append failed: %s",strerror (errno)); MM_LOG (tmp,ERROR); } ret = NIL; } if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ /* else preserve \Marked status */ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); tp[1] = sbuf.st_mtime; /* preserve mtime */ utime (file,tp); /* set the times */ fclose (df); /* close the file */ unlockfd (ld,lock); /* release exclusive parse/append permission */ MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Internal routines */ /* Tenex mail return internal message size in bytes * Accepts: MAIL stream * message # * Returns: internal size of message */ unsigned long tenex_size (MAILSTREAM *stream,unsigned long m) { MESSAGECACHE *elt = mail_elt (stream,m); return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset : LOCAL->filesize) - (elt->private.special.offset + elt->private.special.text.size); } /* Tenex mail generate file string * Accepts: temporary buffer to write into * mailbox name string * Returns: local file string or NIL if failure */ char *tenex_file (char *dst,char *name) { char tmp[MAILTMPLEN]; char *s = mailboxfile (dst,name); /* return our standard inbox */ return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/INBOX",tmp) ? "~/INBOX" : "mail.txt") : s; } /* Tenex mail parse mailbox * Accepts: MAIL stream * Returns: T if parse OK * NIL if failure, stream aborted */ long tenex_parse (MAILSTREAM *stream) { struct stat sbuf; MESSAGECACHE *elt = NIL; unsigned char c,*s,*t,*x; char tmp[MAILTMPLEN]; unsigned long i,j; long curpos = LOCAL->filesize; long nmsgs = stream->nmsgs; long recent = stream->recent; short added = NIL; short silent = stream->silent; fstat (LOCAL->fd,&sbuf); /* get status */ if (sbuf.st_size < curpos) { /* sanity check */ sprintf (tmp,"Mailbox shrank from %lu to %lu!", (unsigned long) curpos,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } stream->silent = T; /* don't pass up mm_exists() events yet */ while (sbuf.st_size - curpos){/* while there is stuff to parse */ /* get to that position in the file */ lseek (LOCAL->fd,curpos,L_SET); if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", (unsigned long) curpos,(unsigned long) sbuf.st_size, i ? strerror (errno) : "no data read"); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ if (!(s = strchr (LOCAL->buf,'\012'))) { sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s", (unsigned long) curpos,i,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s = '\0'; /* tie off header line */ i = (s + 1) - LOCAL->buf; /* note start of text offset */ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { sprintf (tmp,"Unable to parse internal header at %lu: %s", (unsigned long) curpos,(char *) LOCAL->buf); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } *s++ = '\0'; *t++ = '\0'; /* tie off fields */ added = T; /* note that a new message was added */ /* swell the cache */ mail_exists (stream,++nmsgs); /* instantiate an elt for this message */ (elt = mail_elt (stream,nmsgs))->valid = T; elt->private.uid = ++stream->uid_last; /* note file offset of header */ elt->private.special.offset = curpos; /* in case error */ elt->private.special.text.size = 0; /* header size not known yet */ elt->private.msg.header.text.size = 0; x = s; /* parse the header components */ if (mail_parse_date (elt,LOCAL->buf) && (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) elt->private.special.text.size = i; else { /* oops */ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", curpos,(char *) LOCAL->buf,(char *) x,(char *) t); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } /* make sure didn't run off end of file */ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) { sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", elt->private.special.offset,(unsigned long) curpos, (unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); tenex_close (stream,NIL); return NIL; } c = t[10]; /* remember first system flags byte */ t[10] = '\0'; /* tie off flags */ j = strtoul (t,NIL,8); /* get user flags value */ t[10] = c; /* restore first system flags byte */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; /* calculate system flags */ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; if (j & fDELETED) elt->deleted = T; if (j & fFLAGGED) elt->flagged = T; if (j & fANSWERED) elt->answered = T; if (j & fDRAFT) elt->draft = T; if (!(j & fOLD)) { /* newly arrived message? */ elt->recent = T; recent++; /* count up a new recent message */ /* mark it as old */ tenex_update_status (stream,nmsgs,NIL); } } fsync (LOCAL->fd); /* make sure all the fOLD flags take */ /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ LOCAL->filetime = sbuf.st_mtime; if (added && !stream->rdonly){/* make sure atime updated */ time_t tp[2]; tp[0] = time (0); tp[1] = LOCAL->filetime; utime (stream->mailbox,tp); } stream->silent = silent; /* can pass up events now */ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ mail_recent (stream,recent); /* and of change in recent messages */ return LONGT; /* return the winnage */ } /* Tenex get cache element with status updating from file * Accepts: MAIL stream * message number * Returns: cache element */ MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno) { MESSAGECACHE *elt = mail_elt (stream,msgno); struct { /* old flags */ unsigned int seen : 1; unsigned int deleted : 1; unsigned int flagged : 1; unsigned int answered : 1; unsigned int draft : 1; unsigned long user_flags; } old; old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; old.answered = elt->answered; old.draft = elt->draft; old.user_flags = elt->user_flags; tenex_read_flags (stream,elt); if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || (old.answered != elt->answered) || (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) MM_FLAGS (stream,msgno); /* let top level know */ return elt; } /* Tenex read flags from file * Accepts: MAIL stream * Returns: cache element */ void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) { unsigned long i,j; /* noop if readonly and have valid flags */ if (stream->rdonly && elt->valid) return; /* set the seek pointer */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* read the new flags */ if (read (LOCAL->fd,LOCAL->buf,12) < 0) { sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); fatal (LOCAL->buf); } /* calculate system flags */ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; elt->flagged = i & fFLAGGED ? T : NIL; elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; LOCAL->buf[10] = '\0'; /* tie off flags */ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ /* set up all valid user flags (reversed!) */ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && stream->user_flags[i]) elt->user_flags |= 1 << i; elt->valid = T; /* have valid flags now */ } /* Tenex update status string * Accepts: MAIL stream * message number * flag saying whether or not to sync */ void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) { time_t tp[2]; struct stat sbuf; MESSAGECACHE *elt = mail_elt (stream,msgno); unsigned long j,k = 0; /* readonly */ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt); else { /* readwrite */ j = elt->user_flags; /* get user flags */ /* reverse bits (dontcha wish we had CIRC?) */ while (j) k |= 1 << (29 - find_rightmost_bit (&j)); /* print new flag string */ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fDRAFT * elt->draft))); /* get to that place in the file */ lseek (LOCAL->fd,(off_t) elt->private.special.offset + elt->private.special.text.size - 13,L_SET); /* write new flags */ write (LOCAL->fd,LOCAL->buf,12); if (syncflag) { /* sync if requested */ fsync (LOCAL->fd); fstat (LOCAL->fd,&sbuf); /* get new write time */ tp[1] = LOCAL->filetime = sbuf.st_mtime; tp[0] = time (0); /* make sure read is later */ utime (stream->mailbox,tp); } } } /* Tenex locate header for a message * Accepts: MAIL stream * message number * pointer to returned header size * Returns: position of header in file */ unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size) { unsigned long siz; long i = 0; char c = '\0'; char *s = NIL; MESSAGECACHE *elt = tenex_elt (stream,msgno); unsigned long ret = elt->private.special.offset + elt->private.special.text.size; unsigned long msiz = tenex_size (stream,msgno); /* is header size known? */ if (!(*size = elt->private.msg.header.text.size)) { lseek (LOCAL->fd,ret,L_SET);/* get to header position */ /* search message for LF LF */ for (siz = 0; siz < msiz; siz++) { if (--i <= 0) /* read another buffer as necessary */ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN)); /* two newline sequence? */ if ((c == '\012') && (*s == '\012')) { /* yes, note for later */ elt->private.msg.header.text.size = (*size = siz + 1); return ret; /* return to caller */ } else c = *s++; /* next character */ } /* header consumes entire message */ elt->private.msg.header.text.size = *size = msiz; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/truncate.c000066400000000000000000000015321137544547100236100ustar00rootroot00000000000000/* * Program: Truncate a file * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 June 1994 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Emulator for ftruncate() call * Accepts: file descriptor * length * Returns: 0 if success, -1 if failure */ int ftruncate (int fd,off_t length) { struct flock fb; fb.l_whence = 0; fb.l_len = 0; fb.l_start = length; fb.l_type = F_WRLCK; /* write lock on file space */ return fcntl (fd,F_FREESP,&fb); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/tz_bsd.c000066400000000000000000000013711137544547100232510ustar00rootroot00000000000000/* * Program: BSD-style Time Zone String * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 August 1994 * Last Edited: 7 November 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Append local timezone name * Accepts: destination string */ void rfc822_timezone (char *s,void *t) { /* append timezone from tm struct */ sprintf (s + strlen (s)," (%.50s)",((struct tm *) t)->tm_zone); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/tz_nul.c000066400000000000000000000012231137544547100232730ustar00rootroot00000000000000/* * Program: Null Time Zone String (unknown) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 August 1994 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Append local timezone name * Accepts: destination string */ void rfc822_timezone (char *s,void *t) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/tz_sv4.c000066400000000000000000000014571137544547100232220ustar00rootroot00000000000000/* * Program: SVR4-style Time Zone String * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 30 August 1994 * Last Edited: 7 November 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Append local timezone name * Accepts: destination string */ void rfc822_timezone (char *s,void *t) { tzset (); /* get timezone from TZ environment stuff */ sprintf (s + strlen (s)," (%.50s)", tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/unix.c000066400000000000000000002475321137544547100227620ustar00rootroot00000000000000/* * Program: UNIX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 10 November 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ #include #include #include extern int errno; /* just in case */ #include #include "mail.h" #include "osdep.h" #include #include #include "unix.h" #include "pseudo.h" #include "fdstring.h" #include "misc.h" #include "dummy.h" /* UNIX I/O stream local data */ typedef struct unix_local { unsigned int dirty : 1; /* disk copy needs updating */ unsigned int pseudo : 1; /* uses a pseudo message */ int fd; /* mailbox file descriptor */ int ld; /* lock file descriptor */ char *lname; /* lock file name */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* last snarf time (for mbox driver) */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */ unsigned long textlen; /* current text length */ char *line; /* returned line */ } UNIXLOCAL; /* Convenient access to local data */ #define LOCAL ((UNIXLOCAL *) stream->local) /* UNIX protected file structure */ typedef struct unix_file { MAILSTREAM *stream; /* current stream */ off_t curpos; /* current file position */ off_t protect; /* protected position */ off_t filepos; /* current last written file position */ char *buf; /* overflow buffer */ size_t buflen; /* current overflow buffer length */ char *bufpos; /* current buffer position */ } UNIXFILE; /* Function prototypes */ DRIVER *unix_valid (char *name); long unix_isvalid_fd (int fd); void *unix_parameters (long function,void *value); void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void unix_list (MAILSTREAM *stream,char *ref,char *pat); void unix_lsub (MAILSTREAM *stream,char *ref,char *pat); long unix_create (MAILSTREAM *stream,char *mailbox); long unix_delete (MAILSTREAM *stream,char *mailbox); long unix_rename (MAILSTREAM *stream,char *old,char *newname); MAILSTREAM *unix_open (MAILSTREAM *stream); void unix_close (MAILSTREAM *stream,long options); char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags); long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); long unix_ping (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream); void unix_expunge (MAILSTREAM *stream); long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg); void unix_abort (MAILSTREAM *stream); char *unix_file (char *dst,char *name); int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op); void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock); int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op); char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr); unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag); long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock); long unix_extend (MAILSTREAM *stream,unsigned long size); void unix_write (UNIXFILE *f,char *s,unsigned long i); void unix_phys_write (UNIXFILE *f,char *buf,size_t size); /* UNIX mail routines */ /* Driver dispatch used by MAIL */ DRIVER unixdriver = { "unix", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ unix_valid, /* mailbox is valid for us */ unix_parameters, /* manipulate parameters */ unix_scan, /* scan mailboxes */ unix_list, /* list mailboxes */ unix_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ unix_create, /* create mailbox */ unix_delete, /* delete mailbox */ unix_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ unix_open, /* open mailbox */ unix_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ unix_header, /* fetch message header */ unix_text, /* fetch message text */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ unix_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ unix_ping, /* ping mailbox to see if still alive */ unix_check, /* check for new messages */ unix_expunge, /* expunge deleted messages */ unix_copy, /* copy messages to another mailbox */ unix_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM unixproto = {&unixdriver}; /* driver parameters */ static long unix_fromwidget = T; /* UNIX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *unix_valid (char *name) { int fd; DRIVER *ret = NIL; char *t,file[MAILTMPLEN]; struct stat sbuf; time_t tp[2]; errno = EINVAL; /* assume invalid argument */ /* must be non-empty file */ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { /* OK if mailbox format good */ if (unix_isvalid_fd (fd)) ret = &unixdriver; else errno = -1; /* invalid format */ close (fd); /* close the file */ /* \Marked status? */ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) { tp[0] = sbuf.st_atime; /* yes, preserve atime and mtime */ tp[1] = sbuf.st_mtime; utime (file,tp); /* set the times */ } } } return ret; /* return what we should */ } /* UNIX mail test for valid mailbox * Accepts: file descriptor * scratch buffer * Returns: T if valid, NIL otherwise */ long unix_isvalid_fd (int fd) { int zn; int ret = NIL; char tmp[MAILTMPLEN],*s,*t,c = '\n'; memset (tmp,'\0',MAILTMPLEN); if (read (fd,tmp,MAILTMPLEN-1) >= 0) { for (s = tmp; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');) c = *s++; if (c == '\n') VALID (s,t,ret,zn); } return ret; /* return what we should */ } /* UNIX manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *unix_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = dummy_file ((char *) value,"INBOX"); break; case SET_FROMWIDGET: unix_fromwidget = (long) value; case GET_FROMWIDGET: ret = (void *) unix_fromwidget; break; } return ret; } /* UNIX mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { if (stream) dummy_scan (NIL,ref,pat,contents); } /* UNIX mail list mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_list (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_list (NIL,ref,pat); } /* UNIX mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void unix_lsub (MAILSTREAM *stream,char *ref,char *pat) { if (stream) dummy_lsub (NIL,ref,pat); } /* UNIX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long unix_create (MAILSTREAM *stream,char *mailbox) { char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN]; long ret = NIL; int i,fd; time_t ti = time (0); if (!(s = dummy_file (mbx,mailbox))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); MM_LOG (tmp,ERROR); } /* create underlying file */ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) { /* done if made directory */ if ((s = strrchr (s,'/')) && !s[1]) return T; if ((fd = open (mbx,O_WRONLY, (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) { sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } /* in case a whiner with no life */ else if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T; else { /* initialize header */ memset (tmp,'\0',MAILTMPLEN); sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti)); rfc822_fixed_date (s = tmp + strlen (tmp)); /* write the pseudo-header */ sprintf (s += strlen (s), "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000", pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) ti); for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i)) sprintf (s += strlen (s)," %s",default_user_flag (i)); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg); if ((write (fd,tmp,strlen (tmp)) < 0) || close (fd)) { sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx, strerror (errno)); MM_LOG (tmp,ERROR); unlink (mbx); /* delete the file */ } else ret = T; /* success */ } close (fd); /* close file, set proper protections */ } return ret ? set_mbx_protections (mailbox,mbx) : NIL; } /* UNIX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long unix_delete (MAILSTREAM *stream,char *mailbox) { return unix_rename (stream,mailbox,NIL); } /* UNIX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long unix_rename (MAILSTREAM *stream,char *old,char *newname) { long ret = NIL; char c,*s = NIL; char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; DOTLOCK lockx; int fd,ld; long i; struct stat sbuf; MM_CRITICAL (stream); /* get the c-client lock */ if (!dummy_file (file,old) || (newname && !((s = mailboxfile (tmp,newname)) && *s))) sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); /* lock out other c-clients */ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0) sprintf (tmp,"Mailbox %.80s is in use by another process",old); else { if ((fd = unix_lock (file,O_RDWR,S_IREAD|S_IWRITE,&lockx,LOCK_EX)) < 0) sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno)); else { if (newname) { /* want rename? */ /* found superior to destination name? */ if (s = strrchr (s,'/')) { c = *++s; /* remember first character of inferior */ *s = '\0'; /* tie off to get just superior */ /* name doesn't exist, create it */ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && !dummy_create_path (stream,tmp,get_dir_protection (newname))) { unix_unlock (fd,NIL,&lockx); unix_unlock (ld,NIL,NIL); unlink (lock); MM_NOCRITICAL (stream); return ret; /* return success or failure */ } *s = c; /* restore full name */ } if (rename (file,tmp)) sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); else ret = T; /* set success */ } else if (unlink (file)) sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); else ret = T; /* set success */ unix_unlock (fd,NIL,&lockx); } unix_unlock (ld,NIL,NIL); /* flush the lock */ unlink (lock); } MM_NOCRITICAL (stream); /* no longer critical */ if (!ret) MM_LOG (tmp,ERROR); /* log error */ return ret; /* return success or failure */ } /* UNIX mail open * Accepts: Stream to open * Returns: Stream on success, NIL on failure */ MAILSTREAM *unix_open (MAILSTREAM *stream) { long i; int fd; char tmp[MAILTMPLEN]; DOTLOCK lock; long retry; /* return prototype for OP_PROTOTYPE call */ if (!stream) return user_flags (&unixproto); retry = stream->silent ? 1 : KODRETRY; if (stream->local) fatal ("unix recycle stream"); stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL)); /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); /* canonicalize the stream mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); MM_LOG (tmp,ERROR); return NIL; } /* flush old name */ fs_give ((void **) &stream->mailbox); /* save canonical name */ stream->mailbox = cpystr (tmp); LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); stream->sequence++; /* bump sequence number */ /* make lock for read/write access */ if (!stream->rdonly) while (retry) { /* try to lock file */ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) { if (retry-- == KODRETRY) {/* no, first time through? */ if (i) { /* learned the other guy's PID? */ kill ((int) i,SIGUSR2); sprintf (tmp,"Trying to get mailbox lock from process %ld",i); MM_LOG (tmp,WARN); } else retry = 0; /* give up */ } if (!stream->silent) { /* nothing if silent stream */ if (retry) sleep (1); /* wait a second before trying again */ else MM_LOG ("Mailbox is open by another process, access is readonly", WARN); } } else { /* got the lock, nobody else can alter state */ LOCAL->ld = fd; /* note lock's fd and name */ LOCAL->lname = cpystr (tmp); /* make sure mode OK (don't use fchmod()) */ chmod (LOCAL->lname,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL)); if (stream->silent) i = 0;/* silent streams won't accept KOD */ else { /* note our PID in the lock */ sprintf (tmp,"%d",getpid ()); write (fd,tmp,(i = strlen (tmp))+1); } ftruncate (fd,i); /* make sure tied off */ fsync (fd); /* make sure it's available */ retry = 0; /* no more need to try */ } } /* parse mailbox */ stream->nmsgs = stream->recent = 0; /* will we be able to get write access? */ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) { MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); flock (LOCAL->ld,LOCK_UN); /* release the lock */ close (LOCAL->ld); /* close the lock file */ LOCAL->ld = -1; /* no more lock fd */ unlink (LOCAL->lname); /* delete it */ } /* reset UID validity */ stream->uid_validity = stream->uid_last = 0; if (stream->silent && !stream->rdonly && (LOCAL->ld < 0)) unix_abort (stream); /* abort if can't get RW silent stream */ /* parse mailbox */ else if (unix_parse (stream,&lock,LOCK_SH)) { unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ } if (!LOCAL) return NIL; /* failure if stream died */ /* make sure upper level knows readonly */ stream->rdonly = (LOCAL->ld < 0); /* notify about empty mailbox */ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL); if (!stream->rdonly) { /* flags stick if readwrite */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = T; if (!stream->uid_nosticky) {/* users with lives get permanent keywords */ stream->perm_user_flags = 0xffffffff; /* and maybe can create them too! */ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T; } } return stream; /* return stream alive to caller */ } /* UNIX mail close * Accepts: MAIL stream * close options */ void unix_close (MAILSTREAM *stream,long options) { int silent = stream->silent; stream->silent = T; /* go silent */ /* expunge if requested */ if (options & CL_EXPUNGE) unix_expunge (stream); /* else dump final checkpoint */ else if (LOCAL->dirty) unix_check (stream); stream->silent = silent; /* restore old silence state */ unix_abort (stream); /* now punt the file and local data */ } /* UNIX mail fetch message header * Accepts: MAIL stream * message # to fetch * pointer to returned header text length * option flags * Returns: message header in RFC822 format */ /* lines to filter from header */ static STRINGLIST *unix_hlines = NIL; char *unix_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags) { MESSAGECACHE *elt; unsigned char *s,*t,*tl; *length = 0; /* default to empty */ if (flags & FT_UID) return "";/* UID call "impossible" */ elt = mail_elt (stream,msgno);/* get cache */ if (!unix_hlines) { /* once only code */ STRINGLIST *lines = unix_hlines = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Status")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-Keywords")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-UID")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAP")); lines = lines->next = mail_newstringlist (); lines->text.size = strlen ((char *) (lines->text.data = (unsigned char *) "X-IMAPbase")); } /* go to header position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.header.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.header.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.header.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; } else { /* need to make a CRLF version */ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1), elt->private.msg.header.text.size); /* tie off string, and convert to CRLF */ s[elt->private.msg.header.text.size] = '\0'; *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, elt->private.msg.header.text.size); fs_give ((void **) &s); /* free readin buffer */ } *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT); return LOCAL->buf; /* return processed copy */ } /* UNIX mail fetch message text * Accepts: MAIL stream * message # to fetch * pointer to returned stringstruct * option flags * Returns: T on success, NIL if failure */ long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) { char *s; unsigned long i; MESSAGECACHE *elt; /* UID call "impossible" */ if (flags & FT_UID) return NIL; elt = mail_elt (stream,msgno);/* get cache element */ /* if message not seen */ if (!(flags & FT_PEEK) && !elt->seen) { /* mark message seen and dirty */ elt->seen = elt->private.dirty = LOCAL->dirty = T; MM_FLAGS (stream,msgno); } s = unix_text_work (stream,elt,&i,flags); INIT (bs,mail_string,s,i); /* set up stringstruct */ return T; /* success */ } /* UNIX mail fetch message text worker routine * Accepts: MAIL stream * message cache element * pointer to returned header text length * option flags */ char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags) { FDDATA d; STRING bs; unsigned char *s,*t,*tl,tmp[CHUNK]; /* go to text position */ lseek (LOCAL->fd,elt->private.special.offset + elt->private.msg.text.offset,L_SET); if (flags & FT_INTERNAL) { /* initial data OK? */ if (elt->private.msg.text.text.size > LOCAL->buflen) { fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->private.msg.text.text.size) + 1); } /* read message */ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); /* got text, tie off string */ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; /* squeeze out CRs (in case from PC) */ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++) if ((*t != '\r') || (t[1] != '\n')) *s++ = *t; /* adjust length */ LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0'; return LOCAL->buf; } /* have it cached already? */ if (elt->private.uid != LOCAL->uid) { /* not cached, cache it now */ LOCAL->uid = elt->private.uid; /* is buffer big enough? */ if (elt->rfc822_size > LOCAL->text.size) { /* excessively conservative, but the right thing is too hard to do */ fs_give ((void **) &LOCAL->text.data); LOCAL->text.data = (unsigned char *) fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); } d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = elt->private.special.offset + elt->private.msg.text.offset; d.chunk = tmp; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) { case '\r': /* carriage return seen */ *s++ = SNX (&bs); /* copy it and any succeeding LF */ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); break; case '\n': *s++ = '\r'; /* insert a CR */ default: *s++ = SNX (&bs); /* copy characters */ } *s = '\0'; /* tie off buffer */ /* calculate length of cached data */ LOCAL->textlen = s - LOCAL->text.data; } *length = LOCAL->textlen; /* return from cache */ return (char *) LOCAL->text.data; } /* UNIX per-message modify flag * Accepts: MAIL stream * message cache element */ void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) { /* only after finishing */ if (elt->valid) elt->private.dirty = LOCAL->dirty = T; } /* UNIX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL */ long unix_ping (MAILSTREAM *stream) { DOTLOCK lock; struct stat sbuf; long reparse; /* big no-op if not readwrite */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) { if (stream->rdonly) { /* does he want to give up readwrite? */ /* checkpoint if we changed something */ if (LOCAL->dirty) unix_check (stream); flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ close (LOCAL->ld); /* close the readwrite lock file */ LOCAL->ld = -1; /* no more readwrite lock fd */ unlink (LOCAL->lname); /* delete the readwrite lock file */ } else { /* see if need to reparse */ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) { /* get current mailbox size */ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); else stat (stream->mailbox,&sbuf); reparse = (sbuf.st_size != LOCAL->filesize); } /* parse if mailbox changed */ if (reparse && unix_parse (stream,&lock,LOCK_SH)) { /* unlock mailbox */ unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* and stream */ MM_NOCRITICAL (stream); /* done with critical */ } } } return LOCAL ? LONGT : NIL; /* return if still alive */ } /* UNIX mail check mailbox * Accepts: MAIL stream */ void unix_check (MAILSTREAM *stream) { DOTLOCK lock; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,&lock,LOCK_EX)) { /* any unsaved changes? */ if (LOCAL->dirty && unix_rewrite (stream,NIL,&lock)) { if (!stream->silent) MM_LOG ("Checkpoint completed",NIL); } /* no checkpoint needed, just unlock */ else unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ } } /* UNIX mail expunge mailbox * Accepts: MAIL stream */ void unix_expunge (MAILSTREAM *stream) { unsigned long i; DOTLOCK lock; char *msg = NIL; /* parse and lock mailbox */ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && unix_parse (stream,&lock,LOCK_EX)) { /* count expunged messages if not dirty */ if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; if (!LOCAL->dirty) { /* not dirty and no expunged messages */ unix_unlock (LOCAL->fd,stream,&lock); msg = "No messages deleted, so no update needed"; } else if (unix_rewrite (stream,&i,&lock)) { if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); else msg = "Mailbox checkpointed, but no messages expunged"; } /* rewrite failed */ else unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ MM_NOCRITICAL (stream); /* done with critical */ if (msg && !stream->silent) MM_LOG (msg,NIL); } else if (!stream->silent) MM_LOG("Expunge ignored on readonly mailbox",WARN); } /* UNIX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */ long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { struct stat sbuf; int fd; char *s,file[MAILTMPLEN]; DOTLOCK lock; time_t tp[2]; unsigned long i,j; MESSAGECACHE *elt; long ret = T; mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))) return NIL; /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { if (pc) return (*pc) (stream,sequence,mailbox,options); unix_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; default: if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox); MM_LOG (LOCAL->buf,ERROR); return NIL; } LOCAL->buf[0] = '\0'; MM_CRITICAL (stream); /* go critical */ if ((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR);/* log the error */ return NIL; /* failed */ } fstat (fd,&sbuf); /* get current file size */ /* write all requested messages to mailbox */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL; else { /* internal header succeeded */ s = unix_header (stream,i,&j,FT_INTERNAL); /* header size, sans trailing newline */ if (j && (s[j - 2] == '\n')) j--; if (write (fd,s,j) < 0) ret = NIL; else { /* message header succeeded */ j = unix_xstatus (stream,LOCAL->buf,elt,NIL); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* message status succeeded */ s = unix_text_work (stream,elt,&j,FT_INTERNAL); if ((write (fd,s,j) < 0) || (write (fd,"\n",1) < 0)) ret = NIL; } } } } if (!ret || fsync (fd)) { /* force out the update */ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); ftruncate (fd,sbuf.st_size); ret = NIL; } tp[1] = time (0); /* set mtime to now */ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ else tp[0] = /* else preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; utime (file,tp); /* set the times */ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */ MM_NOCRITICAL (stream); /* release critical */ /* log the error */ if (!ret) MM_LOG (LOCAL->buf,ERROR); /* delete if requested message */ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = elt->private.dirty = LOCAL->dirty = T; return ret; } /* UNIX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ #define BUFLEN 8*MAILTMPLEN long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN]; time_t tp[2]; FILE *sf,*df; MESSAGECACHE elt; DOTLOCK lock; STRING *message; MAILSTREAM *tstream = NIL; long ret = LONGT; if (!stream) { /* stream specified? */ stream = &unixproto; /* no, default stream to prototype */ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) fs_give ((void **) &stream->user_flags[i]); stream->kwd_create = T; /* allow new flags */ } /* make sure valid mailbox */ if (!unix_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (!compare_cstring (mailbox,"INBOX")) { unix_create (NIL,"INBOX");/* create empty INBOX */ break; } MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; case 0: /* merely empty file? */ break; case EINVAL: sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } unlink (tmp); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); MM_LOG (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR); else if (!unix_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); } /* get next message */ else if (MM_APPEND (af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ MM_CRITICAL (stream); /* go critical */ if (((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { MM_NOCRITICAL (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ rewind (sf); for (; i && ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) && (fwrite (buf,1,j,df) == j)); i -= j); fclose (sf); /* done with scratch file */ tp[1] = time (0); /* set mtime to now */ /* make sure append wins, fsync() necessary */ if (i || (fflush (df) == EOF) || fsync (fd)) { sprintf (buf,"Message append failed: %s",strerror (errno)); MM_LOG (buf,ERROR); ftruncate (fd,sbuf.st_size); tp[0] = /* preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; ret = NIL; /* return error */ } else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ utime (file,tp); /* set the times */ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */ fclose (df); /* note that unix_unlock() released the fd */ MM_NOCRITICAL (stream); /* release critical */ return ret; } /* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */ int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg) { int ti,zn,c; unsigned long i,uf; char *x,tmp[MAILTMPLEN]; int hdrp = T; long f = mail_parse_flags (stream,flags,&uf); /* build initial header */ if ((fprintf (sf,"From %s@%s %sStatus: ", myusername (),mylocalhost (),date) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if (putc ('\n',sf) == EOF) return NIL; while (SIZE (msg)) { /* copy text to scratch file */ /* disregard CRs */ while ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') == '\r'); /* see if line needs special treatment */ if ((c == 'F') || (hdrp && ((c == 'S') || (c == 'X')))) { /* copy line to buffer */ for (i = 1,tmp[0] = c; (c != '\n') && (i < MAILTMPLEN); ) if ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') != '\r') tmp[i++] = c; /* possible "From " line? */ if ((i > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') && (tmp[4] == ' ')) { /* yes, see if need to write a widget */ if (!(ti = unix_fromwidget || (c != '\n'))) VALID (tmp,x,ti,zn); if (ti && (putc ('>',sf) == EOF)) return NIL; } /* insert X- before metadata header */ else if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && (tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && (tmp[5] == 's') && (tmp[6] == ':')) || (((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && (tmp[5] == ':')) || ((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && (tmp[4] == 'A') && (tmp[5] == 'P') && ((tmp[6] == ':') || ((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && (tmp[8] == 's') && (tmp[9] == 'e') && (tmp[10] == ':')))) || ((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && (tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && (tmp[7] == 's') && (tmp[8] == ':')) || ((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && (tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && (tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && (tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF)) return NIL; /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; /* set up to copy remainder of line */ if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); else continue; /* end of line or end of message */ } /* check for end of header */ else if (hdrp && (c == '\n')) hdrp = NIL; /* copy line, tossing out CR */ do if ((c != '\r') && (putc (c,sf) == EOF)) return NIL; while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* write trailing newline and return */ return (putc ('\n',sf) == EOF) ? NIL : T; } /* Internal routines */ /* UNIX mail abort stream * Accepts: MAIL stream */ void unix_abort (MAILSTREAM *stream) { if (LOCAL) { /* only if a file is open */ if (LOCAL->fd >= 0) close (LOCAL->fd); if (LOCAL->ld >= 0) { /* have a mailbox lock? */ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ close (LOCAL->ld); /* close the lock file */ unlink (LOCAL->lname); /* and delete it */ } if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); /* free local text buffers */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ } } /* UNIX open and lock mailbox * Accepts: file name to open/lock * file open mode * destination buffer for lock file name * type of locking operation (LOCK_SH or LOCK_EX) */ int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op) { int fd; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_FILELOCK,NIL); /* try locking the easy way */ if (dotlock_lock (file,lock,-1)) { /* got dotlock file, easy open */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } /* no dot lock file, open file now */ else if ((fd = open (file,flags,mode)) >= 0) { /* try paranoid way to make a dot lock file */ if (dotlock_lock (file,lock,fd)) { close (fd); /* get fresh fd in case of timing race */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } else flock (fd,op); /* paranoid way failed, just flock() it */ } (*bn) (BLOCK_NONE,NIL); return fd; } /* UNIX unlock and close mailbox * Accepts: file descriptor * (optional) mailbox stream to check atime/mtime * (optional) lock file name */ void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock) { if (stream) { /* need to muck with times? */ struct stat sbuf; time_t tp[2]; time_t now = time (0); fstat (fd,&sbuf); /* get file times */ if (LOCAL->ld >= 0) { /* yes, readwrite session? */ tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else if (stream->recent) { /* readonly with recent messages */ if ((sbuf.st_atime >= sbuf.st_mtime) || (sbuf.st_atime >= sbuf.st_ctime)) /* keep past mtime, whack back atime */ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; else now = 0; /* no time change needed */ } /* readonly with no recent messages */ else if ((sbuf.st_atime < sbuf.st_mtime) || (sbuf.st_atime < sbuf.st_ctime)) { tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else now = 0; /* no time change needed */ /* set the times, note change */ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; } flock (fd,LOCK_UN); /* release flock'ers */ if (!stream) close (fd); /* close the file if no stream */ dotlock_unlock (lock); /* flush the lock file if any */ } /* UNIX mail parse and lock mailbox * Accepts: MAIL stream * space to write lock file name * type of locking operation * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure */ int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op) { int zn; unsigned long i,j,k,m; unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; int ti = 0,retain = T; unsigned long nmsgs = stream->nmsgs; unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; unsigned long recent = stream->recent; unsigned long oldnmsgs = stream->nmsgs; short silent = stream->silent; short pseudoseen = NIL; struct stat sbuf; STRING bs; FDDATA d; MESSAGECACHE *elt; mail_lock (stream); /* guard against recursion or pingers */ /* toss out previous descriptor */ if (LOCAL->fd >= 0) close (LOCAL->fd); MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */ if ((LOCAL->fd = unix_lock (stream->mailbox,(LOCAL->ld >= 0) ? O_RDWR : O_RDONLY,NIL,lock,op)) < 0) { sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (tmp,ERROR); unix_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } fstat (LOCAL->fd,&sbuf); /* get status */ /* validate change in size */ if (sbuf.st_size < LOCAL->filesize) { sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); MM_LOG (tmp,ERROR); /* this is pretty bad */ unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); MM_NOCRITICAL (stream); /* done with critical */ return NIL; } /* new data? */ else if (i = sbuf.st_size - LOCAL->filesize) { d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = LOCAL->filesize; /* get to that position in the file */ d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ /* skip leading whitespace for broken MTAs */ while (((c = CHR (&bs)) == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) SNX (&bs); if (SIZE (&bs)) { /* read new data */ /* remember internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = unix_mbxline (stream,&bs,&i); t = NIL,zn = 0; if (i) VALID (s,t,ti,zn); /* see if valid From line */ if (!ti) { /* someone pulled the rug from under us */ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", (char *) s); MM_LOG (tmp,ERROR); unix_unlock (LOCAL->fd,stream,lock); unix_abort (stream); mail_unlock (stream); /* done with critical */ MM_NOCRITICAL (stream); return NIL; } stream->silent = T; /* quell main program new message events */ do { /* found a message */ /* instantiate first new message */ mail_exists (stream,++nmsgs); (elt = mail_elt (stream,nmsgs))->valid = T; recent++; /* assume recent by default */ elt->recent = T; /* note position/size of internal header */ elt->private.special.offset = j; elt->private.msg.header.offset = elt->private.special.text.size = i; /* generate plausible IMAPish date string */ date[2] = date[6] = date[20] = '-'; date[11] = ' '; date[14] = date[17] = ':'; /* dd */ date[0] = t[ti - 2]; date[1] = t[ti - 1]; /* mmm */ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; /* hh */ date[12] = t[ti + 1]; date[13] = t[ti + 2]; /* mm */ date[15] = t[ti + 4]; date[16] = t[ti + 5]; if (t[ti += 6] == ':') {/* ss */ date[18] = t[++ti]; date[19] = t[++ti]; ti++; /* move to space */ } else date[18] = date[19] = '0'; /* yy -- advance over timezone if necessary */ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4); date[7] = t[ti + 1]; date[8] = t[ti + 2]; date[9] = t[ti + 3]; date[10] = t[ti + 4]; /* zzz */ t = zn ? (t + zn + 1) : (unsigned char *) "LCL"; date[21] = *t++; date[22] = *t++; date[23] = *t++; if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0'; else { /* numeric time zone */ date[24] = *t++; date[25] = *t++; date[26] = '\0'; date[20] = ' '; } /* set internal date */ if (!mail_parse_date (elt,date)) { sprintf (tmp,"Unable to parse internal date: %s",(char *) date); MM_LOG (tmp,WARN); } do { /* look for message body */ s = t = unix_mbxline (stream,&bs,&i); if (i) switch (*s) { /* check header lines */ case 'X': /* possible X-???: line */ if (s[1] == '-') { /* must be immediately followed by hyphen */ /* X-Status: becomes Status: in S case */ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' && s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2; /* possible X-Keywords */ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' && s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && s[8] == 'd' && s[9] == 's' && s[10] == ':') { SIZEDTEXT uf; retain = NIL; /* don't retain continuation */ s += 11; /* flush leading whitespace */ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){ while (*s == ' ') s++; /* find end of keyword */ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); /* got a keyword? */ if ((k = (u - s)) && (k < MAXUSERFLAG)) { uf.data = (unsigned char *) s; uf.size = k; for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j) if (!compare_csizedtext (stream->user_flags[j],&uf)) { elt->user_flags |= ((long) 1) << j; break; } } s = u; /* advance to next keyword */ } break; } /* possible X-IMAP */ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') && (s[5] == 'P') && ((m = (s[6] == ':')) || ((s[6] == 'b') && (s[7] == 'a') && (s[8] == 's') && (s[9] == 'e') && (s[10] == ':')))) { retain = NIL; /* don't retain continuation */ if ((nmsgs == 1) && !stream->uid_validity) { /* advance to data */ s += m ? 7 : 11; /* flush whitespace */ while (*s == ' ') s++; j = 0; /* slurp UID validity */ /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush whitespace */ while (*s == ' ') s++; /* must have valid UID validity and UID last */ if (j && isdigit (*s)) { /* pseudo-header seen if X-IMAP */ if (m) pseudoseen = LOCAL->pseudo = T; /* save UID validity */ stream->uid_validity = j; j = 0; /* slurp UID last */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* save UID last */ stream->uid_last = j; /* process keywords */ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n')); s = u,j++) { /* flush leading whitespace */ while (*s == ' ') s++; u = strpbrk (s," \n\r"); /* got a keyword? */ if ((k = (u - s)) && j < NUSERFLAGS) { if (stream->user_flags[j]) fs_give ((void **) &stream->user_flags[j]); stream->user_flags[j] = (char *) fs_get (k + 1); strncpy (stream->user_flags[j],s,k); stream->user_flags[j][k] = '\0'; } } } } break; } /* possible X-UID */ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' && s[5] == ':') { retain = NIL; /* don't retain continuation */ /* only believe if have a UID validity */ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) { s += 6; /* advance to UID value */ /* flush whitespace */ while (*s == ' ') s++; j = 0; /* found a digit? */ while (isdigit (*s)) { j *= 10; /* yes, add it in */ j += *s++ - '0'; } /* flush remainder of line */ while (*s != '\n') s++; /* make sure not duplicated */ if (elt->private.uid) sprintf (tmp,"Message %lu UID %lu already has UID %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,elt->private.uid); /* make sure UID doesn't go backwards */ else if (j <= prevuid) sprintf (tmp,"Message %lu UID %lu less than %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,prevuid + 1); /* or skip by mailbox's recorded last */ else if (j > stream->uid_last) sprintf (tmp,"Message %lu UID %lu greater than last %lu", pseudoseen ? elt->msgno - 1 : elt->msgno, j,stream->uid_last); else { /* normal UID case */ prevuid = elt->private.uid = j; break; /* exit this cruft */ } MM_LOG (tmp,WARN); /* invalidate UID validity */ stream->uid_validity = 0; elt->private.uid = 0; } break; } } /* otherwise fall into S case */ case 'S': /* possible Status: line */ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' && s[5] == 's' && s[6] == ':') { retain = NIL; /* don't retain continuation */ s += 6; /* advance to status flags */ do switch (*s++) {/* parse flags */ case 'R': /* message read */ elt->seen = T; break; case 'O': /* message old */ if (elt->recent) { elt->recent = NIL; recent--; /* it really wasn't recent */ } break; case 'D': /* message deleted */ elt->deleted = T; break; case 'F': /* message flagged */ elt->flagged = T; break; case 'A': /* message answered */ elt->answered = T; break; case 'T': /* message is a draft */ elt->draft = T; break; default: /* some other crap */ break; } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))); break; /* all done */ } /* otherwise fall into default case */ default: /* ordinary header line */ if ((*s == 'S') || (*s == 's') || (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) { unsigned char *e,*v; /* must match what mail_filter() does */ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1); (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') && ((c > ' ') || ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))); *v++ = *u++); *v = '\0'; /* tie off */ /* matches internal header? */ if (!compare_cstring (tmp,"STATUS") || !compare_cstring (tmp,"X-STATUS") || !compare_cstring (tmp,"X-KEYWORDS") || !compare_cstring (tmp,"X-UID") || !compare_cstring (tmp,"X-IMAP") || !compare_cstring (tmp,"X-IMAPBASE")) { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus %s header in message %lu", (char *) tmp,elt->msgno); MM_LOG (err,WARN); retain = NIL; /* don't retain continuation */ break; /* different case or something */ } } /* retain or non-continuation? */ if (retain || ((*s != ' ') && (*s != '\t'))) { retain = T; /* retaining continuation now */ /* line length in LF format newline */ k = i - (((i >= 2) && (s[i - 2] == '\r')) ? 1 : 0); /* "internal" header size */ elt->private.data += k; /* message size */ elt->rfc822_size += k + 1; } else { char err[MAILTMPLEN]; sprintf (err,"Discarding bogus continuation in msg %lu: %.80s", elt->msgno,(char *) s); if (u = strpbrk (err,"\r\n")) *u = '\0'; MM_LOG (err,WARN); break; /* different case or something */ } break; } } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); /* "internal" header sans trailing newline */ if (i) elt->private.data--; /* assign a UID if none found */ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { prevuid = elt->private.uid = ++stream->uid_last; elt->private.dirty = T; } else elt->private.dirty = elt->recent; /* note size of header, location of text */ elt->private.msg.header.text.size = (elt->private.msg.text.offset = (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - elt->private.special.text.size; k = m = 0; /* no previous line size yet */ /* note current position */ j = LOCAL->filesize + GETPOS (&bs); if (i) do { /* look for next message */ s = unix_mbxline (stream,&bs,&i); if (i) { /* got new data? */ VALID (s,t,ti,zn); /* yes, parse line */ if (!ti) { /* not a header line, add it to message */ elt->rfc822_size += k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0)); /* update current position */ j = LOCAL->filesize + GETPOS (&bs); } } } while (i && !ti); /* until found a header */ elt->private.msg.text.text.size = j - (elt->private.special.offset + elt->private.msg.text.offset); if (k == 2) { /* last line was blank? */ elt->private.msg.text.text.size -= (m ? 1 : 2); elt->rfc822_size -= 2; } } while (i); /* until end of buffer */ if (pseudoseen) { /* flush pseudo-message if present */ /* decrement recent count */ if (mail_elt (stream,1)->recent) recent--; /* and the exists count */ mail_exists (stream,nmsgs--); mail_expunged(stream,1);/* fake an expunge of that message */ } /* need to start a new UID validity? */ if (!stream->uid_validity) { stream->uid_validity = time (0); /* in case a whiner with no life */ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) stream->uid_nosticky = T; else if (nmsgs) { /* don't bother if empty file */ LOCAL->dirty = T; /* make dirty to restart UID epoch */ /* need to rewrite msg 1 if not pseudo */ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; MM_LOG ("Assigning new unique identifiers to all messages",NIL); } } stream->nmsgs = oldnmsgs; /* whack it back down */ stream->silent = silent; /* restore old silent setting */ /* notify upper level of new mailbox sizes */ mail_exists (stream,nmsgs); mail_recent (stream,recent); /* mark dirty so O flags are set */ if (recent) LOCAL->dirty = T; } } /* no change, don't babble if never got time */ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime) MM_LOG ("New mailbox modification time but apparently no changes",WARN); /* update parsed file size and time */ LOCAL->filesize = sbuf.st_size; LOCAL->filetime = sbuf.st_mtime; return T; /* return the winnage */ } /* UNIX read line from mailbox * Accepts: mail stream * stringstruct * pointer to line size * Returns: pointer to input line */ char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) { unsigned long i,j,k,m; char *s,*t,*te,p1[CHUNK]; char *ret = ""; /* flush old buffer */ if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* if buffer needs refreshing */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) { /* find newline */ /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* difficult case if line spans buffer */ if ((i = s - bs->curpos) == bs->cursize) { memcpy (p1,bs->curpos,i); /* remember what we have so far */ /* load next buffer */ SETPOS (bs,k = GETPOS (bs) + i); /* end of fast scan */ te = (t = (s = bs->curpos) + bs->cursize) - 12; /* fast scan in overlap buffer */ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { --s; /* back up */ break; /* exit loop */ } /* final character-at-a-time scan */ while ((s < t) && (*s != '\n')) ++s; /* huge line? */ if ((j = s - bs->curpos) == bs->cursize) { SETPOS (bs,GETPOS (bs) + j); /* look for end of line (s-l-o-w!!) */ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); SETPOS (bs,k); /* go back to where it started */ } /* got size of data, make buffer for return */ ret = LOCAL->line = (char *) fs_get (i + j + 2); memcpy (ret,p1,i); /* copy first chunk */ while (j) { /* copy remainder */ if (!bs->cursize) SETPOS (bs,GETPOS (bs)); memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); i += k; /* account for this much read in */ j -= k; bs->curpos += k; /* increment new position */ bs->cursize -= k; /* eat that many bytes */ } if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */ ret[i++] = '\n'; /* make sure newline at end */ ret[i] = '\0'; /* makes debugging easier */ } else { /* this is easy */ ret = bs->curpos; /* string it at this position */ bs->curpos += ++i; /* increment new position */ bs->cursize -= i; /* eat that many bytes */ } *size = i; /* return that to user */ } else *size = 0; /* end of data, return empty */ return ret; } /* UNIX make pseudo-header * Accepts: MAIL stream * buffer to write pseudo-header * Returns: length of pseudo-header */ unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr) { int i; char *s,tmp[MAILTMPLEN]; time_t now = time (0); rfc822_fixed_date (tmp); sprintf (hdr,"From %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu", pseudo_from,ctime (&now), tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, (unsigned long) now,mylocalhost (),stream->uid_validity, stream->uid_last); for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) sprintf (s += strlen (s)," %s",stream->user_flags[i]); sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg); return strlen (hdr); /* return header length */ } /* UNIX make status string * Accepts: MAIL stream * destination string to write * message cache entry * non-zero flag to write UID (.LT. 0 to write UID base info too) * Returns: length of string */ unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, long flag) { char *t,stack[64]; char *s = status; unsigned long n; int pad = 50; /* This used to use sprintf(), but thanks to certain cretinous C libraries with horribly slow implementations of sprintf() I had to change it to this mess. At least it should be fast. */ /* need to write X-IMAPbase: header? */ if ((flag < 0) && !stream->uid_nosticky) { *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P'; *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' '; t = stack; n = stream->uid_validity; /* push UID validity digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID validity digits from stack */ while (t > stack) *s++ = *--t; *s++ = ' '; n = stream->uid_last; /* push UID last digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); /* pop UID last digits from stack */ while (t > stack) *s++ = *--t; for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n]) for (*s++ = ' '; *t; *s++ = *t++); *s++ = '\n'; pad += 30; /* increased padding if have IMAPbase */ } *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->seen) *s++ = 'R'; if (flag) *s++ = 'O'; /* only write O if have a UID */ *s++ = '\n'; *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; if (elt->deleted) *s++ = 'D'; if (elt->flagged) *s++ = 'F'; if (elt->answered) *s++ = 'A'; if (elt->draft) *s++ = 'T'; *s++ = '\n'; if (!stream->uid_nosticky) { /* cretins with no life can't use this */ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w'; *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':'; if (n = elt->user_flags) do { *s++ = ' '; for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); } while (n); n = s - status; /* get size of stuff so far */ /* pad X-Keywords to make size constant */ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; *s++ = '\n'; if (flag) { /* want to include UID? */ t = stack; n = elt->private.uid; /* push UID digits on the stack */ do *t++ = (char) (n % 10) + '0'; while (n /= 10); *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; *s++ = ' '; /* pop UID from stack */ while (t > stack) *s++ = *--t; *s++ = '\n'; } } *s++ = '\n'; *s = '\0'; /* end of extended message status */ return s - status; /* return size of resulting string */ } /* Rewrite mailbox file * Accepts: MAIL stream, must be critical and locked * return pointer to number of expunged messages if want expunge * lock file name * Returns: T if success and mailbox unlocked, NIL if failure */ #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock) { MESSAGECACHE *elt; UNIXFILE f; char *s; time_t tp[2]; long ret,flag; unsigned long i,j; unsigned long recent = stream->recent; unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0; if (nexp) *nexp = 0; /* initially nothing expunged */ /* calculate size of mailbox after rewrite */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) if (!(elt = mail_elt (stream,i))->deleted || !nexp) { /* add RFC822 size of this message */ size += elt->private.special.text.size + elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag) + elt->private.msg.text.text.size + 1; flag = 1; /* only count X-IMAPbase once */ } /* no messages, has a life, and no pseudo */ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) { LOCAL->pseudo = T; /* so make a pseudo-message now */ size = unix_pseudo (stream,LOCAL->buf); } /* extend the file as necessary */ if (ret = unix_extend (stream,size)) { /* Set up buffered I/O file structure * curpos current position being written through buffering * filepos current position being written physically to the disk * bufpos current position being written in the buffer * protect current maximum position that can be written to the disk * before buffering is forced * The code tries to buffer so that that disk is written in multiples of * OVERBLOWBUFLEN bytes. */ f.stream = stream; /* note mail stream */ f.curpos = f.filepos = 0; /* start of file */ f.protect = stream->nmsgs ? /* initial protection pointer */ mail_elt (stream,1)->private.special.offset : 8192; f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); if (LOCAL->pseudo) /* update pseudo-header */ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf)); /* loop through all messages */ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { elt = mail_elt (stream,i);/* get cache */ if (nexp && elt->deleted){/* expunge this message? */ /* one less recent message */ if (elt->recent) --recent; mail_expunged(stream,i);/* notify upper levels */ ++*nexp; /* count up one more expunged message */ } else { /* preserve this message */ i++; /* advance to next message */ if ((flag < 0) || /* need to rewrite message? */ elt->private.dirty || (f.curpos != elt->private.special.offset) || (elt->private.msg.header.text.size != (elt->private.data + unix_xstatus (stream,LOCAL->buf,elt,flag)))) { unsigned long newoffset = f.curpos; /* yes, seek to internal header */ lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); /* see if need to squeeze out a CR */ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') { LOCAL->buf[--elt->private.special.text.size - 1] = '\n'; --size; /* squeezed out a CR from PC */ } /* protection pointer moves to RFC822 header */ f.protect = elt->private.special.offset + elt->private.msg.header.offset; /* write internal header */ unix_write (&f,LOCAL->buf,elt->private.special.text.size); /* get RFC822 header */ s = unix_header (stream,elt->msgno,&j,FT_INTERNAL); /* in case this got decremented */ elt->private.msg.header.offset = elt->private.special.text.size; /* header size, sans trailing newline */ if ((j < 2) || (s[j - 2] == '\n')) j--; if (j != elt->private.data) fatal ("header size inconsistent"); /* protection pointer moves to RFC822 text */ f.protect = elt->private.special.offset + elt->private.msg.text.offset; unix_write (&f,s,j); /* write RFC822 header */ /* write status and UID */ unix_write (&f,LOCAL->buf, j = unix_xstatus (stream,LOCAL->buf,elt,flag)); flag = 1; /* only write X-IMAPbase once */ /* new file header size */ elt->private.msg.header.text.size = elt->private.data + j; /* did text move? */ if (f.curpos != f.protect) { /* get message text */ s = unix_text_work (stream,elt,&j,FT_INTERNAL); /* this can happen if CRs were squeezed */ if (j < elt->private.msg.text.text.size) { /* so fix up counts */ size -= elt->private.msg.text.text.size - j; elt->private.msg.text.text.size = j; } /* can't happen it says here */ else if (j > elt->private.msg.text.text.size) fatal ("text size inconsistent"); /* new text offset, status/UID may change it */ elt->private.msg.text.offset = f.curpos - newoffset; /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : (f.curpos + j + 1); unix_write (&f,s,j);/* write text */ /* write trailing newline */ unix_write (&f,"\n",1); } else { /* tie off header and status */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\n",1); } } /* new internal header offset */ elt->private.special.offset = newoffset; elt->private.dirty =NIL;/* message is now clean */ } else { /* no need to rewrite this message */ /* tie off previous message if needed */ unix_write (&f,NIL,NIL); /* protection pointer moves to next message */ f.protect = (i <= stream->nmsgs) ? mail_elt (stream,i)->private.special.offset : size; /* locate end of message text */ j = f.filepos + elt->private.special.text.size + elt->private.msg.header.text.size + elt->private.msg.text.text.size; /* trailing newline already there? */ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect; else { /* trailing newline missing, write it */ f.curpos = f.filepos = j; unix_write (&f,"\n",1); } } } } unix_write (&f,NIL,NIL); /* tie off final message */ if (size != f.filepos) fatal ("file size inconsistent"); fs_give ((void **) &f.buf); /* free buffer */ /* make sure tied off */ ftruncate (LOCAL->fd,LOCAL->filesize = size); fsync (LOCAL->fd); /* make sure the updates take */ if (size && (flag < 0)) fatal ("lost UID base information"); LOCAL->dirty = NIL; /* no longer dirty */ /* notify upper level of new mailbox sizes */ mail_exists (stream,stream->nmsgs); mail_recent (stream,recent); /* set atime to now, mtime a second earlier */ tp[1] = (tp[0] = time (0)) - 1; /* set the times, note change */ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; close (LOCAL->fd); /* close and reopen file */ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,NIL)) < 0) { sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno)); MM_LOG (LOCAL->buf,ERROR); unix_abort (stream); } dotlock_unlock (lock); /* flush the lock file */ } return ret; /* return state from algorithm */ } /* Extend UNIX mailbox file * Accepts: MAIL stream * new desired size * Return: T if success, else NIL */ long unix_extend (MAILSTREAM *stream,unsigned long size) { unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0; if (i) { /* does the mailbox need to grow? */ if (i > LOCAL->buflen) { /* make sure have enough space */ /* this user won the lottery all right */ fs_give ((void **) &LOCAL->buf); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1); } memset (LOCAL->buf,'\0',i); /* get a block of nulls */ while (T) { /* until write successful or punt */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break; else { long e = errno; /* note error before doing ftruncate */ ftruncate (LOCAL->fd,LOCAL->filesize); if (MM_DISKERROR (stream,e,NIL)) { fsync (LOCAL->fd); /* user chose to punt */ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e)); if (!stream->silent) MM_LOG (LOCAL->buf,ERROR); return NIL; } } } } return LONGT; } /* Write data to buffered file * Accepts: buffered file pointer * file data or NIL to indicate "flush buffer" * date size (ignored for "flush buffer") * Does not return until success */ void unix_write (UNIXFILE *f,char *buf,unsigned long size) { unsigned long i,j,k; if (buf) { /* doing buffered write? */ i = f->bufpos - f->buf; /* yes, get size of current buffer data */ /* yes, have space in current buffer chunk? */ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) { /* yes, fill up buffer as much as we can */ memcpy (f->bufpos,buf,k = min (j,size)); f->bufpos += k; /* new buffer position */ f->curpos += k; /* new current position */ if (j -= k) return; /* all done if still have buffer free space */ buf += k; /* full, get new unwritten data pointer */ size -= k; /* new data size */ i += k; /* new buffer data size */ } /* This chunk of the buffer is full. See if can make some space by * writing to the disk, if there's enough unprotected space to do so. * Try to fill out any unaligned chunk, along with any subsequent full * chunks that will fit in unprotected space. */ /* any unprotected space we can write to? */ if (j = min (i,f->protect - f->filepos)) { /* yes, filepos not at chunk boundary? */ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j)) j -= k; /* yes, and can write out partial chunk */ else k = 0; /* no partial chunk to write */ /* if at least a chunk free, write that too */ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN); if (k) { /* write data if there is anything we can */ unix_phys_write (f,f->buf,k); /* slide buffer */ if (i -= k) memmove (f->buf,f->buf + k,i); f->bufpos = f->buf + i; /* new end of buffer */ } } /* Have flushed the buffer as best as possible. All done if no more * data to write. Otherwise, if the buffer is empty AND if the unwritten * data is larger than a chunk AND the unprotected space is also larger * than a chunk, then write as many chunks as we can directly from the * data. Buffer the rest, expanding the buffer as needed. */ if (size) { /* have more data that we need to buffer? */ /* can write any of it to disk instead? */ if ((f->bufpos == f->buf) && ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) { /* write as much as we can right now */ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN)); buf += j; /* new data pointer */ size -= j; /* new data size */ f->curpos += j; /* advance current pointer */ } if (size) { /* still have data that we need to buffer? */ /* yes, need to expand the buffer? */ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) { /* note current position in buffer */ j = f->bufpos - f->buf; i += OVERFLOWBUFLEN; /* yes, grow another chunk */ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN)); /* in case buffer relocated */ f->bufpos = f->buf + j; } /* buffer remaining data */ memcpy (f->bufpos,buf,size); f->bufpos += size; /* new end of buffer */ f->curpos += size; /* advance current pointer */ } } } else { /* flush buffer to disk */ unix_phys_write (f,f->buf,i = f->bufpos - f->buf); f->bufpos = f->buf; /* reset buffer */ /* update positions */ f->curpos = f->protect = f->filepos; } } /* Physical disk write * Accepts: buffered file pointer * buffer address * buffer size * Does not return until success */ void unix_phys_write (UNIXFILE *f,char *buf,size_t size) { MAILSTREAM *stream = f->stream; /* write data at desired position */ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) || (write (LOCAL->fd,buf,size) < 0))) { int e; char tmp[MAILTMPLEN]; sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno)); MM_LOG (tmp,ERROR); MM_DISKERROR (NIL,e,T); /* serious problem, must retry */ } f->filepos += size; /* update file position */ } /* mbox mail routines */ /* Function prototypes */ DRIVER *mbox_valid (char *name); long mbox_create (MAILSTREAM *stream,char *mailbox); long mbox_delete (MAILSTREAM *stream,char *mailbox); long mbox_rename (MAILSTREAM *stream,char *old,char *newname); long mbox_status (MAILSTREAM *stream,char *mbx,long flags); MAILSTREAM *mbox_open (MAILSTREAM *stream); long mbox_ping (MAILSTREAM *stream); void mbox_check (MAILSTREAM *stream); void mbox_expunge (MAILSTREAM *stream); long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* MBOX mail routines */ /* Driver dispatch used by MAIL */ DRIVER mboxdriver = { "mbox", /* driver name */ /* driver flags */ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY, (DRIVER *) NIL, /* next driver */ mbox_valid, /* mailbox is valid for us */ unix_parameters, /* manipulate parameters */ unix_scan, /* scan mailboxes */ unix_list, /* find mailboxes */ unix_lsub, /* find subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ mbox_create, /* create mailbox */ mbox_delete, /* delete mailbox */ mbox_rename, /* rename mailbox */ mbox_status, /* status of mailbox */ mbox_open, /* open mailbox */ unix_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ unix_header, /* fetch message header */ unix_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ unix_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mbox_ping, /* ping mailbox to see if still alive */ mbox_check, /* check for new messages */ mbox_expunge, /* expunge deleted messages */ unix_copy, /* copy messages to another mailbox */ mbox_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM mboxproto = {&mboxdriver}; /* MBOX mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *mbox_valid (char *name) { /* only INBOX, mbox must exist */ if (!compare_cstring (name,"INBOX") && (unix_valid ("mbox") || !errno) && (unix_valid (sysinbox()) || !errno || (errno == ENOENT))) return &mboxdriver; return NIL; /* can't win (yet, anyway) */ } /* MBOX mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */ long mbox_create (MAILSTREAM *stream,char *mailbox) { return unix_create (NIL,"mbox"); } /* MBOX mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */ long mbox_delete (MAILSTREAM *stream,char *mailbox) { return mbox_rename (stream,mailbox,NIL); } /* MBOX mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */ long mbox_rename (MAILSTREAM *stream,char *old,char *newname) { char tmp[MAILTMPLEN]; long ret = unix_rename (stream,"~/mbox",newname); /* recreate file if renamed INBOX */ if (ret) unix_create (NIL,"mbox"); else MM_LOG (tmp,ERROR); /* log error */ return ret; /* return success */ } /* MBOX Mail status * Accepts: mail stream * mailbox name * status flags * Returns: T on success, NIL on failure */ long mbox_status (MAILSTREAM *stream,char *mbx,long flags) { MAILSTATUS status; unsigned long i; MAILSTREAM *tstream = NIL; MAILSTREAM *systream = NIL; /* make temporary stream (unless this mbx) */ if (!stream && !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; status.flags = flags; /* return status values */ status.messages = stream->nmsgs; status.recent = stream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) status.unseen++; status.uidnext = stream->uid_last + 1; status.uidvalidity = stream->uid_validity; if (!status.recent && /* calculate post-snarf results */ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { status.messages += systream->nmsgs; status.recent += systream->recent; if (flags & SA_UNSEEN) /* must search to get unseen messages */ for (i = 1; i <= systream->nmsgs; i++) if (!mail_elt (systream,i)->seen) status.unseen++; /* kludge but probably good enough */ status.uidnext += systream->nmsgs; } MM_STATUS(stream,mbx,&status);/* pass status to main program */ if (tstream) mail_close (tstream); if (systream) mail_close (systream); return T; /* success */ } /* MBOX mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *mbox_open (MAILSTREAM *stream) { unsigned long i = 1; unsigned long recent = 0; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &mboxproto; /* change mailbox file name */ fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr ("mbox"); /* open mailbox, snarf new mail */ if (!(unix_open (stream) && mbox_ping (stream))) return NIL; stream->inbox = T; /* mark that this is an INBOX */ /* notify upper level of mailbox sizes */ mail_exists (stream,stream->nmsgs); while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent; mail_recent (stream,recent); /* including recent messages */ return stream; } /* MBOX mail ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ static int snarfed = 0; /* number of snarfs */ long mbox_ping (MAILSTREAM *stream) { int sfd; unsigned long size; struct stat sbuf; char *s; DOTLOCK lock,lockx; /* time to try snarf and sysinbox non-empty? */ if (LOCAL && !stream->rdonly && !stream->lock && (time (0) >= (LOCAL->lastsnarf + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && !stat (sysinbox (),&sbuf) && sbuf.st_size) { /* yes, open and lock sysinbox */ if ((sfd = unix_lock (sysinbox (),O_RDWR,NIL,&lockx,LOCK_EX)) >= 0) { /* locked sysinbox in good format? */ if (fstat (sfd,&sbuf) || !(size = sbuf.st_size) || !unix_isvalid_fd (sfd)) { sprintf (LOCAL->buf,"Mail drop %s is not in standard Unix format", sysinbox ()); MM_LOG (LOCAL->buf,ERROR); } /* sysinbox good, parse and excl-lock mbox */ else if (unix_parse (stream,&lock,LOCK_EX)) { lseek (sfd,0,L_SET); /* read entire sysinbox into memory */ read (sfd,s = (char *) fs_get (size + 1),size); s[size] = '\0'; /* tie it off */ /* append to end of mbox */ lseek (LOCAL->fd,LOCAL->filesize,L_SET); /* copy to mbox */ if ((write (LOCAL->fd,s,size) < 0) || fsync (LOCAL->fd)) { sprintf (LOCAL->buf,"New mail move failed: %s",strerror (errno)); MM_LOG (LOCAL->buf,WARN); /* revert mbox to previous size */ ftruncate (LOCAL->fd,LOCAL->filesize); } /* sysinbox better not have changed */ else if (fstat (sfd,&sbuf) || (size != sbuf.st_size)) { sprintf (LOCAL->buf,"Mail drop %s lock failure, old=%lu now=%lu", sysinbox (),size,(unsigned long) sbuf.st_size); MM_LOG (LOCAL->buf,ERROR); /* revert mbox to previous size */ ftruncate (LOCAL->fd,LOCAL->filesize); /* Believe it or not, a Singaporean government system actually had * symlinks from /var/mail/user to ~user/mbox. To compound this * error, they used an SVR4 system; BSD and OSF locks would have * prevented it but not SVR4 locks. */ if (!fstat (sfd,&sbuf) && (size == sbuf.st_size)) syslog (LOG_ALERT,"File %s and %s are the same file!", sysinbox (),stream->mailbox); } else { /* data copied OK */ ftruncate (sfd,0); /* truncate sysinbox to zero bytes */ if (!snarfed++) { /* have we snarfed before? */ /* syslog if server, else mm_log() */ sprintf (LOCAL->buf,"Moved %lu bytes of new mail to %s from %s", size,stream->mailbox,sysinbox ()); if (strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL), "unknown")) syslog (LOG_INFO,"%s host= %s",LOCAL->buf,tcp_clienthost ()); else MM_LOG (LOCAL->buf,WARN); } } /* done with sysinbox text */ fs_give ((void **) &s); /* all done with mbox */ unix_unlock (LOCAL->fd,stream,&lock); mail_unlock (stream); /* unlock the stream */ /* done with critical */ MM_NOCRITICAL (stream); } /* all done with sysinbox */ unix_unlock (sfd,NIL,&lockx); } LOCAL->lastsnarf = time (0);/* note time of last snarf */ } return unix_ping (stream); /* do the unix routine now */ } /* MBOX mail check mailbox * Accepts: MAIL stream */ void mbox_check (MAILSTREAM *stream) { /* do local ping, then do unix routine */ if (mbox_ping (stream)) unix_check (stream); } /* MBOX mail expunge mailbox * Accepts: MAIL stream */ void mbox_expunge (MAILSTREAM *stream) { unix_expunge (stream); /* do expunge */ mbox_ping (stream); /* do local ping */ } /* MBOX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */ long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { return unix_append (stream,"mbox",af,data); } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/unix.h000066400000000000000000000146011137544547100227540ustar00rootroot00000000000000/* * Program: UNIX mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 20 December 1989 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* DEDICATION * * This file is dedicated to my dog, Unix, also known as Yun-chan and * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after * a two-month bout with cirrhosis of the liver. * * He was a dear friend, and I miss him terribly. * * Lift a leg, Yunie. Luv ya forever!!!! */ /* Validate line * Accepts: pointer to candidate string to validate as a From header * return pointer to end of date/time field * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') * return pointer to offset from t of time zone (if non-zero) * Returns: t,ti,zn set if valid From string, else ti is NIL */ #define VALID(s,x,ti,zn) { \ ti = 0; \ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ (s[4] == ' ')) { \ for (x = s + 5; *x && *x != '\012'; x++); \ if (*x) { \ if (x[-1] == '\015') --x; \ if (x - s >= 41) { \ for (zn = -1; x[zn] != ' '; zn--); \ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ x += zn - 12; \ } \ if (x - s >= 27) { \ if (x[-5] == ' ') { \ if (x[-8] == ':') zn = 0,ti = -5; \ else if (x[-9] == ' ') ti = zn = -9; \ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ ti = zn = -11; \ } \ else if (x[-4] == ' ') { \ if (x[-9] == ' ') zn = -4,ti = -9; \ } \ else if (x[-6] == ' ') { \ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ zn = -6,ti = -11; \ } \ if (ti && !((x[ti - 3] == ':') && \ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ (x[ti - 11] == ' '))) ti = 0; \ } \ } \ } \ } /* You are not expected to understand this macro, but read the next page if * you are not faint of heart. * * Known formats to the VALID macro are: * From user Wed Dec 2 05:53 1992 * BSD From user Wed Dec 2 05:53:22 1992 * SysV From user Wed Dec 2 05:53 PST 1992 * rn From user Wed Dec 2 05:53:22 PST 1992 * From user Wed Dec 2 05:53 -0700 1992 * emacs From user Wed Dec 2 05:53:22 -0700 1992 * From user Wed Dec 2 05:53 1992 PST * From user Wed Dec 2 05:53:22 1992 PST * From user Wed Dec 2 05:53 1992 -0700 * Solaris From user Wed Dec 2 05:53:22 1992 -0700 * * Plus all of the above with `` remote from xxx'' after it. Thank you very * much, smail and Solaris, for making my life considerably more complicated. */ /* * What? You want to understand the VALID macro anyway? Alright, since you * insist. Actually, it isn't really all that difficult, provided that you * take it step by step. * * Line 1 Initializes the return ti value to failure (0); * Lines 2-3 Validates that the 1st-5th characters are ``From ''. * Lines 4-6 Validates that there is an end of line and points x at it. * Lines 7-14 First checks to see if the line is at least 41 characters long. * If so, it scans backwards to find the rightmost space. From * that point, it scans backwards to see if the string matches * `` remote from''. If so, it sets x to point to the space at * the start of the string. * Line 15 Makes sure that there are at least 27 characters in the line. * Lines 16-21 Checks if the date/time ends with the year (there is a space * five characters back). If there is a colon three characters * further back, there is no timezone field, so zn is set to 0 * and ti is set in front of the year. Otherwise, there must * either to be a space four characters back for a three-letter * timezone, or a space six characters back followed by a + or - * for a numeric timezone; in either case, zn and ti become the * offset of the space immediately before it. * Lines 22-24 Are the failure case for line 14. If there is a space four * characters back, it is a three-letter timezone; there must be a * space for the year nine characters back. zn is the zone * offset; ti is the offset of the space. * Lines 25-28 Are the failure case for line 20. If there is a space six * characters back, it is a numeric timezone; there must be a * space eleven characters back and a + or - five characters back. * zn is the zone offset; ti is the offset of the space. * Line 29-32 If ti is valid, make sure that the string before ti is of the * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise * invalidate ti. There must be a colon three characters back * and a space six or nine characters back (depending upon * whether or not the character six characters back is a colon). * There must be a space three characters further back (in front * of the day), one seven characters back (in front of the month), * and one eleven characters back (in front of the day of week). * ti is set to be the offset of the space before the time. * * Why a macro? It gets invoked a *lot* in a tight loop. On some of the * newer pipelined machines it is faster being open-coded than it would be if * subroutines are called. * * Why does it scan backwards from the end of the line, instead of doing the * much easier forward scan? There is no deterministic way to parse the * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to * see if unquoted spaces were possible. They are, and I've encountered enough * evil mail to be totally unwilling to trust that ``it will never happen''. */ /* Build parameters */ #define KODRETRY 15 /* kiss-of-death retry in seconds */ #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define CHUNK 16384 /* read-in chunk size */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/utime.c000066400000000000000000000017741137544547100231160ustar00rootroot00000000000000/* * Program: BSD utime() emulator * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 10 October 1996 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #undef utime /* Portable utime() that takes it args like real Unix systems * Accepts: file path * traditional utime() argument * Returns: utime() results */ int portable_utime (char *file,time_t timep[2]) { struct utimbuf times; /* in case there's other cruft there */ memset (×,0,sizeof (struct utimbuf)); times.actime = timep[0]; /* copy the portable values */ times.modtime = timep[1]; return utime (file,×); /* now call the SVR4 routine */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/unix/write.c000066400000000000000000000030231137544547100231120ustar00rootroot00000000000000/* * Program: Write data, treating partial writes as an error * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 26 May 1995 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* The whole purpose of this unfortunate routine is to deal with DOS and * certain cretinous versions of UNIX which decided that the "bytes actually * written" return value from write() gave them license to use that for things * that are really errors, such as disk quota exceeded, maximum file size * exceeded, disk full, etc. * * BSD won't screw us this way on the local filesystem, but who knows what * some NFS-mounted filesystem will do. */ #undef write /* Write data to file * Accepts: file descriptor * I/O vector structure * number of vectors in structure * Returns: number of bytes written if successful, -1 if failure */ long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1); long safe_write (int fd,char *buf,long nbytes) { long i,j; if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) { while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) && (errno == EINTR)); if (j < 0) return j; } return nbytes; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/000077500000000000000000000000001137544547100214405ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/build.com000066400000000000000000000051721137544547100232440ustar00rootroot00000000000000$! Program: Portable c-client build for VMS $! $! Author: Mark Crispin $! Networks and Distributed Computing $! Computing & Communications $! University of Washington $! Administration Building, AG-44 $! Seattle, WA 98195 $! Internet: MRC@CAC.Washington.EDU $! $! Date: 2 August 1994 $! Last Edited: 6 February 2004 $! $! The IMAP toolkit provided in this Distribution is $! Copyright 2004 University of Washington. $! $! The full text of our legal notices is contained in the file called $! CPYRIGHT, included with this Distribution. $! $! $! Change this to your local timezone. This value is the number of minutes $! east of UTC (formerly known as GMT). Sample values: -300 (US east coast), $! -480 (US west coast), 540 (Japan), 60 (western Europe). $! VAX C's HELP information says that you should be able to use gmtime(), but $! it returns 0 for the struct. ftime(), you ask? It, too, returns 0 for a $! timezone. Nothing sucks like a VAX! $! $ CC_TIMEZONE=-480 $! $! CC options $! $ CC_PREF = "/OPTIMIZE/INCLUDE=[]" $ CC_PREF = CC_PREF + "/DEFINE=net_getbuffer=NET_GETBUF" $ CC_PREF = CC_PREF + "/DEFINE=LOCALTIMEZONE='CC_TIMEZONE'" $! $! Determine TCP type $! $ TCP_TYPE = "VMSN" ! default to none $ IF F$LOCATE("MULTINET", P1) .LT. F$LENGTH(P1) $ THEN $ DEFINE SYS MULTINET_ROOT:[MULTINET.INCLUDE.SYS],sys$library $ DEFINE NETINET MULTINET_ROOT:[MULTINET.INCLUDE.NETINET] $ DEFINE ARPA MULTINET_ROOT:[MULTINET.INCLUDE.ARPA] $ TCP_TYPE = "VMSM" ! Multinet $ LINK_OPT = ",LINK_MNT/OPTION" $ ENDIF $ IF F$LOCATE("NETLIB", P1) .LT. F$LENGTH(P1) $ THEN $ DEFINE SYS SYS$LIBRARY: ! normal .H location $ DEFINE NETINET SYS$LIBRARY: $ DEFINE ARPA SYS$LIBRARY: $ LINK_OPT = ",LINK_NLB/OPTION" $ TCP_TYPE = "VMSL" ! NETLIB $ ENDIF $ IF TCP_TYPE .EQS. "VMSN" $ THEN $ DEFINE SYS SYS$LIBRARY: ! normal .H location $ DEFINE NETINET SYS$LIBRARY: $ DEFINE ARPA SYS$LIBRARY: $ LINK_OPT = "" $ ENDIF $! $ COPY TCP_'TCP_TYPE'.C TCP_VMS.C; $! $ COPY OS_VMS.H OSDEP.H; $ SET VERIFY $ CC'CC_PREF' MAIL $ CC'CC_PREF' IMAP4R1 $ CC'CC_PREF' SMTP $ CC'CC_PREF' NNTP $ CC'CC_PREF' POP3 $ CC'CC_PREF' DUMMYVMS $ CC'CC_PREF' RFC822 $ CC'CC_PREF' MISC $ CC'CC_PREF' OS_VMS $ CC'CC_PREF' SMANAGER $ CC'CC_PREF' FLSTRING $ CC'CC_PREF' NEWSRC $ CC'CC_PREF' NETMSG $ CC'CC_PREF' UTF8 $ CC'CC_PREF' MTEST $ CC'CC_PREF' MAILUTIL $! $ LINK MTEST,OS_VMS,MAIL,IMAP4R1,SMTP,NNTP,POP3,DUMMYVMS,RFC822,MISC,UTF8,- SMANAGER,FLSTRING,NEWSRC,NETMSG,SYS$INPUT:/OPTION'LINK_OPT',LINK/OPTION PSECT=_CTYPE_,NOWRT $ LINK MAILUTIL,OS_VMS,MAIL,IMAP4R1,SMTP,NNTP,POP3,DUMMYVMS,RFC822,MISC,UTF8,- SMANAGER,FLSTRING,NEWSRC,NETMSG,SYS$INPUT:/OPTION'LINK_OPT',LINK/OPTION PSECT=_CTYPE_,NOWRT $ SET NOVERIFY $ EXIT tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/clean.com000066400000000000000000000011441137544547100232220ustar00rootroot00000000000000$! Program: Portable c-client cleanup for VMS $! $! Author: Mark Crispin $! Networks and Distributed Computing $! Computing & Communications $! University of Washington $! Administration Building, AG-44 $! Seattle, WA 98195 $! Internet: MRC@CAC.Washington.EDU $! $! Date: 14 June 1995 $! Last Edited: 6 February 2004 $! $! The IMAP toolkit provided in this Distribution is $! Copyright 2004 University of Washington. $! $! The full text of our legal notices is contained in the file called $! CPYRIGHT, included with this Distribution. $! $ DELETE *.OBJ;*,OSDEP.*;*,TCP_VMS.C;*,MTEST.EXE;*,MAILUTIL.EXE;* tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/dummy.h000066400000000000000000000021051137544547100227420ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/dummyvms.c000066400000000000000000000160701137544547100234710ustar00rootroot00000000000000/* * Program: Dummy routines for VMS * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 May 1993 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include "mail.h" #include "osdep.h" #include "dummy.h" #include "misc.h" /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char tmp[MAILTMPLEN]; /* must be valid local mailbox */ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ? &dummydriver : NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { return NIL; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { /* return silently */ } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * driver type to use * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { return NIL; /* always fails */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; /* OP_PROTOTYPE call or silence */ if (!stream || stream->silent) return NIL; if (compare_cstring (stream->mailbox,"INBOX")) { sprintf (tmp,"Not a mailbox: %s",stream->mailbox); mm_log (tmp,ERROR); return NIL; /* always fails */ } if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ long dummy_ping (MAILSTREAM *stream) { return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * append callback function * data for callback * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { char tmp[MAILTMPLEN]; sprintf (tmp,"Can't append to %s",mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { if (*pat == '{' || (ref && (*ref == '{'))) return NIL; /* write name with reference */ if (ref && *ref) sprintf (tmp,"%s%s",ref,pat); else strcpy (tmp,pat); /* ignore reference, only need mailbox name */ return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/env_vms.c000066400000000000000000000072561137544547100232730ustar00rootroot00000000000000/* * Program: VMS environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 August 1994 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static char *myUserName = NIL; /* user name */ static char *myLocalHost = NIL; /* local host name */ static char *myHomeDir = NIL; /* home directory name */ static char *myNewsrc = NIL; /* newsrc file name */ #include "pmatch.c" /* include wildcard pattern matcher */ /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_USERNAME: myUserName = cpystr ((char *) value); case GET_USERNAME: ret = (void *) myUserName; break; case SET_HOMEDIR: myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: if (!myNewsrc) { /* set news file name if not defined */ char tmp[MAILTMPLEN]; sprintf (tmp,"%s:.newsrc",myhomedir ()); myNewsrc = cpystr (tmp); } ret = (void *) myNewsrc; break; } return ret; } /* Write current time * Accepts: destination string * optional format of day-of-week prefix * format of date and time */ static void do_date (char *date,char *prefix,char *fmt) { time_t tn = time (0); struct tm *t = localtime (&tn); int zone = LOCALTIMEZONE + (t->tm_isdst ? 60 : 0); if (prefix) { /* want day of week? */ sprintf (date,prefix,days[t->tm_wday]); date += strlen (date); /* make next sprintf append */ } /* output the date */ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d"); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d"); } /* Return my user name * Returns: my user name */ char *myusername () { struct stat sbuf; char tmp[MAILTMPLEN]; if (!myUserName) { /* get user name if don't have it yet */ myUserName = cpystr (cuserid (NIL)); myHomeDir = cpystr ("SYS$LOGIN"); } return myUserName; } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { if (!myHomeDir) myusername ();/* initialize if first time */ return myHomeDir; } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { return NIL; /* no default prototype */ } /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ void syslog (int priority,const char *message,...) { } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ void openlog (const char *ident,int logopt,int facility) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/env_vms.h000066400000000000000000000033271137544547100232730ustar00rootroot00000000000000/* * Program: VMS environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 August 1994 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\SUBSCRIPTIONS.TXT",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\SUBSCRIPTIONS.TMP",myhomedir ()) /* Function prototypes */ #include "env.h" char *myusername (); /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ void openlog (const char *ident,int logopt,int facility); void syslog (int priority,const char *message,...); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/fs_vms.c000066400000000000000000000022341137544547100231020ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { free (*block); *block = NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/ftl_vms.c000066400000000000000000000013271137544547100232610ustar00rootroot00000000000000/* * Program: DOS/VMS/TOPS-20 crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/link.opt000066400000000000000000000000301137544547100231120ustar00rootroot00000000000000SYS$SHARE:VAXCRTL/SHARE tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/link_mnt.opt000066400000000000000000000000471137544547100240000ustar00rootroot00000000000000MULTINET:MULTINET_SOCKET_LIBRARY/SHARE tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/link_nlb.opt000066400000000000000000000000211137544547100237450ustar00rootroot00000000000000NETLIB_SHR/SHARE tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/linkage.c000066400000000000000000000017001137544547100232140ustar00rootroot00000000000000/* * Program: Default driver linkage * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 7 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ mail_link (&imapdriver); /* link in the imap driver */ mail_link (&nntpdriver); /* link in the nntp driver */ mail_link (&pop3driver); /* link in the pop3 driver */ mail_link (&dummydriver); /* link in the dummy driver */ auth_link (&auth_md5); /* link in the md5 authenticator */ auth_link (&auth_pla); /* link in the plain authenticator */ auth_link (&auth_log); /* link in the log authenticator */ tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/linkage.h000066400000000000000000000013401137544547100232210ustar00rootroot00000000000000/* * Program: Default driver linkage * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 13 June 1995 * Last Edited: 7 February 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ extern DRIVER imapdriver; extern DRIVER nntpdriver; extern DRIVER pop3driver; extern DRIVER dummydriver; extern AUTHENTICATOR auth_log; extern AUTHENTICATOR auth_md5; extern AUTHENTICATOR auth_pla; tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/nl_vms.c000066400000000000000000000045311137544547100231050ustar00rootroot00000000000000/* * Program: UNIX/VMS newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { long i = srcl * 2,j; unsigned char c,*d = src; if (*dst) { /* candidate destination provided? */ /* count NLs if doesn't fit worst-case */ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++; /* still too small, must reset destination */ if (i > *dstl) fs_give ((void **) dst); } /* make a new buffer if needed */ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1); d = *dst; /* destination string */ if (srcl) do { /* main copy loop */ if ((c = *src++) < '\016') { /* prepend CR to LF */ if (c == '\012') *d++ = '\015'; /* unlikely CR */ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) { *d++ = c; /* copy the CR */ c = *src++; /* grab the LF */ --srcl; /* adjust the count */ } } *d++ = c; /* copy character */ } while (--srcl); *d = '\0'; /* tie off destination */ return d - *dst; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { unsigned long pos = GETPOS (s); unsigned long i = SIZE (s); unsigned long j = i; while (j--) switch (SNX (s)) {/* search for newlines */ case '\015': /* unlikely carriage return */ if (j && (CHR (s) == '\012')) { SNX (s); /* eat the line feed */ j--; } break; case '\012': /* line feed? */ i++; default: /* ordinary chararacter */ break; } SETPOS (s,pos); /* restore old position */ return i; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/os_vms.c000066400000000000000000000030771137544547100231210ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- VMS version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 August 1994 * Last Edited: 7 April 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_vms.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include #include #include extern int errno; /* just in case */ #include "misc.h" #include "fs_vms.c" #include "ftl_vms.c" #include "nl_vms.c" #include "env_vms.c" #include "tcp_vms.c" #define server_login(user,pass,authuser,argc,argv) NIL #define authserver_login(user,authuser,argc,argv) NIL #define myusername() "" /* dummy definition to prevent build errors */ #define MD5ENABLE "" #include "auth_md5.c" #include "auth_pla.c" #include "auth_log.c" /* Emulator for UNIX getpass() call * Accepts: prompt * Returns: password */ #define PWDLEN 128 /* used by Linux */ char *getpass (const char *prompt) { char *s; static char pwd[PWDLEN]; fputs (prompt,stdout); fgets (pwd,PWDLEN-1,stdin); if (s = strchr (pwd,'\n')) *s = '\0'; return pwd; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/os_vms.h000066400000000000000000000016671137544547100231310ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- VMS version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 August 1994 * Last Edited: 1 May 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END #include "env_vms.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #define gethostid clock #define random rand #define unlink delete char *getpass (const char *prompt); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/pmatch.c000066400000000000000000000053271137544547100230670ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-independent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/tcp_vms.h000066400000000000000000000017761137544547100232770ustar00rootroot00000000000000/* * Program: VMS TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer */ #define BUFLEN 8192 /* TCP I/O stream */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ int tcpsi; /* input socket */ int tcpso; /* output socket */ int ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; /* Local function prototypes */ long tcp_abort (TCPSTREAM *stream); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/tcp_vmsl.c000066400000000000000000000212431137544547100234350ustar00rootroot00000000000000/* * Program: VMS TCP/IP routines for Netlib. * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 August 1994 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Thanks to Yehavi Bourvine at The Hebrew University of Jerusalem who contributed the original VMS code */ #include /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { return NIL; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; unsigned long sock; int status; char tmp[MAILTMPLEN]; /* hostname to connect to */ struct dsc$descriptor HostDesc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL }; port &= 0xffff; /* erase flags */ /* assign a local socket */ if (!((status = net_assign (&sock)) & 0x1)) { sprintf (tmp,"Unable to assign to net, status=%d",status); mm_log (tmp,ERROR); return NIL; } if (!((status = net_bind (&sock,1)) & 0x1)) { sprintf (tmp,"Unable to create local socket, status=%d",status); mm_log (tmp,ERROR); return NIL; } /* open connection */ HostDesc.dsc$w_length = strlen (host); HostDesc.dsc$a_pointer = host; if (!((status = tcp_connect (&sock,&HostDesc,port)) & 0x1)) { sprintf (tmp,"Can't connect to %.80s,%lu: %s",host,port,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* create TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); stream->host = cpystr (host); /* copy official host name */ /* copy local host name */ stream->localhost = cpystr (mylocalhost ()); stream->port = port; /* copy port number */ /* init sockets */ stream->tcpsi = stream->tcpso = sock; stream->ictr = 0; /* init input counter */ return stream; /* return success */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) return NIL; /* special case of newline broken by buffer */ if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; char *bufptr = buffer; while (size > 0) { /* until request satisfied */ if (!tcp_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (bufptr,stream->iptr,n); bufptr += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } bufptr[0] = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { char tmp[MAILTMPLEN]; int i,status; /* Note: the doc says we need here dynamic descriptor, but we need static * one... */ struct dsc$descriptor BufDesc = {BUFLEN,DSC$K_DTYPE_T,DSC$K_CLASS_S, stream->ibuf}; static short iosb[4]; if (stream->tcpsi < 0) return NIL; while (stream->ictr < 1) { /* if nothing in the buffer */ if (!((status = tcp_receive(&(stream->tcpsi), &BufDesc, iosb)) & 0x1)) { sprintf (tmp,"Error reading from TcpIp/NETLIB, status=%d",status); mm_log (tmp,ERROR); return tcp_abort (stream); } if (iosb[1] > BUFLEN) i = BUFLEN; else i = iosb[1]; if (i < 1) return tcp_abort (stream); stream->ictr = i; /* set new byte count */ stream->iptr = stream->ibuf;/* point at TCP buffer */ } return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int status; struct dsc$descriptor_s BufDesc = {strlen(string),DSC$K_DTYPE_T, DSC$K_CLASS_S,string }; /* 2 = Do not add \r\n */ return ((status = tcp_send (&(stream->tcpso),&BufDesc,2)) & 0x1) ? T : tcp_abort (stream); } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (stream); /* nuke the stream */ /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { if (stream->tcpsi >= 0) { /* no-op if no socket */ /* nuke the socket */ tcp_disconnect (&(stream->tcpsi)); stream->tcpsi = stream->tcpso = -1; } return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* Return my local host name * Returns: my local host name */ char *mylocalhost () { int status; char tmp[MAILTMPLEN]; if (!myLocalHost) { /* have local host yet? */ /* receives local host name */ struct dsc$descriptor LocalhostDesc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,NULL}; if (!((status = net_get_hostname (&LocalhostDesc)) & 0x1)) { sprintf (tmp,"Can't get local hostname, status=%d",status); mm_log (tmp,ERROR); return "UNKNOWN"; } strncpy (tmp,LocalhostDesc.dsc$a_pointer,LocalhostDesc.dsc$w_length); tmp[LocalhostDesc.dsc$w_length] = '\0'; str$free1_dx (&LocalhostDesc); myLocalHost = cpystr (tmp); } return myLocalHost; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { return name; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/tcp_vmsm.c000066400000000000000000000273111137544547100234400ustar00rootroot00000000000000/* * Program: VMS TCP/IP routines for Multinet * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 August 1994 * Last Edited: 7 June 2002 * * The IMAP toolkit provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_read = 0; /* TCP timeouts, in seconds */ static long ttmo_write = 0; /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; int sock; char *s; struct sockaddr_in sin; struct hostent *host_name; char hostname[MAILTMPLEN]; char tmp[MAILTMPLEN]; struct protoent *pt = getprotobyname ("tcp"); struct servent *sv = NIL; port &= 0xffff; /* erase flags */ if (service) { /* service specified? */ if (*service == '*') { /* yes, special alt driver kludge? */ sv = getservbyname (service + 1,"tcp"); } else sv = getservbyname (service,"tcp"); } /* user service name port */ if (sv) port = ntohs (sin.sin_port = sv->s_port); /* copy port number in network format */ else sin.sin_port = htons (port); /* The domain literal form is used (rather than simply the dotted decimal as with other Unix programs) because it has to be a valid "host name" in mailsystem terminology. */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (hostname,host+1); /* yes, copy number part */ hostname[(strlen (hostname))-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) { sin.sin_family = AF_INET; /* family is always Internet */ strcpy (hostname,host); /* hostname is user's argument */ } else { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else { /* lookup host name, note that brain-dead Unix requires lowercase! */ strcpy (hostname,host); /* in case host is in write-protected memory */ if ((host_name = gethostbyname (lcase (hostname)))) { /* copy address type */ sin.sin_family = host_name->h_addrtype; /* copy host name */ strcpy (hostname,host_name->h_name); /* copy host addresses */ memcpy (&sin.sin_addr,host_name->h_addr,host_name->h_length); } else { sprintf (tmp,"No such host as %.80s",host); mm_log (tmp,ERROR); return NIL; } } /* get a TCP stream */ if ((sock = socket (sin.sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) { sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* open connection */ if (connect (sock,(struct sockaddr *)&sin,sizeof (sin)) < 0) { sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port, strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* create TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); /* copy official host name */ stream->host = cpystr (hostname); /* get local name */ gethostname (tmp,MAILTMPLEN-1); stream->localhost = cpystr ((host_name = gethostbyname (tmp)) ? host_name->h_name : tmp); /* init sockets */ stream->port = port; /* port number */ stream->tcpsi = stream->tcpso = sock; stream->ictr = 0; /* init input counter */ return stream; /* return success */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; char *bufptr = buffer; while (size > 0) { /* until request satisfied */ if (!tcp_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (bufptr,stream->iptr,n); bufptr += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } bufptr[0] = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); if (stream->tcpsi < 0) return NIL; while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_read; /* read timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */ FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */ errno = NIL; /* block and read */ while (((i = select (getdtablesize (),&fds,0,&efds,ttmo_read ? &tmo:0))<0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = socket_read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); if (i < 1) return tcp_abort (stream); stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ } return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; fd_set fds; struct timeval tmo; time_t t = time (0); if (stream->tcpso < 0) return NIL; while (size > 0) { /* until request satisfied */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_write; /* write timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */ errno = NIL; /* block and write */ while (((i = select (getdtablesize (),0,&fds,0,ttmo_write ? &tmo : 0)) < 0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = socket_write (stream->tcpso,string,size)) < 0) && (errno == EINTR)); if (i < 0) return tcp_abort (stream); size -= i; /* how much we sent */ string += i; } return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (stream); /* nuke the stream */ /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { int i; if (stream->tcpsi >= 0) { /* no-op if no socket */ /* nuke the socket */ socket_close (stream->tcpsi); if (stream->tcpsi != stream->tcpso) socket_close (stream->tcpso); stream->tcpsi = stream->tcpso = -1; } return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* Return my local host name * Returns: my local host name */ char *mylocalhost () { char tmp[MAILTMPLEN]; struct hostent *hn; if (!myLocalHost) { /* have local host yet? */ gethostname(tmp,MAILTMPLEN);/* get local host name */ myLocalHost = cpystr ((hn = gethostbyname (tmp)) ? hn->h_name : tmp); } return myLocalHost; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { char host[MAILTMPLEN]; struct hostent *he; /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name; /* note that Unix requires lowercase! */ else return (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/vms/tcp_vmsn.c000066400000000000000000000073421137544547100234430ustar00rootroot00000000000000/* * Program: Dummy VMS TCP/IP routines for non-TCP/IP systems * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 2 August 1994 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { return NIL; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { char tmp[MAILTMPLEN]; port &= 0xffff; /* erase flags */ if (port) sprintf (tmp,"Can't connect to %.80s,%d: no TCP",host,port); else sprintf (tmp,"Can't connect to %.80s,%s: no TCP",host,service); mm_log (tmp,ERROR); return NIL; } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { return NIL; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { return NIL; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { return NIL; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return NIL; } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { return NIL; } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return NIL; } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return NIL; } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { return NIL; } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return 0xffffffff; /* return port number */ } /* Return my local host name * Returns: my local host name */ char *mylocalhost () { /* have local host yet? */ if (!myLocalHost) myLocalHost = cpystr (getenv ("SYS$NODE")); return myLocalHost; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { return name; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/000077500000000000000000000000001137544547100214115ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/drivers.bat000066400000000000000000000013421137544547100235570ustar00rootroot00000000000000@ECHO OFF REM Program: Driver Linkage Generator for DOS/NT REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 11 October 1989 REM Last Edited: 6 July 2004 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 1988-2004 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Erase old driver linkage IF EXIST LINKAGE.* DEL LINKAGE.* REM Now define the new list FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D EXIT 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/drivraux.bat000066400000000000000000000015631137544547100237520ustar00rootroot00000000000000@ECHO OFF REM Program: Driver Linkage Generator auxillary for NT/Win9x REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 11 October 1989 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. ECHO extern DRIVER %1driver; >> LINKAGE.H REM Note the introduction of the caret to quote the ampersand in NT if "%OS%" == "Windows_NT" ECHO mail_link (^&%1driver); /* link in the %1 driver */ >> LINKAGE.C if "%OS%" == "" ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/dummy.h000066400000000000000000000021051137544547100227130ustar00rootroot00000000000000/* * Program: Dummy routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 9 May 1991 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Exported function prototypes */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void dummy_list (MAILSTREAM *stream,char *ref,char *pat); void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat); long dummy_create (MAILSTREAM *stream,char *mailbox); long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode); long dummy_delete (MAILSTREAM *stream,char *mailbox); long dummy_rename (MAILSTREAM *stream,char *old,char *newname); char *dummy_file (char *dst,char *name); long dummy_canonicalize (char *tmp,char *ref,char *pat); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/dummywce.c000066400000000000000000000201061137544547100234060ustar00rootroot00000000000000/* * Program: Dummy routines for WCE * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 24 May 1993 * Last Edited: 14 October 2003 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include #include #include #include "mail.h" #include "osdep.h" #include #include #include "dummy.h" #include "misc.h" /* Function prototypes */ DRIVER *dummy_valid (char *name); void *dummy_parameters (long function,void *value); MAILSTREAM *dummy_open (MAILSTREAM *stream); void dummy_close (MAILSTREAM *stream,long options); long dummy_ping (MAILSTREAM *stream); void dummy_check (MAILSTREAM *stream); void dummy_expunge (MAILSTREAM *stream); long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); /* Dummy routines */ /* Driver dispatch used by MAIL */ DRIVER dummydriver = { "dummy", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ dummy_valid, /* mailbox is valid for us */ dummy_parameters, /* manipulate parameters */ dummy_scan, /* scan mailboxes */ dummy_list, /* list mailboxes */ dummy_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ dummy_create, /* create mailbox */ dummy_delete, /* delete mailbox */ dummy_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ dummy_open, /* open mailbox */ dummy_close, /* close mailbox */ NIL, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message structure */ NIL, /* fetch header */ NIL, /* fetch text */ NIL, /* fetch message data */ NIL, /* unique identifier */ NIL, /* message number from UID */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ dummy_ping, /* ping mailbox to see if still alive */ dummy_check, /* check for new messages */ dummy_expunge, /* expunge deleted messages */ dummy_copy, /* copy messages to another mailbox */ dummy_append, /* append string message to mailbox */ NIL /* garbage collect stream */ }; /* prototype stream */ MAILSTREAM dummyproto = {&dummydriver}; /* driver parameters */ static char *file_extension = NIL; /* Dummy validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ DRIVER *dummy_valid (char *name) { char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ return (name && *name && (*name != '{') && (s = mailboxfile (tmp,name)) && (!*s || !stat (s,&sbuf))) ? &dummydriver : NIL; } /* Dummy manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *dummy_parameters (long function,void *value) { return value; } /* Dummy scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */ void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) { /* return silently */ } /* Dummy list mailboxes * Accepts: mail stream * reference * pattern to search */ void dummy_list (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy list subscribed mailboxes * Accepts: mail stream * pattern to search */ void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat) { /* return silently */ } /* Dummy create mailbox * Accepts: mail stream * mailbox name to create * Returns: T on success, NIL on failure */ long dummy_create (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Dummy delete mailbox * Accepts: mail stream * mailbox name to delete * Returns: T on success, NIL on failure */ long dummy_delete (MAILSTREAM *stream,char *mailbox) { return NIL; /* always fails */ } /* Mail rename mailbox * Accepts: mail stream * old mailbox name * new mailbox name * Returns: T on success, NIL on failure */ long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { return NIL; /* always fails */ } /* Dummy open * Accepts: stream to open * Returns: stream on success, NIL on failure */ MAILSTREAM *dummy_open (MAILSTREAM *stream) { char tmp[MAILTMPLEN]; /* OP_PROTOTYPE call or silence */ if (!stream || stream->silent) return NIL; if (compare_cstring (stream->mailbox,"INBOX")) { sprintf (tmp,"Not a mailbox: %s",stream->mailbox); mm_log (tmp,ERROR); return NIL; /* always fails */ } if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ } /* Dummy close * Accepts: MAIL stream * options */ void dummy_close (MAILSTREAM *stream,long options) { /* return silently */ } /* Dummy ping mailbox * Accepts: MAIL stream * Returns: T if stream alive, else NIL * No-op for readonly files, since read/writer can expunge it from under us! */ long dummy_ping (MAILSTREAM *stream) { return T; } /* Dummy check mailbox * Accepts: MAIL stream * No-op for readonly files, since read/writer can expunge it from under us! */ void dummy_check (MAILSTREAM *stream) { dummy_ping (stream); /* invoke ping */ } /* Dummy expunge mailbox * Accepts: MAIL stream */ void dummy_expunge (MAILSTREAM *stream) { /* return silently */ } /* Dummy copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * options * Returns: T if copy successful, else NIL */ long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) { if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy"); return NIL; } /* Dummy append message string * Accepts: mail stream * destination mailbox * stringstruct of message to append * Returns: T on success, NIL on failure */ long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { char tmp[MAILTMPLEN]; sprintf (tmp,"Can't append to %s",mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } /* Dummy canonicalize name * Accepts: buffer to write name * reference * pattern * Returns: T if success, NIL if failure */ long dummy_canonicalize (char *tmp,char *ref,char *pat) { char dev[4]; /* initially no device */ dev[0] = dev[1] = dev[2] = dev[3] = '\0'; if (ref) switch (*ref) { /* preliminary reference check */ case '{': /* remote names not allowed */ return NIL; /* disallowed */ case '\0': /* empty reference string */ break; default: /* all other names */ if (ref[1] == ':') { /* start with device name? */ dev[0] = *ref++; dev[1] = *ref++; } break; } if (pat[1] == ':') { /* device name in pattern? */ dev[0] = *pat++; dev[1] = *pat++; ref = NIL; /* ignore reference */ } switch (*pat) { case '#': /* namespace names */ if (mailboxfile (tmp,pat)) strcpy (tmp,pat); else return NIL; /* unknown namespace */ break; case '{': /* remote names not allowed */ return NIL; case '\\': /* rooted name */ ref = NIL; /* ignore reference */ break; } /* make sure device names are rooted */ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\'; /* build name */ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat); ucase (tmp); /* force upper case */ return T; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/env_wce.c000066400000000000000000000171651137544547100232150ustar00rootroot00000000000000/* * Program: WCE environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 8 July 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ static char *myUserName = NIL; /* user name */ static char *myLocalHost = NIL; /* local host name */ static char *myClientHost = NIL;/* client host name */ static char *myServerHost = NIL;/* server host name */ static char *myHomeDir = NIL; /* home directory name */ static char *myNewsrc = NIL; /* newsrc file name */ static char *sysInbox = NIL; /* system inbox name */ static long list_max_level = 5; /* maximum level of list recursion */ static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ /* home namespace */ static NAMESPACE nshome = {"",'\\',NIL,NIL}; /* namespace list */ static NAMESPACE *nslist[3] = {&nshome,NIL,NIL}; static long alarm_countdown = 0;/* alarm count down */ static void (*alarm_rang) (); /* alarm interrupt function */ static unsigned int rndm = 0; /* initial `random' number */ /* Dummy definitions to prevent errors */ #define server_login(user,pass,authuser,argc,argv) NIL #define authserver_login(user,authuser,argc,argv) NIL #define myusername() "" #define MD5ENABLE "\\.nosuch.." #include "pmatch.c" /* include wildcard pattern matcher */ /* Environment manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *env_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case GET_NAMESPACE: ret = (void *) nslist; break; case SET_HOMEDIR: myHomeDir = cpystr ((char *) value); case GET_HOMEDIR: ret = (void *) myHomeDir; break; case SET_LOCALHOST: myLocalHost = cpystr ((char *) value); case GET_LOCALHOST: ret = (void *) myLocalHost; break; case SET_NEWSRC: if (myNewsrc) fs_give ((void **) &myNewsrc); myNewsrc = cpystr ((char *) value); case GET_NEWSRC: if (!myNewsrc) { /* set news file name if not defined */ char tmp[MAILTMPLEN]; sprintf (tmp,"%s\\NEWSRC",myhomedir ()); myNewsrc = cpystr (tmp); } ret = (void *) myNewsrc; break; case SET_SYSINBOX: if (sysInbox) fs_give ((void **) &sysInbox); sysInbox = cpystr ((char *) value); case GET_SYSINBOX: ret = (void *) sysInbox; break; case SET_LISTMAXLEVEL: list_max_level = (long) value; case GET_LISTMAXLEVEL: ret = (void *) list_max_level; break; case SET_DISABLE822TZTEXT: no822tztext = value ? T : NIL; case GET_DISABLE822TZTEXT: ret = (void *) (no822tztext ? VOIDT : NIL); break; } return ret; } /* Write current time * Accepts: destination string * optional format of day-of-week prefix * format of date and time * flag whether to append symbolic timezone */ static void do_date (char *date,char *prefix,char *fmt,int suffix) { time_t tn = time (0); struct tm *t = gmtime (&tn); int zone = t->tm_hour * 60 + t->tm_min; int julian = t->tm_yday; t = localtime (&tn); /* get local time now */ /* minus UTC minutes since midnight */ zone = t->tm_hour * 60 + t->tm_min - zone; /* julian can be one of: * 36x local time is December 31, UTC is January 1, offset -24 hours * 1 local time is 1 day ahead of UTC, offset +24 hours * 0 local time is same day as UTC, no offset * -1 local time is 1 day behind UTC, offset -24 hours * -36x local time is January 1, UTC is December 31, offset +24 hours */ if (julian = t->tm_yday -julian) zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; if (prefix) { /* want day of week? */ sprintf (date,prefix,days[t->tm_wday]); date += strlen (date); /* make next sprintf append */ } /* output the date */ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); if (suffix) { /* append timezone suffix if desired */ char *tz; tzset (); /* get timezone from TZ environment stuff */ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]; if (tz && tz[0]) sprintf (date + strlen (date)," (%s)",tz); } } /* Write current time in RFC 822 format * Accepts: destination string */ void rfc822_date (char *date) { do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d", no822tztext ? NIL : T); } /* Write current time in internal format * Accepts: destination string */ void internal_date (char *date) { do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL); } /* Return random number */ long random () { if (!rndm) srand (rndm = (unsigned) time (0L)); return (long) rand (); } /* Return default drive * Returns: default drive */ static char *defaultDrive (void) { char *s; return ((s = getenv ("SystemDrive")) && *s) ? s : "C:"; } /* Return home drive from environment variables * Returns: home drive */ static char *homeDrive (void) { char *s; return ((s = getenv ("HOMEDRIVE")) && *s) ? s : defaultDrive (); } /* Return home path from environment variables * Accepts: path to write into * Returns: home path or NIL if it can't be determined */ static char *homePath (char *path) { int i; char *s; if (!((s = getenv ("HOMEPATH")) && (i = strlen (s)))) return NIL; if (((s[i-1] == '\\') || (s[i-1] == '/'))) s[i-1] = '\0'; sprintf (path,"%s%s",homeDrive (),s); return path; } /* Return my home directory name * Returns: my home directory name */ char *myhomedir () { char tmp[MAILTMPLEN]; /* initialize if first time */ if (!myHomeDir) myHomeDir = homePath (tmp); return myHomeDir ? myHomeDir : homeDrive (); } /* Return system standard INBOX * Accepts: buffer string */ char *sysinbox () { char tmp[MAILTMPLEN]; if (!sysInbox) { /* initialize if first time */ sprintf (tmp,"%s\\INBOX",myhomedir ()); sysInbox = cpystr (tmp); /* system inbox is from mail spool */ } return sysInbox; } /* Return mailbox file name * Accepts: destination buffer * mailbox name * Returns: file name */ char *mailboxfile (char *dst,char *name) { char *dir = myhomedir (); *dst = '\0'; /* default to empty string */ if (((name[0] == 'I') || (name[0] == 'i')) && ((name[1] == 'N') || (name[1] == 'n')) && ((name[2] == 'B') || (name[2] == 'b')) && ((name[3] == 'O') || (name[3] == 'o')) && ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) name = NIL; /* reject namespace names or names with / */ if (name && ((*name == '#') || strchr (name,'/'))) return NIL; else if (!name) return dst; /* driver selects the INBOX name */ /* absolute path name? */ else if ((*name == '\\') || (name[1] == ':')) return strcpy (dst,name); /* build resulting name */ sprintf (dst,"%s\\%s",dir,name); return dst; /* return it */ } /* Determine default prototype stream to user * Accepts: type (NIL for create, T for append) * Returns: default prototype stream */ MAILSTREAM *default_proto (long type) { extern MAILSTREAM CREATEPROTO,APPENDPROTO; return type ? &APPENDPROTO : &CREATEPROTO; } /* Emulator for BSD syslog() routine * Accepts: priority * message * parameters */ void syslog (int priority,const char *message,...) { } /* Emulator for BSD openlog() routine * Accepts: identity * options * facility */ void openlog (const char *ident,int logopt,int facility) { } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/env_wce.h000066400000000000000000000037661137544547100232240ustar00rootroot00000000000000/* * Program: WCE environment routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\MAILBOX.LST",myhomedir ()) #define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\MAILBOX.TMP",myhomedir ()) #define L_SET SEEK_SET /* Function prototypes */ #include "env.h" static char *defaultDrive (void); static char *homeDrive (void); static char *homePath (char *path); char *sysinbox (); long random (); unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src, unsigned long srcl); unsigned long unix_crlflen (STRING *s); #define getpid random /* syslog() emulation */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until syslog() is called */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */ void openlog (const char *ident,int logopt,int facility); void syslog (int priority,const char *message,...); tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/fs_wce.c000066400000000000000000000022341137544547100230240ustar00rootroot00000000000000/* * Program: Free storage management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Get a block of free storage * Accepts: size of desired block * Returns: free storage block */ void *fs_get (size_t size) { void *block = malloc (size ? size : (size_t) 1); if (!block) fatal ("Out of memory"); return (block); } /* Resize a block of free storage * Accepts: ** pointer to current block * new size */ void fs_resize (void **block,size_t size) { if (!(*block = realloc (*block,size ? size : (size_t) 1))) fatal ("Can't resize memory"); } /* Return a block of free storage * Accepts: ** pointer to free storage block */ void fs_give (void **block) { free (*block); *block = NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/ftl_wce.c000066400000000000000000000013271137544547100232030ustar00rootroot00000000000000/* * Program: DOS/VMS/TOPS-20 crash management routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Report a fatal error * Accepts: string to output */ void fatal (char *string) { mm_fatal (string); /* pass up the string */ abort (); /* die horribly */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/makefile.wce000066400000000000000000000044161137544547100236730ustar00rootroot00000000000000# Program: Portable C client makefile -- WCE version # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 11 May 1989 # Last Edited: 6 July 2004 # # The IMAP toolkit provided in this Distribution is # Copyright 1988-2004 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. EXTRAAUTHENTICATORS= DEFAULTAUTHENTICATORS=md5 pla log EXTRADRIVERS = DRIVERS = imap nntp pop3 DEFAULTDRIVER = dummy CFLAGS= /MT /W3 /D_SH3_ -nologo $(EXTRACFLAGS) CC = shcl CCLIENTLIB= cclient.lib all: $(CCLIENTLIB) .c.obj: $(CC) -c $(CFLAGS) $*.c osdep.h: os_wce.h copy os_wce.h osdep.h drivers $(EXTRADRIVERS) $(DRIVERS) dummy setproto $(DEFAULTDRIVER) $(DEFAULTDRIVER) mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS) mail.obj: mail.h misc.h osdep.h mail.c misc.obj: mail.h misc.h misc.c flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c rfc822.obj: mail.h rfc822.h misc.h rfc822.c smanager.obj: mail.h misc.h smanager.c utf8.obj: mail.h misc.h osdep.h utf8.h imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c pop3.obj: mail.h pop3.h rfc822.h misc.h osdep.h pop3.c smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c os_wce.obj: mail.h osdep.h env_wce.h fs.h ftl.h nl.h tcp.h tcp_wce.h \ os_wce.c fs_wce.c ftl_wce.c nl_wce.c env_wce.c tcp_wce.c \ auth_md5.c auth_log.c pmatch.c dummywce.obj: mail.h dummy.h misc.h osdep.h dummywce.c $(CCLIENTLIB): mail.obj misc.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_wce.obj \ dummywce.obj erase $(CCLIENTLIB) LIB /NOLOGO /OUT:cclient.lib \ mail.obj misc.obj flstring.obj netmsg.obj \ newsrc.obj rfc822.obj smanager.obj utf8.obj \ imap4r1.obj nntp.obj pop3.obj smtp.obj os_wce.obj \ dummywce.obj clean: del *.lib *.obj linkage.* osdep.* auths.c *.exe *.exp || rem # A monument to a hack of long ago and far away... love: @echo not war? tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/mkautaux.bat000066400000000000000000000016631137544547100237460ustar00rootroot00000000000000@ECHO OFF REM Program: Authenticator Linkage Generator auxillary for NT/Win9x REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 6 December 1995 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H REM Note the introduction of the caret to quote the ampersand in NT if "%OS%" == "Windows_NT" ECHO auth_link (^&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C if "%OS%" == "" ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C ECHO #include "auth_%1.c" >> AUTHS.C tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/mkauths.bat000066400000000000000000000013451137544547100235600ustar00rootroot00000000000000@ECHO OFF REM Program: Authenticator Linkage Generator for DOS REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 6 December 1995 REM Last Edited:24 October 2000 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 2000 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Erase old authenticators list IF EXIST AUTHS.C DEL AUTHS.C REM Now define the new list FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D EXIT 0 tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/nl_wce.c000066400000000000000000000027511137544547100230310ustar00rootroot00000000000000/* * Program: Windows/TOPS-20 newline routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Copy string with CRLF newlines * Accepts: destination string * pointer to size of destination string buffer * source string * length of source string * Returns: length of copied string */ unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl, unsigned char *src,unsigned long srcl) { /* flush destination buffer if too small */ if (*dst && (srcl > *dstl)) fs_give ((void **) dst); if (!*dst) { /* make a new buffer if needed */ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1); if (dstl) *dstl = srcl; /* return new buffer length to main program */ } /* copy strings */ if (srcl) memcpy (*dst,src,(size_t) srcl); *(*dst + srcl) = '\0'; /* tie off destination */ return srcl; /* return length */ } /* Length of string after strcrlfcpy applied * Accepts: source string * Returns: length of string */ unsigned long strcrlflen (STRING *s) { return SIZE (s); /* no-brainer on DOS! */ } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/os_wce.c000066400000000000000000000016441137544547100230410ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- WCE version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include "tcp_wce.h" /* must be before osdep includes tcp.h */ #include "mail.h" #include "osdep.h" #include #include #include #include #include #include #include "misc.h" #include "fs_wce.c" #include "ftl_wce.c" #include "nl_wce.c" #include "env_wce.c" #include "tcp_wce.c" #include "auths.c" tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/os_wce.h000066400000000000000000000021041137544547100230360ustar00rootroot00000000000000/* * Program: Operating-system dependent routines -- WCE version * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 May 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include /* missing in string.h */ _CRTIMP char * __cdecl strpbrk (const char *, const char *); _CRTIMP char * __cdecl strrchr(const char *, int); #include #undef ERROR #include #undef ERROR #define ERROR (long) 2 /* must match mail.h */ #include #include #define gethostid clock #define WSA_VERSION ((1 << 8) | 1) #include "env_wce.h" #include "fs.h" #include "ftl.h" #include "nl.h" #include "tcp.h" #undef noErr #undef MAC tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/pmatch.c000066400000000000000000000053271137544547100230400ustar00rootroot00000000000000/* * Program: IMAP Wildcard Matching Routines (case-independent) * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 15 June 2000 * Last Edited: 27 April 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 1988-2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* Wildcard pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if pattern matches base, else NIL */ long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ /* % at end, OK if no inferiors */ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T; /* scan remainder of string until delimiter */ do if (pmatch_full (s,pat+1,delim)) return T; while ((*s != delim) && *s++); break; case '*': /* match 0 or more characters */ if (!pat[1]) return T; /* * at end, unconditional match */ /* scan remainder of string */ do if (pmatch_full (s,pat+1,delim)) return T; while (*s++); break; case '\0': /* end of pattern */ return *s ? NIL : T; /* success if also end of base */ default: /* match this character */ return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? pmatch_full (s+1,pat+1,delim) : NIL; } return NIL; } /* Directory pattern match * Accepts: base string * pattern string * delimiter character * Returns: T if base is a matching directory of pattern, else NIL */ long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim) { switch (*pat) { case '%': /* non-recursive */ if (!*s) return T; /* end of base means have a subset match */ if (!*++pat) return NIL; /* % at end, no inferiors permitted */ /* scan remainder of string until delimiter */ do if (dmatch (s,pat,delim)) return T; while ((*s != delim) && *s++); if (*s && !s[1]) return T; /* ends with delimiter, must be subset */ return dmatch (s,pat,delim);/* do new scan */ case '*': /* match 0 or more characters */ return T; /* unconditional match */ case '\0': /* end of pattern */ break; default: /* match this character */ if (*s) return ((isupper (*pat) ? tolower (*pat) : *pat) == (isupper (*s) ? tolower (*s) : *s)) ? dmatch (s+1,pat+1,delim) : NIL; /* end of base, return if at delimiter */ else if (*pat == delim) return T; break; } return NIL; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/setproto.bat000066400000000000000000000012671137544547100237660ustar00rootroot00000000000000@ECHO OFF REM Program: Set default prototype for DOS/NT REM REM Author: Mark Crispin REM Networks and Distributed Computing REM Computing & Communications REM University of Washington REM Administration Building, AG-44 REM Seattle, WA 98195 REM Internet: MRC@CAC.Washington.EDU REM REM Date: 9 October 1995 REM Last Edited: 6 July 2004 REM REM The IMAP toolkit provided in this Distribution is REM Copyright 1988-2004 University of Washington. REM REM The full text of our legal notices is contained in the file called REM CPYRIGHT, included with this Distribution. REM Set the default drivers ECHO #define CREATEPROTO %1proto >> LINKAGE.H ECHO #define APPENDPROTO %2proto >> LINKAGE.H tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/tcp_wce.c000066400000000000000000000537561137544547100232210ustar00rootroot00000000000000/* * Program: Winsock TCP/IP routines * * Author: Mark Crispin from Mike Seibel's Winsock code * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 3 September 2004 * * The IMAP toolkit provided in this Distribution is * Copyright 2004 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #define TCPMAXSEND 32768 /* Private functions */ int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst, unsigned long port); long tcp_abort (SOCKET *sock); char *tcp_name (struct sockaddr_in *sin,long flag); char *tcp_name_valid (char *s); /* Private data */ int wsa_initted = 0; /* init ? */ static int wsa_sock_open = 0; /* keep track of open sockets */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_read = 0; /* TCP timeouts, in seconds */ static long ttmo_write = 0; static long allowreversedns = T;/* allow reverse DNS lookup */ static long tcpdebug = NIL; /* extra TCP debugging telemetry */ /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; case SET_ALLOWREVERSEDNS: allowreversedns = (long) value; case GET_ALLOWREVERSEDNS: ret = (void *) allowreversedns; break; case SET_TCPDEBUG: tcpdebug = (long) value; case GET_TCPDEBUG: ret = (void *) tcpdebug; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number and optional silent flag * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; int i; SOCKET sock = INVALID_SOCKET; int silent = (port & NET_SILENT) ? T : NIL; char *s; struct sockaddr_in sin; struct hostent *he; char hostname[MAILTMPLEN]; char tmp[MAILTMPLEN]; struct servent *sv = NIL; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (i = (int) WSAStartup (WSA_VERSION,&wsock)) { wsa_initted = 0; /* in case we try again */ sprintf (tmp,"Unable to start Windows Sockets (%d)",i); mm_log (tmp,ERROR); return NIL; } } port &= 0xffff; /* erase flags */ /* lookup service */ if (service && (sv = getservbyname (service,"tcp"))) port = ntohs (sin.sin_port = sv->s_port); /* copy port number in network format */ else sin.sin_port = htons ((u_short) port); /* The domain literal form is used (rather than simply the dotted decimal as with other Windows programs) because it has to be a valid "host name" in mailsystem terminology. */ sin.sin_family = AF_INET; /* family is always Internet */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (tmp,host+1); /* yes, copy number part */ tmp[strlen (tmp)-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (tmp)) == INADDR_NONE) { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } else { sin.sin_family = AF_INET; /* family is always Internet */ strcpy (hostname,host); (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (&sin,tmp,hostname,port); (*bn) (BLOCK_NONE,NIL); } } else { /* lookup host name */ if (tcpdebug) { sprintf (tmp,"DNS resolution %.80s",host); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */ if (!(he = gethostbyname (lcase (strcpy (tmp,host))))) sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError(),host); (*bn) (BLOCK_NONE,NIL); if (he) { /* DNS resolution won? */ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG); /* copy address type */ sin.sin_family = he->h_addrtype; /* copy host name */ strcpy (hostname,he->h_name); wsa_sock_open++; /* prevent tcp_abort() from freeing in loop */ for (i = 0; (sock == INVALID_SOCKET) && (s = he->h_addr_list[i]); i++) { if (i && !silent) mm_log (tmp,WARN); memcpy (&sin.sin_addr,s,he->h_length); (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (&sin,tmp,hostname,port); (*bn) (BLOCK_NONE,NIL); } wsa_sock_open--; /* undo protection */ } } if (sock == INVALID_SOCKET) { /* error? */ if (!silent) mm_log (tmp,ERROR); tcp_abort (&sock); /* do possible cleanup action */ } else { /* got a socket, create TCP/IP stream */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); stream->port = port; /* port number */ /* init socket */ stream->tcpsi = stream->tcpso = sock; stream->ictr = 0; /* init input counter */ /* copy official host name */ stream->host = cpystr (hostname); if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG); } return stream; /* return success */ } /* Open a TCP socket * Accepts: Internet socket address block * scratch buffer * host name for error message * port number for error message * Returns: socket if success, else -1 with error string in scratch buffer */ int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst, unsigned long port) { int sock; char *s; sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr)); mm_log (tmp,NIL); /* get a TCP stream */ if ((sock = socket (sin->sin_family,SOCK_STREAM,0)) == INVALID_SOCKET) { sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError()); return -1; } wsa_sock_open++; /* count this socket as open */ /* open connection */ if (connect (sock,(struct sockaddr *) sin,sizeof (struct sockaddr_in)) == SOCKET_ERROR) { switch (WSAGetLastError ()){/* analyze error */ case WSAECONNREFUSED: s = "Refused"; break; case WSAENOBUFS: s = "Insufficient system resources"; break; case WSAETIMEDOUT: s = "Timed out"; break; case WSAEHOSTUNREACH: s = "Host unreachable"; break; default: s = "Unknown error"; break; } sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s, WSAGetLastError ()); tcp_abort (&sock); /* flush socket */ sock = INVALID_SOCKET; } return sock; /* return the socket */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; /* always NIL on Windows */ } /* TCP/IP receive line * Accepts: TCP/IP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { int n,m; char *st,*ret,*stp; char c = '\0'; char d; /* make sure have data */ if (!tcp_getdata (stream)) return NIL; st = stream->iptr; /* save start of string */ n = 0; /* init string count */ while (stream->ictr--) { /* look for end of line */ d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,st,n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } n++; /* count another character searched */ c = d; /* remember previous character */ } /* copy partial string from buffer */ memcpy ((ret = stp = (char *) fs_get (n)),st,n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[n - 1] = '\0'; /* tie off string with null */ } /* else recurse to get remainder */ else if (st = tcp_getline (stream)) { ret = (char *) fs_get (n + 1 + (m = strlen (st))); memcpy (ret,stp,n); /* copy first part */ memcpy (ret + n,st,m); /* and second part */ fs_give ((void **) &stp); /* flush first part */ fs_give ((void **) &st); /* flush second part */ ret[n + m] = '\0'; /* tie off string with null */ } return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s) { unsigned long n; /* make sure socket still alive */ if (stream->tcpsi == INVALID_SOCKET) return NIL; /* can transfer bytes from buffer? */ if (n = min (size,stream->ictr)) { memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */ s += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } if (size) { int i; fd_set fds; struct timeval tmo; time_t tc,t = time (0); blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_TCPREAD,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG); FD_ZERO (&fds); /* initialize selection vector */ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */ tmo.tv_sec = ttmo_read; tmo.tv_usec = 0; /* block and read */ switch ((stream->tcpsi == stream->tcpso) ? select (stream->tcpsi+1,&fds,0,0, ttmo_read ? &tmo : (struct timeval *) 0) : 1) { case SOCKET_ERROR: /* error */ if (WSAGetLastError () != WSAEINTR) return tcp_abort (&stream->tcpsi); break; case 0: /* timeout */ tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) break; return tcp_abort (&stream->tcpsi); default: if (stream->tcpsi == stream->tcpso) while (((i = recv (stream->tcpsi,s,(int) min (maxposint,size),0)) == SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR)); else while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) && (errno == EINTR)); switch (i) { case SOCKET_ERROR: /* error */ case 0: /* no data read */ return tcp_abort (&stream->tcpsi); default: s += i; /* point at new place to write */ size -= i; /* reduce byte count */ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } } } (*bn) (BLOCK_NONE,NIL); } *s = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { struct timeval tmo; int i; fd_set fds; time_t tc,t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); FD_ZERO (&fds); /* initialize selection vector */ if (stream->tcpsi == INVALID_SOCKET) return NIL; (*bn) (BLOCK_TCPREAD,NIL); tmo.tv_sec = ttmo_read; tmo.tv_usec = 0; while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG); FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */ /* block and read */ switch ((stream->tcpsi == stream->tcpso) ? select (stream->tcpsi+1,&fds,0,0, ttmo_read ? &tmo : (struct timeval *) 0) : 1) { case SOCKET_ERROR: /* error */ if (WSAGetLastError () != WSAEINTR) return tcp_abort (&stream->tcpsi); break; case 0: /* timeout */ tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) break; return tcp_abort (&stream->tcpsi); default: if (stream->tcpsi == stream->tcpso) while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) == SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR)); else while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); switch (i) { case SOCKET_ERROR: /* error */ case 0: /* no data read */ return tcp_abort (&stream->tcpsi); default: stream->ictr = i; /* set new byte count */ /* point at TCP buffer */ stream->iptr = stream->ibuf; if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG); } } } (*bn) (BLOCK_NONE,NIL); return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; struct timeval tmo; fd_set fds; time_t tc,t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); tmo.tv_sec = ttmo_write; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ if (stream->tcpso == INVALID_SOCKET) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG); FD_SET (stream->tcpso,&fds);/* set bit in selection vector */ /* block and write */ switch ((stream->tcpsi == stream->tcpso) ? select (stream->tcpso+1,NULL,&fds,NULL, tmo.tv_sec ? &tmo : (struct timeval *) 0) : 1) { case SOCKET_ERROR: /* error */ if (WSAGetLastError () != WSAEINTR) return tcp_abort (&stream->tcpsi); break; case 0: /* timeout */ tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) break; return tcp_abort (&stream->tcpsi); default: if (stream->tcpsi == stream->tcpso) while (((i = send (stream->tcpso,string, (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR)); else while (((i = write (stream->tcpso,string, min (size,TCPMAXSEND))) < 0) && (errno == EINTR)); if (i == SOCKET_ERROR) return tcp_abort (&stream->tcpsi); size -= i; /* count this size */ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); string += i; } } (*bn) (BLOCK_NONE,NIL); return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (&stream->tcpsi); /* nuke the socket */ /* flush host names */ if (stream->host) fs_give ((void **) &stream->host); if (stream->remotehost) fs_give ((void **) &stream->remotehost); if (stream->localhost) fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: WinSock socket * Returns: NIL, always */ long tcp_abort (SOCKET *sock) { blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* something to close? */ if (sock && (*sock != INVALID_SOCKET)) { (*bn) (BLOCK_TCPCLOSE,NIL); closesocket (*sock); /* WinSock socket close */ *sock = INVALID_SOCKET; (*bn) (BLOCK_NONE,NIL); wsa_sock_open--; /* drop this socket */ } /* no more open streams? */ if (wsa_initted && !wsa_sock_open) { mm_log ("Winsock cleanup",NIL); wsa_initted = 0; /* no more sockets, so... */ WSACleanup (); /* free up resources until needed */ } return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* use tcp_remotehost() if want guarantees */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { if (!stream->remotehost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); stream->remotehost = /* get socket's peer name */ ((getpeername (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr (stream->host) : tcp_name (&sin,NIL); } return stream->remotehost; } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { if (!stream->localhost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); stream->localhost = /* get socket's name */ ((stream->port & 0xffff000) || ((getsockname (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0))) ? cpystr (mylocalhost ()) : tcp_name (&sin,NIL); } return stream->localhost; /* return local host name */ } /* TCP/IP get client host address (server calls only) * Returns: client host address */ char *tcp_clientaddr () { if (!myClientAddr) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myClientAddr = /* get stdin's peer name */ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr)); } return myClientAddr; } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { if (!myClientHost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myClientHost = /* get stdin's peer name */ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (&sin,T); } return myClientHost; } /* TCP/IP get server host address (server calls only) * Returns: server host address */ char *tcp_serveraddr () { if (!myServerAddr) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); myServerAddr = /* get stdin's peer name */ ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr)); } return myServerAddr; } /* TCP/IP get server host name (server calls only) * Returns: server host name */ static long myServerPort = -1; char *tcp_serverhost () { if (!myServerHost) { struct sockaddr_in sin; int sinlen = sizeof (struct sockaddr_in); if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (WSAStartup (WSA_VERSION,&wsock)) { wsa_initted = 0; return "random-pc"; /* try again later? */ } } /* get stdin's name */ if ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) || (sinlen <= 0)) myServerHost = cpystr (mylocalhost ()); else { myServerHost = tcp_name (&sin,NIL); myServerPort = ntohs (sin.sin_port); } } return myServerHost; } /* TCP/IP get server port number (server calls only) * Returns: server port number */ long tcp_serverport () { if (!myServerHost) tcp_serverhost (); return myServerPort; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { char *ret,host[MAILTMPLEN]; struct hostent *he; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name; (*bn) (BLOCK_DNSLOOKUP,NIL); if (tcpdebug) { sprintf (host,"DNS canonicalization %.80s",name); mm_log (host,TCPDEBUG); } /* note that NT requires lowercase! */ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name; (*bn) (BLOCK_NONE,NIL); if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG); return ret; } /* TCP/IP return name from socket * Accepts: socket * verbose flag * Returns: cpystr name */ char *tcp_name (struct sockaddr_in *sin,long flag) { char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN]; sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr)); if (allowreversedns) { struct hostent *he; blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL); void *data; if (tcpdebug) { sprintf (tmp,"Reverse DNS resolution %s",adr); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); /* translate address to name */ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr, sizeof (struct in_addr), sin->sin_family)) ? (char *) he->h_name : NIL)) { /* produce verbose form if needed */ if (flag) sprintf (ret = tmp,"%s %s",t,adr); else ret = t; } (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); /* alarms OK now */ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG); } return cpystr (ret); } /* Return my local host name * Returns: my local host name */ char *mylocalhost (void) { if (!myLocalHost) { char tmp[MAILTMPLEN]; if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (WSAStartup (WSA_VERSION,&wsock)) { wsa_initted = 0; return "random-pc"; /* try again later? */ } } myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ? "random-pc" : tcp_canonical (tmp)); } return myLocalHost; } /* Validate name * Accepts: domain name * Returns: T if valid, NIL otherwise */ char *tcp_name_valid (char *s) { int c; char *ret,*tail; /* must be non-empty and not too long */ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) { /* must be alnum, dot, or hyphen */ while ((c = *s++) && (s <= tail) && (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.'))); if (c) ret = NIL; } return ret; } tkrat_2.2cvs20100105-dfsg.orig/imap/src/osdep/wce/tcp_wce.h000066400000000000000000000020731137544547100232100ustar00rootroot00000000000000/* * Program: Winsock TCP/IP routines * * Author: Mike Seibel from Unix version by Mark Crispin * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 24 October 2000 * * The IMAP toolkit provided in this Distribution is * Copyright 2000 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* TCP input buffer -- must be large enough to prevent overflow */ #define BUFLEN 8192 /* TCP I/O stream (must be before osdep.h is included) */ #define TCPSTREAM struct tcp_stream TCPSTREAM { char *host; /* host name */ char *remotehost; /* remote host name */ unsigned long port; /* port number */ char *localhost; /* local host name */ int tcpsi; /* input tcp socket */ int tcpso; /* output tcp socket */ long ictr; /* input counter */ char *iptr; /* input pointer */ char ibuf[BUFLEN]; /* input buffer */ }; tkrat_2.2cvs20100105-dfsg.orig/imap/src/tmail/000077500000000000000000000000001137544547100206275ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/src/tmail/Makefile000066400000000000000000000017031137544547100222700ustar00rootroot00000000000000# Program: tmail Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 5 April 1993 # Last Edited: 18 November 2002 # # The IMAP toolkit software provided in this Distribution is # Copyright 2002 University of Washington. # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. C = ../c-client CCLIENTLIB = $C/c-client.a SHELL = /bin/sh # Get local definitions from c-client directory CC = `cat $C/CCTYPE` CFLAGS = -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` tmail: $(CCLIENTLIB) tmail.o $(CC) $(CFLAGS) -o tmail tmail.o $(LDFLAGS) tmail.o: $C/mail.h $C/misc.h $C/osdep.h $(CCLIENTLIB): cd $C;make clean: rm -f *.o tmail # A monument to a hack of long ago and far away... love: @echo 'not war?' tkrat_2.2cvs20100105-dfsg.orig/imap/src/tmail/tmail.1000066400000000000000000000130321137544547100220160ustar00rootroot00000000000000.TH TMAIL 1 "May 18, 2004" .SH NAME tmail \- Mail Delivery Module .nh .SH SYNOPSIS .B tmail .I [\-D] [-f from_name] [\-I inbox_specifier] user[+folder] ... .SH DESCRIPTION .I tmail delivers mail to a user's INBOX or a designated folder. .I tmail may be configured as a drop-in replacement for .IR binmail (1), .IR mail.local (1) or any program intended for use for mail delivery by .IR sendmail (8). .PP .I tmail is intended to be used for direct delivery by the mailer daemon; .IR dmail (1) is the preferred tool for user applications, e.g. a mail delivery filter such as .IR procmail (1) . If .I tmail is used for a user application, then the calling program must be aware of the restrictions noted below. .PP When .I tmail exits, it returns exit status values to enable .IR sendmail (8) to determine whether a message was delivered successfully or had a temporary (requeue for later delivery) or permanent (return to sender) failure. .PP If the .I +folder extension is included in the user argument, .I tmail will attempt to deliver to the designated folder. If the folder does not exist or the extension is not included, the message is delivered to the user's INBOX. If delivery is to INBOX and no INBOX currently exists, .I tmail will create a new INBOX, using the \fB-I\fR flag if specified. .I tmail recognizes the format of an existing INBOX or folder, and appends the new message in that format. .PP The \fB-D\fR flag specifies debugging; this enables additional message telemetry. .PP The \fB-f\fR or \fB-r\fR flag is used by .IR sendmail (8) to specify a Return-Path. The header .br Return-Path: <\fIfrom_name\fR> .br is prepended to the message before delivery. .PP The \fB-I\fR flag is used by .IR sendmail (8) to specify an alternative INBOX name. This affects the location and format of INBOX, and requires privileges. If specified, it should be in one of three forms: .sp The first form of argument to \fB-I\fR is the string "INBOX", which means to write to the system default inbox using the system default mailbox format. These system defaults are defined when the c-client library is built. .sp The second form of argument to \fB-I\fR is a delivery specification, consisting of "#driver.", a c-client mailbox format driver name, "/", and a file name. This will write to the specified file in the specified format. For example, #driver.mbx/INBOX will write to file "INBOX" in the home directory in mbx format; and #driver.unix/mail/incoming will write to file "incoming" in the user's "mail" subdirectory in unix (default UNIX) format. .sp The third form of argument to \fB-I\fR is any other name. Normally, this will write to the specified file on the user's home directory in the specified format. However, certain names are special. These are: .PP .nf value equivalant to ----- ------------- INBOX.MTX #driver.mtx/INBOX.MTX mbox #driver.unix/mbox mail.txt #driver.tenex/mail.txt .fi .PP If \fB-I\fR is not specified, the default action is \fB-I INBOX\fR. .PP If multiple recipients are specified on the command line, .I tmail spawns one child process per recipient to perform actual delivery. This way of calling .I tmail is not recommended; see below under .IR RESTRICTIONS . .SH INSTALLATION If .I tmail is to be used for mail delivery from .IR sendmail (8), it .I must be installed setuid root. .sp .I tmail is invoked from sendmail.cf. Look for the "Mlocal" line, and substitute the path name for the .I tmail binary in place of /bin/mail, /usr/lib/mail.local, etc. You should also add the flag to invoke .I tmail with CRLF style newlines; this is usually done with E=\\r\\n in the Mlocal line. .sp For example, this is what is used on the author's system with sendmail version 8: .sp .nf Mlocal, P=/usr/local/etc/tmail, F=lsDFMAw5:/|@qPrn+, S=10/30, R=20/40, E=\\r\\n, T=DNS/RFC822/X-Unix, A=tmail $u .fi .PP If .I tmail is to be called with the \fB-I\fR flag, it must be invoked with both real and effective UID root. Many sendmail configurations invoke the local mailer as the sending user when that user is local, which will prevent \fB-I\fR from working. .SH SECURITY CONSIDERATIONS If .I tmail is invoked by an ordinary user, the Received: header line will indicate the name or UID of the user that invoked it. .PP Ordinary users are not permitted to use the \fB-I\fR flag since otherwise a user could create any file on another user's directory. .PP .I tmail can deliver mail to home directories. In addition, .I tmail can be used to deliver mail to other mail folders in a home directory or an inferior directory of a home directory. .SH RESTRICTIONS The calling program should invoke .I tmail with CRLF newlines, otherwise .I tmail will complain in syslog. .PP Absolute pathnames and .I ~user specifications are not permitted in .I +folder extensions. .PP Ordinary users are not permitted to use the \fB-I\fR flag. .PP IMAP4 namespace names are not yet supported in .I +folder extensions. .PP It is not possible to use .I tmail to deliver to .IR mh (1) format mailboxes. .PP If delivery to multiple users is specified and delivery to any single user fails, the entire delivery will be reported as having failed, even though delivery to other users may have succeeded. If .I tmail is used for mail delivery from .IR sendmail (8), a separate tmail invocation should be done for each user. Otherwise a delivery failure for a single user in a message going to multiple users will cause multiple deliveries to all the other users every time .IR sendmail (8), retries. .SH AUTHOR Mark Crispin, MRC@CAC.Washington.EDU .SH "SEE ALSO" binmail(1) .br sendmail(8) tkrat_2.2cvs20100105-dfsg.orig/imap/src/tmail/tmail.c000066400000000000000000000556721137544547100221200ustar00rootroot00000000000000/* * Program: Mail Delivery Module * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 5 April 1993 * Last Edited: 14 July 2002 * * The IMAP tools software provided in this Distribution is * Copyright 2002 University of Washington. * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ #include #include #include extern int errno; /* just in case */ #include #include #include #include "mail.h" #include "osdep.h" #include "misc.h" #include "linkage.h" /* Globals */ char *version = "2002(14)"; /* tmail release version */ int debug = NIL; /* debugging (don't fork) */ int trycreate = NIL; /* flag saying gotta create before appending */ int critical = NIL; /* flag saying in critical code */ char *sender = NIL; /* message origin */ char *inbox = NIL; /* inbox file */ /* Function prototypes */ void file_string_init (STRING *s,void *data,unsigned long size); char file_string_next (STRING *s); void file_string_setpos (STRING *s,unsigned long i); int main (int argc,char *argv[]); int deliver (FILE *f,unsigned long msglen,char *user); long ibxpath (MAILSTREAM *ds,char **mailbox,char *path); int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path, uid_t uid,char *tmp); int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp); int fail (char *string,int code); char *getusername (char *s,char **t); /* File string driver for file stringstructs */ STRINGDRIVER file_string = { file_string_init, /* initialize string structure */ file_string_next, /* get next byte in string structure */ file_string_setpos /* set position in string structure */ }; /* Cache buffer for file stringstructs */ #define CHUNKLEN 16384 char chunk[CHUNKLEN]; /* Initialize file string structure for file stringstruct * Accepts: string structure * pointer to string * size of string */ void file_string_init (STRING *s,void *data,unsigned long size) { s->data = data; /* note fd */ s->size = size; /* note size */ s->chunk = chunk; s->chunksize = (unsigned long) CHUNKLEN; SETPOS (s,0); /* set initial position */ } /* Get next character from file stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ char file_string_next (STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for file stringstruct * Accepts: string structure * new position */ void file_string_setpos (STRING *s,unsigned long i) { if (i > s->size) i = s->size; /* don't permit setting beyond EOF */ s->offset = i; /* set new offset */ s->curpos = s->chunk; /* reset position */ /* set size of data */ if (s->cursize = min (s->chunksize,SIZE (s))) { /* move to that position in the file */ fseek ((FILE *) s->data,s->offset,SEEK_SET); fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data); } } /* Main program */ int main (int argc,char *argv[]) { FILE *f = NIL; int pid,c,ret = 0; unsigned long msglen,status = 0; char *s,tmp[MAILTMPLEN]; uid_t ruid = getuid (); struct passwd *pwd; openlog ("tmail",LOG_PID,LOG_MAIL); #include "linkage.c" /* make sure have some arguments */ if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE)); /* process all flags */ while (argc && (*(s = *++argv)) == '-') { argc--; /* gobble this argument */ switch (s[1]) { /* what is this flag? */ case 'D': /* debug */ debug = T; /* don't fork */ break; case 'd': /* obsolete flag meaning multiple users */ break; case 'I': /* inbox specifier */ if (argc--) inbox = cpystr (*++argv); else _exit (fail ("missing argument to -I",EX_USAGE)); break; case 'f': /* new name for this flag */ case 'r': /* flag giving return path */ if (argc--) sender = cpystr (*++argv); else _exit (fail ("missing argument to -r",EX_USAGE)); break; default: /* anything else */ _exit (fail ("unknown switch",EX_USAGE)); } } if (!argc) ret = fail ("no recipients",EX_USAGE); else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL); else { /* build delivery headers */ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender); /* start Received line: */ fprintf (f,"Received: via tmail-%s",version); /* not root or daemon? */ if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) { pwd = getpwuid (ruid); /* get unprivileged user's information */ if (inbox) { if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name); else sprintf (tmp,"UID %ld",(long) ruid); strcat (tmp," is not privileged to use -I"); _exit (fail (tmp,EX_USAGE)); } fputs (" (invoked by ",f); if (pwd) fprintf (f,"user %s",pwd->pw_name); else fprintf (f,"UID %ld",(long) ruid); fputs (")",f); } /* write "for" if single recipient */ if (argc == 1) fprintf (f," for %s",*argv); fputs ("; ",f); rfc822_date (tmp); fputs (tmp,f); fputs ("\015\012",f); /* copy text from standard input */ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) || (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE)); if (s[-1] == '\015') { /* nuke leading "From " line */ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') || (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f); while ((c = getchar ()) != EOF) putc (c,f); } else { mm_log ("tmail called with LF-only newlines",WARN); if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') || (tmp[3] != 'm') || (tmp[4] != ' ')) { *s++ = '\015'; /* overwrite NL with CRLF */ *s++ = '\012'; *s = '\0'; /* tie off string */ fputs (tmp,f); /* write line */ } /* copy text from standard input */ while ((c = getchar ()) != EOF) { /* add CR if needed */ if (c == '\012') putc ('\015',f); putc (c,f); } } msglen = ftell (f); /* size of message */ fflush (f); /* make sure all changes written out */ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL); else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL); /* single delivery */ else if (argc == 1) ret = deliver (f,msglen,*argv); else do { /* multiple delivery uses daughter forks */ if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR); else if (pid) { /* mother process */ grim_pid_reap_status (pid,NIL,(void *) status); /* normal termination? */ if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8; } /* daughter process */ else _exit (deliver (f,msglen,*argv)); } while (--argc && *argv++); mm_dlog (ret ? "error in delivery" : "all recipients delivered"); } if (f) fclose (f); /* all done with temporary file */ _exit (ret); /* normal exit */ return 0; /* stupid gcc */ } /* Deliver message to recipient list * Accepts: file description of message temporary file * size of message temporary file in bytes * recipient name * Returns: NIL if success, else error code */ int deliver (FILE *f,unsigned long msglen,char *user) { MAILSTREAM *ds = NIL; DRIVER *dv = NIL; char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN]; struct passwd *pwd; STRING st; struct stat sbuf; uid_t duid; uid_t euid = geteuid (); /* get user record */ if (!(pwd = getpwnam (getusername (user,&mailbox)))) { sprintf (tmp,"no such user as %.80s",user); return fail (tmp,EX_NOUSER); } /* absurd is absurd */ if (mailbox && (strlen (mailbox) > 256)) return fail ("absurd folder name",EX_NOUSER); /* big security hole if this is allowed */ if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER); if (duid != euid) { /* avoid obnoxious initgroups() msg if self */ setgid (pwd->pw_gid); /* initialize groups */ initgroups (user,pwd->pw_gid); if (setuid (duid)) { /* log in as that user */ sprintf (tmp,"unable to log in UID %ld from UID %ld", (long) duid,(long) euid); return fail (tmp,EX_NOUSER); } } /* can't use pwd after this point */ env_init (pwd->pw_name,pwd->pw_dir); sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX"); mm_dlog (tmp); /* prepare stringstruct */ INIT (&st,file_string,(void *) f,msglen); if (mailbox) { /* non-INBOX name */ switch (mailbox[0]) { /* make sure a valid name */ default: /* other names, try to deliver if not INBOX */ if (!strstr (mailbox,"..") && !strstr (mailbox,"//") && !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] && !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL; case '%': case '*': /* wildcards not valid */ case '#': /* namespace name not valid */ case '/': /* absolute path names not valid */ case '~': /* user names not valid */ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox); mm_log (tmp,WARN); break; } mm_dlog ("retrying delivery to INBOX"); SETPOS (&st,0); /* rewind stringstruct just in case */ } /* -I specified and not "-I INBOX"? */ if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) && ((inbox[1] == 'N') || (inbox[1] == 'n')) && ((inbox[2] == 'B') || (inbox[2] == 'b')) && ((inbox[3] == 'O') || (inbox[3] == 'o')) && ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) { /* "-I #driver.xxx/name"? */ if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) && ((inbox[2] == 'r') || (inbox[2] == 'R')) && ((inbox[3] == 'i') || (inbox[3] == 'I')) && ((inbox[4] == 'v') || (inbox[4] == 'V')) && ((inbox[5] == 'e') || (inbox[5] == 'E')) && ((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') && (s = strchr (inbox+8,'/'))) { *s = '\0'; /* temporarily tie off driver name */ if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) && (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) && (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) { path[0] = '\0'; /* bad -I argument, no path resolved */ sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox); mm_log (tmp,WARN); } *s = '/'; /* restore delimiter */ } /* resolve "-I other" specification */ else if (mailboxfile (path,inbox) && path[0]) { /* resolution succeeded, impute driver */ if (!strcmp (inbox,"mail.txt")) dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex"); else if (!strcmp (inbox,"INBOX.MTX")) dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx"); else if (!strcmp (inbox,"mbox")) dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix"); } else { /* bad -I argument */ path[0] = '\0'; /* no path resolved */ sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox); mm_log (tmp,WARN); } if (*path) { /* -I successfully resolved a path? */ MAILSTREAM dpr; dpr.dtb = dv; if (dv) ds = &dpr; /* supplicate to the Evil One if necessary */ if (lstat (path,&sbuf) && !path_create (ds,path)) { /* the Evil One rejected the plea */ sprintf (tmp,"Unable to create %.80s, -I ignored",path); mm_log (tmp,WARN); } /* now attempt delivery */ else return deliver_safely (ds,&st,inbox,path,duid,tmp); } } /* no -I, resolve "INBOX" into path */ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) { /* clear box, get generic INBOX prototype */ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE))) fatal ("no INBOX prototype"); /* standard system driver? */ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) { strcpy (path,sysinbox ());/* use system INBOX */ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */ return deliver_safely (ds,&st,mailbox,path,duid,tmp); } else { /* other driver, try ~/INBOX */ if ((mailboxfile (path,"&&&&&") == path) && (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") && !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name); return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp); } } /* not dummy, deliver to driver imputed path */ if (strcmp (ds->dtb->name,"dummy")) return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ? deliver_safely (ds,&st,mailbox,path,duid,tmp) : fail ("unable to resolve INBOX path",EX_CANTCREAT); /* dummy, empty imputed append path exist? */ if (ibxpath (ds = default_proto (T),&mailbox,path) && !lstat (path,&sbuf) && !sbuf.st_size) return deliver_safely (ds,&st,mailbox,path,duid,tmp); /* impute path that we will create */ if (!ibxpath (ds = default_proto (NIL),&mailbox,path)) return fail ("unable to resolve INBOX",EX_CANTCREAT); } /* black box, must create, get create proto */ else if (lstat (path,&sbuf)) ds = default_proto (NIL); else { /* black box, existing file */ /* empty file, get append prototype */ if (!sbuf.st_size) ds = default_proto (T); /* non-empty, get prototype from its data */ else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE))) fatal ("no INBOX prototype"); /* error if unknown format */ if (!strcmp (ds->dtb->name,"phile")) return fail ("unknown format INBOX",EX_UNAVAILABLE); /* otherwise can deliver to it */ return deliver_safely (ds,&st,mailbox,path,duid,tmp); } sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path); mm_dlog (tmp); /* supplicate to the Evil One */ if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT); sprintf (tmp,"created %.80s",path); mm_dlog (tmp); /* deliver the message */ return deliver_safely (ds,&st,mailbox,path,duid,tmp); } /* Resolve INBOX from driver prototype into mailbox name and filesystem path * Accepts: driver prototype * pointer to mailbox name string pointer * buffer to return mailbox path * Returns: T if success, NIL if error */ long ibxpath (MAILSTREAM *ds,char **mailbox,char *path) { char *s,tmp[MAILTMPLEN]; long ret = T; if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) strcpy (path,sysinbox ()); /* use system INBOX for unix and MMDF */ else if (!strcmp (ds->dtb->name,"tenex")) ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL; else if (!strcmp (ds->dtb->name,"mtx")) ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL; else if (!strcmp (ds->dtb->name,"mbox")) ret = (mailboxfile (path,"mbox") == path) ? T : NIL; /* better not be a namespace driver */ else if (ds->dtb->flags & DR_NAMESPACE) return NIL; /* INBOX in home directory */ else ret = ((mailboxfile (path,"&&&&&") == path) && (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL; if (ret) { /* don't bother if lossage */ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name); *mailbox = cpystr (tmp); /* name of INBOX in this namespace */ } return ret; } /* Deliver safely * Accepts: prototype stream to force mailbox format * stringstruct of message temporary file or NIL for check only * mailbox name * filesystem path name * user id * scratch buffer for messages * Returns: NIL if success, else error code */ int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path, uid_t uid,char *tmp) { struct stat sbuf; int i = delivery_unsafe (path,uid,&sbuf,tmp); if (i) return i; /* give up now if delivery unsafe */ /* directory, not file */ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) { if (sbuf.st_mode & 0001) { /* listable directories may be worrisome */ sprintf (tmp,"WARNING: directory %.80s is listable",path); mm_log (tmp,WARN); } } else { /* file, not directory */ if (sbuf.st_nlink != 1) { /* multiple links may be worrisome */ sprintf (tmp,"WARNING: multiple links to file %.80s",path); mm_log (tmp,WARN); } if (sbuf.st_mode & 0111) { /* executable files may be worrisome */ sprintf (tmp,"WARNING: file %.80s is executable",path); mm_log (tmp,WARN); } } if (sbuf.st_mode & 0002) { /* public-write files may be worrisome */ sprintf (tmp,"WARNING: file %.80s is publicly-writable",path); mm_log (tmp,WARN); } if (sbuf.st_mode & 0004) { /* public-write files may be worrisome */ sprintf (tmp,"WARNING: file %.80s is publicly-readable",path); mm_log (tmp,WARN); } /* so far, so good */ sprintf (tmp,"%s appending to %.80s (%s %.80s)", prt ? prt->dtb->name : "default",mailbox, ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path); mm_dlog (tmp); /* do the append now! */ if (!mail_append (prt,mailbox,st)) { sprintf (tmp,"message delivery failed to %.80s",path); return fail (tmp,EX_CANTCREAT); } /* note success */ sprintf (tmp,"delivered to %.80s",path); mm_log (tmp,NIL); /* make sure nothing evil this way comes */ return delivery_unsafe (path,uid,&sbuf,tmp); } /* Verify that delivery is safe * Accepts: path name * user id * stat buffer * scratch buffer for messages * Returns: NIL if delivery is safe, error code if unsafe */ int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp) { u_short type; sprintf (tmp,"Verifying safe delivery to %.80s by UID %ld",path,(long) uid); mm_dlog (tmp); /* prepare message just in case */ sprintf (tmp,"delivery to %.80s unsafe: ",path); /* unsafe if can't get its status */ if (lstat (path,sbuf)) strcat (tmp,strerror (errno)); else if (sbuf->st_uid != uid) /* unsafe if UID does not match */ sprintf (tmp + strlen (tmp),"uid mismatch (%ld != %ld)", (long) sbuf->st_uid,(long) uid); /* unsafe if not a regular file */ else if (((type = sbuf->st_mode & (S_IFMT | S_ISUID | S_ISGID)) != S_IFREG)&& (type != S_IFDIR)) { strcat (tmp,"can't deliver to "); /* unsafe if setuid */ if (type & S_ISUID) strcat (tmp,"setuid file"); /* unsafe if setgid */ else if (type & S_ISGID) strcat (tmp,"setgid file"); else switch (type) { case S_IFCHR: strcat (tmp,"character special"); break; case S_IFBLK: strcat (tmp,"block special"); break; case S_IFLNK: strcat (tmp,"symbolic link"); break; case S_IFSOCK: strcat (tmp,"socket"); break; default: sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type); } } else return NIL; /* OK to deliver */ return fail (tmp,EX_CANTCREAT); } /* Report an error * Accepts: string to output */ int fail (char *string,int code) { mm_log (string,ERROR); /* pass up the string */ #if T switch (code) { case EX_USAGE: case EX_OSERR: case EX_SOFTWARE: case EX_NOUSER: case EX_CANTCREAT: case EX_UNAVAILABLE: code = EX_TEMPFAIL; /* coerce these to TEMPFAIL */ default: break; } #endif return code; /* error code to return */ } /* Get user name from username+mailbox specifier * Accepts: username/mailbox specifier * pointer to return location for mailbox specifier * Returns: user name, mailbox specifier value NIL if INBOX, patches out + */ char *getusername (char *s,char **t) { char tmp[MAILTMPLEN]; if (*t = strchr (s,'+')) { /* have a mailbox specifier? */ *(*t)++ = '\0'; /* yes, tie off user name */ /* user+ and user+INBOX same as user */ if (!**t || !strcmp ("INBOX",ucase (strcpy (tmp,*t)))) *t = NIL; } return s; /* return user name */ } /* Co-routines from MAIL library */ /* Message matches a search * Accepts: MAIL stream * message number */ void mm_searched (MAILSTREAM *stream,unsigned long msgno) { fatal ("mm_searched() call"); } /* Message exists (i.e. there are that many messages in the mailbox) * Accepts: MAIL stream * message number */ void mm_exists (MAILSTREAM *stream,unsigned long number) { fatal ("mm_exists() call"); } /* Message expunged * Accepts: MAIL stream * message number */ void mm_expunged (MAILSTREAM *stream,unsigned long number) { fatal ("mm_expunged() call"); } /* Message flags update seen * Accepts: MAIL stream * message number */ void mm_flags (MAILSTREAM *stream,unsigned long number) { } /* Mailbox found * Accepts: MAIL stream * delimiter * mailbox name * mailbox attributes */ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) { fatal ("mm_list() call"); } /* Subscribed mailbox found * Accepts: MAIL stream * delimiter * mailbox name * mailbox attributes */ void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) { fatal ("mm_lsub() call"); } /* Mailbox status * Accepts: MAIL stream * mailbox name * mailbox status */ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { fatal ("mm_status() call"); } /* Notification event * Accepts: MAIL stream * string to log * error flag */ void mm_notify (MAILSTREAM *stream,char *string,long errflg) { char tmp[MAILTMPLEN]; tmp[11] = '\0'; /* see if TRYCREATE */ if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T; mm_log (string,errflg); /* just do mm_log action */ } /* Log an event for the user to see * Accepts: string to log * error flag */ void mm_log (char *string,long errflg) { if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */ else { /* ordinary logging */ fprintf (stderr,"%s\n",string); switch (errflg) { case NIL: /* no error */ syslog (LOG_INFO,"%s",string); break; case PARSE: /* parsing problem */ case WARN: /* warning */ syslog (LOG_WARNING,"%s",string); break; case ERROR: /* error */ default: syslog (LOG_ERR,"%s",string); break; } } } /* Log an event to debugging telemetry * Accepts: string to log */ void mm_dlog (char *string) { if (debug) fprintf (stderr,"%s\n",string); syslog (LOG_DEBUG,"%s",string); } /* Get user name and password for this host * Accepts: parse of network mailbox name * where to return user name * where to return password * trial count */ void mm_login (NETMBX *mb,char *username,char *password,long trial) { fatal ("mm_login() call"); } /* About to enter critical code * Accepts: stream */ void mm_critical (MAILSTREAM *stream) { critical = T; /* note in critical code */ } /* About to exit critical code * Accepts: stream */ void mm_nocritical (MAILSTREAM *stream) { critical = NIL; /* note not in critical code */ } /* Disk error found * Accepts: stream * system error code * flag indicating that mailbox may be clobbered * Returns: T if user wants to abort */ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { return T; } /* Log a fatal error event * Accepts: string to log */ void mm_fatal (char *string) { printf ("?%s\n",string); /* shouldn't happen normally */ } tkrat_2.2cvs20100105-dfsg.orig/imap/tools/000077500000000000000000000000001137544547100200725ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/imap/tools/Makefile000066400000000000000000000012701137544547100215320ustar00rootroot00000000000000# Program: Tools Makefile # # Author: Mark Crispin # Networks and Distributed Computing # Computing & Communications # University of Washington # Administration Building, AG-44 # Seattle, WA 98195 # Internet: MRC@CAC.Washington.EDU # # Date: 2 June 1995 # Last Edited: 24 October 2000 # # The IMAP toolkit provided in this Distribution is # Copyright 2000 University of Washington. # # The full text of our legal notices is contained in the file called # CPYRIGHT, included with this Distribution. CC=cc RM=rm -f uahelper: $(CC) -o uahelper uahelper.c $(LDFLAGS) clean: sh -c '$(RM) *.o uahelper || true' # A monument to a hack of long ago and far away... love: @echo 'not war?' tkrat_2.2cvs20100105-dfsg.orig/imap/tools/an000077500000000000000000000005401137544547100204150ustar00rootroot00000000000000#!/bin/sh BASE=`pwd` if [ ! -d $3 ]; then mkdir $3 fi for i in $2/Makefile* ; do if [ -f $i ] ; then $1 "$BASE/$i" "$BASE/$3" fi done if [ -f $2/drivers ]; then $1 "$BASE/$2/drivers" "$BASE/$3" fi if [ -f $2/mkauths ]; then $1 "$BASE/$2/mkauths" "$BASE/$3" fi cd $2 for j in *.[ch]; do ln -s "$BASE/$2/$j" "$BASE/$3" done exit 0 tkrat_2.2cvs20100105-dfsg.orig/imap/tools/ua000077500000000000000000000006441137544547100204310ustar00rootroot00000000000000#!/bin/sh BASE=`pwd` if [ ! -f tools/uahelper ]; then (cd tools;make) fi if [ ! -d $3 ]; then mkdir $3 fi for i in $2/Makefile* ; do if [ -f $i ] ; then $1 "$BASE/$i" "$BASE/$3" fi done if [ -f $2/drivers ]; then $1 "$BASE/$2/drivers" "$BASE/$3" fi if [ -f $2/mkauths ]; then $1 "$BASE/$2/mkauths" "$BASE/$3" fi cd $2 for j in *.[ch]; do "$BASE/tools/uahelper" < $j > "$BASE/$3/$j" done exit 0 tkrat_2.2cvs20100105-dfsg.orig/imap/tools/uahelper.c000066400000000000000000000163231137544547100220500ustar00rootroot00000000000000/* * Program: unANSIify * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * 4545 15th Ave NE * Seattle, WA 98105-4527 * Internet: MRC@CAC.Washington.EDU * * Date: 1 June 1995 * Last Edited: 2 July 2001 * * The IMAP toolkit provided in this Distribution is * Copyright 2001 University of Washington. * * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this Distribution. */ /* This program is designed to make traditional C sources out of my source * files which use ANSI syntax. This program depends heavily upon knowledge * of the way I write code, and is not a general purpose tool. I hope that * someday someone will provide a general purpose tool... */ #include #define LINELENGTH 1000 /* Find first non-whitespace * Accepts: string * Returns: 0 if no non-whitespace found, or pointer to non-whitespace */ char *fndnws (s) char *s; { while ((*s == ' ') || (*s == '\t')) if ((*s++ == '\n') || !*s) return (char *) 0; return s; } /* Find first whitespace * Accepts: string * Returns: 0 if no whitespace found, or pointer to whitespace */ char *fndws (s) char *s; { while ((*s != ' ') && (*s != '\t')) if ((*s++ == '\n') || !*s) return (char *) 0; return s; } /* Find end of commend * Accepts: string * Returns: -1 if end of comment found, else 0 */ int fndcmt (s) char *s; { while (*s && (*s != '\n')) if ((*s++ == '*') && (*s == '/')) return -1; return 0; } /* Output a line * Accepts: string */ void poot (s) char *s; { if (s) fputs (s,stdout); } /* Skip prefix * Accepts: string * Returns: updated string */ char *skipfx (s) char *s; { char c,*t = s,*tt; /* skip leading whitespace too */ while ((*s == ' ') || (*s == '\t')) *s++; if (s != t) { c = *s; /* save character */ *s = '\0'; /* tie off prefix */ poot (t); /* output prefix */ *s = c; /* restore character */ } /* a typedef? */ if (((s[0] == 't') && (s[1] == 'y') && (s[2] == 'p') && (s[3] == 'e') && (s[4] == 'd') && (s[5] == 'e') && (s[6] == 'f')) && (t = fndws (s)) && (t = fndnws (t))) { if ((t[0] == 'u') && (t[1] == 'n') && (t[2] == 's') && (t[3] == 'i') && (t[4] == 'g') && (t[5] == 'n') && (t[6] == 'e') && (t[7] == 'd') && (tt = fndws (t+7)) && (tt = fndnws (tt))) t = tt; c = *t; /* save character */ *t = '\0'; /* tie off prefix */ poot (s); /* output prefix */ *t = c; /* restore character */ s = t; /* new string pointer */ } /* static with known prefix */ else if (((s[0] == 's') && (s[1] == 't') && (s[2] == 'a') && (s[3] == 't') && (s[4] == 'i') && (s[5] == 'c') && (s[6] == ' ')) && (((s[7] == 'u') && (s[8] == 'n') && (s[9] == 's') && (s[10] == 'i') && (s[11] == 'g') && (s[12] == 'n') && (s[13] == 'e') && (s[14] == 'd')) || ((s[7] == 's') && (s[8] == 't') && (s[9] == 'r') && (s[10] == 'u') && (s[11] == 'c') && (s[12] == 't')) || ((s[7] == 'd') && (s[8] == 'o')) || ((s[9] == 'e') && (s[10] == 'l') && (s[11] == 's') && (s[12] == 'e'))) && (t = fndws (s)) && (t = fndnws (t)) && (t = fndws (t)) && (t = fndnws (t))) { c = *t; /* save character */ *t = '\0'; /* tie off prefix */ poot (s); /* output prefix */ *t = c; /* restore character */ s = t; /* new string pointer */ } /* one of the known prefixes? */ else if ((((s[0] == 'u') && (s[1] == 'n') && (s[2] == 's') && (s[3] == 'i') && (s[4] == 'g') && (s[5] == 'n') && (s[6] == 'e') && (s[7] == 'd')) || ((s[0] == 's') && (s[1] == 't') && (s[2] == 'r') && (s[3] == 'u') && (s[4] == 'c') && (s[5] == 't')) || ((s[0] == 's') && (s[1] == 't') && (s[2] == 'a') && (s[3] == 't') && (s[4] == 'i') && (s[5] == 'c')) || ((s[0] == 'd') && (s[1] == 'o')) || ((s[0] == 'e') && (s[1] == 'l') && (s[2] == 's') && (s[3] == 'e'))) && (t = fndws (s)) && (t = fndnws (t))) { c = *t; /* save character */ *t = '\0'; /* tie off prefix */ poot (s); /* output prefix */ *t = c; /* restore character */ s = t; /* new string pointer */ } /* may look like a proto, but isn't */ else if ((s[0] == 'r') && (s[1] == 'e') && (s[2] == 't') && (s[3] == 'u') && (s[4] == 'r') && (s[5] == 'n')) { poot (s); return 0; } return s; } /* UnANSI a line * Accepts: string */ void unansi (s) char *s; { char c,*t = s,*u,*v; while (t[1] && (t[1] != '\n')) t++; switch (*t) { case ',': /* continued on next line? */ /* slurp remainder of line */ fgets (t + 1,LINELENGTH - (t + 1 - s),stdin); unansi (s); /* try again */ break; case ';': /* function prototype? */ /* yes, tie it off */ *(fndws (fndnws (fndws (fndnws (s))))) = '\0'; printf ("%s ();\n",s); /* and output non-ANSI form */ break; case ')': *t = '\0'; /* tie off args */ if (*(t = fndnws (fndws (fndnws (fndws (fndnws (s)))))) == '(') { *t++ = '\0'; /* tie off */ while ((*t == ' ') || (*t == '\t')) t++; if ((t[0] == 'v') && (t[1] == 'o') && (t[2] == 'i') && (t[3] == 'd') && !t[4]) *t = '\0'; /* make void be same as null */ printf ("%s(",s); /* output start of function */ s = t; while (*s) { /* for each argument */ while ((*s == ' ') || (*s == '\t')) s++; for (u = v = s; (*u && (*u != ',') && (*u != '[')); u++) if ((*u == ' ') || (*u == '\t')) v = u; c = *u; /* remember delimiter */ *u = '\0'; /* tie off argument name */ while (*++v == '*'); /* remove leading pointer indication */ fputs (v,stdout); /* write variable name */ *(s = u) = c; /* restore delimiter */ while (*s && (*s != ',')) *s++; if (*s) fputc (*s++,stdout); } puts (")"); /* end of function */ while (*t) { /* for each argument */ fputs (" ",stdout); while ((*t == ' ') || (*t == '\t')) t++; while (*t && (*t != ',')) fputc (*t++,stdout); puts (";"); if (*t == ',') t++; } } else printf ("%s)",s); /* say what?? */ break; default: /* doesn't look like a function */ poot (s); } } main () { char *s,*t,line[LINELENGTH]; int c; while (s = fgets (line,LINELENGTH,stdin)) switch (line[0]) { case '/': /* comment */ if ((s[1] != '*') || fndcmt (s+2)) poot (line); else do poot (line); while (!fndcmt (line) && (s = fgets (line,LINELENGTH,stdin))); break; case '{': /* open function */ case '}': /* close function */ case '\f': /* formfeed */ case '\n': /* newline */ case '#': /* preprocessor command */ poot (line); break; case '\t': /* whitespace */ case ' ': /* look like function arg def in structure? */ if ((t = skipfx (line)) && (s = fndws (t)) && (s = fndnws (s)) && (((*s == '(') && (s[1] == '*')) || ((*s == '*') && (s[1] == '(') && (s[2] == '*'))) && (s = fndws (s)) && (s[-1] == ')') && (s = fndnws (s)) && (*s == '(')) unansi (t); else poot (t); break; default: /* begins with anything else */ /* look like function proto or def? */ if ((t = skipfx (line)) && (s = fndws (t)) && (s = fndnws (s)) && (s = fndws (s)) && (s = fndnws (s)) && (*s == '(')) unansi (t); else poot (t); break; } } tkrat_2.2cvs20100105-dfsg.orig/lib/000077500000000000000000000000001137544547100165525ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/lib/.body1000066400000000000000000000000571137544547100175730ustar00rootroot00000000000000This letter should have an invalid to address. tkrat_2.2cvs20100105-dfsg.orig/lib/.cvsignore000066400000000000000000000000561137544547100205530ustar00rootroot00000000000000Makefile CMDS* test pkgIndex.tcl cmd .gdbinit tkrat_2.2cvs20100105-dfsg.orig/lib/.purify000066400000000000000000000007011137544547100200670ustar00rootroot00000000000000suppress MLK ...; XimParseStringFile suppress PLK ...; XimParseStringFile suppress MLK ...; _XlcSjisLoader suppress MLK ...; _XlcCreateLC suppress umr ...; res_send suppress umr Tcl_Release suppress umr _libc_write; _X11TransSocketWrite suppress abr lcase suppress mlk malloc; MenuNewEntry suppress mlk malloc; Sync suppress mlk malloc; Tcl_GetFile; DisplaySetupProc suppress mlk malloc; TkBTreeGetTags suppress mlk malloc; fs_get; cpystr; RatAlias tkrat_2.2cvs20100105-dfsg.orig/lib/.tkratrc000066400000000000000000000023531137544547100202300ustar00rootroot00000000000000# Stubs for commands the library expects to find proc RatLog {level message time} { puts "ratLog: $level $message $time" } proc RatStatus {message} { puts "RatStatus: $message" } proc RatFormatDate {year month day hour min sec} { puts "RatFormatDate $year $month $day $hour $min $sec" return DATE } proc RatLogin {host trial user prot} { puts "RatLogin called {$host $trial $user $prot}" return {ruric *} } proc RatWantSave {} { return } proc RatEncodingCompat {wanted avail} { set wanted [string tolower $wanted] set avail [string tolower $avail] if ![string compare $wanted $avail] { return 1 } if {![string compare us-ascii $wanted] && [regexp iso-8859- $avail]} { return 1 } return 0 } proc RatDSNRecieve {subject action recipient id} { puts "RatDSNRecieve" puts "\tSubject: $subject" puts "\tAction: $action" puts "\tRecipient: $recipient" puts "\tId: $id" } proc bgerror {message} { puts "BgError: $message" } set compressProg gzip set compressSuffix .gz if [llength [info commands wm]] { wm withdraw . bind . RatCleanup } set tkrat_version 0.50 set tkrat_patchlevel X source ../tkrat/options.tcl OptionsInit OptionsRead puts ".tkratrc read" tkrat_2.2cvs20100105-dfsg.orig/lib/Makefile.in000066400000000000000000000147531137544547100206310ustar00rootroot00000000000000############################################################################# # TkRat software and its included text is Copyright 1996-2004 by # # Martin Forssn. # # # # The full text of the legal notice is contained in the file called # # COPYRIGHT, included with this distribution. # ############################################################################# # If autoconf failed to locate the the tcl/tk include files and the # compilation fails (the compilation may succeed even if autoconf was # unable to locate the files) you can edit the definitions below to # point at the includes. When you have edited the definition you must # rerun configure. # FIX_INCLUDE = -I/strangepath/include FIX_INCLUDE = # To change the compiler switches, for example to change from -O # to -g, change the following line: EXTRA_CFLAGS = @CFLAGS@ @MEM_DEBUG_FLAGS@ # Installation directories datarootdir = @datarootdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ MAN_DIR = @mandir@ BIN_DIR = @bindir@ DATA_DIR = @datadir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` LIB_DIR = @libdir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` # Pointer to the imap c-client directory C_CLIENT = ${TOP_DIR}/imap/c-client # Used for development environment only ETAGS = @etags@ # Some versions of make, like SGI's, use the following variable to # determine which shell to use for executing commands: SHELL = /bin/sh #---------------------------------------------------------------- # The information below is modified by the configure script when # Makefile is generated from Makefile.in. You shouldn't normally # modify any of this stuff by hand. #---------------------------------------------------------------- CC = @CC@ INCLUDES = @TCL_INCLUDE@ @XINCLUDES@ TK_DEFS = @TK_DEFS@ TOP_DIR = @srcdir@/.. SHLIB_CFLAGS = @SHLIB_CFLAGS@ SHLIB_LD = @SHLIB_LD@ SHLIB_LD_LIBS = @SHLIB_LD_LIBS@ SHLIB_SUFFIX = @SHLIB_SUFFIX@ SHLIB_LD_LIBS = @SHLIB_LD_LIBS@ VERSION = @VERSION@ RATLIB_SUFFIX = @RATLIB_SUFFIX@ INSTALL = @INSTALL@ TCLSH = @tclsh@ TRANSFORM = @program_transform_name@ EXTRA_LIBS = @EXTRA_LIBS@ INSTALL_PREFIX = @INSTALL_PREFIX@ ################################################### # You shouldn't need to touch anything below here # ################################################### CFLAGS = ${TK_DEFS} ${EXTRA_CFLAGS} -I${C_CLIENT} ${FIX_INCLUDE} ${INCLUDES} \ ${SHLIB_CFLAGS} -DVERSION=\"${VERSION}\" C_CLIENT_LIB = ${C_CLIENT}/c-client.a RATLIB = ratatosk${RATLIB_SUFFIX} RATOLDLIB = ratold${RATLIB_SUFFIX} DUMMYLIB = ratDummy${SHLIB_SUFFIX} HEADERS = rat.h ratFolder.h ratPGP.h ratStdFolder.h SRC = ratAppInit.c ratFolder.c ratStdFolder.c ratMessage.c ratCode.c \ ratAddress.c ratDbase.c ratDbFolder.c ratStdMessage.c ratDbMessage.c \ ratFrMessage.c ratSender.c ratExp.c ratSequence.c \ ratMailcap.c ratCompat.c ratPGP.c ratPGPprog.c ratPwCache.c \ ratDisFolder.c ratPrint.c ratWatchdog.c ratBusy.c ratAddrList.c \ ratMsgList.c OBJ = ${SRC:.c=.o} OLDSRC = ratHold.c OLDOBJ = ${OLDSRC:.c=.o} all: ${RATLIB} ${RATOLDLIB} pkgIndex.tcl clean: rm -f ${OBJ} ${OLDOBJ} ${RATLIB} ${RATOLDLIB} ratDummy.o \ ${DUMMYLIB} core pkgIndex.tcl ${RATLIB}: ${OBJ} ${C_CLIENT_LIB} ${SHLIB_LD} -o ${RATLIB} ${OBJ} ${C_CLIENT_LIB} $(EXTRA_LIBS) ${SHLIB_LD_LIBS} ${RATOLDLIB}: ${OLDOBJ} ${SHLIB_LD} -o ${RATOLDLIB} ${OLDOBJ} $(EXTRA_LIBS) ${SHLIB_LD_LIBS} ${DUMMYLIB}: ratDummy.o ${SHLIB_LD} -o ${DUMMYLIB} ratDummy.o $(EXTRA_LIBS) ${SHLIB_LD_LIBS} pkgIndex.tcl: ${RATLIB} ${RATOLDLIB} ${DUMMYLIB} echo "if [catch {load ./${DUMMYLIB}; \ load ./${RATLIB}; \ load ./${RATOLDLIB}; \ pkg_mkIndex . ${RATLIB} ${RATOLDLIB}} error] \ {puts \$$error; exit 1}" | ${TCLSH} install.bin: ${RATLIB} ${RATOLDLIB} ${DUMMYLIB} if test ! -d ${INSTALL_PREFIX}${LIB_DIR} ; then\ ${INSTALL} -m 0755 -d ${INSTALL_PREFIX}${LIB_DIR} ;\ fi ${INSTALL} -m 0755 ${RATLIB} ${INSTALL_PREFIX}${LIB_DIR}/${RATLIB} ${INSTALL} -m 0755 ${RATOLDLIB} ${INSTALL_PREFIX}${LIB_DIR}/${RATOLDLIB} INST=${INSTALL_PREFIX}${LIB_DIR}; \ echo "if [catch {load ./${DUMMYLIB}; \ cd $$INST; \ pkg_mkIndex -load ./*${SHLIB_SUFFIX} . \ *${SHLIB_SUFFIX}} error] \ {puts \$$error; exit 1}" | ${TCLSH} install: install.bin tags-internal: ${ETAGS} --append --no-globals --output=../TAGS ${HEADERS} ${SRC} MD = ../imap/c-client/mail.h ../imap/c-client/linkage.h \ ../imap/c-client/tcp.h ../imap/c-client/nl.h \ ../imap/c-client/rfc822.h ../imap/c-client/env.h \ ../imap/c-client/smtp.h ../imap/c-client/misc.h ratAddress.o: ratAddress.c rat.h ../config.h ${MD} ratAppInit.o: Makefile ratAppInit.c ratFolder.h ratStdFolder.h rat.h \ ../config.h ratPGP.h ${MD} ratBusy.o: ratBusy.c rat.h ../config.h ${MD} ratCode.o: ratCode.c rat.h ../config.h ${MD} ratCompat.o: ratCompat.c rat.h ../config.h ${MD} ratDSN.o: ratDSN.c ratFolder.h rat.h ../config.h ${MD} ratDbFolder.o: ratDbFolder.c ratFolder.h rat.h ../config.h ${MD} ratDbMessage.o: ratDbMessage.c ratFolder.h rat.h ../config.h ${MD} ratDbase.o: ratDbase.c ratFolder.h rat.h ../config.h ${MD} ratDisFolder.o: ratDisFolder.c ratStdFolder.h ratFolder.h rat.h ../config.h \ ${MD} ../imap/c-client/mbx.h ratExp.o: ratExp.c ratFolder.h rat.h ../config.h ${MD} ratFolder.o: ratFolder.c ratFolder.h rat.h ../config.h ${MD} ratFrMessage.o: ratFrMessage.c ratFolder.h rat.h ../config.h ${MD} ratHold.o: ratHold.c rat.h ../config.h ${MD} ratMailcap.o: ratMailcap.c ratFolder.h rat.h ../config.h ${MD} ratMessage.o: ratMessage.c ratFolder.h rat.h ../config.h ratPGP.h ${MD} ratPGP.o: ratPGP.c ratFolder.h rat.h ../config.h ratPGP.h ${MD} ratPGPprog.o: ratPGPprog.c ratFolder.h rat.h ../config.h ratPGP.h ${MD} ratPrint.o: ratPrint.c ratFolder.h rat.h ../config.h ${MD} ratPwCache.o: ratPwCache.c rat.h ../config.h ${MD} \ ../imap/c-client/env_unix.h ratSender.o: ratSender.c ratFolder.h rat.h ../config.h ratPGP.h ${MD} ratStdFolder.o: ratStdFolder.c ratStdFolder.h ratFolder.h rat.h ../config.h \ ${MD} ../imap/c-client/linkage.c ratStdMessage.o: ratStdMessage.c ratStdFolder.h ratFolder.h rat.h ../config.h \ ${MD} ratWatchdog.o: ratWatchdog.c rat.h ../config.h ${MD} tkrat_2.2cvs20100105-dfsg.orig/lib/rat.h000066400000000000000000000247351137544547100175240ustar00rootroot00000000000000/* * rat.h -- * * Declarations for things used internally by the Ratatosk * procedures but not exported outside the module. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #ifndef _RAT_H #define _RAT_H #include "../config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TM_IN_SYS_TIME # include #else # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef __STDC__ # include #else # include #endif #include /* * dirent definitions */ #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif /* * Wait */ #include #if HAVE_SYS_WAIT_H # include #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif /* Last chance guess for WNOHANG */ #ifndef WNOHANG #define WNOHANG 1 #endif #include #ifndef CONST84 # define CONST84 #endif #ifndef CONST85 # define CONST85 #endif /* * Sigh, tcl uses different prototypes for its replacement functions for * malloc, realloc and free than in the original functions. Also tcl * version 8.2 does always use these replacement functions. */ #ifdef TCL_MEM_DEBUG # undef ckalloc # undef ckfree # undef ckrealloc # define ckalloc(x) Tcl_DbCkalloc(x, __FILE__, __LINE__) # define ckfree(x) Tcl_DbCkfree((char*)x, __FILE__, __LINE__) # define ckrealloc(x,y) ((x)? \ Tcl_DbCkrealloc((char*)(x), (y),__FILE__, __LINE__) \ : Tcl_DbCkalloc((y), __FILE__, __LINE__)) #else /* TCL_MEM_DEBUG */ # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 2 # undef ckalloc # undef ckfree # undef ckrealloc # define ckalloc(x) Tcl_Alloc(x) # define ckfree(x) Tcl_Free((char*)x) # define ckrealloc(x,y) ((x) ? Tcl_Realloc((char*)x, y) : Tcl_Alloc(y)) # else /* TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 2 */ # undef ckrealloc # define ckrealloc(x,y) ((x) ? realloc(x, y) : malloc(y)) # endif /* TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 2 */ #endif /* TCL_MEM_DEBUG */ #define FILEMODE 0666 /* The mode of created files */ #define DIRMODE 0777 /* The mode of created directories */ /* * The structure returned by RatDbGet which describes an entry in the * database. */ typedef enum { TO, FROM, CC, MESSAGE_ID, REFERENCE, SUBJECT, DATE, KEYWORDS, RSIZE, STATUS, EX_TIME, EX_TYPE, FILENAME, RATDBETYPE_END } RatDbEType; typedef struct RatDbEntry { char *content[RATDBETYPE_END]; } RatDbEntry; /* * The different classes of log messages */ typedef enum { RAT_BABBLE, RAT_PARSE, RAT_WARN, RAT_ERROR, RAT_FATAL, RAT_INFO } RatLogLevel; typedef enum { RATLOG_TIME, RATLOG_EXPLICIT, RATLOG_NOWAIT } RatLogType; typedef struct RatFolderInfo *RatFolderInfoPtr; /* * Current data */ typedef enum { RAT_HOST, RAT_MAILBOX, RAT_EMAILADDRESS, RAT_PERSONAL, RAT_HELO } RatCurrentType; /* * A SMTP channel */ typedef void *SMTPChannel; /* * Hexadecimal characters */ extern char alphabetHEX[17]; /* * This is used by the sender child process to indicate that logging should * be done though its own special function */ extern int is_sender_child; /* * Password to use for SMTP authentication */ extern char *smtp_passwd; /* ratAppInit.c */ extern Tcl_Interp *timerInterp; extern char *dayName[]; extern char *monthName[]; extern char *RatGetCurrent(Tcl_Interp *interp, RatCurrentType what, const char *role); extern void RatLog (Tcl_Interp *interp, RatLogLevel level, CONST84 char *message, RatLogType type); extern void RatLogF (Tcl_Interp *interp, RatLogLevel level, char *tag, RatLogType type, ...); extern Tcl_Obj *RatMangleNumber(int number); extern int RatSearch (char *searchFor, char *searchIn); extern long RatDelaySoutr (void *stream_x, char *string); extern void RatInitDelayBuffer (); extern int RatTranslateWrite(Tcl_Channel channel, CONST84 char *charbuf, int len); extern MESSAGE *RatParseMsg (Tcl_Interp *interp, unsigned char *message); extern int RatIsEmpty (const char *string); extern int RatEncodingCompat (Tcl_Interp *interp, char *wanted, char *avail); extern Tcl_ObjCmdProc RatGenIdCmd; extern Tcl_Obj *RatFormatDate(Tcl_Interp *interp, struct tm *tm); extern int RatGetTimeZone(unsigned long currentTime); extern void RatDStringApendNoCRLF(Tcl_DString *ds, const char *s, int length); extern CONST84 char *RatGetPathOption(Tcl_Interp *interp, char *name); extern CONST84 char *RatTranslateFileName(Tcl_Interp *interp, CONST84 char *name); extern char *RatReadAndCanonify(Tcl_Interp *interp, char *filename, unsigned long *size, int canonify); extern void RatCanonalize(Tcl_DString *ds); extern char* RatGenId(); extern ssize_t SafeRead(int fd, void *buf, size_t count); extern struct passwd* GetPw(void); /* RatSender.c */ extern void RatNudgeSender(Tcl_Interp *interp); extern void RatSenderLog(const char *logcmd); /* ratFolder.c */ extern int RatFolderInit (Tcl_Interp *interp); extern Tcl_TimerProc RatFolderUpdateTime; /* ratStdFolder.c */ extern void ClearStdPasswds(int freethem); extern Tcl_ObjCmdProc RatCheckEncodingsCmd; /* ratCode.c */ extern char *RatDecodeHeader(Tcl_Interp *interp, const char *string, int adr); extern Tcl_DString *RatDecode(Tcl_Interp *interp, int cte, const char *data, int length, const char *charset); extern char *RatEncodeHeaderLine(Tcl_Interp *interp, Tcl_Obj *line, int nameLength); extern void RatEncodeAddresses(Tcl_Interp *interp, ADDRESS *adrPtr); extern Tcl_Encoding RatGetEncoding(Tcl_Interp *interp, const char *name); extern Tcl_Obj *RatCode64(Tcl_Obj *oPtr); extern char *RatUtf8toMutf7(const char *src); extern char *RatMutf7toUtf8(const char *src); extern Tcl_DString* RatEncodeQP(const unsigned char *line); extern Tcl_ObjCmdProc RatEncodeQPCmd; extern unsigned char *RatDecodeQP(unsigned char *line); extern Tcl_ObjCmdProc RatDecodeQPCmd; extern void RatEncodeParameters(Tcl_Interp *interp, PARAMETER *p); extern void RatDecodeParameters(Tcl_Interp *interp, PARAMETER *p); extern Tcl_ObjCmdProc RatDecodeUrlcCmd; /* ratAddress.c */ extern void RatInitAddressHandling(Tcl_Interp *interp); extern void RatInitAddresses(Tcl_Interp *interp, ADDRESS *addressPtr); extern int RatAddressIsMe(Tcl_Interp *interp, ADDRESS *adrPtr, int trustUser); extern int RatAddressCompare(ADDRESS *adr1Ptr, ADDRESS* adr2Ptr); extern void RatAddressTranslate(Tcl_Interp *interp, ADDRESS *adrPtr); extern void RatAddressTranslate(Tcl_Interp *interp, ADDRESS *adrPtr); extern char *RatAddressMail(ADDRESS *adrPtr); extern char *RatAddressFull(Tcl_Interp *interp, ADDRESS *adrPtr, char *role); extern size_t RatAddressSize(ADDRESS *adrPtr, int all); extern void RatGenerateAddresses(Tcl_Interp *interp, const char *role, char *msgh, ADDRESS **from, ADDRESS **sender); extern CONST84 char *RatFindCharInHeader(CONST84 char *header, char m); extern void RatInitAddessHandling(Tcl_Interp *interp); extern Tcl_ObjCmdProc RatSplitAdrCmd; /* ratDbase.c */ extern int RatDbInsert (Tcl_Interp *interp, const char *to, const char *from, const char *cc, const char *msgid, const char *ref, const char *subject, long date, const char *flags, const char *keywords, long exDate, const char *exType, const char *fromline, const char *mail, int length); extern int RatDbSetStatus (Tcl_Interp *interp, int index, char *status); extern int RatDbSearch (Tcl_Interp *interp, Tcl_Obj *exp, int *numFoundPtr, int **foundPtrPtr, int *expError); extern RatDbEntry *RatDbGetEntry (int index); extern MESSAGE *RatDbGetMessage (Tcl_Interp *interp, int index, char **bufPtr); extern char *RatDbGetHeaders (Tcl_Interp *interp, int index); extern char *RatDbGetFrom(Tcl_Interp *interp, int index); extern char *RatDbGetText (Tcl_Interp *interp, int index); extern int RatDbExpunge (Tcl_Interp *interp); extern int RatDbDaysSinceExpire (Tcl_Interp *interp); extern int RatDbExpire (Tcl_Interp *interp, char *infolder, char *backupDirectory); extern void RatDbClose(void); extern int RatDbCheck(Tcl_Interp *interp, int fix); extern Tcl_ObjCmdProc RatDbaseInfoCmd; extern Tcl_ObjCmdProc RatDbaseKeywordsCmd; extern int RatDbSetInfo(Tcl_Interp *interp, int *indexes, int num_indexes, Tcl_Obj *keywords, Tcl_Obj *ex_time, Tcl_Obj *ex_type); /* ratMessage.c */ extern int RatMessageGetHeader(Tcl_Interp *interp, char *srcHeader); Tcl_Obj *RatWrapMessage(Tcl_Interp *interp, Tcl_Obj *oPtr); /* ratMailcap.c */ extern Tcl_ObjCmdProc RatMailcapReloadCmd; /* ratCompat.c */ #ifndef HAVE_SNPRINTF extern size_t snprintf (char *buf, size_t buflen, const char *fmt, ...); #endif /* HAVE_SNPRINTF */ #ifndef HAVE_STRLCPY extern char *strlcpy(char *dst, const char *src, size_t n); #endif /* HAVE_STRLCPY */ #ifndef HAVE_STRLCAT extern char *strlcat(char *dst, const char *src, size_t n); #endif /* HAVE_STRLCAT */ /* ratPwCache.c */ extern char *RatGetCachedPassword(Tcl_Interp *interp, const char *spec); extern void RatCachePassword(Tcl_Interp *interp, const char *spec, const char *passwd, int store); extern void RatPasswdCachePurge(Tcl_Interp *interp, int disk_also); extern Tcl_ObjCmdProc RatPasswdCachePurgeCmd; /* ratPrint.c */ extern Tcl_ObjCmdProc RatPrettyPrintMsgCmd; /* ratWatchdog.c */ extern void RatReleaseWatchdog(const char *tmpdir); /* ratBusy.c */ extern void RatSetBusy(Tcl_Interp *interp); extern void RatClearBusy(Tcl_Interp *interp); extern Tcl_ObjCmdProc RatBusyCmd; /* ratAddrList.c */ extern Tcl_ObjCmdProc RatGetMatchingAddrsImplCmd; /* ratSequence.c */ typedef void *rat_sequence_t; extern rat_sequence_t RatSequenceInit(void); extern void RatSequenceAdd(rat_sequence_t seq, unsigned long no); extern int RatSequenceNotempty(rat_sequence_t seq); extern char *RatSequenceGet(rat_sequence_t seq); extern void RatSequenceFree(rat_sequence_t seq); extern Tcl_ObjCmdProc RatCreateSequenceCmd; #endif /* _RAT_H */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratAddrList.c000066400000000000000000000050531137544547100211360ustar00rootroot00000000000000/* * ratAddrlist.c -- * * This file contains the native support for searching in the address list * * TkRat software and its included text is Copyright 1996-2005 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "rat.h" /* *---------------------------------------------------------------------- * * GetMatchingAddrsImplCmd -- * * This routine creates an address command by an address given * as argument * * Results: * A list of address entity names is appended to the result * * Side effects: * New address entities are created, * * *---------------------------------------------------------------------- */ int RatGetMatchingAddrsImplCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { int i, listc, max, matchlen, found=0; Tcl_Obj **listv, *ret, *o; char *match, *name, *email, buf[1024]; ADDRESS adr; if (4 != objc || TCL_OK != Tcl_ListObjGetElements(interp, objv[1], &listc, &listv) || TCL_OK != Tcl_GetIntFromObj(interp, objv[3], &max)) { Tcl_AppendResult(interp, "Usage: ", Tcl_GetString(objv[0]), " addrlist match max", (char*)NULL); return TCL_ERROR; } match = Tcl_GetStringFromObj(objv[2], &matchlen); ret = Tcl_NewObj(); for (i=0; inext) { newPtr = mail_newaddr(); if (adrPtr->personal) { newPtr->personal = cpystr(RatDecodeHeader(interp, adrPtr->personal, 0)); } if (adrPtr->adl) newPtr->adl = cpystr(adrPtr->adl); if (adrPtr->mailbox) newPtr->mailbox = cpystr(adrPtr->mailbox); if (adrPtr->host) newPtr->host = cpystr(adrPtr->host); if (adrPtr->error) newPtr->error = cpystr(adrPtr->error); sprintf(name, "RatAddress%d", numAddresses++); Tcl_CreateObjCommand(interp, name, RatAddressCmd, (ClientData) newPtr, RatDeleteAddress); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewStringObj(name, -1)); } Tcl_SetObjResult(interp, rPtr); } /* *---------------------------------------------------------------------- * * RatAddressCmd -- * * This routine handles the address entity commands. See ../doc/interface * for a documentation of them. * * Results: * A standard tcl result. * * Side effects: * May be some * * *---------------------------------------------------------------------- */ static int RatAddressCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { ADDRESS *adrPtr = (ADDRESS*)clientData; Tcl_CmdInfo info; int useup; if (objc < 2) goto usage; if (!strcmp(Tcl_GetString(objv[1]), "isMe")) { if (3 == objc) { Tcl_GetBooleanFromObj(interp, objv[2], &useup); } else { useup = 1; } Tcl_SetObjResult(interp, Tcl_NewBooleanObj( RatAddressIsMe(interp, adrPtr, useup))); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "compare")) { if (objc != 3) goto usage; if (0 == Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &info)) { Tcl_AppendResult(interp, "there is no address entity \"", Tcl_GetString(objv[2]), "\"", (char *) NULL); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewBooleanObj( RatAddressCompare(adrPtr, (ADDRESS*)info.objClientData))); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "set")) { if (objc != 5) goto usage; ckfree(adrPtr->mailbox); ckfree(adrPtr->personal); ckfree(adrPtr->host); adrPtr->personal = cpystr(Tcl_GetString(objv[2])); adrPtr->mailbox = cpystr(Tcl_GetString(objv[3])); adrPtr->host = cpystr(Tcl_GetString(objv[4])); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "get")) { if (objc != 3) goto usage; if (!strcasecmp(Tcl_GetString(objv[2]), "rfc822")) { char *out = RatAddressFull(interp, adrPtr, NULL); Tcl_SetResult(interp, out, TCL_VOLATILE); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[2]), "mail")) { Tcl_SetResult(interp, RatAddressMail(adrPtr), TCL_VOLATILE); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[2]), "name")) { if (adrPtr->personal) { Tcl_SetResult(interp, adrPtr->personal, TCL_VOLATILE); } return TCL_OK; } else { goto usage; } } usage: Tcl_AppendResult(interp, "Illegal usage of \"", Tcl_GetString(objv[0]), "\"", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatDeleteAddress -- * * Frees the client data of an address entity. * * Results: * None. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static void RatDeleteAddress(ClientData clientData) { ADDRESS *adrPtr = (ADDRESS*)clientData; ckfree(adrPtr->personal); ckfree(adrPtr->adl); ckfree(adrPtr->mailbox); ckfree(adrPtr->host); ckfree(adrPtr->error); ckfree(adrPtr); } /* *---------------------------------------------------------------------- * * RatRebuildMyAddressTable -- * * Watch for changes to role email addresses and update the current * role address table. * * * Results: * None. * * Side effects: * See above. * * *---------------------------------------------------------------------- */ static void RatRebuildMyAddressTable(Tcl_HashTable *myaddresstable) { Tcl_HashEntry *hPtr, *entryPtr; Tcl_HashSearch search; int new; char *emailaddress; Tcl_DeleteHashTable(myaddresstable); /* * Walk current role address table to find all the email addresses * and add entries for each to the the address table. */ Tcl_InitHashTable(myaddresstable, TCL_STRING_KEYS); for (hPtr = Tcl_FirstHashEntry(myRolesCurrent, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { emailaddress = Tcl_GetHashValue(hPtr); entryPtr = Tcl_CreateHashEntry(myaddresstable, emailaddress, &new); Tcl_SetHashValue(entryPtr, "role"); } entryPtr = Tcl_CreateHashEntry(myaddresstable, myDefaultEmailAddress, &new); Tcl_SetHashValue(entryPtr, "me"); } /* *---------------------------------------------------------------------- * * RatRoleWatcher -- * * Watch for changes to role email addresses and update the current * role address table. * * MyAddressTable is rebuilt if clientData != NULL * * * Results: * None. * * Side effects: * See above. * * *---------------------------------------------------------------------- */ static char * RatRoleWatcher(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, CONST84 char *name2, int flags) { char role[1024], *comma; char *emailaddress, *oldaddress, *cp; int new; Tcl_HashEntry *entryPtr; if (flags & TCL_INTERP_DESTROYED) { return NULL; } strlcpy(role, name2, sizeof(role)); comma = strchr(role, ','); if (comma) { *comma = '\0'; } emailaddress = cpystr(RatGetCurrent(interp, RAT_EMAILADDRESS, role)); for (cp = emailaddress; *cp; cp++) { *cp = tolower((unsigned char) *cp); } entryPtr = Tcl_CreateHashEntry(myRolesCurrent, role, &new); if (new == 0) { oldaddress = Tcl_GetHashValue(entryPtr); if (oldaddress) { ckfree(oldaddress); } } Tcl_SetHashValue(entryPtr, emailaddress); if (clientData) { RatRebuildMyAddressTable(clientData); } return NULL; } /* *---------------------------------------------------------------------- * * RatRoleListWatcher -- * * Update myAddressesTable whenever the roles option is changed. * * Results: * None. * * Side effects: * See above. * * *---------------------------------------------------------------------- */ static char* RatRoleListWatcher(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, CONST84 char *name2, int flags) { Tcl_Obj **objv, *oPtr; Tcl_HashTable *myRolesTmp; Tcl_HashEntry *entryPtr; Tcl_HashSearch search; int objc, i, new; char *role, *emailaddress; char buf[1024]; if (flags & TCL_INTERP_DESTROYED) { return NULL; } /* * The role list has changed, so swap the the role tables so that * the changes can be found and the new table updated. */ myRolesTmp = myRolesCurrent; myRolesCurrent = myRolesPrevious; myRolesPrevious = myRolesTmp; Tcl_InitHashTable(myRolesCurrent, TCL_STRING_KEYS); /* * Copy entries common to the old and new lists of roles. * Add email addresses and set up traces on any new roles. */ oPtr = Tcl_GetVar2Ex(interp, name1, name2, flags & TCL_GLOBAL_ONLY); if (oPtr != NULL) { Tcl_ListObjGetElements(interp, oPtr, &objc, &objv); for (i=0; imailbox == NULL) || (adrPtr->host == NULL)) { return 0; } snprintf(buf, sizeof(buf), "%s@%s", adrPtr->mailbox, adrPtr->host); for (cp = buf; *cp; cp++) { *cp = tolower((unsigned char) *cp); } entryPtr = Tcl_FindHashEntry(&myAddressesTable, buf); if (entryPtr != NULL) { cp = (char *)Tcl_GetHashValue(entryPtr); if (cp[0] == 'm' || (trustUser && (cp[0] == 'r'))) { return 1; } } if (trustUser && Tcl_GetCommandInfo(interp, "RatUP_IsMe", &cmdInfo)) { Tcl_DString cmd; int isMe; Tcl_Obj *oPtr; Tcl_DStringInit(&cmd); Tcl_DStringAppendElement(&cmd, "RatUP_IsMe"); Tcl_DStringAppendElement(&cmd, adrPtr->mailbox ? adrPtr->mailbox : ""); Tcl_DStringAppendElement(&cmd, adrPtr->host ? adrPtr->host : ""); Tcl_DStringAppendElement(&cmd, adrPtr->personal ? adrPtr->personal:""); Tcl_DStringAppendElement(&cmd, adrPtr->adl?adrPtr->adl:""); if (TCL_OK == Tcl_Eval(interp, Tcl_DStringValue(&cmd)) && (oPtr = Tcl_GetObjResult(interp)) && TCL_OK == Tcl_GetBooleanFromObj(interp, oPtr, &isMe)) { Tcl_DStringFree(&cmd); return isMe; } Tcl_DStringFree(&cmd); } return 0; } /* *---------------------------------------------------------------------- * * RatAddressCompare -- * * Check if two addresses are equal. * * Results: * If they are then zero is returned otherwise non zero. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatAddressCompare(ADDRESS *adr1Ptr, ADDRESS *adr2Ptr) { if (((adr1Ptr->mailbox && adr2Ptr->mailbox && !strcasecmp(adr1Ptr->mailbox, adr2Ptr->mailbox)) || adr1Ptr->mailbox == adr2Ptr->mailbox) && ((adr1Ptr->host && adr2Ptr->host && !strcasecmp(adr1Ptr->host, adr2Ptr->host)) || adr1Ptr->host == adr2Ptr->host)) { return 0; } else { return 1; } } /* *---------------------------------------------------------------------- * * RatAddressTranslate -- * * Let the user do their translation of this address. * * Results: * None. * * Side effects: * The address may be affected. * * *---------------------------------------------------------------------- */ void RatAddressTranslate(Tcl_Interp *interp, ADDRESS *adrPtr) { Tcl_CmdInfo cmdInfo; Tcl_DString cmd; Tcl_Obj *oPtr, *lPtr; char **destPtrPtr = NULL, *s; int argc, i; if (!Tcl_GetCommandInfo(interp, "RatUP_Translate", &cmdInfo)) { return; } Tcl_DStringInit(&cmd); Tcl_DStringAppendElement(&cmd, "RatUP_Translate"); Tcl_DStringAppendElement(&cmd,adrPtr->mailbox?adrPtr->mailbox:""); Tcl_DStringAppendElement(&cmd,adrPtr->host?adrPtr->host:""); Tcl_DStringAppendElement(&cmd,adrPtr->personal?adrPtr->personal:""); Tcl_DStringAppendElement(&cmd,adrPtr->adl?adrPtr->adl:""); if (TCL_OK != Tcl_Eval(interp, Tcl_DStringValue(&cmd)) || !(lPtr = Tcl_GetObjResult(interp)) || TCL_OK != Tcl_ListObjLength(interp, lPtr, &argc) || 4 != argc) { RatLogF(interp, RAT_ERROR, "translate_error", RATLOG_TIME, Tcl_DStringValue(&cmd)); } else { for (i=0; i<4; i++) { switch(i) { case 0: destPtrPtr = &adrPtr->mailbox; break; case 1: destPtrPtr = &adrPtr->host; break; case 2: destPtrPtr = &adrPtr->personal; break; case 3: destPtrPtr = &adrPtr->adl; break; } Tcl_ListObjIndex(interp, lPtr, i, &oPtr); s = Tcl_GetString(oPtr); if ( (*s && (!(*destPtrPtr) || strcmp(s,*destPtrPtr))) || (!*s && *destPtrPtr)) { ckfree(*destPtrPtr); if (*s) { *destPtrPtr = cpystr(s); } else { *destPtrPtr = NULL; } } } } Tcl_DStringFree(&cmd); } /* *---------------------------------------------------------------------- * * RatAliasCmd -- * * Implements the RatAlias command as per ../doc/interface * * Results: * Probably. * * Side effects: * Probably. * * *---------------------------------------------------------------------- */ static int RatAliasCmd(ClientData dummy,Tcl_Interp *interp,int objc,Tcl_Obj *CONST objv[]) { Tcl_Obj *oPtr; if (objc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " option ?arg?\"", (char *) NULL); return TCL_ERROR; } if (!strcmp(Tcl_GetString(objv[1]), "add")) { AliasInfo *aliasPtr; Tcl_HashEntry *entryPtr; Tcl_Obj **fobjv; char *key, *s; int new, fobjc; aliasPtr = (AliasInfo*)ckalloc(sizeof(AliasInfo)); switch (objc) { case 5: aliasPtr->book = Tcl_NewStringObj("Personal", -1); key = Tcl_GetString(objv[2]); aliasPtr->fullname = objv[3]; aliasPtr->content = objv[4]; aliasPtr->comment = Tcl_NewObj(); aliasPtr->pgp_key = Tcl_NewObj(); aliasPtr->flags = 0; break; case 6: aliasPtr->book = Tcl_NewStringObj("Personal", -1); key = Tcl_GetString(objv[2]); aliasPtr->fullname = objv[3]; aliasPtr->content = objv[4]; aliasPtr->comment = Tcl_NewObj(); aliasPtr->pgp_key = Tcl_NewObj(); aliasPtr->flags = 0; break; case 7: aliasPtr->book = objv[2]; key = Tcl_GetString(objv[3]); aliasPtr->fullname = objv[4]; aliasPtr->content = objv[5]; aliasPtr->comment = objv[6]; aliasPtr->pgp_key = Tcl_NewObj(); aliasPtr->flags = 0; break; case 8: aliasPtr->book = objv[2]; key = Tcl_GetString(objv[3]); aliasPtr->fullname = objv[4]; aliasPtr->content = objv[5]; aliasPtr->comment = objv[6]; aliasPtr->pgp_key = Tcl_NewObj(); if (!strcmp(Tcl_GetString(objv[7]), "nofullname")) { aliasPtr->flags = ALIAS_FLAG_NOFULLNAME; } else { aliasPtr->flags = 0; } break; case 9: aliasPtr->book = objv[2]; key = Tcl_GetString(objv[3]); aliasPtr->fullname = objv[4]; aliasPtr->content = objv[5]; aliasPtr->comment = objv[6]; aliasPtr->pgp_key = objv[7]; aliasPtr->flags = 0; if (TCL_OK == Tcl_ListObjGetElements(interp, objv[8], &fobjc, &fobjv)) { int i, j; for (i=0; iflags |= alias_flags[j].flag; break; } } } } break; default: Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " add book name fullname content comment options\"", (char *) NULL); return TCL_ERROR; } if (!key || !*key) { ckfree(aliasPtr); Tcl_SetResult(interp, "The name can not be an empty string", TCL_STATIC); return TCL_OK; } if (overrideBook) { Tcl_IncrRefCount(aliasPtr->book); Tcl_DecrRefCount(aliasPtr->book); aliasPtr->book = overrideBook; } aliasPtr->parsed = NULL; aliasPtr->mark = 0; if (Tcl_IsShared(aliasPtr->content)) { aliasPtr->content = Tcl_DuplicateObj(aliasPtr->content); } s = cpystr(Tcl_GetString(aliasPtr->content)); rfc822_parse_adrlist(&aliasPtr->parsed, s, ""); ckfree(s); aliasPtr->address = NULL; if (aliasPtr->parsed && aliasPtr->parsed->next) { aliasPtr->flags |= ALIAS_FLAG_ISLIST; } else if (aliasPtr->parsed) { char buf[1024]; snprintf(buf, sizeof(buf), "%s@%s", aliasPtr->parsed->mailbox, aliasPtr->parsed->host); if (!aliasPtr->parsed->personal && !(aliasPtr->flags & ALIAS_FLAG_NOFULLNAME)) { aliasPtr->parsed->personal = cpystr(Tcl_GetString(aliasPtr->fullname)); } aliasPtr->address = cpystr(buf); entryPtr = Tcl_CreateHashEntry(&addressTable, buf, &new); Tcl_SetHashValue(entryPtr, (ClientData)aliasPtr); } Tcl_IncrRefCount(aliasPtr->book); Tcl_IncrRefCount(aliasPtr->fullname); Tcl_IncrRefCount(aliasPtr->content); Tcl_IncrRefCount(aliasPtr->comment); Tcl_IncrRefCount(aliasPtr->pgp_key); entryPtr = Tcl_CreateHashEntry(&aliasTable, key, &new); Tcl_SetHashValue(entryPtr, (ClientData)aliasPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "delete")) { Tcl_HashEntry *entryPtr; AliasInfo *aliasPtr; int i; for (i=2; ibook); Tcl_DecrRefCount(aliasPtr->fullname); Tcl_DecrRefCount(aliasPtr->content); Tcl_DecrRefCount(aliasPtr->comment); Tcl_DecrRefCount(aliasPtr->pgp_key); if (aliasPtr->address && (entryPtr = Tcl_FindHashEntry(&addressTable, aliasPtr->address))) { Tcl_DeleteHashEntry(entryPtr); ckfree(aliasPtr->address); } mail_free_address(&aliasPtr->parsed); ckfree(aliasPtr); } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "get")) { Tcl_HashEntry *entryPtr; AliasInfo *aliasPtr; if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " get alias\"", (char *) NULL); return TCL_ERROR; } if (!(entryPtr=Tcl_FindHashEntry(&aliasTable,Tcl_GetString(objv[2])))){ Tcl_SetResult(interp, "Illegal alias", TCL_STATIC); return TCL_ERROR; } aliasPtr = (AliasInfo*)Tcl_GetHashValue(entryPtr); oPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->book); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->fullname); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->content); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->comment); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->pgp_key); Tcl_ListObjAppendElement(interp, oPtr, RatGetFlagsList(interp, aliasPtr)); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "list")) { Tcl_HashEntry *entryPtr; Tcl_HashSearch search; AliasInfo *aliasPtr; char *key, keybuf[1024]; if ((objc != 3 && objc != 4) || (objc == 4 && strcmp("nocase", Tcl_GetString(objv[3])))) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " list var ?nocase?\"", (char*)NULL); return TCL_ERROR; } for (entryPtr = Tcl_FirstHashEntry(&aliasTable, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { aliasPtr = (AliasInfo*) Tcl_GetHashValue(entryPtr); oPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->book); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->fullname); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->content); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->comment); Tcl_ListObjAppendElement(interp, oPtr, aliasPtr->pgp_key); Tcl_ListObjAppendElement(interp, oPtr, RatGetFlagsList(interp, aliasPtr)); key = Tcl_GetHashKey(&aliasTable, entryPtr); if (objc == 4) { strlcpy(keybuf, key, sizeof(keybuf)); lcase((unsigned char*)keybuf); key = keybuf; } Tcl_SetVar2Ex(interp, Tcl_GetString(objv[2]), key, oPtr, 0); } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "read")) { Tcl_Channel channel; int ret; if (objc != 4) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " read book filename\"", (char *) NULL); return TCL_ERROR; } if (NULL == (channel = Tcl_OpenFileChannel(interp, Tcl_GetString(objv[3]), "r", 0))) { return TCL_ERROR; } /* XXX */ Tcl_SetChannelOption(interp, channel, "-encoding", "utf-8"); oPtr = Tcl_NewObj(); while (0 <= Tcl_GetsObj(channel, oPtr) && !Tcl_Eof(channel)) { Tcl_AppendToObj(oPtr, ";", 1); } Tcl_Close(interp, channel); overrideBook = objv[2]; ret = Tcl_EvalObjEx(interp, oPtr, TCL_EVAL_DIRECT); overrideBook = NULL; return ret; } else if (!strcmp(Tcl_GetString(objv[1]), "save")) { Tcl_HashEntry *entryPtr; Tcl_HashSearch search; AliasInfo *aliasPtr; Tcl_Channel channel; Tcl_Obj *lPtr; if (objc != 4) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0])," save book filename\"", (char*)NULL); return TCL_ERROR; } if (NULL == (channel = Tcl_OpenFileChannel(interp, Tcl_GetString(objv[3]), "w", 0666))) { return TCL_ERROR; } for (entryPtr = Tcl_FirstHashEntry(&aliasTable, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { aliasPtr = (AliasInfo*) Tcl_GetHashValue(entryPtr); if (strcmp(Tcl_GetString(objv[2]), Tcl_GetString(aliasPtr->book))){ continue; } lPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, lPtr, aliasPtr->book); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj(Tcl_GetHashKey(&aliasTable,entryPtr),-1)); Tcl_ListObjAppendElement(interp, lPtr, aliasPtr->fullname); Tcl_ListObjAppendElement(interp, lPtr, aliasPtr->content); Tcl_ListObjAppendElement(interp, lPtr, aliasPtr->comment); Tcl_ListObjAppendElement(interp, lPtr, aliasPtr->pgp_key); Tcl_ListObjAppendElement(interp, lPtr, RatGetFlagsList(interp, aliasPtr)); Tcl_IncrRefCount(lPtr); Tcl_WriteChars(channel, "RatAlias add ", -1); Tcl_WriteObj(channel, lPtr); Tcl_DecrRefCount(lPtr); Tcl_WriteChars(channel, "\n", 1); } return Tcl_Close(interp, channel); } else if (!strcmp(Tcl_GetString(objv[1]), "expand")) { Tcl_HashEntry *entryPtr; AliasInfo *aliasPtr; AliasExpand ae; Tcl_DString list; char *role, *c, *s; ADDRESS *adrPtr, *baseAdrPtr = NULL; if (objc != 5) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " expand level adrlist role\"", (char *) NULL); return TCL_ERROR; } if (!strcmp(Tcl_GetString(objv[2]), "display")) { ae.target = EXPAND_DISPLAY; } else if (!strcmp(Tcl_GetString(objv[2]), "sending")) { ae.target = EXPAND_SENDING; } else if (!strcmp(Tcl_GetString(objv[2]), "pgp")) { ae.target = EXPAND_PGP; } else if (!strcmp(Tcl_GetString(objv[2]), "pgpactions")) { ae.target = EXPAND_PGPACTIONS; } else { Tcl_AppendResult(interp, "bad level argument \"", Tcl_GetString(objv[2]), "\" should be display," " sending, pgp or pgpactions", (char *) NULL); return TCL_ERROR; } role = Tcl_GetString(objv[4]); ae.host = RatGetCurrent(interp, RAT_HOST, role); oPtr = Tcl_GetVar2Ex(interp, "option", "lookup_name", TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &ae.lookup_in_passwd); ae.flags = 0; /* * Ignore empty addresses */ for (c = Tcl_GetString(objv[3]); *c && isspace((unsigned char)*c);c++); if (!*c) { if (EXPAND_PGPACTIONS == ae.target) { Tcl_Obj *robjv[2]; robjv[0] = robjv[1] = Tcl_NewBooleanObj(0); Tcl_SetObjResult(interp, Tcl_NewListObj(2, robjv)); } return TCL_OK; } /* * Create unique mark * Reset all aliases if alias mark has wrapped */ if (0 == ++aliasMark) { Tcl_HashSearch search; aliasMark++; for (entryPtr = Tcl_FirstHashEntry(&aliasTable, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { aliasPtr = (AliasInfo*) Tcl_GetHashValue(entryPtr); aliasPtr->mark = 0; } } /* * Simplify whitespace and parse list */ s = cpystr(Tcl_GetString(objv[3])); for (c=s; *c; c++) { if ('\n' == *c || '\r' == *c || '\t' == *c) { *c = ' '; } } ae.mark = cpystr(ae.host); for (c=ae.mark; *c; c++) { *c = '#'; } rfc822_parse_adrlist(&baseAdrPtr, s, ae.mark); ckfree(s); for (adrPtr = baseAdrPtr; adrPtr; adrPtr = adrPtr->next) { if (adrPtr->error || (adrPtr->host && adrPtr->host[0] == '.')) { mail_free_address(&baseAdrPtr); Tcl_SetResult(interp, "Error in address list", TCL_STATIC); return TCL_ERROR; } } Tcl_DStringInit(&list); RatExpandAlias(interp, baseAdrPtr, &list, &ae); mail_free_address(&baseAdrPtr); if (EXPAND_PGPACTIONS == ae.target) { Tcl_Obj *robjv[2]; robjv[0] = Tcl_NewBooleanObj(ae.flags & ALIAS_FLAG_PGP_SIGN); robjv[1] = Tcl_NewBooleanObj(ae.flags & ALIAS_FLAG_PGP_ENCRYPT); Tcl_SetObjResult(interp, Tcl_NewListObj(2, robjv)); } else { Tcl_DStringResult(interp, &list); } return TCL_OK; } else { Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[1]), "\": must be one of add, delete, get, list, read, save,", " or expand", (char *) NULL); return TCL_ERROR; } } /* *---------------------------------------------------------------------- * * RatAddressMail -- * * Prints the mail address in rfc822 format of an ADDRESS entry. * Only one address is printed and there is NO fullname. * * Results: * Pointer to a static storage area where the string is stored. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatAddressMail(ADDRESS *adrPtr) { static char *store = NULL; static int length = 0; size_t size = RatAddressSize(adrPtr, 1); if (size > length) { length = size+1024; store = ckrealloc(store, length); } store[0] = '\0'; rfc822_address(store, adrPtr); return store; } /* *---------------------------------------------------------------------- * * RatAddressFull -- * * Prints the full address in rfc822 format of an ADDRESS entry. * * Results: * Pointer to a static storage area where the string is stored. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatAddressFull(Tcl_Interp *interp, ADDRESS *adrPtr, char *role) { static char *store = NULL; static int length = 0; size_t size = RatAddressSize(adrPtr, 1); ADDRESS *next = adrPtr->next; int host_replaced = 0; if (size > length) { length = size+1024; store = ckrealloc(store, length); } store[0] = '\0'; adrPtr->next = NULL; if (NULL == adrPtr->host && role) { adrPtr->host = RatGetCurrent(interp, RAT_HOST, role); host_replaced = 1; } rfc822_write_address_full(store, adrPtr, NULL); adrPtr->next = next; if (host_replaced) { adrPtr->host = NULL; } if (strstr(store, "=?")) { return RatDecodeHeader(interp, store, 1); } else { return store; } } /* *---------------------------------------------------------------------- * * RatSplitAdrCmd -- * * This routine takes an address list as argument and splits it. * * Results: * A list of addresses contained in the argument * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatSplitAdrCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *rPtr; CONST84 char *s, *e, *n; if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " addresslist\"", (char *) NULL); return TCL_ERROR; } rPtr = Tcl_NewObj(); s = Tcl_GetString(objv[1]); while (*s) { while (*s && isspace(*s)) { s++; } e = n = RatFindCharInHeader(s, ','); if (NULL == e) { e = n = s+strlen(s); } for (e--; isspace(*e) && e>s; e--) { /* Do nothing */ } Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewStringObj(s, e-s+1)); s = n; if (*s) { s++; } } Tcl_SetObjResult(interp, rPtr); return TCL_OK; } #ifdef MEM_DEBUG void ratAddressCleanup() { Tcl_HashEntry *e; Tcl_HashSearch s; for (e = Tcl_FirstHashEntry(&aliasTable, &s); e; e =Tcl_NextHashEntry(&s)){ ckfree(Tcl_GetHashValue(e)); } Tcl_DeleteHashTable(&aliasTable); ckfree(mem_store); } #endif /* MEM_DEBUG */ /* *---------------------------------------------------------------------- * * RatAddressSize -- * * Calculate the maximum size of and address list (or single address) * * Results: * The maximum length of the address * * Side effects: * None. * * *---------------------------------------------------------------------- */ size_t RatAddressSize(ADDRESS *adrPtr, int all) { ADDRESS *a,tadr; char tmp[MAILTMPLEN]; size_t len, t; tadr.next = NULL; for (len = 0, a = adrPtr; a; a = a->next) { t = (tadr.mailbox = a->mailbox) ? 2*strlen (a->mailbox) : 3; if ((tadr.personal = a->personal)) t += 3 + 2*strlen (a->personal); if ((tadr.adl = a->adl)) t += 1 + 2*strlen (a->adl); if ((tadr.host = a->host)) t += 1 + 2*strlen (a->host); if (t < MAILTMPLEN) { /* ignore ridiculous addresses */ tmp[0] = '\0'; rfc822_write_address (tmp,&tadr); t = strlen(tmp); } len += t+2; if (!all) break; } return len; } /* *---------------------------------------------------------------------- * * RatGenerateAddresses -- * * Generates addresses to be used when sending email * * Results: * The generated addresses are left in the ADDRESS-pointers * (which are assumed to not point to anything) * * Side effects: * None. * * *---------------------------------------------------------------------- */ void RatGenerateAddresses(Tcl_Interp *interp, const char *role, char *msgh, ADDRESS **from, ADDRESS **sender) { char *tmp, host[1024]; const char *ctmp; Tcl_Obj *oPtr; int useFrom, cs; strlcpy(host, RatGetCurrent(interp, RAT_HOST, role), sizeof(host)); *from = NULL; *sender = NULL; oPtr = Tcl_GetVar2Ex(interp, "option", "use_from", TCL_GLOBAL_ONLY); if (TCL_OK != Tcl_GetBooleanFromObj(interp, oPtr, &useFrom)) { useFrom = 0; } if (useFrom && (ctmp = Tcl_GetVar2(interp, msgh, "from",TCL_GLOBAL_ONLY)) && !RatIsEmpty(ctmp)) { tmp = cpystr(ctmp); rfc822_parse_adrlist(from, tmp, host); ckfree(tmp); } oPtr = Tcl_GetVar2Ex(interp, "option","create_sender",TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &cs); if (*from && cs) { ADDRESS *adrPtr; for (adrPtr = *from; adrPtr; adrPtr = adrPtr->next) { if (RatAddressIsMe(interp, adrPtr, 0)) { break; } } if (!adrPtr) { *sender = mail_newaddr(); (*sender)->personal = cpystr(RatGetCurrent(interp, RAT_PERSONAL, role)); (*sender)->mailbox = cpystr(RatGetCurrent(interp, RAT_MAILBOX, role)); (*sender)->host = cpystr(host); RatEncodeAddresses(interp, *sender); } } else if (!*from) { *from = mail_newaddr(); (*from)->personal = cpystr(RatGetCurrent(interp, RAT_PERSONAL, role)); (*from)->mailbox = cpystr(RatGetCurrent(interp, RAT_MAILBOX, role)); (*from)->host = cpystr(host); } RatEncodeAddresses(interp, *from); } /* *---------------------------------------------------------------------- * * RatGenerateAddressesCmd -- * * See ../doc/interface for documentation. * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatGenerateAddressesCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { ADDRESS *from, *sender; const char *role; char buf[1024]; Tcl_Obj *oPtr; if (2 != objc) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " handler\"", (char *) NULL); return TCL_ERROR; } role = Tcl_GetVar2(interp, Tcl_GetString(objv[1]), "role",TCL_GLOBAL_ONLY); RatGenerateAddresses(interp, role, Tcl_GetString(objv[1]), &from, &sender); oPtr = Tcl_NewObj(); buf[0] = '\0'; rfc822_write_address_full(buf, from, NULL); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewStringObj(buf, -1)); buf[0] = '\0'; rfc822_write_address_full(buf, sender, NULL); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewStringObj(buf, -1)); buf[0] = '\0'; mail_free_address(&from); mail_free_address(&sender); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatExpandAlias -- * * Expand an alias list * * Results: * None * * Side effects: * May modify the given list. * * *---------------------------------------------------------------------- */ static void RatExpandAlias(Tcl_Interp *interp, ADDRESS *address, Tcl_DString *list, AliasExpand *ae) { AliasInfo *aliasPtr = NULL; Tcl_HashEntry *entryPtr; struct passwd *pwPtr; ADDRESS *ta, *next_address, aliasAddr; char key_buf[1024], out_buf[1024]; char *key, *s; for (; address; address = address->next) { if (address->host && strlen(address->host) && (ae->mark == NULL || strcmp(ae->mark, address->host))) { snprintf(key_buf, sizeof(key_buf), "%s@%s", address->mailbox, address->host); key = key_buf; } else { key = address->mailbox; } if (!key) { continue; } if (NULL != (entryPtr = Tcl_FindHashEntry(&aliasTable, key)) && (aliasPtr = (AliasInfo*)Tcl_GetHashValue(entryPtr)) && aliasPtr->mark != aliasMark) { /* Key found in alias list */ aliasPtr->mark = aliasMark; if (EXPAND_PGP == ae->target && Tcl_GetCharLength(aliasPtr->pgp_key)) { Tcl_DStringAppendElement(list, Tcl_GetString(aliasPtr->pgp_key)); continue; } else if (EXPAND_PGPACTIONS == ae->target) { ae->flags |= aliasPtr->flags; } if (EXPAND_DISPLAY == ae->target) { ta = &aliasAddr; aliasAddr.personal = Tcl_GetString(aliasPtr->fullname); aliasAddr.adl = NULL; aliasAddr.mailbox = key; aliasAddr.host = ""; aliasAddr.next = NULL; } else { RatExpandAlias(interp, aliasPtr->parsed, list, ae); continue; } } else if (EXPAND_PGPACTIONS == ae->target && (entryPtr = Tcl_FindHashEntry(&addressTable, key)) && (aliasPtr = (AliasInfo*)Tcl_GetHashValue(entryPtr))) { ae->flags |= aliasPtr->flags; continue; } else if (EXPAND_PGP == ae->target && (entryPtr = Tcl_FindHashEntry(&addressTable, key)) && (aliasPtr = (AliasInfo*)Tcl_GetHashValue(entryPtr)) && Tcl_GetCharLength(aliasPtr->pgp_key)) { Tcl_DStringAppendElement(list, Tcl_GetString(aliasPtr->pgp_key)); continue; } else if (ae->lookup_in_passwd && key && NULL != (pwPtr = getpwnam(key)) && address->personal == NULL && ae->target != EXPAND_PGP && (NULL == entryPtr || NULL == aliasPtr)) { /* Key found in /etc/passwd */ address->personal = cpystr(pwPtr->pw_gecos); if (NULL != (s = strchr(address->personal, ','))) { *s = '\0'; } ta = address; } else { ta = address; } if (ae->mark != NULL && address->host && !strcmp(ae->mark, address->host)) { if (EXPAND_DISPLAY == ae->target) { address->host[0] = '\0'; } else { strncpy(address->host, ae->host, strlen(address->host)+1); } } if (EXPAND_PGP == ae->target) { snprintf(out_buf, sizeof(out_buf), "%s@%s", ta->mailbox, ((ta->host && *ta->host) ? ta->host : ae->host)); Tcl_DStringAppendElement(list, out_buf); } else if (RatAddressSize(ta, 1) < sizeof(out_buf)) { next_address = ta->next; ta->next = NULL; out_buf[0] = '\0'; rfc822_write_address_full(out_buf, ta, NULL); ta->next = next_address; if (Tcl_DStringLength(list)) { Tcl_DStringAppend(list, ", ", 2); } Tcl_DStringAppend(list, out_buf, -1); } } } /* *---------------------------------------------------------------------- * * RatFindCharInHeader -- * * Finds the next unquoted instance of a given character in a header * field. * * Results: * Returns the address of the next instance of the sought character * or NULL if no instance was found. * * Side effects: * None. * * *---------------------------------------------------------------------- */ CONST84 char* RatFindCharInHeader(CONST84 char *header, char m) { ParseState ps; CONST84 char *c; ps = STATE_NORMAL; for (c = header; *c; c++) { switch (ps) { case STATE_NORMAL: if ('"' == *c) { ps = STATE_QUOTED; } else if ('[' == *c) { ps = STATE_LITERAL; } else if ('(' == *c) { ps = STATE_COMMENT; } else if ('\\' == *c) { ps = STATE_ESCAPED; } else if (m == *c) { return c; } break; case STATE_ESCAPED: ps = STATE_NORMAL; break; case STATE_COMMENT: if (')' == *c) { ps = STATE_NORMAL; } else if ('\\' == *c) { ps = STATE_ESCAPED; } break; case STATE_QUOTED: if ('"' == *c) { ps = STATE_NORMAL; } else if ('\\' == *c) { ps = STATE_ESCAPED; } break; case STATE_LITERAL: if (']' == *c) { ps = STATE_NORMAL; } else if ('\\' == *c) { ps = STATE_ESCAPED; } break; } } return NULL; } /* *---------------------------------------------------------------------- * * RatGetFlagsList -- * * Returns a list of flags defined for this alias * field. * * Results: * A tcl_object containing a list of strings. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static Tcl_Obj* RatGetFlagsList(Tcl_Interp *interp, AliasInfo *aliasPtr) { Tcl_Obj *flagsPtr = Tcl_NewObj(); int i; for (i=0; alias_flags[i].name; i++) { if (aliasPtr->flags & alias_flags[i].flag) { Tcl_ListObjAppendElement( interp, flagsPtr,Tcl_NewStringObj(alias_flags[i].name,-1)); } } return flagsPtr; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratAppInit.c000066400000000000000000002030401137544547100207700ustar00rootroot00000000000000/* * ratAppInit.c -- * * Provides a default version of the Tcl_AppInit procedure for * use in wish and similar Tk-based applications. * * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include #include #include "ratFolder.h" #include "ratStdFolder.h" #include "ratPGP.h" #include /* * Version */ #define LIBVERSION "2.2dev" #define LIBDATE "20020719" /* * Length of status string */ #define STATUS_STRING "Status: RO\n" #define STATUS_LENGTH 11 /* * The following variable is a special hack that is needed in order for * Sun shared libraries to be used for Tcl. */ #ifdef NEED_MATHERR extern int matherr(); int *tclDummyMathPtr = (int *) matherr; #endif /* * The following structure is used by the RatBgExec command to keep the * information about processes running in the background. */ typedef struct RatBgInfo { Tcl_Interp *interp; int numPids; int *pidPtr; int status; Tcl_Obj *exitStatus; struct RatBgInfo *nextPtr; } RatBgInfo; /* * How often we should check for dead processes (in milliseconds) */ #define DEAD_INTERVAL 200 /* * How often we should touch the file in the tmp directory (in milliseconds) */ #define TOUCH_INTERVAL (24*60*60*1000) /* * Names of days and months as per rfc822. */ char *dayName[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; char *monthName[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; /* * This is used by the sender child process to indicate that logging should * be done though its own special function */ int is_sender_child = 0; /* * Buffer for delayed output */ static char ratDelayBuffer[3]; /* * KOD-handler (Kiss Of Death) */ static Tcl_AsyncHandler kodhandler; /* * Interpreter for timer procedures */ Tcl_Interp *timerInterp; /* * Local functions */ static Tcl_TimerProc RatChildHandler; static Tcl_TimerProc RatTmpdirToucher; static Tcl_VarTraceProc RatReject; static Tcl_AppInitProc RatAppInit; static Tcl_VarTraceProc RatOptionWatcher; static Tcl_ObjCmdProc RatGetCurrentCmd; static Tcl_ObjCmdProc RatBgExecCmd; static Tcl_ObjCmdProc RatGetCTECmd; static Tcl_ObjCmdProc RatCleanupCmd; static Tcl_ObjCmdProc RatTildeSubstCmd; static Tcl_ObjCmdProc RatTimeCmd; static Tcl_ObjCmdProc RatLockCmd; static Tcl_ObjCmdProc RatIsLockedCmd; static Tcl_ObjCmdProc RatEncodingCmd; static Tcl_ObjCmdProc RatDSECmd; static Tcl_ObjCmdProc RatExpireCmd; static Tcl_ObjCmdProc RatLLCmd; static Tcl_ObjCmdProc RatGenCmd; static Tcl_ObjCmdProc RatWrapCitedCmd; static Tcl_ObjCmdProc RatDbaseCheckCmd; static Tcl_ObjCmdProc RatMangleNumberCmd; static void KodHandlerSig(int s); static Tcl_AsyncProc KodHandlerAsync; static Tcl_IdleProc KodHandlerIdle; static void RatPopulateStruct(char *base, BODY *bodyPtr); static Tcl_VarTraceProc RatSetCharset; static Tcl_ExitProc RatExit; static Tcl_ObjCmdProc RatEncodeMutf7Cmd; static Tcl_ObjCmdProc RatLibSetOnlineModeCmd; static Tcl_ObjCmdProc RatTestCmd; static Tcl_ObjCmdProc RatNudgeSenderCmd; static Tcl_ObjCmdProc RatExtractAddressesCmd; static Tcl_ObjCmdProc RatGenerateDateCmd; static Tcl_ObjCmdProc RatGenerateMsgIdCmd; #ifdef MEM_DEBUG static char **mem_months = NULL; #endif /* MEM_DEBUG */ int Ratatosk_Init(Tcl_Interp *interp) { RatAppInit(interp); return Tcl_PkgProvide(interp, "ratatosk", VERSION); } int Ratatosk_SafeInit(Tcl_Interp *interp) { RatAppInit(interp); return Tcl_PkgProvide(interp, "ratatosk", VERSION); } /* *---------------------------------------------------------------------- * * RatAppInit -- * * This procedure performs application-specific initialization. * Most applications, especially those that incorporate additional * packages, will have their own version of this procedure. * * Results: * Returns a standard Tcl completion code, and leaves an error * message in the result if an error occurs. * * Side effects: * Depends on the startup script. * *---------------------------------------------------------------------- */ static int RatAppInit(Tcl_Interp *interp) { struct passwd *pwPtr = NULL; double tcl_version; Tcl_Obj *oPtr; char *c, tmp[1024]; CONST84 char *v; int i; setlocale(LC_TIME, ""); setlocale(LC_CTYPE, ""); setlocale(LC_COLLATE, ""); timerInterp = interp; /* * Check tcl version * But do it softly and ignore unexpected errors */ oPtr = Tcl_GetVar2Ex(interp, "tcl_version", NULL, TCL_GLOBAL_ONLY); if (TCL_OK == Tcl_GetDoubleFromObj(interp, oPtr, &tcl_version) && tcl_version < 8.3) { fprintf(stderr, "TkRat requires tcl/tk 8.3 or later (detected %4.1f)\n", tcl_version); exit(1); } /* * Create temp-directory */ if (NULL == (v = RatGetPathOption(interp, "tmp"))) { v = "/tmp"; } for (i=0; i<100; i++) { snprintf(tmp, sizeof(tmp), "%s/rat.%x-%d", v, getpid(), i); if (0 == mkdir(tmp, 0700)) { break; } if (EEXIST != errno) { fprintf(stderr, "Failed to create tmp-directory '%s': %s\n", tmp, strerror(errno)); exit(1); } } if (100 == i) { fprintf(stderr, "Failed to create temporary directory '%s'\n", tmp); } Tcl_SetVar(interp, "rat_tmp", tmp, TCL_GLOBAL_ONLY); RatReleaseWatchdog(tmp); Tcl_CreateTimerHandler(TOUCH_INTERVAL, RatTmpdirToucher, NULL); /* * Initialize some variables */ Tcl_SetVar(interp, "ratSenderSending", "0", TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "ratNetOpenFailures", NULL, Tcl_NewIntObj(0), TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "ratCurrent", "charset", Tcl_GetEncodingName(NULL), TCL_GLOBAL_ONLY); Tcl_TraceVar2(interp, "ratCurrent", "charset", TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, RatSetCharset, NULL); Tcl_SetVar2(interp, "rat_lib", "version", LIBVERSION, TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "rat_lib", "date", LIBDATE, TCL_GLOBAL_ONLY); #ifdef HAVE_OPENSSL Tcl_SetVar(interp, "ratHaveOpenSSL", "1", TCL_GLOBAL_ONLY); #else /* HAVE_OPENSSL */ Tcl_SetVar(interp, "ratHaveOpenSSL", "0", TCL_GLOBAL_ONLY); #endif /* HAVE_OPENSSL */ /* * Initialize c-client library */ v = RatGetPathOption(interp, "ssh_path"); if (v && *v) { tcp_parameters(SET_SSHPATH, (void*)v); } v = Tcl_GetVar2(interp, "option", "ssh_command", TCL_GLOBAL_ONLY); if (v && *v) { tcp_parameters(SET_SSHCOMMAND, (void*)v); } oPtr = Tcl_GetVar2Ex(interp, "option", "ssh_timeout", TCL_GLOBAL_ONLY); if (oPtr && TCL_OK == Tcl_GetIntFromObj(interp, oPtr, &i) && i != 0) { tcp_parameters(SET_SSHTIMEOUT, (void*)i); } i = 1; mail_parameters(NIL, SET_USERHASNOLIFE, (void*)i); /* * Initialize async handlers and setup signal handler */ kodhandler = Tcl_AsyncCreate(KodHandlerAsync, (ClientData)interp); signal(SIGUSR2, KodHandlerSig); /* * Make sure we know who we are and that we keep track of any changes */ Tcl_TraceVar2(interp, "option", NULL, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES, RatOptionWatcher, NULL); /* * Make sure that env(USER), env(GECOS), env(HOME) and env(MAIL) are set. * If not then we initialize them. */ if (!Tcl_GetVar2(interp, "env", "USER", TCL_GLOBAL_ONLY)) { if (pwPtr == NULL) { pwPtr = GetPw(); } Tcl_SetVar2(interp, "env", "USER", pwPtr->pw_name, TCL_GLOBAL_ONLY); } if (!Tcl_GetVar2(interp, "env", "GECOS", TCL_GLOBAL_ONLY)) { if (pwPtr == NULL) { pwPtr = GetPw(); } strlcpy(tmp, pwPtr->pw_gecos, sizeof(tmp)); if ((c = strchr(tmp, ','))) { *c = '\0'; } Tcl_SetVar2(interp, "env", "GECOS", tmp, TCL_GLOBAL_ONLY); } if (!Tcl_GetVar2(interp, "env", "HOME", TCL_GLOBAL_ONLY)) { if (pwPtr == NULL) { pwPtr = GetPw(); } Tcl_SetVar2(interp, "env", "HOME", pwPtr->pw_dir, TCL_GLOBAL_ONLY); } if (!Tcl_GetVar2(interp, "env", "MAIL", TCL_GLOBAL_ONLY)) { char buf[1024]; if (pwPtr == NULL) { pwPtr = GetPw(); } snprintf(buf, sizeof(buf), "/var/spool/mail/%s", pwPtr->pw_name); Tcl_SetVar2(interp, "env", "MAIL", buf, TCL_GLOBAL_ONLY); } /* * Call the init procedures for included packages. Each call should * look like this: * * if (Mod_Init(interp) == TCL_ERROR) { * return TCL_ERROR; * } * * where "Mod" is the name of the module. */ if (RatFolderInit(interp) == TCL_ERROR) { return TCL_ERROR; } RatInitAddressHandling(interp); /* * Call Tcl_CreateObjCommand for application-specific commands, if * they weren't already created by the init procedures called above. */ Tcl_CreateObjCommand(interp, "RatGetCurrent", RatGetCurrentCmd, NULL,NULL); Tcl_CreateObjCommand(interp, "RatBgExec", RatBgExecCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatGenId", RatGenIdCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatGetCTE", RatGetCTECmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatCleanup", RatCleanupCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatTildeSubst", RatTildeSubstCmd, NULL,NULL); Tcl_CreateObjCommand(interp, "RatTime", RatTimeCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatLock", RatLockCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatIsLocked", RatIsLockedCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatDaysSinceExpire", RatDSECmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatExpire", RatExpireCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatLL", RatLLCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatGen", RatGenCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatWrapCited", RatWrapCitedCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatDbaseCheck", RatDbaseCheckCmd, NULL,NULL); Tcl_CreateObjCommand(interp, "RatMailcapReload", RatMailcapReloadCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatPGP", RatPGPCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatMangleNumber", RatMangleNumberCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatCheckEncodings", RatCheckEncodingsCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatPurgePwChache", RatPasswdCachePurgeCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatPrettyPrintMsg", RatPrettyPrintMsgCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatEncodeMutf7", RatEncodeMutf7Cmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatLibSetOnlineMode", RatLibSetOnlineModeCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatEncoding", RatEncodingCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatBusy", RatBusyCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatTest", RatTestCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatEncodeQP", RatEncodeQPCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatDecodeQP", RatDecodeQPCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatCreateMessage", RatCreateMessageCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatNudgeSender",RatNudgeSenderCmd,NULL,NULL); Tcl_CreateObjCommand(interp, "RatExtractAddresses", RatExtractAddressesCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatGenerateDate", RatGenerateDateCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatDbaseInfo", RatDbaseInfoCmd, NULL,NULL); Tcl_CreateObjCommand(interp, "RatGetMatchingAddrsImpl", RatGetMatchingAddrsImplCmd, NULL,NULL); Tcl_CreateObjCommand(interp, "RatGenerateMsgId", RatGenerateMsgIdCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatCreateSequence", RatCreateSequenceCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatCheckListFormat", RatCheckListFormatCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatDecodeUrlc", RatDecodeUrlcCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatDbaseKeywords", RatDbaseKeywordsCmd, NULL, NULL); Tcl_CreateExitHandler(RatExit, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatGetCurrent -- * * Get current host, domain, mailbox and personal values. * These values depends on the current role. * The algorithm for building host is: * if option($role,from) is set and contains a domain then * use that domain * else * if gethostname() returns a name with a dot in it then * use it as host * else * use the result of gethostname and the value of option(domain) * endif * endif * * Results: * A pointer to the requested value. This pointer is valid until the * next call to RatGetCurrent. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatGetCurrent(Tcl_Interp *interp, RatCurrentType what, const char *role) { ADDRESS *address = NULL; static char buf[1024]; char *result = NULL, *personal, hostbuf[1024]; CONST84 char *host, *from, *uqdom, *helo, *mailbox; Tcl_Obj *oPtr; host = Tcl_GetHostName(); if (!strchr(host, '.')) { CONST84 char *domain = Tcl_GetVar2(interp, "option", "domain", TCL_GLOBAL_ONLY); if (domain && *domain) { strlcpy(hostbuf, host, sizeof(buf)); strlcat(hostbuf, ".", sizeof(buf)); strlcat(hostbuf, domain, sizeof(buf)); host = hostbuf; } } snprintf(buf, sizeof(buf), "%s,from", role); from = Tcl_GetVar2(interp, "option", buf, TCL_GLOBAL_ONLY); if (from && '\0' != *from) { char *s = cpystr(from); rfc822_parse_adrlist(&address, s, (char*)host); ckfree(s); } switch (what) { case RAT_HOST: snprintf(buf, sizeof(buf), "%s,uqa_domain", role); uqdom = Tcl_GetVar2(interp, "option", buf, TCL_GLOBAL_ONLY); if (uqdom && 0 < strlen(uqdom)) { strlcpy(buf, uqdom, sizeof(buf)); } else if (address && address->host) { strlcpy(buf, address->host, sizeof(buf)); } else { strlcpy(buf, host, sizeof(buf)); } result = buf; break; case RAT_MAILBOX: if (address && address->mailbox) { strlcpy(buf, address->mailbox, sizeof(buf)); } else { strlcpy(buf, Tcl_GetVar2(interp, "env", "USER", TCL_GLOBAL_ONLY), sizeof(buf)); } result = buf; break; case RAT_EMAILADDRESS: if (address && address->host) { host = address->host; } else { snprintf(buf, sizeof(buf), "%s,uqa_domain", role); uqdom = Tcl_GetVar2(interp, "option", buf, TCL_GLOBAL_ONLY); if (uqdom && 0 < strlen(uqdom)) { host = uqdom; } /* else use previous host value */ } if (address && address->mailbox) { mailbox = address->mailbox; } else { mailbox = Tcl_GetVar2(interp, "env", "USER", TCL_GLOBAL_ONLY); } snprintf(buf, sizeof(buf), "%s@%s", mailbox, host); result = buf; break; case RAT_PERSONAL: if (address && address->personal) { oPtr = Tcl_NewStringObj(address->personal, -1); } else { oPtr = Tcl_GetVar2Ex(interp, "env", "GECOS", TCL_GLOBAL_ONLY), Tcl_IncrRefCount(oPtr); } personal = RatEncodeHeaderLine(interp, oPtr, 0); Tcl_DecrRefCount(oPtr); strlcpy(buf, personal, sizeof(buf)); result = buf; break; case RAT_HELO: snprintf(buf, sizeof(buf), "%s,smtp_helo", role); helo = Tcl_GetVar2(interp, "option", buf, TCL_GLOBAL_ONLY); if (helo && 0 < strlen(helo)) { strlcpy(buf, helo, sizeof(buf)); } else if (address && address->host) { strlcpy(buf, address->host, sizeof(buf)); } else { strlcpy(buf, host, sizeof(buf)); } result = buf; break; } if (from && '\0' != *from) { mail_free_address(&address); } return result; } /* *---------------------------------------------------------------------- * * RatGetCurrentCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatGetCurrentCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { RatCurrentType what = -1; char *result; if (3 == objc) { if (!strcmp("host", Tcl_GetString(objv[1]))) { what = RAT_HOST; } else if (!strcmp("mailbox", Tcl_GetString(objv[1]))) { what = RAT_MAILBOX; } else if (!strcmp("personal", Tcl_GetString(objv[1]))) { what = RAT_PERSONAL; } else if (!strcmp("smtp_helo", Tcl_GetString(objv[1]))) { what = RAT_HELO; } } if (3 != objc || -1 == what) { Tcl_AppendResult(interp, "Usage: ", Tcl_GetString(objv[0]), " what role", (char*) NULL); return TCL_ERROR; } result = RatGetCurrent(interp, what, Tcl_GetString(objv[2])); Tcl_SetResult(interp, result, TCL_VOLATILE); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatLog -- * * Sends a log message to the interface * * Results: * None. * * Side effects: * The tcl command 'RatLog' will be called * * *---------------------------------------------------------------------- */ void RatLog(Tcl_Interp *interp, RatLogLevel level, CONST84 char *message, RatLogType type) { static char *buf = NULL; static int bufsize = 0; CONST84 char *argv = message; char *parsedMsg; char *typeStr; int levelNumber; switch(level) { case RAT_BABBLE: levelNumber = 0; break; case RAT_PARSE: levelNumber = 1; break; case RAT_INFO: levelNumber = 2; break; case RAT_WARN: levelNumber = 3; break; case RAT_ERROR: levelNumber = 4; break; case RAT_FATAL: /* fallthrough */ default: levelNumber = 5; break; } switch(type) { case RATLOG_TIME: typeStr = "time"; break; case RATLOG_EXPLICIT: typeStr = "explicit"; break; case RATLOG_NOWAIT: /* fallthrough */ default: typeStr = "nowait"; break; } parsedMsg = Tcl_Merge(1, (CONST84 char * CONST84 *)&argv); if (bufsize < 16 + strlen(parsedMsg) + 9) { bufsize = 1024 + strlen(parsedMsg); buf = (char*)ckrealloc(buf, bufsize); } snprintf(buf, bufsize, "RatLog %d %s %s", levelNumber, parsedMsg, typeStr); if (is_sender_child) { RatSenderLog(buf); } else { if (TCL_OK != Tcl_GlobalEval(interp, buf)) { Tcl_AppendResult(interp, "Error: '", Tcl_GetStringResult(interp), "'\nWhile executing '", buf, "'\n", NULL); } } ckfree(parsedMsg); } /* *---------------------------------------------------------------------- * * RatLogF -- * * Sends a log message to the interface. The difference between this * function and RatLog is that this one takes arguments like printf. * But instead of the format string this one takes and index into * the text array, thus giving localized logging. * * Results: * None. * * Side effects: * See RatLog * * *---------------------------------------------------------------------- */ void RatLogF (Tcl_Interp *interp, RatLogLevel level, char *tag, RatLogType type,...) { va_list argList; char buf[1024]; CONST84 char *fmt = Tcl_GetVar2(interp, "t", tag, TCL_GLOBAL_ONLY); if (NULL == fmt) { snprintf(buf, sizeof(buf), "Internal error: RatLogF '%s'", tag); RatLog(interp, RAT_ERROR, buf, 0); return; } va_start(argList, type); #ifdef HAVE_SNPRINTF vsnprintf(buf, sizeof(buf), fmt, argList); #else vsprintf(buf, fmt, argList); #endif va_end(argList); RatLog(interp, level, buf, type); } /* *---------------------------------------------------------------------- * * RatMangleNumber -- * * Creates a string representation of the given number that is maximum * four characters long. The actual mangling is done in the tcl-proc * ratMangleNumber. * * Results: * Returns a pointer to a static buffer containg the string * representation of the number. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj* RatMangleNumber(int number) { static char buf[32]; /* Scratch area */ if (number < 1000) { sprintf(buf, "%d", number); } else if (number < 10240) { sprintf(buf, "%.1fk", number/1024.0); } else if (number < 1048576) { sprintf(buf, "%dk", (number+512)/1024); } else if (number < 10485760) { sprintf(buf, "%.1fM", number/1048576.0); } else { sprintf(buf, "%dM", (number+524288)/1048576); } return Tcl_NewStringObj(buf, -1); } /* *---------------------------------------------------------------------- * * RatMangleNumberCmdCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * A list of strings to display to the user. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatMangleNumberCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { int number; if (2 != objc || TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &number)) { Tcl_AppendResult(interp, "Usage: ", Tcl_GetString(objv[0]), " number", (char*) NULL); return TCL_ERROR; } Tcl_SetObjResult(interp, RatMangleNumber(number)); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatBgExecCmd -- * * See ../doc/interface * * Results: * The return value is normally TCL_OK and the result can be found * in the result. If something goes wrong TCL_ERROR is returned * and an error message will be left in the result. * * Side effects: * AN entry is added to ratBgInfoPtr. * * *---------------------------------------------------------------------- */ static int RatBgExecCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { static RatBgInfo *ratBgList = NULL; RatBgInfo *bgInfoPtr; Tcl_Obj *lPtr, *oPtr; Tcl_DString ds; int i; if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " exitStatus cmd\"", (char *) NULL); return TCL_ERROR; } bgInfoPtr = (RatBgInfo*)ckalloc(sizeof(*bgInfoPtr)); bgInfoPtr->interp = interp; bgInfoPtr->exitStatus = objv[1]; Tcl_IncrRefCount(objv[1]); Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, "exec -- ", 5); Tcl_DStringAppend(&ds, Tcl_GetString(objv[2]), -1); Tcl_DStringAppend(&ds, " &", 2); if (TCL_OK != Tcl_Eval(interp, Tcl_DStringValue(&ds))) { Tcl_DStringFree(&ds); Tcl_SetVar(bgInfoPtr->interp, Tcl_GetString(bgInfoPtr->exitStatus), "-1", TCL_GLOBAL_ONLY); Tcl_DecrRefCount(objv[1]); ckfree(bgInfoPtr); return TCL_ERROR; } Tcl_DStringFree(&ds); lPtr = Tcl_GetObjResult(interp); Tcl_ListObjLength(interp, lPtr, &bgInfoPtr->numPids); bgInfoPtr->pidPtr = (int*)ckalloc(bgInfoPtr->numPids*sizeof(int)); for (i=0; inumPids; i++) { Tcl_ListObjIndex(interp, lPtr, i, &oPtr); Tcl_GetIntFromObj(interp, oPtr, &bgInfoPtr->pidPtr[i]); } if (!ratBgList) { Tcl_CreateTimerHandler(DEAD_INTERVAL, RatChildHandler, &ratBgList); } bgInfoPtr->nextPtr = ratBgList; ratBgList = bgInfoPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * * RatChildHandler -- * * This process checks if processes in a pipeline are dead. When * all are dead the corresponding variables are set etc. * * Results: * None. * * Side effects: * Sets variables mentioned in the RatBgInfo structure. * * *---------------------------------------------------------------------- */ void RatChildHandler(ClientData clientData) { RatBgInfo *bgInfoPtr, **bgInfoPtrPtr = (RatBgInfo**)clientData; int i, allDead, status, result; while (*bgInfoPtrPtr) { bgInfoPtr = *bgInfoPtrPtr; allDead = 1; for (i = 0; i < bgInfoPtr->numPids; i++) { if (bgInfoPtr->pidPtr[i]) { result = waitpid(bgInfoPtr->pidPtr[i], &status, WNOHANG); if ((result == bgInfoPtr->pidPtr[i]) || ((result == -1) && (errno == ECHILD))) { bgInfoPtr->pidPtr[i] = 0; if (i == bgInfoPtr->numPids-1) { bgInfoPtr->status = WEXITSTATUS(status); } } else { allDead = 0; } } } if (allDead) { char buf[36]; sprintf(buf, "%d", bgInfoPtr->status); Tcl_SetVar(bgInfoPtr->interp, Tcl_GetString(bgInfoPtr->exitStatus), buf, TCL_GLOBAL_ONLY); *bgInfoPtrPtr = bgInfoPtr->nextPtr; ckfree(bgInfoPtr->pidPtr); Tcl_DecrRefCount(bgInfoPtr->exitStatus); ckfree(bgInfoPtr); } else { bgInfoPtrPtr = &(*bgInfoPtrPtr)->nextPtr; } } if (*(RatBgInfo**)clientData) { Tcl_CreateTimerHandler(DEAD_INTERVAL, RatChildHandler, clientData); } } /* *---------------------------------------------------------------------- * * RatTmpdirToucher -- * * This timer touches a file in the tmp-directory. This should * prevent the directory to be removed by and tmp-removal programs. * * Results: * None. * * Side effects: * None. * * *---------------------------------------------------------------------- */ void RatTmpdirToucher(ClientData clientData) { char buf[1024]; const char *tmp = Tcl_GetVar(timerInterp, "rat_tmp", TCL_GLOBAL_ONLY); int fd, l; snprintf(buf, sizeof(buf), "%s/mark", tmp); if (0 <= (fd = open(buf, O_RDWR|O_TRUNC|O_CREAT, 0644))) { l = safe_write(fd, "mark", 4); /* Ignore result */ lseek(fd, 0, SEEK_SET); SafeRead(fd, buf, 4); close(fd); } Tcl_CreateTimerHandler(TOUCH_INTERVAL, RatTmpdirToucher, NULL); } /* *---------------------------------------------------------------------- * * RatGenIdCmd -- * * See ../doc/interface * * Results: * The return value is normally TCL_OK and the result can be found * in the result. If something goes wrong TCL_ERROR is returned * and an error message will be left in the result area. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatGenId() { static long lastid = 0; static char buf[64]; long t = time(NULL); if (t <= lastid) lastid++; else lastid = t; snprintf(buf, sizeof(buf), "%lx.%x", lastid, (int)getpid()); return buf; } /* *---------------------------------------------------------------------- * * RatGenIdCmd -- * * See ../doc/interface * * Results: * The return value is normally TCL_OK and the result can be found * in the result. If something goes wrong TCL_ERROR is returned * and an error message will be left in the result area. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatGenIdCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_SetResult(interp, RatGenId(), TCL_VOLATILE); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatGetCTECmd -- * * See ../doc/interface * * Results: * The return value is normally TCL_OK and the result can be found * in the result area If something goes wrong TCL_ERROR is returned * and an error message will be left in the result area. * * Side effects: * The file passed as argument is read. * * *---------------------------------------------------------------------- */ static int RatGetCTECmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CONST84 char *fileName; FILE *fp; int seen8bit = 0; int seenZero = 0; int c; if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " filename\"", (char *) NULL); return TCL_ERROR; } fileName = RatTranslateFileName(interp, Tcl_GetString(objv[1])); if (NULL == (fp = fopen(fileName, "r"))) { RatLogF(interp, RAT_ERROR, "failed_to_open_file", RATLOG_TIME, Tcl_PosixError(interp)); Tcl_SetResult(interp, "binary", TCL_STATIC); return TCL_OK; } while (c = getc(fp), !feof(fp)) { if (0 == c) { seenZero = 1; break; } else if (0x80 & c) { seen8bit = 1; } } if (seenZero) { Tcl_SetResult(interp, "binary", TCL_STATIC); } else if (seen8bit) { Tcl_SetResult(interp, "8bit", TCL_STATIC); } else { Tcl_SetResult(interp, "7bit", TCL_STATIC); } fclose(fp); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatCleanup -- * * See ../doc/interface * * Results: * The return value is always TCL_OK. * * Side effects: * The database is closed. * * *---------------------------------------------------------------------- */ static int RatCleanupCmd(ClientData dummy, Tcl_Interp *interp,int objc, Tcl_Obj *CONST objv[]) { RatDbClose(); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatTildeSubst -- * * See ../doc/interface * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatTildeSubstCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_DString buffer; char *expandedName; if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " filename\"", (char *) NULL); return TCL_ERROR; } expandedName = Tcl_TranslateFileName(interp, Tcl_GetString(objv[1]), &buffer); Tcl_SetResult(interp, expandedName, TCL_VOLATILE); Tcl_DStringFree(&buffer); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatTime -- * * See ../doc/interface * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatTimeCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { time_t goal; if (objc > 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " [+days]\"", (char *) NULL); return TCL_ERROR; } goal = time(NULL); if (objc == 2) { int i; Tcl_GetIntFromObj(interp, objv[1], &i); goal += i*24*60*60; } Tcl_SetObjResult(interp, Tcl_NewIntObj((int)goal)); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatSearch -- * * Does a case insensitive search of a string. * * Results: * Returns 1 if the searchFor string is found in the searchIn string * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatSearch(char *searchFor, char *searchIn) { static unsigned char *buf = NULL; /* Used to hold lowercase version */ static int bufLength = 0; /* Length of static buffer */ int i, j, lengthFor, lengthIn, s, d; for (s=d=0; searchFor[s];) { if (d >= bufLength) { bufLength += 16; buf = (unsigned char*)ckrealloc(buf, bufLength); } if (!(0x80 & (unsigned char)searchFor[s]) && isupper((unsigned char)searchFor[s])) { buf[d++] = tolower((unsigned char)searchFor[s++]); } else { buf[d++] = searchFor[s++]; } } buf[d] = '\0'; lengthFor = d; lengthIn = strlen(searchIn); for (i = 0; i <= lengthIn-lengthFor; i++) { for (j=0; buf[j]; j++) { if (0x80 & buf[j]) { if (!(0x80 & (unsigned char)searchIn[i+j]) || Tcl_UtfNcasecmp((char*)buf+j, searchIn+i+j, 1)) { break; } j = Tcl_UtfNext((char*)buf+j)-(char*)buf-1; } else if (isupper((unsigned char)searchIn[i+j])) { if (buf[j] != tolower((unsigned char)searchIn[i+j])) { break; } } else if (buf[j] != searchIn[i+j]) { break; } } if (!buf[j]) { return 1; } } return 0; } /* *---------------------------------------------------------------------- * * RatLock -- * * See ../doc/interface * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatLockCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_Obj *value; int i; if (objc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " variable ...\"", (char *) NULL); return TCL_ERROR; } for (i=1; itype) { for (partPtr = bodyPtr->nested.part; partPtr; partPtr = partPtr->next) { RatPopulateStruct(base, &partPtr->body); } } else { bodyPtr->contents.text.data = (unsigned char*)ckalloc(bodyPtr->contents.text.size+1); memcpy(bodyPtr->contents.text.data, base+bodyPtr->contents.offset, bodyPtr->contents.text.size); bodyPtr->contents.text.data[bodyPtr->contents.text.size] = '\0'; } } /* *---------------------------------------------------------------------- * * RatParseMsg -- * * Parses the message given as argument into an MESSAGE structure. * The data at message is used in place so it may not be freed * before the MESSAGE structure is freed. * * Results: * Returns a pointer to a newly allocated MESSAGE structure * * Side effects: * None. * * *---------------------------------------------------------------------- */ MESSAGE* RatParseMsg(Tcl_Interp *interp, unsigned char *message) { int length; /* Length of header */ int bodyOffset = 0; /* Offset of body from start of header */ MESSAGE *msgPtr; /* Pointer to message to return */ STRING bodyString; /* Body data */ for (length = 0; message[length]; length++) { if (message[length] == '\n' && message[length+1] == '\n') { length++; bodyOffset = length+1; break; } if (message[length]=='\r' && message[length+1]=='\n' && message[length+2]=='\r' && message[length+3]=='\n') { length += 2; bodyOffset = length+2; break; } } msgPtr = (MESSAGE*)ckalloc(sizeof(MESSAGE)); msgPtr->text.text.data = (unsigned char*)message; msgPtr->text.text.size = strlen((char*)message); msgPtr->text.offset = bodyOffset; INIT(&bodyString, mail_string, (void*) (char*)(message+bodyOffset), strlen((char*)message)-bodyOffset); rfc822_parse_msg(&msgPtr->env, &msgPtr->body, (char*)message, length, &bodyString, RatGetCurrent(interp, RAT_HOST, ""), NIL); RatPopulateStruct((char*)message+bodyOffset, msgPtr->body); return msgPtr; } /* *---------------------------------------------------------------------- * * RatDSECmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * A standard tcl result and the requested number is left in the * result string. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatDSECmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_SetObjResult(interp, Tcl_NewIntObj(RatDbDaysSinceExpire(interp))); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatExpireCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatExpireCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " inbox backupDir\"", (char *) NULL); return TCL_ERROR; } return RatDbExpire(interp, Tcl_GetString(objv[1]), Tcl_GetString(objv[2])); } /* *---------------------------------------------------------------------- * * RatIsEmpty -- * * Check if a string contains anything else than whitespace. * * Results: * Returns null if the string contains other chars than whitespace. * Otherwise non-null is returned. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatIsEmpty (const char *string) { while (string && *string && isspace((unsigned char)*string)) { string++; } if (string && *string) { return 0; } return 1; } /* *---------------------------------------------------------------------- * * RatLLCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * The length of the given line. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatLLCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { CONST84 char *cPtr; int l; if (2 != objc) { Tcl_AppendResult(interp, "Usage: ", Tcl_GetString(objv[0]), " line", (char*) NULL); return TCL_ERROR; } for (l=0, cPtr = Tcl_GetString(objv[1]); *cPtr; cPtr = Tcl_UtfNext(cPtr)) { if ('\t' == *cPtr) { l += 8-l%8; } else { l++; } } Tcl_SetObjResult(interp, Tcl_NewIntObj(l)); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatGenCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * A string of spaces with the given length * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatGenCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *s; int i, l; if (2 != objc || TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &l)) { Tcl_AppendResult(interp, "Usage: ", Tcl_GetString(objv[0]), " length", (char*) NULL); return TCL_ERROR; } s = Tcl_NewObj(); for (i=0; icmdName); Tcl_GlobalEval(interp, buf); } RatLogF(interp, RAT_ERROR, "mailbox_stolen", RATLOG_TIME); strlcpy(buf, "foreach fh $folderWindowList {FolderWindowClear $fh}", sizeof(buf)); Tcl_GlobalEval(interp, buf); } /* *---------------------------------------------------------------------- * * RatFormatDate -- * * Print the data in a short format. * * Results: * A pointer to a static area. * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Obj* RatFormatDate(Tcl_Interp *interp, struct tm *tm) { char buf[1024]; const char *format; format = Tcl_GetVar2(interp, "option", "date_format", TCL_GLOBAL_ONLY); strftime(buf, sizeof(buf), format, tm); return Tcl_NewStringObj(buf, -1); } /* *---------------------------------------------------------------------- * * RatGetTimeZone -- * * Determines the current timezone. The method varies wildly * between different platform implementations, so its hidden in * this function. * * This function is shamelessy stolen from tcl8.0p2 * * Results: * The return value is the local time zone, measured in * minutes away from GMT (-ve for east, +ve for west). * * Side effects: * None. * *---------------------------------------------------------------------- */ int RatGetTimeZone(unsigned long currentTime) { /* * Determine how a timezone is obtained from "struct tm". If there is no * time zone in this struct (very lame) then use the timezone variable. * This is done in a way to make the timezone variable the method of last * resort, as some systems have it in addition to a field in "struct tm". * The gettimeofday system call can also be used to determine the time * zone. */ #if defined(HAVE_TM_TZADJ) # define TCL_GOT_TIMEZONE time_t curTime = (time_t) currentTime; struct tm *timeDataPtr = localtime(&curTime); int timeZone; timeZone = timeDataPtr->tm_tzadj / 60; if (timeDataPtr->tm_isdst) { timeZone += 60; } return timeZone; #endif #if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE) # define TCL_GOT_TIMEZONE time_t curTime = (time_t) currentTime; struct tm *timeDataPtr = localtime(&curTime); int timeZone; timeZone = -(timeDataPtr->tm_gmtoff / 60); if (timeDataPtr->tm_isdst) { timeZone += 60; } return timeZone; #endif #if defined(USE_DELTA_FOR_TZ) #define TCL_GOT_TIMEZONE 1 /* * This hack replaces using global var timezone or gettimeofday * in situations where they are buggy such as on AIX when libbsd.a * is linked in. */ int timeZone; time_t tt; struct tm *stm; tt = 849268800L; /* 1996-11-29 12:00:00 GMT */ stm = localtime(&tt); /* eg 1996-11-29 6:00:00 CST6CDT */ /* The calculation below assumes a max of +12 or -12 hours from GMT */ timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min); return timeZone; /* eg +360 for CST6CDT */ #endif /* * Must prefer timezone variable over gettimeofday, as gettimeofday does * not return timezone information on many systems that have moved this * information outside of the kernel. */ #if defined(HAVE_TIMEZONE_VAR) && !defined (TCL_GOT_TIMEZONE) # define TCL_GOT_TIMEZONE static int setTZ = 0; int timeZone; if (!setTZ) { tzset(); setTZ = 1; } /* * Note: this is not a typo in "timezone" below! See tzset * documentation for details. */ timeZone = timezone / 60; return timeZone; #endif #if !defined(NO_GETTOD) && !defined (TCL_GOT_TIMEZONE) # define TCL_GOT_TIMEZONE struct timeval tv; struct timezone tz; int timeZone; gettimeofday(&tv, &tz); timeZone = tz.tz_minuteswest; if (tz.tz_dsttime) { timeZone += 60; } return timeZone; #endif #ifndef TCL_GOT_TIMEZONE /* * Cause compile error, we don't know how to get timezone. */ error: autoconf did not figure out how to determine the timezone. #endif } /* *---------------------------------------------------------------------- * * RatSetCharset -- * * Set the system charset * * Results: * None. * * Side effects: * The system character set is modified * * *---------------------------------------------------------------------- */ static char* RatSetCharset(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, CONST84 char *name2, int flags) { static char buf[1024]; CONST84 char *charset; charset = Tcl_GetVar2(interp, "ratCurrent", "charset", TCL_GLOBAL_ONLY); if (TCL_OK != Tcl_SetSystemEncoding(interp, charset)) { strlcpy(buf, Tcl_GetStringResult(interp), sizeof(buf)); return buf; } else { return NULL; } } /* *---------------------------------------------------------------------- * * RatExit -- * * Cleanup on program exit * * Results: * None. * * Side effects: * Frees allocated memory * * *---------------------------------------------------------------------- */ void RatExit(ClientData clientData) { #ifdef MEM_DEBUG static int cleaned = 0; int i; if (cleaned) { return; } for (i=0; i<12 && mem_months; i++) { ckfree(mem_months[i]); } ratStdMessageCleanup(); ratStdFolderCleanup(); ratMessageCleanup(); ratAddressCleanup(); cleaned = 1; #endif /* MEM_DEBUG */ } /* *---------------------------------------------------------------------- * * RatDStringApendNoCRLF -- * * A version of TCL_DStringAPpend which also converts CRLF-linenedings * to single LF. * * Results: * none * * Side effects: * none * * *---------------------------------------------------------------------- */ void RatDStringApendNoCRLF(Tcl_DString *ds, const char *s, int length) { int i; if (-1 == length) { length = strlen(s); } for (i=0; inext = mail_newbody_parameter(); p = p->next; } else { f = p = mail_newbody_parameter(); } p->attribute = (char*)ucase( (unsigned char*)cpystr(Tcl_GetString(pv[0]))); p->value = cpystr(Tcl_GetString(pv[1])); } RatEncodeParameters(interp, f); oPtr = Tcl_NewObj(); for (p=f; p; p = p->next) { ov[0] = Tcl_NewStringObj(p->attribute, -1); ov[1] = Tcl_NewStringObj(p->value, -1); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewListObj(2, ov)); } Tcl_SetObjResult(interp, oPtr); return TCL_OK; } else { Tcl_AppendResult(interp, "Bad usage", TCL_STATIC); return TCL_ERROR; } } /* *---------------------------------------------------------------------- * * RatReadAndCanonify -- * * Read a file and canonalize the line endings, from system local * to CRLF. * * Results: * Returns a pointer to a memory area containing the data. It is * the callers responsibility to eventually free this data with * a call to ckfree(). * * Side effects: * Various * * *---------------------------------------------------------------------- */ char* RatReadAndCanonify(Tcl_Interp *interp, char *filename_utf, unsigned long *size, int canonify) { char *buf; CONST84 char *filename; struct stat sbuf; FILE *fp; Tcl_ResetResult(interp); filename = RatTranslateFileName(interp, filename_utf); fp = fopen(filename, "r"); if (NULL == fp) { return NULL; /* XXX Handle error better */ } fstat(fileno(fp), &sbuf); if (canonify) { int c, allocated, used; allocated = sbuf.st_size + sbuf.st_size/40; used = 0; buf = (char*)ckalloc(allocated+1); while (c=fgetc(fp), !feof(fp)) { if (used >= allocated-1) { allocated += 1024; buf = (char*)ckrealloc(buf, allocated); } if ('\n' == c) { buf[used++] = '\r'; } buf[used++] = c; } buf[used] = '\0'; *size = used; } else { buf = (char*)ckalloc(sbuf.st_size+1); if (1 != fread(buf, sbuf.st_size, 1, fp)) { sbuf.st_size = 0; } buf[sbuf.st_size] = '\0'; *size = sbuf.st_size; } fclose(fp); return buf; } /* *---------------------------------------------------------------------- * * RatCanonalize -- * * Canonalizes a give DString. That is it makes sure that every * newline is expressed as CRLF. * * Results: * Modifies the given DString. * * Side effects: * None. * * *---------------------------------------------------------------------- */ void RatCanonalize(Tcl_DString *ds) { char *s, *c, *t = cpystr(Tcl_DStringValue(ds)); Tcl_DStringSetLength(ds, 0); for (s=t; (c=strchr(s, '\n')); s=c+1) { Tcl_DStringAppend(ds, s, c-s); if ('\r' == c[-1]) { Tcl_DStringAppend(ds, "\n", 1); } else { Tcl_DStringAppend(ds, "\r\n", 2); } } Tcl_DStringAppend(ds, s, strlen(s)); ckfree(t); } /* *---------------------------------------------------------------------- * * RatNudgeSenderCmd -- * * Command used to nudge the sender * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ static int RatNudgeSenderCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { RatNudgeSender(interp); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatExtractAddressesCmd -- * * Se ../doc/interface * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ static int RatExtractAddressesCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *oPtr = Tcl_NewObj(); ADDRESS *alist = NULL, *a; char *host, buf[1024]; int i; if (objc < 2) { Tcl_AppendResult(interp, "Bad usage", TCL_STATIC); return TCL_ERROR; } host = RatGetCurrent(interp, RAT_HOST, Tcl_GetString(objv[1])); for (i=2; inext) { buf[0] = '\0'; rfc822_address(buf, a); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewStringObj(buf, -1)); } mail_free_address(&alist); } Tcl_SetObjResult(interp, oPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatGenerateDateCmd -- * * Se ../doc/interface * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ static int RatGenerateDateCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { char buf[1024]; rfc822_date(buf); Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); return TCL_OK; } /* * These are from auth_md5.c of c-client */ #define MD5BLKLEN 64 /* MD5 block length */ #define MD5DIGLEN 16 /* MD5 digest length */ typedef struct { unsigned long chigh; /* high 32bits of byte count */ unsigned long clow; /* low 32bits of byte count */ unsigned long state[4]; /* state (ABCD) */ unsigned char buf[MD5BLKLEN]; /* input buffer */ unsigned char *ptr; /* buffer position */ } MD5CONTEXT; /* Prototypes */ void md5_init (MD5CONTEXT *ctx); void md5_update (MD5CONTEXT *ctx,unsigned char *data,unsigned long len); void md5_final (unsigned char *digest,MD5CONTEXT *ctx); /* *---------------------------------------------------------------------- * * RatGenerateMsgIdCmd -- * * Se ../doc/interface * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ static int RatGenerateMsgIdCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { char *domain, digest_hex[MD5DIGLEN*2+1], buf[1024]; unsigned char digest[MD5DIGLEN]; int i; MD5CONTEXT ctx; if (objc < 2) { Tcl_AppendResult(interp, "Bad usage", TCL_STATIC); return TCL_ERROR; } domain = RatGetCurrent(interp, RAT_HOST, Tcl_GetString(objv[1])); snprintf(buf, sizeof(buf), "tkrat.%s.%ld.%d", domain, time(NULL),getpid()); md5_init(&ctx); md5_update(&ctx, (unsigned char*)buf, strlen(buf)); md5_final(digest, &ctx); for(i=0; i", digest_hex, domain); Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); return TCL_OK; } /* *---------------------------------------------------------------------- * * SafeRead -- * * Read a specified number of bytes from a socket. This function will * continue until it has received an EOF, error or the requested * number of bytes. * * Results: * Returns the number of bytes actually read or a negative value * on error. * * Side effects: * None * * *---------------------------------------------------------------------- */ ssize_t SafeRead(int fd, void *buf, size_t count) { ssize_t got = 0; ssize_t l; while (got < count) { l = read(fd, buf+got, count-got); if (l < 0 && errno == EINTR) { continue; } else if (l <= 0) { return got; } got += l; } return got; } /* *---------------------------------------------------------------------- * * GetPw -- * * Get the passwd struct for the current user. This function will * never return NULL, but may exit if the call fails. * * Results: * Returns a pointer to a passwd struct. The function will exit * if the getpwuid call fails. * * Side effects: * None * * *---------------------------------------------------------------------- */ struct passwd* GetPw() { struct passwd *pwPtr = getpwuid(getuid()); if (!pwPtr) { fprintf(stderr, "You don't exist, go away!\n"); exit(1); } return pwPtr; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratBusy.c000066400000000000000000000073731137544547100203610ustar00rootroot00000000000000/* * ratBusy.c -- * * Interface to the blt_busy stuff. * * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "rat.h" static int busyCount = 0; static Tcl_Obj *childrenPtr = NULL; static Tcl_Obj *winfoCmdPtr = NULL; static Tcl_Obj *updateCmdPtr = NULL; static Tcl_Obj *balloonCmdPtr = NULL; static Tcl_Obj *trueObjPtr = NULL; static Tcl_Obj *falseObjPtr = NULL; /* *---------------------------------------------------------------------- * * RatSetBusy -- * * Makes the interface busy by changing the cursor etc. * This function can be called multiple times and it will stack. * * Results: * None. * * Side effects: * None. * * *---------------------------------------------------------------------- */ void RatSetBusy(Tcl_Interp *interp) { int objc, i; Tcl_Obj **objv, *argv[2]; char buf[1024]; if (0 < busyCount++) { return; } if (NULL == balloonCmdPtr) { balloonCmdPtr = Tcl_NewStringObj("rat_balloon::SetIgnore", -1); Tcl_IncrRefCount(balloonCmdPtr); trueObjPtr = Tcl_NewBooleanObj(1); Tcl_IncrRefCount(trueObjPtr); falseObjPtr = Tcl_NewBooleanObj(0); Tcl_IncrRefCount(falseObjPtr); } argv[0] = balloonCmdPtr; argv[1] = trueObjPtr; i = Tcl_EvalObjv(interp, 2, argv, 0); if (NULL == winfoCmdPtr) { winfoCmdPtr = Tcl_NewStringObj("winfo children .", -1); Tcl_IncrRefCount(winfoCmdPtr); updateCmdPtr = Tcl_NewStringObj("update idletasks", -1); Tcl_IncrRefCount(updateCmdPtr); } if (TCL_OK == Tcl_EvalObjEx(interp, winfoCmdPtr, 0)) { childrenPtr = Tcl_GetObjResult(interp); } else { childrenPtr = Tcl_NewObj(); } Tcl_IncrRefCount(childrenPtr); Tcl_ListObjGetElements(interp, childrenPtr, &objc, &objv); for (i=0; i= '0' && c <= '9') { return c - '0'; } if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } return c - 'a' + 10; } /* *---------------------------------------------------------------------- * * FindMimeHdr -- * * Find a string encoded according to rfc2047 * * Results: * Returns data in most arguments. * * Side effects: * None * *---------------------------------------------------------------------- */ static int FindMimeHdr(Tcl_Interp *interp, unsigned char *hdr, unsigned char **sPtr, unsigned char **ePtr, Tcl_Encoding *encoding, int *code, unsigned char **data, int *length) { unsigned char *sCharset, *eCharset, *cPtr, c; for (cPtr = hdr; *cPtr; cPtr++) { if ('=' == cPtr[0] && '?' == cPtr[1]) { *sPtr = cPtr; sCharset = cPtr+2; for (cPtr+=2; '?' != *cPtr && *cPtr; cPtr++); if ('?' != *cPtr) return 0; if ('?' != cPtr[2]) continue; switch (cPtr[1]) { case 'b': case 'B': *code = ENCBASE64; break; case 'q': case 'Q': *code = ENCQUOTEDPRINTABLE; break; default: continue; } eCharset = cPtr; *data = cPtr+3; for (cPtr+=3, *length = 0; *cPtr && ('?' != *cPtr || '=' != cPtr[1]); cPtr++, (*length)++); if ('?' != *cPtr) return 0; *ePtr = cPtr+2; c = *eCharset; *eCharset = '\0'; *encoding = RatGetEncoding(interp, (char*)sCharset); *eCharset = c; return 1; } } return 0; } /* *---------------------------------------------------------------------- * * RatDecodeHeader -- * * Decodes a header line encoded according to rfc2047. * * Results: * Returns a pointer to a static storage area * * Side effects: * None * * TODO, handle address entries correct * *---------------------------------------------------------------------- */ char* RatDecodeHeader(Tcl_Interp *interp, const char *data, int adr) { static Tcl_DString ds, tmp; static int initialized = 0; unsigned char *sPtr, *ePtr, *decoded, *text, *cPtr, *point = (unsigned char*)data; int length, code, first = 1; unsigned long dlen; unsigned int i; Tcl_Encoding encoding; Tcl_DString *myPtr = NULL; if (!data || !*data) { return ""; } if (!initialized) { Tcl_DStringInit(&ds); initialized = 1; } else { Tcl_DStringSetLength(&ds, 0); } /* * Check for headers from buggy programs (with raw eight-bit data * in them) */ for (cPtr = (unsigned char*)data; *cPtr; cPtr++) { if (*cPtr & 0x80) { myPtr = (Tcl_DString*)ckalloc(sizeof(Tcl_DString)); Tcl_DStringInit(myPtr); Tcl_ExternalToUtfDString(NULL, data, -1, myPtr); data = Tcl_DStringValue(myPtr); point = (unsigned char*)data; break; } } while (FindMimeHdr(interp, point, &sPtr, &ePtr, &encoding, &code, &text, &length)) { if (sPtr != point) { if (!first) { for (cPtr = point; cPtr < sPtr && isspace(*cPtr); cPtr++); if (cPtr < sPtr) { Tcl_DStringAppend(&ds, (char*)point, sPtr-point); } } else { for (i=0; i>4)&0x3); if (strchr(alphabet64, '=')-alphabet64 != lbuf[2]) { d[index++] = lbuf[1] << 4 | ((lbuf[2]>>2)&0xf); if (strchr(alphabet64, '=')-alphabet64 != lbuf[3]) { d[index++] = lbuf[2] << 6 | (lbuf[3]&0x3f); } } Tcl_DStringAppend(&decoded, d, index); } } src = Tcl_DStringValue(&decoded); srcLength = Tcl_DStringLength(&decoded); } else if (cte == ENCQUOTEDPRINTABLE) { /* Handle quoted-printable */ while (dataIndex < length) { if ('=' == data[dataIndex]) { if ('\r' == data[dataIndex+1]) { dataIndex += 3; } else if ('\n' == data[dataIndex+1]) { dataIndex += 2; } else { d[0] = (HexValue(data[dataIndex+1])<<4) + HexValue(data[dataIndex+2]); Tcl_DStringAppend(&decoded, d, 1); dataIndex += 3; } } else { Tcl_DStringAppend(&decoded, &data[dataIndex++], 1); } } src = Tcl_DStringValue(&decoded); srcLength = Tcl_DStringLength(&decoded); } else { /* Handle unencoded */ src = data; srcLength = length; dataIndex = length; } /* Convert charset to utf-8 if needed */ if (charset && strcasecmp(charset, "utf-8")) { Tcl_Encoding enc = RatGetEncoding(interp, charset); Tcl_ExternalToUtfDString(enc, src, srcLength, dsPtr); } else { Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, src, srcLength); } /* Fix line endings for text */ if (charset) { len = Tcl_DStringLength(dsPtr); for (src = dst = Tcl_DStringValue(dsPtr); *src; src++) { if (*src != '\r') { *dst++ = *src; } else { len--; } } Tcl_DStringSetLength(dsPtr, len); } /* Free temporary storage */ Tcl_DStringFree(&decoded); return dsPtr; } /* *---------------------------------------------------------------------- * * CreateEncWord -- * * Tres to create an encoded word (if needed) by the given string. * It uses at most length bytes from raw and stores the result in * dest. The result will be no more than maxUse characters. * * Results: * Returns non-zero if the encoding was successful. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int CreateEncWord(Tcl_Interp *interp, Tcl_Encoding enc, const char *charset, unsigned char *raw, int length, Tcl_DString *dest, int maxUse) { unsigned char buf[1024], buf2[1024]; Tcl_EncodingState state; int i, consumed, wrote, d; /* * Check if we must encode this */ for (i=0; i sizeof(buf)-1) { maxUse = sizeof(buf)-1; } /* * Try to convert to external encoding */ if (TCL_OK != Tcl_UtfToExternal(interp, enc, (char*)raw, length, TCL_ENCODING_START|TCL_ENCODING_END, &state, (char*)buf2, sizeof(buf2), &consumed, &wrote, NULL) || consumed != length) { return 0; } /* * Convert into quoted-printable, check that we have room all the time */ snprintf((char*)buf, sizeof(buf), "=?%s?Q?", charset); for (i=0, d=strlen((char*)buf); i= maxUse-2) { return 0; } buf[d++] = '='; buf[d++] = alphabetHEX[buf2[i]>>4]; buf[d++] = alphabetHEX[buf2[i]&0xf]; } else { buf[d++] = buf2[i]; } } if (i < wrote) { return 0; } buf[d++] = '?'; buf[d++] = '='; Tcl_DStringAppend(dest, (char*)buf, d); return 1; } /* *---------------------------------------------------------------------- * * RatEncodeHeaderLine -- * * Encodes one header line according to MIME (rfc2047). * The nameLength argument should tell how long the header name is in * characters. This is so that the line folding can do its job properly. * * Results: * A block of encoded header line. This block of data will be valid * until the next call to this function. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatEncodeHeaderLine (Tcl_Interp *interp, Tcl_Obj *line, int nameLength) { static Tcl_DString ds; static int initialized = 0; int l, l1, pre = nameLength, maxUse; char *s; const char *charset; Tcl_Encoding enc; if (NULL == line) { return NULL; } if (!initialized) { Tcl_DStringInit(&ds); initialized = 1; } else { Tcl_DStringSetLength(&ds, 0); } s = Tcl_GetString(line); enc = FindEncoding(interp, s, &charset); /* * Do while we have characters left to consume * - Find candidate for line-break * - Loop while it can NOT be encoded into a word * - Search backwards for new canidate * - If no new canididate is found switch to test every character */ while (*s) { if ((int)strlen(s)+pre <= RFC2047_MAX_LINE_LENGTH) { l = strlen(s); } else { for (l = RFC2047_MAX_LINE_LENGTH-pre; l>0 && !isspace(s[l]); l--); if (0 == l) { l = RFC2047_MAX_LINE_LENGTH-pre; } } maxUse = RFC2047_MAX_LINE_LENGTH-pre; while (!CreateEncWord(interp, enc, charset, (unsigned char*)s, l, &ds, maxUse)) { for (l1 = l-1; l1 > 0 && !isspace(s[l1]); l1--); if (0 < l1) { l = l1; } else { maxUse = 1024; l--; } } s += l; if (*s) { Tcl_DStringAppend(&ds, "\n", 1); for (pre=0; isspace(*s) && prepersonal) { c = adrPtr->personal; if (c[0] == c[strlen(c)-1] && (*c == '"' || *c == '\'')) { memmove(c, c+1, strlen(c)); c[strlen(c)-1] = '\0'; } for (c = adrPtr->personal; *c; c++) { if (*c & 0x80) { oPtr = Tcl_NewStringObj(adrPtr->personal, -1); Tcl_IncrRefCount(oPtr); c = RatEncodeHeaderLine(interp, oPtr, 0); Tcl_DecrRefCount(oPtr); ckfree(adrPtr->personal); adrPtr->personal = cpystr(c); } } } adrPtr = adrPtr->next; } } /* *---------------------------------------------------------------------- * * RatGetEncoding -- * * Return the tcl-encoding attached to the given name. This name * may be mapped from a MIME-name into a tcl-name. * * Results: * A tcl Tcl_Endoding blob. The given encoding must be freed by the * caller by calling Tcl_FreeEncoding(). * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Encoding RatGetEncoding(Tcl_Interp *interp, const char *name) { Tcl_Encoding enc; const char *tclName; char lname[256]; if (NULL == name) { return NULL; } strlcpy(lname, name, sizeof(lname)); lcase((unsigned char*)lname); tclName = Tcl_GetVar2(interp, "charsetMapping", lname, TCL_GLOBAL_ONLY); if (NULL == tclName) { tclName = lname; } enc = Tcl_GetEncoding(interp, tclName); if (NULL == enc) { return NULL; } return enc; } /* *---------------------------------------------------------------------- * * RatCheckEncoding -- * * Check if the given encoding can encode the given string * * Results: * Non-zero if all characters in the give string can be encoded * successfully * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatCheckEncoding(Tcl_Interp *interp, char *encoding_name, const char *string, int length) { Tcl_EncodingState state; Tcl_Encoding enc; char buf[1024]; int ret, in; if (NULL == (enc = RatGetEncoding(interp, encoding_name))) { return 0; } ret = 0; while (length && TCL_CONVERT_UNKNOWN != ret) { ret = Tcl_UtfToExternal(interp, enc, string, length, TCL_ENCODING_STOPONERROR|TCL_ENCODING_START, &state, buf, sizeof(buf), &in, NULL, NULL); string += in; length -= in; } Tcl_FreeEncoding(enc); return TCL_CONVERT_UNKNOWN != ret; } /* *---------------------------------------------------------------------- * * RatCheckEncodingsCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * See above * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatCheckEncodingsCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { int i, listLength, srcLen; Tcl_Obj *oPtr, *vPtr; char *src; if (3 != objc) { Tcl_AppendResult(interp, "Usage: ", Tcl_GetString(objv[0]), \ " variable charsets", (char*) NULL); return TCL_ERROR; } vPtr = Tcl_GetVar2Ex(interp, Tcl_GetString(objv[1]), NULL, 0); if (NULL == vPtr) { goto done; } Tcl_ListObjLength(interp, objv[2], &listLength); src = Tcl_GetStringFromObj(vPtr, &srcLen); for (i=0; i 0; l -= 3, cPtr += 3) { buf[0] = alphabet64[cPtr[0] >> 2]; buf[1] = alphabet64[((cPtr[0] << 4) + (l>1 ? (cPtr[1]>>4) : 0))&0x3f]; buf[2] = l > 1 ? alphabet64[((cPtr[1]<<2) + (l>2 ? (cPtr[2]>>6) : 0)) & 0x3f] : '='; buf[3] = l > 2 ? alphabet64[cPtr[2] & 0x3f] : '='; Tcl_AppendToObj(dPtr, (char*)buf, 4); if (18 == ++ll || l < 4) { Tcl_AppendToObj(dPtr, "\n", 1); ll = 0; } } return dPtr; } /* *---------------------------------------------------------------------- * * RatUtf8to16 -- * * Convert the given utf-8 character to UCS-2 * * Results: * Returns the number of characters consumed from src * On failure a negative number is returned. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatUtf8to16(const unsigned char *src, unsigned char *dst) { if (0 == (*src & 0x80)) { dst[0] = 0; dst[1] = *src; return 1; } else if (0xc0 == (*src & 0xe0)) { if (!(src[1] & 0x80)) { return 1; } dst[0] = (src[0] & 0x1f) >> 2; dst[1] = ((src[0] & 0x03) << 6) + (src[1] & 0x3f); return 2; } else if (0xe0 == (*src & 0xf0)) { if (!(src[1] & 0x80) && !(src[2] & 0x80)) { return 1; } dst[0] = ((src[0] & 0x0f) << 4) + ((src[1] & 0x3f) >> 2); dst[1] = ((src[1] & 0x03) << 6) + (src[2] & 0x3f); return 3; } else { dst[0] = 0; dst[1] = *src; return 1; } } /* *---------------------------------------------------------------------- * * RatUtf16to8 -- * * Convert the given UCS-2 character to utf-8 * * Results: * Returns the length of the generated string on success. * On failure a negative number is returned. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatUtf16to8(const unsigned char *src, unsigned char *dst) { if (src[0] >= 0x08) { dst[0] = 0xe0 | (src[0] >> 4); dst[1] = 0x80 | ((src[0] & 0x0f) << 2) | (src[1] >> 6); dst[2] = 0x80 | (src[1] & 0x3f); return 3; } else if (src[0] || src[1] > 0x7f) { dst[0] = 0xc0 | (src[0] << 2) | (src[1] >> 6); dst[1] = 0x80 | (src[1] & 0x3f); return 2; } else { dst[0] = src[1]; return 1; } } /* *---------------------------------------------------------------------- * * RatUtf8toMutf7 -- * * Convert the given utf-8 encoded text to modified utf-7 * * Results: * Returns a pointer to a static buffer containing the new text * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatUtf8toMutf7(const char *signed_src) { static unsigned char *dst = NULL; static int dstlen = 0; unsigned char buf[3], *src = (unsigned char*)signed_src; int len = 0, overflow = 0; if (dstlen < strlen((char*)src)*3+1) { dstlen = strlen((char*)src)*3; dst = (unsigned char *)ckrealloc(dst, dstlen); } while (*src) { if ('&' == *src) { if (dstlen <= len+2) { dstlen += 128; dst = (unsigned char *)ckrealloc(dst, dstlen); } dst[len++] = '&'; dst[len++] = '-'; src++; } else if (*src & 0x80) { if (dstlen <= len+6) { dstlen += 128; dst = (unsigned char *)ckrealloc(dst, dstlen); } dst[len++] = '&'; do { if (dstlen <= len+5) { dstlen += 128; dst = (unsigned char *)ckrealloc(dst, dstlen); } if (overflow) { buf[0] = buf[3]; if (*src & 0x80) { src += RatUtf8to16(src, buf+1); } else { buf[1] = buf[2] = 0; } overflow = 0; } else { src += RatUtf8to16(src, buf); if (*src & 0x80) { src += RatUtf8to16(src, buf+2); overflow = 1; } else { buf[2] = buf[3] = 0; } } dst[len++] = modified64[buf[0] >> 2]; dst[len++] = modified64[((buf[0] << 4) + (buf[1]>>4)) & 0x3f]; if (buf[1] || buf[2]) { dst[len++] = modified64[((buf[1]<<2) + (buf[2]>>6)) & 0x3f]; if (buf[2]) { dst[len++] = modified64[buf[2] & 0x3f]; } } } while (*src & 0x80 || overflow); if (strchr(modified64, *src) || '\0' == *src) { dst[len++] = '-'; } } else { if (dstlen <= len+1) { dstlen += 128; dst = (unsigned char *)ckrealloc(dst, dstlen); } dst[len++] = *src++; } } dst[len] = '\0'; return (char*)dst; } /* *---------------------------------------------------------------------- * * RatMutf7toUtf8 -- * * Convert the given modified utf-7 encoded text to utf-8 * * Results: * Returns the length of the generated string on success. * On failure a negative number is returned. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatMutf7toUtf8(const char *signed_src) { static unsigned char *dst = NULL; static int dstlen = 0; unsigned char utf16[2], lbuf[4], *src = (unsigned char*)signed_src; int i, l, len=0, odd; if (dstlen < strlen((char*)src)*3) { dstlen = strlen((char*)src)*3; dst = (unsigned char *)ckrealloc(dst, dstlen); } while (*src) { if (len >= dstlen) { dstlen += 128; dst = (unsigned char *)ckrealloc(dst, dstlen); } if ('&' == *src && '-' == src[1]) { dst[len++] = '&'; src += 2; } else if ('&' == *src) { src++; odd = 0; do { for (i=0; i<4; i++) { if (strchr(modified64, *src)) { lbuf[i] = strchr(modified64, *src++) - modified64; } else { lbuf[i] = 0; } } if (odd) { odd = 0; if (len >= dstlen+6) { dstlen += 128; dst = (unsigned char *)ckrealloc(dst, dstlen); } utf16[1] = (lbuf[0] << 2) | (lbuf[1] >> 4); len += RatUtf16to8(utf16, dst+len); utf16[0] = (lbuf[1] << 4) | (lbuf[2] >> 2); utf16[1] = (lbuf[2] << 6) | lbuf[3]; if (utf16[0] != 0 || utf16[1] != 0) { l = RatUtf16to8(utf16, dst+len); len += l; } } else { if (len >= dstlen+3) { dstlen += 128; dst = (unsigned char *)ckrealloc(dst, dstlen); } utf16[0] = (lbuf[0] << 2) | (lbuf[1] >> 4); utf16[1] = (lbuf[1] << 4) | (lbuf[2] >> 2); len += RatUtf16to8(utf16, dst+len); utf16[0] = (lbuf[2] << 6) | lbuf[3]; odd = 1; } } while (strchr(modified64, *src)); if ('-' == *src) { src++; } } else { dst[len++] = *src++; } } dst[len] = '\0'; return (char*)dst; } /* *---------------------------------------------------------------------- * * RatEncodeQP - * * Encode the given text to QP * * Results: * Returns an intialized Tcl_DString pointer. It is up to the caller to * free this when not needing it anymore. * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_DString* RatEncodeQP(const unsigned char *line) { Tcl_DString *ds = (Tcl_DString*)ckalloc(sizeof(*ds)); const unsigned char *c; char buf[4]; Tcl_DStringInit(ds); for (c=line; *c; c++) { if ('=' == *c || 0x80 <= *c) { snprintf(buf, sizeof(buf), "=%02X", *c); Tcl_DStringAppend(ds, buf, 3); } else { Tcl_DStringAppend(ds, (char*)c, 1); } } return ds; } /* *---------------------------------------------------------------------- * * RatEncodeQPCmd -- * * See ../doc/interface * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ int RatEncodeQPCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Encoding enc; Tcl_DString ext, *encoded; if (objc != 3) { Tcl_AppendResult(interp, "Bad usage", TCL_STATIC); return TCL_ERROR; } enc = Tcl_GetEncoding(interp, Tcl_GetString(objv[1])); Tcl_UtfToExternalDString(enc, Tcl_GetString(objv[2]), -1, &ext); encoded = RatEncodeQP((unsigned char*)Tcl_DStringValue(&ext)); Tcl_DStringFree(&ext); Tcl_DStringResult(interp, encoded); Tcl_FreeEncoding(enc); ckfree(encoded); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatDecodeQP - * * Dencode the given text from QP * * Results: * Returns a pointer to a string. This string has been allocated with * ckalloc and it is up to the caller to free it when not needing it. * * Side effects: * None. * * *---------------------------------------------------------------------- */ unsigned char* RatDecodeQP(unsigned char *line) { unsigned char *s, *d; d = s = line; while (*s) { if ('=' == *s && isxdigit(s[1]) && isxdigit(s[2])) { *d++ = (HexValue(s[1])<<4) + HexValue(s[2]); s += 3; } else { *d++ = *s++; } } *d = '\0'; return line; } /* *---------------------------------------------------------------------- * * RatDecodeQPCmd -- * * See ../doc/interface * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ int RatDecodeQPCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Encoding enc; Tcl_DString utf; char *text; if (objc != 3) { Tcl_AppendResult(interp, "Bad usage", TCL_STATIC); return TCL_ERROR; } enc = Tcl_GetEncoding(interp, Tcl_GetString(objv[1])); text = cpystr(Tcl_GetString(objv[2])); RatDecodeQP((unsigned char*)text); Tcl_ExternalToUtfDString(enc, text, -1, &utf); ckfree(text); Tcl_DStringResult(interp, &utf); Tcl_FreeEncoding(enc); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatRFC2231EncodeParameter -- * * Encodes a parameter according to rfc2231 * * Results: * None * * Side effects: * Modifies the parameter list * * *---------------------------------------------------------------------- */ static PARAMETER* RatRFC2231EncodeParameters(Tcl_Interp *interp, PARAMETER *inparam) { Tcl_DString value; int encoded = 0, is_long = 0, i, l; char *v = inparam->value, buf[1024], *key, *old_value; unsigned char *c; /* Check for non us-ascii characters */ for (c = (unsigned char*)v; *c && *c<128; c++); if (*c) { Tcl_Encoding enc; Tcl_DString external; const char *charset; Tcl_DStringInit(&value); encoded = 1; enc = FindEncoding(interp, inparam->value, &charset); Tcl_DStringAppend(&value, charset, -1); Tcl_DStringAppend(&value, "''", 2); Tcl_UtfToExternalDString(enc, v, strlen(v), &external); for (c=(unsigned char*)Tcl_DStringValue(&external); *c; c++) { if (*c > 128 || strchr(tspecials, *c)) { snprintf(buf, sizeof(buf), "%%%02X", *c); Tcl_DStringAppend(&value, buf, 3); } else { Tcl_DStringAppend(&value, (char*)c, 1); } } Tcl_DStringFree(&external); v = Tcl_DStringValue(&value); } /* Check for long value */ if (strlen(inparam->attribute) + strlen(inparam->value) > 72) { is_long = 1; } /* No changes? */ if (!encoded && !is_long) { return inparam; } /* Only encoded? */ if (!is_long) { l = strlen(inparam->attribute)+2; key = ckalloc(l); strlcpy(key, inparam->attribute, l); strlcat(key, "*", l); ckfree(inparam->attribute); inparam->attribute = key; ckfree(inparam->value); inparam->value = cpystr(v); Tcl_DStringFree(&value); return inparam; } /* Needs length wrapping */ i = 0; key = inparam->attribute; l = 72 - strlen(key); inparam->attribute = NULL; old_value = inparam->value; while(1) { if (inparam->attribute) { PARAMETER *n = (PARAMETER*)ckalloc(sizeof(PARAMETER)); n->next = inparam->next; inparam->next = n; inparam = n; } snprintf(buf, sizeof(buf), "%s*%d%s", key, i++, (encoded?"*":"")); inparam->attribute = cpystr(buf); if (strlen(v) > l) { inparam->value = (char*)ckalloc(l+1); memcpy(inparam->value, v, l); inparam->value[l] = '\0'; v += l; } else { inparam->value = cpystr(v); break; } } ckfree(key); ckfree(old_value); if (encoded) { Tcl_DStringFree(&value); } return inparam; } /* *---------------------------------------------------------------------- * * RatEncodeParameters -- * * Encode parameters according to the current settings. * * Results: * None * * Side effects: * Modifies the parameter list * * *---------------------------------------------------------------------- */ void RatEncodeParameters(Tcl_Interp *interp, PARAMETER *inparam) { Tcl_Obj *oPtr; PARAMETER *p; char *enc_mode; oPtr = Tcl_GetVar2Ex(interp, "option", "parm_enc", TCL_GLOBAL_ONLY); enc_mode = Tcl_GetString(oPtr); for (p = inparam; p; p = p->next) { int is_long = 0; char *v = p->value; unsigned char *c; /* Check for non us-ascii characters and length */ for (c = (unsigned char*)v; *c && *c<128; c++); if (strlen(p->attribute) + strlen(p->value) > 72) { is_long = 1; } if (*c && !strcmp("rfc2047", enc_mode)) { oPtr = Tcl_NewStringObj(p->value, -1); v = RatEncodeHeaderLine (interp, oPtr, 0); ckfree(p->value); p->value = cpystr(v); Tcl_DecrRefCount(oPtr); } else if ((*c || is_long) && !strcmp("rfc2231", enc_mode)) { p = RatRFC2231EncodeParameters(interp, p); } else if ((*c || is_long) && !strcmp("both", enc_mode)) { PARAMETER *p2 = mail_newbody_parameter(); p2->attribute = cpystr(p->attribute); p2->value = p->value; p2->next = p->next; p->next = p2; if (*c) { oPtr = Tcl_NewStringObj(p->value, -1); /* The -1000 is a kludge to avoid line breaks */ v = RatEncodeHeaderLine (interp, oPtr, -1000); p->value = cpystr(v); Tcl_DecrRefCount(oPtr); } else { p->value = cpystr(p->value); } p = p2; p = RatRFC2231EncodeParameters(interp, p); } } } /* *---------------------------------------------------------------------- * * FindEncoding -- * * Find a suitable encoding * * Results: * The encoding * * Side effects: * None * * *---------------------------------------------------------------------- */ static Tcl_Encoding FindEncoding(Tcl_Interp *interp, char *string, char const **charset) { Tcl_Obj **objv; int objc, i; Tcl_ListObjGetElements(interp, Tcl_GetVar2Ex(interp, "option", "charset_candidates", TCL_GLOBAL_ONLY), &objc, &objv); for (i=0; inext) { value_used = 0; if (NULL == strchr(p->attribute, '*') || !Tcl_RegExpExec(interp, exp, p->attribute, p->attribute)) { v = RatDecodeHeader(interp, p->value, 0); if (strcmp(v, p->value)) { ckfree(p->value); p->value = cpystr(v); } continue; } Tcl_RegExpRange(exp, 2, &start, &end); encoded = (start != NULL); Tcl_RegExpRange(exp, 1, &start, &end); /* * Join the parts of a broken value */ if (start != NULL) { Tcl_DStringInit(&value); value_used = 1; Tcl_DStringAppend(&value, p->value, -1); while (p->next && NULL != strchr(p->next->attribute, '*') && Tcl_RegExpExec(interp, exp, p->next->attribute, p->next->attribute) && (Tcl_RegExpRange(exp, 1, &start, &end), 1) && '0' != start[1]) { PARAMETER *pn = p->next; Tcl_DStringAppend(&value, pn->value, -1); p->next = pn->next; ckfree(pn->value); ckfree(pn->attribute); ckfree(pn); } } else if (encoded) { Tcl_DStringInit(&value); Tcl_DStringAppend(&value, p->value, -1); } /* * Decode encoded values */ if (encoded) { Tcl_Encoding enc = NULL; unsigned char *s, *d, *b; if (!value_used) { Tcl_DStringInit(&value); Tcl_DStringAppend(&value, p->value, -1); value_used = 1; } b = d = (unsigned char*)ckalloc(Tcl_DStringLength(&value)+1); for (s = (unsigned char*)Tcl_DStringValue(&value); *s && '\'' != *s; s++); if ('\'' == *s) { *s = '\0'; enc = RatGetEncoding(interp, Tcl_DStringValue(&value)); for (s++; *s && '\'' != *s; s++); if (*s) s++; while (*s) { if ('%' == *s && s[1] && s[2]) { *d++ = (HexValue(s[1])<<4) + HexValue(s[2]); s += 3; } else { *d++ = *s++; } } *d = '\0'; Tcl_DStringFree(&value); Tcl_ExternalToUtfDString(enc,(char*)b,strlen((char*)b),&value); ckfree(b); } } if (value_used) { *strchr(p->attribute, '*') = '\0'; ckfree(p->value); p->value = cpystr(Tcl_DStringValue(&value)); Tcl_DStringFree(&value); } } } /* *---------------------------------------------------------------------- * * ratDecodeUrlcCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * See above * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatDecodeUrlcCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { char *decoded, *src, *d, *header; int addr; if (objc != 3 || Tcl_GetBooleanFromObj(interp, objv[2], &addr)) { Tcl_AppendResult(interp, "Bad usage", TCL_STATIC); return TCL_ERROR; } src = Tcl_GetString(objv[1]); decoded = (char*)ckalloc(strlen(src)+1); for (d=decoded; *src; src++) { if ('%' == *src && src[1] && src[2]) { *d++ = (HexValue(src[1])<<4) + HexValue(src[2]); src += 2; } else { *d++ = *src; } } *d = '\0'; header = RatDecodeHeader(interp, decoded, addr); Tcl_SetObjResult(interp, Tcl_NewStringObj(header, -1)); ckfree(decoded); return TCL_OK; } /* * Test code for Mutf7 <-> utf8 functions static void Test(unsigned char *in) { unsigned char stage1[1024], stage2[1024]; printf("In: %s\n", in); fflush(stdin); RatUtf8toMutf7(in, stage1, sizeof(stage1)); printf("Stage1: %s\n", stage1); fflush(stdin); RatMutf7toUtf8(stage1, stage2, sizeof(stage2)); printf("Stage2: %s\n", stage2); fflush(stdin); if (strcmp(stage2, in)) { printf("ERROR\n"); } printf("\n"); } int main() { Test("får"); Test("Räksmörgås"); Test("å"); Test("åä"); Test("åäö"); Test("åäöå"); Test("åäöåä"); Test("åäöåäö"); return 0; } */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratCompat.c000066400000000000000000000023111137544547100206450ustar00rootroot00000000000000/* * ratCompat.c -- * * This file contains compatibility functions. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "rat.h" #ifndef HAVE_SNPRINTF size_t snprintf (char *buf, size_t buflen, const char *fmt, ...) { va_list argList; int bytes_written; va_start(argList, fmt); bytes_written = vsprintf (buf, fmt, argList); va_end(argList); if (bytes_written >= buflen) { fprintf(stderr, "Buffer overflow in snprintf (%d > %d)\n", bytes_written+1, buflen); abort(); } return bytes_written; } #endif /* HAVE_SNPRINTF */ #ifndef HAVE_STRLCPY char* strlcpy(char *dst, const char *src, size_t n) { int i; for (i=0; src[i] && i #include "ratFolder.h" /* * This is the private part of a Db folder info structure. */ typedef struct DbFolderInfo { int *listPtr; /* List of messages in this folder */ Tcl_Obj *searchExpr; /* The search expression used to create * this folder. */ char *keywords; /* Keywords to add to inserted messages */ char *exDate; /* Expiration date of inserted messages */ char *exType; /* Expiration type of new messages */ Tcl_Obj **infoPtr; /* List of information caches */ } DbFolderInfo; typedef enum {Db_Name, Db_Mail} DbAdrInfo; /* * Procedures private to this module. */ static RatInitProc Db_InitProc; static RatCloseProc Db_CloseProc; static RatUpdateProc Db_UpdateProc; static RatInsertProc Db_InsertProc; static RatSetFlagProc Db_SetFlagProc; static RatGetFlagProc Db_GetFlagProc; static RatCreateProc Db_CreateProc; static RatSetInfoProc Db_SetInfoProc; static RatDbInfoGetProc Db_DbinfoGetProc; static RatDbInfoSetProc Db_DbinfoSetProc; static int GetAddressInfo(Tcl_Interp *interp, Tcl_DString *dsPtr, char *adr, DbAdrInfo info); /* *---------------------------------------------------------------------- * * RatDbFolderInit -- * * Initializes the dbase folder data. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The C-client library is initialized and the apropriate mail drivers * are linked. * * *---------------------------------------------------------------------- */ int RatDbFolderInit(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "RatInsert", RatInsertCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatDbFolderCreate -- * * Creates a db folder entity. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * A db folder is created. * * *---------------------------------------------------------------------- */ RatFolderInfo* RatDbFolderCreate(Tcl_Interp *interp, int append_only, Tcl_Obj *defPtr) { RatFolderInfo *infoPtr; DbFolderInfo *dbPtr; int *listPtr, number, i, objc, eobjc, expError; RatDbEntry *entryPtr; Tcl_Obj **objv, **eobjv; Tcl_ListObjGetElements(interp, defPtr, &objc, &objv); Tcl_IncrRefCount(objv[5]); if (append_only) { number = 0; listPtr = NULL; } else if (TCL_OK != RatDbSearch(interp, objv[5], &number, &listPtr, &expError)) { Tcl_DecrRefCount(objv[5]); if (!expError) { RatLogF(interp, RAT_ERROR, "dbase_error", RATLOG_TIME, Tcl_GetStringResult(interp)); } Tcl_ResetResult(interp); Tcl_AppendResult(interp, "Failed to search dbase \"", Tcl_GetString(objv[5]), "\"", (char *) NULL); return (RatFolderInfo *) NULL; } infoPtr = (RatFolderInfo *) ckalloc(sizeof(*infoPtr)); dbPtr = (DbFolderInfo *) ckalloc(sizeof(*dbPtr)); infoPtr->name = cpystr("Database search"); infoPtr->type = "dbase"; infoPtr->number = number; infoPtr->recent = 0; infoPtr->unseen = 0; for (i=0; inumber; i++) { entryPtr = RatDbGetEntry(listPtr[i]); if (!strchr(entryPtr->content[STATUS], 'O')) { infoPtr->recent++; } if (!strchr(entryPtr->content[STATUS], 'R')) { infoPtr->unseen++; } } infoPtr->size = 0; for (i=0; inumber; i++) { infoPtr->size += atoi(RatDbGetEntry(listPtr[i])->content[RSIZE]); } infoPtr->initProc = Db_InitProc; infoPtr->finalProc = NULL; infoPtr->closeProc = Db_CloseProc; infoPtr->updateProc = Db_UpdateProc; infoPtr->insertProc = Db_InsertProc; infoPtr->setFlagProc = Db_SetFlagProc; infoPtr->getFlagProc = Db_GetFlagProc; infoPtr->infoProc = Db_InfoProc; infoPtr->setInfoProc = Db_SetInfoProc; infoPtr->createProc = Db_CreateProc; infoPtr->syncProc = NULL; infoPtr->dbinfoGetProc = Db_DbinfoGetProc; infoPtr->dbinfoSetProc = Db_DbinfoSetProc; infoPtr->private = (ClientData) dbPtr; dbPtr->listPtr = listPtr; dbPtr->searchExpr = objv[5]; Tcl_ListObjGetElements(interp, objv[5], &eobjc, &eobjv); dbPtr->keywords = NULL; for (i=0; ikeywords = cpystr(Tcl_GetString(eobjv[i+1])); break; } } dbPtr->exDate = cpystr(Tcl_GetString(objv[4])); dbPtr->exType = cpystr(Tcl_GetString(objv[3])); dbPtr->infoPtr = (Tcl_Obj**)ckalloc(sizeof(Tcl_Obj*) * number * RAT_FOLDER_END); for (i=0; i < number*RAT_FOLDER_END; i++) { dbPtr->infoPtr[i] = NULL; } return infoPtr; } /* *---------------------------------------------------------------------- * * Db_InitProc -- * * See the documentation for initProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for initProc in ratFolder.h * * *---------------------------------------------------------------------- */ static void Db_InitProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { return; } /* *---------------------------------------------------------------------- * * Db_CloseProc -- * * See the documentation for closeProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for closeProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Db_CloseProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int expunge) { DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private; int i; if (dbPtr->listPtr) { ckfree(dbPtr->listPtr); } Tcl_DecrRefCount(dbPtr->searchExpr); ckfree(dbPtr->keywords); ckfree(dbPtr->exDate); ckfree(dbPtr->exType); for (i=0; inumber*RAT_FOLDER_END; i++) { if (dbPtr->infoPtr[i]) { Tcl_DecrRefCount(dbPtr->infoPtr[i]); } } ckfree(dbPtr->infoPtr); ckfree(dbPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * Db_UpdateProc -- * * See the documentation for updateProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for updateProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Db_UpdateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, RatUpdateType mode) { DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private; int *listPtr, number, numNew, i, expError; RatDbEntry *entryPtr; if (RAT_SYNC == mode) { int i, j, dst; if (TCL_OK != RatDbExpunge(interp)) { return -1; } infoPtr->size = 0; for (i=dst=0; inumber; i++) { if ((entryPtr = RatDbGetEntry(dbPtr->listPtr[i]))) { dbPtr->listPtr[dst] = dbPtr->listPtr[i]; infoPtr->msgCmdPtr[dst] = infoPtr->msgCmdPtr[i]; infoPtr->size += atoi(entryPtr->content[RSIZE]); for (j=0; jinfoPtr[dst*RAT_FOLDER_END+j] = dbPtr->infoPtr[i*RAT_FOLDER_END+j]; } dst++; } else { if (infoPtr->msgCmdPtr[i]) { RatMessageDelete(interp, infoPtr->msgCmdPtr[i]); } for (j=0; jinfoPtr[i*RAT_FOLDER_END+j]) { Tcl_DecrRefCount(dbPtr->infoPtr[i*RAT_FOLDER_END+j]); } } } } infoPtr->number = dst; } numNew = 0; if (RAT_SYNC == mode || RAT_UPDATE == mode) { if (TCL_OK != RatDbSearch(interp, dbPtr->searchExpr, &number, &listPtr, &expError)){ if (!expError) { RatLogF(interp, RAT_ERROR, "dbase_error", RATLOG_TIME, Tcl_GetStringResult(interp)); } Tcl_ResetResult(interp); Tcl_AppendResult(interp, "Failed to search dbase \"", Tcl_GetString(dbPtr->searchExpr), "\"", (char *) NULL); return -1; } for (i=0 ; i < infoPtr->number && i < number && listPtr[i] == dbPtr->listPtr[i]; i++); if (i != number || i != infoPtr->number) { for (i=0; inumber*RAT_FOLDER_END; i++) { if (dbPtr->infoPtr[i]) { Tcl_DecrRefCount(dbPtr->infoPtr[i]); } } ckfree(dbPtr->infoPtr); ckfree(dbPtr->listPtr); dbPtr->listPtr = listPtr; numNew = number - infoPtr->number; infoPtr->number = number; dbPtr->infoPtr = (Tcl_Obj**) ckalloc(sizeof(Tcl_Obj*)*number*RAT_FOLDER_END); for (i=0; iinfoPtr[i] = NULL; } } infoPtr->recent = 0; infoPtr->unseen = 0; for (i=0; inumber; i++) { entryPtr = RatDbGetEntry(dbPtr->listPtr[i]); if (!strchr(entryPtr->content[STATUS], 'O')) { infoPtr->recent++; } if (!strchr(entryPtr->content[STATUS], 'R')) { infoPtr->unseen++; } } } return numNew; } /* *---------------------------------------------------------------------- * * Db_InsertProc -- * * See the documentation for insertProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for insertProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Db_InsertProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int argc, char *argv[]) { DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private; Tcl_CmdInfo cmdInfo; int i; for (i=0; ikeywords, dbPtr->exDate, dbPtr->exType); } return TCL_OK; } /* *---------------------------------------------------------------------- * * Db_SetFlagProc -- * * See the documentation for setFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for setFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Db_SetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int *ilist, int count, RatFlag flag, int value) { DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private; RatDbEntry *entryPtr; char newStatus[5]; int dst, i, j; for (i=0; ilistPtr[ilist[i]]); for (j=0; entryPtr->content[STATUS][j]; j++) { switch(entryPtr->content[STATUS][j]) { case 'R': flagArray[RAT_SEEN] = 1; break; case 'D': flagArray[RAT_DELETED] = 1; break; case 'F': flagArray[RAT_FLAGGED] = 1; break; case 'A': flagArray[RAT_ANSWERED] = 1; break; case 'T': flagArray[RAT_DRAFT] = 1; break; case 'O': flagArray[RAT_RECENT] = 1; break; } } if (RAT_SEEN == flag && flagArray[RAT_SEEN] != value) { if (value) { infoPtr->unseen--; } else { infoPtr->unseen++; } } flagArray[flag] = value; dst = 0; if (flagArray[RAT_SEEN]) { newStatus[dst++] = 'R'; } if (flagArray[RAT_DELETED]) { newStatus[dst++] = 'D'; } if (flagArray[RAT_FLAGGED]) { newStatus[dst++] = 'F'; } if (flagArray[RAT_ANSWERED]) { newStatus[dst++] = 'A'; } if (flagArray[RAT_DRAFT]) { newStatus[dst++] = 'T'; } if (flagArray[RAT_RECENT]) { newStatus[dst++] = 'O'; } newStatus[dst] = '\0'; j = ilist[i]*RAT_FOLDER_END+RAT_FOLDER_STATUS; if (dbPtr->infoPtr[j]) { Tcl_DecrRefCount(dbPtr->infoPtr[j]); dbPtr->infoPtr[j] = NULL; } if (TCL_OK != RatDbSetStatus(interp, dbPtr->listPtr[ilist[i]], newStatus)) { return TCL_ERROR; } } return TCL_OK; } /* *---------------------------------------------------------------------- * * Db_GetFlagProc -- * * See the documentation for getFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for getFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Db_GetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, RatFlag flag) { DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private; RatDbEntry *entryPtr = RatDbGetEntry(dbPtr->listPtr[index]); char flagChar; int i; switch(flag) { case RAT_SEEN: flagChar = 'R'; break; case RAT_DELETED: flagChar = 'D'; break; case RAT_FLAGGED: flagChar = 'F'; break; case RAT_ANSWERED: flagChar = 'A'; break; case RAT_DRAFT: flagChar = 'T'; break; case RAT_RECENT: flagChar = 'O'; break; default: return 0; } for (i=0; entryPtr->content[STATUS][i]; i++) { if (entryPtr->content[STATUS][i] == flagChar) { return 1; } } return 0; } /* *---------------------------------------------------------------------- * * GetAddressInfo -- * * Gets info from an address. The info argument decides what we * wants to extract. The requested data will be appeded to dsPtr. * * Results: * Returns true if this address points to me. * * Side effects: * The dsPtr DString will be modified * * *---------------------------------------------------------------------- */ static int GetAddressInfo(Tcl_Interp *interp, Tcl_DString *dsPtr, char *adr, DbAdrInfo info) { ADDRESS *addressPtr = NULL; char *s, *host; int ret; host = RatGetCurrent(interp, RAT_HOST, ""); s = cpystr(adr); rfc822_parse_adrlist(&addressPtr, s, host); ckfree(s); if (!addressPtr) { return 0; } ret = RatAddressIsMe(interp, addressPtr, 1); if (Db_Name == info && addressPtr->personal) { Tcl_DStringAppend(dsPtr, addressPtr->personal, -1); } else { Tcl_DStringAppend(dsPtr, RatAddressMail(addressPtr), -1); } mail_free_address(&addressPtr); return ret; } /* *---------------------------------------------------------------------- * * Db_InfoProc -- * * See the documentation for infoProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for infoProc in ratFolder.h * * *---------------------------------------------------------------------- */ Tcl_Obj* Db_InfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index) { RatFolderInfo *infoPtr = (RatFolderInfo*)clientData; return Db_InfoProcInt(interp, infoPtr, type, index); } /* *---------------------------------------------------------------------- * * Db_InfoProcInt -- * * See the documentation for infoProc in ratFolder.h. The difference * between this function and Db_InfoProc is that this expects the * index (rIndex) to be the real folder index. * * Results: * A standard Tcl result. * * Side effects: * See the documentation for infoProc in ratFolder.h * * *---------------------------------------------------------------------- */ Tcl_Obj* Db_InfoProcInt(Tcl_Interp *interp, RatFolderInfo *infoPtr, RatFolderInfoType type, int rIndex) { static Tcl_DString ds; static int initialized = 0; static char buf[1024]; int i, seen, deleted, marked, answered, me, dbIndex, zone, f; DbFolderInfo *dbPtr = (DbFolderInfo*)infoPtr->private; ADDRESS *addressPtr, *address2Ptr; Tcl_Obj *oPtr = NULL; MESSAGECACHE elt; RatDbEntry *entryPtr; struct tm *tmPtr; Tcl_CmdInfo info; time_t time; char *host; dbIndex = dbPtr->listPtr[rIndex]; if (dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type]) { if (type == RAT_FOLDER_INDEX) { Tcl_GetIntFromObj(interp, dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type], &i); if (i < infoPtr->number && dbIndex == dbPtr->listPtr[infoPtr->presentationOrder[i]]) { return dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type]; } } else { return dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type]; } } entryPtr = RatDbGetEntry(dbIndex); if (!initialized) { Tcl_DStringInit(&ds); initialized = 1; } switch (type) { case RAT_FOLDER_SUBJECT: oPtr = Tcl_NewStringObj(entryPtr->content[SUBJECT], -1); break; case RAT_FOLDER_CANONSUBJECT: oPtr = RatFolderCanonalizeSubject(entryPtr->content[SUBJECT]); break; case RAT_FOLDER_NAME: Tcl_DStringSetLength(&ds, 0); if (GetAddressInfo(interp, &ds, entryPtr->content[FROM],Db_Name)) { Tcl_DStringSetLength(&ds, 0); Tcl_DStringAppend(&ds, Tcl_GetVar2(interp, "t", "to", TCL_GLOBAL_ONLY), -1); Tcl_DStringAppend(&ds, ": ", 2); GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Name); } oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); break; case RAT_FOLDER_ANAME: Tcl_DStringSetLength(&ds, 0); GetAddressInfo(interp, &ds, entryPtr->content[FROM],Db_Name); oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); break; case RAT_FOLDER_MAIL_REAL: Tcl_DStringSetLength(&ds, 0); GetAddressInfo(interp, &ds, entryPtr->content[FROM], Db_Mail); oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); break; case RAT_FOLDER_MAIL: Tcl_DStringSetLength(&ds, 0); if (GetAddressInfo(interp, &ds, entryPtr->content[FROM],Db_Mail)) { Tcl_DStringSetLength(&ds, 0); Tcl_DStringAppend(&ds, Tcl_GetVar2(interp, "t", "to", TCL_GLOBAL_ONLY), -1); Tcl_DStringAppend(&ds, ": ", 2); GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Mail); } oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); break; case RAT_FOLDER_NAME_RECIPIENT: if (RatIsEmpty(entryPtr->content[TO])) { oPtr = Tcl_NewStringObj(entryPtr->content[TO], -1); break; } Tcl_DStringSetLength(&ds, 0); if (GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Name)) { Tcl_DStringSetLength(&ds, 0); Tcl_DStringAppend(&ds, Tcl_GetVar2(interp, "t", "from", TCL_GLOBAL_ONLY), -1); Tcl_DStringAppend(&ds, ": ", 2); GetAddressInfo(interp, &ds, entryPtr->content[FROM], Db_Name); } oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); break; case RAT_FOLDER_MAIL_RECIPIENT: if (RatIsEmpty(entryPtr->content[TO])) { oPtr = Tcl_NewStringObj(entryPtr->content[TO], -1); break; } Tcl_DStringSetLength(&ds, 0); if (GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Mail)) { Tcl_DStringSetLength(&ds, 0); Tcl_DStringAppend(&ds, Tcl_GetVar2(interp, "t", "from", TCL_GLOBAL_ONLY), -1); Tcl_DStringAppend(&ds, ": ", 2); GetAddressInfo(interp, &ds, entryPtr->content[FROM], Db_Mail); } oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); break; case RAT_FOLDER_SIZE: oPtr = Tcl_NewIntObj(atoi(entryPtr->content[RSIZE])); break; case RAT_FOLDER_SIZE_F: oPtr = RatMangleNumber(atoi(entryPtr->content[RSIZE])); break; case RAT_FOLDER_DATE_F: time = atoi(entryPtr->content[DATE]); tmPtr = localtime(&time); oPtr = RatFormatDate(interp, tmPtr); break; case RAT_FOLDER_DATE_N: oPtr = Tcl_NewStringObj(entryPtr->content[DATE], -1); break; case RAT_FOLDER_DATE_IMAP4: Tcl_DStringSetLength(&ds, 256); time = atoi(entryPtr->content[DATE]); tmPtr = localtime(&time); elt.day = tmPtr->tm_mday; elt.month = tmPtr->tm_mon+1; elt.year = tmPtr->tm_year+1900-BASEYEAR; elt.hours = tmPtr->tm_hour; elt.minutes = tmPtr->tm_min; elt.seconds = tmPtr->tm_sec; zone = RatGetTimeZone(time); if (zone >= 0) { elt.zoccident = 1; elt.zhours = zone/(60*60); elt.zminutes = (zone%(60*60))/60; } else { elt.zoccident = 0; elt.zhours = (-1*zone)/(60*60); elt.zminutes = ((-1*zone)%(60*60))/60; } oPtr = Tcl_NewStringObj(mail_date(Tcl_DStringValue(&ds), &elt),-1); break; case RAT_FOLDER_STATUS: seen = deleted = marked = answered = me = 0; for (i=0; entryPtr->content[STATUS][i]; i++) { switch (entryPtr->content[STATUS][i]) { case 'R': seen = 1; break; case 'D': deleted = 1; break; case 'F': marked = 1; break; case 'A': answered = 1; break; } } addressPtr = NULL; if (!RatIsEmpty(entryPtr->content[TO])) { Tcl_DStringSetLength(&ds, 0); Tcl_DStringAppend(&ds, entryPtr->content[TO], -1); host = RatGetCurrent(interp, RAT_HOST, ""); rfc822_parse_adrlist(&addressPtr, Tcl_DStringValue(&ds), host); for (address2Ptr = addressPtr; !me && address2Ptr; address2Ptr = address2Ptr->next) { if (RatAddressIsMe(interp, address2Ptr, 1)) { me = 1; } } } mail_free_address(&addressPtr); i = 0; if (!seen) { buf[i++] = 'N'; } if (deleted) { buf[i++] = 'D'; } if (marked) { buf[i++] = 'F'; } if (answered) { buf[i++] = 'A'; } if (me) { buf[i++] = '+'; } else { buf[i++] = ' '; } buf[i] = '\0'; oPtr = Tcl_NewStringObj(buf, -1); break; case RAT_FOLDER_TYPE: return NULL; case RAT_FOLDER_PARAMETERS: return NULL; case RAT_FOLDER_INDEX: for (i=0; i < infoPtr->number; i++) { if (dbIndex == dbPtr->listPtr[infoPtr->presentationOrder[i]]) { oPtr = Tcl_NewIntObj(i+1); break; } } if (i == infoPtr->number) { oPtr = Tcl_NewIntObj(1); } break; case RAT_FOLDER_UID: oPtr = Tcl_NewIntObj(dbIndex); break; case RAT_FOLDER_TO: oPtr = Tcl_NewStringObj(entryPtr->content[TO], -1); break; case RAT_FOLDER_FROM: oPtr = Tcl_NewStringObj(entryPtr->content[FROM], -1); break; case RAT_FOLDER_SENDER: /* fallthrough */ case RAT_FOLDER_CC: /* fallthrough */ case RAT_FOLDER_REPLY_TO: if (NULL == infoPtr->msgCmdPtr[rIndex]) { infoPtr->msgCmdPtr[rIndex] = Db_CreateProc(infoPtr, interp, rIndex); } Tcl_GetCommandInfo(interp, infoPtr->msgCmdPtr[rIndex], &info); oPtr = RatMsgInfo(interp, (MessageInfo*)info.objClientData, type); break; case RAT_FOLDER_FLAGS: Tcl_DStringSetLength(&ds, 0); for (i=0; entryPtr->content[STATUS][i]; i++) { for (f=0; flag_name[f].imap_name; f++) { if (flag_name[f].unix_char==entryPtr->content[STATUS][i]) { Tcl_DStringAppend(&ds, " ", -1); Tcl_DStringAppend(&ds, flag_name[f].imap_name, -1); break; } } } if (Tcl_DStringLength(&ds)) { oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds)+1, -1); } else { oPtr = Tcl_NewStringObj("", 0); } break; case RAT_FOLDER_UNIXFLAGS: oPtr = Tcl_NewStringObj(entryPtr->content[STATUS], -1); break; case RAT_FOLDER_MSGID: oPtr = Tcl_NewStringObj(entryPtr->content[MESSAGE_ID], -1); break; case RAT_FOLDER_REF: oPtr = Tcl_NewStringObj(entryPtr->content[REFERENCE], -1); break; case RAT_FOLDER_THREADING: return NULL; case RAT_FOLDER_END: break; } dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type] = oPtr; return oPtr; } /* *---------------------------------------------------------------------- * * Db_CreateProc -- * * See the documentation for createProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for createProc in ratFolder.h * * *---------------------------------------------------------------------- */ static char* Db_CreateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private; return RatDbMessageCreate(interp, infoPtr, index, dbPtr->listPtr[index]); } /* *---------------------------------------------------------------------- * * Db_SetInfoProc -- * * Sets information about a message * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void Db_SetInfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index, Tcl_Obj *oPtr) { RatFolderInfo *infoPtr = (RatFolderInfo*)clientData; DbFolderInfo *dbPtr = (DbFolderInfo*)infoPtr->private; int i = index*RAT_FOLDER_END+type; if (dbPtr->infoPtr[i]) { Tcl_DecrRefCount(dbPtr->infoPtr[i]); } dbPtr->infoPtr[i] = oPtr; if (oPtr) { Tcl_IncrRefCount(oPtr); } } /* *---------------------------------------------------------------------- * * Db_DbinfoGetProc -- * * Handle the dbinfo_get command. See ../doc/interface for details * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static Tcl_Obj* Db_DbinfoGetProc(RatFolderInfo *infoPtr) { Tcl_Obj *robjv[3]; int len; DbFolderInfo *dbPtr = (DbFolderInfo*)infoPtr->private; len = dbPtr->keywords ? strlen(dbPtr->keywords) : 0; if (len && '{' == dbPtr->keywords[0] && '}' == dbPtr->keywords[len-1]) { robjv[0] = Tcl_NewStringObj(dbPtr->keywords+1, len-2); } else { robjv[0] = Tcl_NewStringObj(dbPtr->keywords, len); } robjv[1] = Tcl_NewLongObj(atol(dbPtr->exDate)); robjv[2] = Tcl_NewStringObj(dbPtr->exType, -1); return Tcl_NewListObj(3, robjv); } /* *---------------------------------------------------------------------- * * Db_DbinfoSetProc -- * * Handle the dbinfo_set command. See ../doc/interface for details * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ int Db_DbinfoSetProc(Tcl_Interp *interp, RatFolderInfoPtr infoPtr, Tcl_Obj *indexes, Tcl_Obj *keywords, Tcl_Obj *ex_date, Tcl_Obj *ex_type) { DbFolderInfo *dbPtr = (DbFolderInfo*)infoPtr->private; int objc, *db_indexes, i, index, r; Tcl_Obj **objv; /* Convert folder indexes to database indexes */ if (TCL_OK != Tcl_ListObjGetElements(interp, indexes, &objc, &objv)) { return TCL_ERROR; } db_indexes = (int*)ckalloc(objc*sizeof(int)); for (i=0; ilistPtr[infoPtr->presentationOrder[index]]; } r = RatDbSetInfo(interp, db_indexes, objc, keywords, ex_date, ex_type); ckfree(db_indexes); return r; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratDbMessage.c000066400000000000000000000271341137544547100212660ustar00rootroot00000000000000/* * ratDbMessage.c -- * * This file contains code which implements dbase messages. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" /* * The ClientData for each message entity */ typedef struct DbMessageInfo { int index; char *buffer; MESSAGE *messagePtr; } DbMessageInfo; /* * The number of message entities created. This is used to create new * unique command names. */ static int numDbMessages = 0; static RatGetHeadersProc Db_GetHeadersProc; static RatGetEnvelopeProc Db_GetEnvelopeProc; static RatInfoProc Db_GetInfoProc; static RatCreateBodyProc Db_CreateBodyProc; static RatFetchTextProc Db_FetchTextProc; static RatEnvelopeProc Db_EnvelopeProc; static RatMsgDeleteProc Db_MsgDeleteProc; static RatMakeChildrenProc Db_MakeChildrenProc; static RatFetchBodyProc Db_FetchBodyProc; static RatBodyDeleteProc Db_BodyDeleteProc; static RatGetInternalDateProc Db_GetInternalDateProc; static RatMsgDbInfoProc Db_MsgDbInfoGetProc; /* *---------------------------------------------------------------------- * * RatDbMessagesInit -- * * Initializes the given MessageProcInfo entry for a dbase message * * Results: * None. * * Side effects: * The given MessageProcInfo is initialized. * * *---------------------------------------------------------------------- */ void RatDbMessagesInit(MessageProcInfo *messageProcInfoPtr) { messageProcInfoPtr->getHeadersProc = Db_GetHeadersProc; messageProcInfoPtr->getEnvelopeProc = Db_GetEnvelopeProc; messageProcInfoPtr->getInfoProc = Db_GetInfoProc; messageProcInfoPtr->createBodyProc = Db_CreateBodyProc; messageProcInfoPtr->fetchTextProc = Db_FetchTextProc; messageProcInfoPtr->envelopeProc = Db_EnvelopeProc; messageProcInfoPtr->msgDeleteProc = Db_MsgDeleteProc; messageProcInfoPtr->makeChildrenProc = Db_MakeChildrenProc; messageProcInfoPtr->fetchBodyProc = Db_FetchBodyProc; messageProcInfoPtr->bodyDeleteProc = Db_BodyDeleteProc; messageProcInfoPtr->getInternalDateProc = Db_GetInternalDateProc; messageProcInfoPtr->dbinfoGetProc = Db_MsgDbInfoGetProc; } /* *---------------------------------------------------------------------- * * RatDbMessageCreate -- * * Creates a dbase message entity * * Results: * The name of the new message entity. * * Side effects: * None. * deleted. * * *---------------------------------------------------------------------- */ char* RatDbMessageCreate(Tcl_Interp *interp, RatFolderInfoPtr infoPtr, int index, int dbIndex) { DbMessageInfo *dbMsgPtr=(DbMessageInfo*)ckalloc(sizeof(DbMessageInfo)); MessageInfo *msgPtr=(MessageInfo*)ckalloc(sizeof(MessageInfo)); int i; msgPtr->folderInfoPtr = infoPtr; msgPtr->type = RAT_DBASE_MESSAGE; msgPtr->bodyInfoPtr = NULL; msgPtr->msgNo = index; msgPtr->fromMe = RAT_ISME_UNKOWN; msgPtr->toMe = RAT_ISME_UNKOWN; msgPtr->clientData = (ClientData)dbMsgPtr; for (i=0; iinfo)/sizeof(*msgPtr->info); i++) { msgPtr->info[i] = NULL; } dbMsgPtr->index = dbIndex; dbMsgPtr->messagePtr = RatDbGetMessage(interp, dbIndex, &dbMsgPtr->buffer); sprintf(msgPtr->name, "RatDbMsg%d", numDbMessages++); Tcl_CreateObjCommand(interp, msgPtr->name, RatMessageCmd, (ClientData) msgPtr, NULL); return msgPtr->name; } /* *---------------------------------------------------------------------- * * Db_GetHeadersProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Db_GetHeadersProc(Tcl_Interp *interp, MessageInfo *msgPtr) { DbMessageInfo *dbMsgPtr=(DbMessageInfo*)msgPtr->clientData; return RatDbGetHeaders(interp, dbMsgPtr->index); } /* *---------------------------------------------------------------------- * * Db_GetEnvelopeProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Db_GetEnvelopeProc(Tcl_Interp *interp, MessageInfo *msgPtr) { static char buf[1024]; DbMessageInfo *dbMsgPtr=(DbMessageInfo*)msgPtr->clientData; RatDbEntry *entryPtr = RatDbGetEntry(dbMsgPtr->index); struct tm *tmPtr; time_t date; date = atoi(entryPtr->content[DATE]); tmPtr = gmtime(&date); snprintf(buf, sizeof(buf), "From %s %s %s %2d %02d:%02d GMT %04d\n", entryPtr->content[FROM], dayName[tmPtr->tm_wday], monthName[tmPtr->tm_mon], tmPtr->tm_mday, tmPtr->tm_hour, tmPtr->tm_min, tmPtr->tm_year+1900); return buf; } /* *---------------------------------------------------------------------- * * Db_CreateBodyProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static BodyInfo* Db_CreateBodyProc(Tcl_Interp *interp, MessageInfo *msgPtr) { DbMessageInfo *dbMsgPtr = (DbMessageInfo*)msgPtr->clientData; msgPtr->bodyInfoPtr = CreateBodyInfo(interp, msgPtr, dbMsgPtr->messagePtr->body); return msgPtr->bodyInfoPtr; } /* *---------------------------------------------------------------------- * * Db_FetchTextProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Db_FetchTextProc(Tcl_Interp *interp, MessageInfo *msgPtr) { DbMessageInfo *dbMsgPtr = (DbMessageInfo*)msgPtr->clientData; return RatDbGetText(interp, dbMsgPtr->index); } /* *---------------------------------------------------------------------- * * Db_EnvelopeProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static ENVELOPE* Db_EnvelopeProc(MessageInfo *msgPtr) { return ((DbMessageInfo*)msgPtr->clientData)->messagePtr->env; } /* *---------------------------------------------------------------------- * * Db_MsgDeleteProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static void Db_MsgDeleteProc(MessageInfo *msgPtr) { DbMessageInfo *dbMsgPtr = (DbMessageInfo*)msgPtr->clientData; mail_free_body(&dbMsgPtr->messagePtr->body); mail_free_envelope(&dbMsgPtr->messagePtr->env); ckfree(dbMsgPtr->messagePtr); ckfree(dbMsgPtr->buffer); ckfree(dbMsgPtr); } /* *---------------------------------------------------------------------- * * Db_MakeChildrenProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static void Db_MakeChildrenProc(Tcl_Interp *interp, BodyInfo *bodyInfoPtr) { BODY *bodyPtr = bodyInfoPtr->bodyPtr; BodyInfo *partInfoPtr, **partInfoPtrPtr; PART *partPtr; if (!bodyInfoPtr->firstbornPtr) { partInfoPtrPtr = &bodyInfoPtr->firstbornPtr; for (partPtr = bodyPtr->nested.part; partPtr; partPtr = partPtr->next) { partInfoPtr = CreateBodyInfo(interp, bodyInfoPtr->msgPtr, &partPtr->body); *partInfoPtrPtr = partInfoPtr; partInfoPtrPtr = &partInfoPtr->nextPtr; } } } /* *---------------------------------------------------------------------- * * Db_FetchBodyProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Db_FetchBodyProc(BodyInfo *bodyInfoPtr, unsigned long *lengthPtr) { if (bodyInfoPtr->decodedTextPtr) { *lengthPtr = Tcl_DStringLength(bodyInfoPtr->decodedTextPtr); return Tcl_DStringValue(bodyInfoPtr->decodedTextPtr); } *lengthPtr = bodyInfoPtr->bodyPtr->contents.text.size; return (char*)bodyInfoPtr->bodyPtr->contents.text.data; } /* *---------------------------------------------------------------------- * * Db_BodyDeleteProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static void Db_BodyDeleteProc(BodyInfo *bodyInfoPtr) { /* Nothing to do */ } /* *---------------------------------------------------------------------- * * Db_GetInternalDateProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static MESSAGECACHE* Db_GetInternalDateProc(Tcl_Interp *interp, MessageInfo *msgPtr) { DbMessageInfo *dbMsgPtr = (DbMessageInfo*)msgPtr->clientData; char *from; from = RatDbGetFrom(interp, dbMsgPtr->index); return RatParseFrom(from); } /* *---------------------------------------------------------------------- * * Db_GetInfoProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ Tcl_Obj* Db_GetInfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int notused) { MessageInfo *msgPtr = (MessageInfo*)clientData; MESSAGE *messagePtr = ((DbMessageInfo*)msgPtr->clientData)->messagePtr; ADDRESS *adrPtr; Tcl_Obj *oPtr; switch (type) { case RAT_FOLDER_SUBJECT: /* fallthrough */ case RAT_FOLDER_CANONSUBJECT: /* fallthrough */ case RAT_FOLDER_NAME: /* fallthrough */ case RAT_FOLDER_ANAME: /* fallthrough */ case RAT_FOLDER_MAIL_REAL: /* fallthrough */ case RAT_FOLDER_MAIL: /* fallthrough */ case RAT_FOLDER_NAME_RECIPIENT: /* fallthrough */ case RAT_FOLDER_MAIL_RECIPIENT: /* fallthrough */ case RAT_FOLDER_SIZE: /* fallthrough */ case RAT_FOLDER_SIZE_F: /* fallthrough */ case RAT_FOLDER_TYPE: /* fallthrough */ case RAT_FOLDER_PARAMETERS: /* fallthrough */ case RAT_FOLDER_DATE_F: /* fallthrough */ case RAT_FOLDER_DATE_N: /* fallthrough */ case RAT_FOLDER_DATE_IMAP4: /* fallthrough */ case RAT_FOLDER_TO: /* fallthrough */ case RAT_FOLDER_FROM: /* fallthrough */ case RAT_FOLDER_STATUS: /* fallthrough */ case RAT_FOLDER_FLAGS: /* fallthrough */ case RAT_FOLDER_UNIXFLAGS: /* fallthrough */ case RAT_FOLDER_MSGID: /* fallthrough */ case RAT_FOLDER_REF: /* fallthrough */ case RAT_FOLDER_UID: /* fallthrough */ case RAT_FOLDER_THREADING: /* fallthrough */ case RAT_FOLDER_INDEX: return Db_InfoProcInt(interp, msgPtr->folderInfoPtr, type, msgPtr->msgNo); case RAT_FOLDER_SENDER: /* fallthrough */ case RAT_FOLDER_CC: /* fallthrough */ case RAT_FOLDER_REPLY_TO: if (type == RAT_FOLDER_SENDER) { adrPtr = messagePtr->env->sender; } else if (type == RAT_FOLDER_CC) { adrPtr = messagePtr->env->cc; } else { adrPtr = messagePtr->env->reply_to; } oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(adrPtr, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), adrPtr); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); return oPtr; case RAT_FOLDER_END: break; } return NULL; } /* *---------------------------------------------------------------------- * * Db_DbinfoGetProc -- * * Handle the dbinfo_get command. See ../doc/interface for details * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static Tcl_Obj* Db_MsgDbInfoGetProc(MessageInfo *msgPtr) { DbMessageInfo *dbMsgPtr = (DbMessageInfo*)msgPtr->clientData; Tcl_Obj *robjv[3]; RatDbEntry *entry; int len; entry = RatDbGetEntry(dbMsgPtr->index); len = strlen(entry->content[KEYWORDS]); if (len && '{' == entry->content[KEYWORDS][0] && '}' == entry->content[KEYWORDS][len-1]) { robjv[0] = Tcl_NewStringObj(entry->content[KEYWORDS]+1, len-2); } else { robjv[0] = Tcl_NewStringObj(entry->content[KEYWORDS], len); } robjv[1] = Tcl_NewLongObj(atol(entry->content[EX_TIME])); robjv[2] = Tcl_NewStringObj(entry->content[EX_TYPE], -1); return Tcl_NewListObj(3, robjv); } tkrat_2.2cvs20100105-dfsg.orig/lib/ratDbase.c000066400000000000000000002577341137544547100204650ustar00rootroot00000000000000/* * ratDbase.c -- * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. * * This file contains support for a database of messages. This file * uses version 4 of the database. The format of the database is as * follows: * * The database directory contains the following entries: * index The main database index. This file consists * of a number of entries separated by newline and * each entry has the following format: * To * From * Cc * Message-Id * References * Subject * Date (UNIX time_t as a string) * Keywords (SPACE separated list) * Size * Status * Expiration time (UNIX time_t as a string) * Expiration event (*) * Filename * Expiration event is one of none, remove, incoming, * backup and custom. Custom is followed by the custom * command. * index.info This file contains information about the database. * It contains two integers. The first is the version * (in this case 3) and the second is the number of * entries in the indexfile. * index.changes This file contains a log of changes made to the * index file. This log is only kept if the multiple * agents has opened the database. In this file each * entry is one line. There are addition entries; * 'a OFFSET' where OFFSET is the position in the * index file where this entry starts. There are also * deletion entries; 'd INDEX'. Finally there are * the status changes; they are of the form: * 's INDEX STATUS' where STATUS is the new status of * the specified message. * lock If this file exists the database is locked and * no other agent may do anything with it. It should * contain a string identifying the agent owning the * lock. * rlock.* Each agent that opens the database should construct * a file with the following name: rlock.HOST:PID . * This file should be touched at least once every * hour. * dbase/ This is a directory which holds a number of * directories which in turn holds the actual messages. * * In the dbase directory messages are stored as recipient-name/number. * Where the last number taken in a recipient-name directory is * contained in a .seq file found in said directory. * All entries are stored in utf-8 */ #include "ratFolder.h" #define DBASE_VERSION 5 /* Version of the database format */ #define EXTRA_ENTRIES 100 /* How many extra entries we should allocate * room for when allocating the entryPtr * array */ #define RLOCK_TIMEOUT 2*60*60 /* Actual timeout time for rlock files */ #define UPDATE_INTERVAL 40*60 /* Time between updates to the rlock file */ static int isRead = 0; /* 0 means that the database hasn't been * read yet */ static int numRead = 0; /* The number of entries in the entryPtr list*/ static int numAlloc; /* The number of entries that will fit * into the entryPtr list */ static RatDbEntry *entryPtr; /* The list of entries in the database */ static char *dbDir = 0; /* Full path to the database directory */ static char *ident = 0; /* String which is used to identify us. * It is of the form hostname:pid */ static int changeSize = 0; /* The number of bytes in the index.changes * file that we have read and incorporated * into our memory resident database */ static int needRewrite = 0; /* 1 if we need to rewrite the indexfile */ static int numChanges = 0; /* Number of changes in the changes file */ static int version; /* Version read */ static long firstDate = 0; /* The earliest date in the dbase */ static long lastDate = 0; /* The last date in the dbase */ static long totSize = 0; /* Total size of dbase file */ /* * This structure is used while checking the dbase */ typedef struct { int fileSize; /* The actual size of the file */ int index; /* Index in the list that handles this entry */ RatDbEntry entry; /* The actual entry in the index file */ } RatDbItem; #define SEARCH_ALL -1 #define SEARCH_ALL_ADDRESSES -2 #define SEARCH_TIME_FROM -3 #define SEARCH_TIME_TO -4 /* * Forward declarations for procedures defined in this file: */ static int Read(Tcl_Interp *interp); static void Lock(Tcl_Interp *interp); static void Unlock(Tcl_Interp *interp); static int Sync(Tcl_Interp *interp, int force); static void Update(ClientData clientData); static int IsRlocked(char *ignore); static void RatDbBuildList(Tcl_Interp *interp, Tcl_DString *dsPtr, char *prefix, char *dir, Tcl_HashTable *tablePtr, int fix); static int NoLFPrint(FILE *fp, const char *s); static void DbaseConvert3to4(Tcl_Interp *interp); /* *---------------------------------------------------------------------- * * Read -- * * Reads the database from disk into memory. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The internal list of entries is allocated and initalized. An * rlock-file is created to indicate that we have the database * open. To keep this lock up to date the Update() procedure must * be called at least once every hour. When the agent won't access * the database anymore it must be closed with a call to RatDbClose(). * * *---------------------------------------------------------------------- */ static int Read(Tcl_Interp *interp) { char buf[1024]; /* Scratch area */ int size; /* Size of indexfile */ struct stat sbuf; /* Buffer for stat() calls */ int fhIndex; /* File handle for index-file */ int fhReadlock; /* File handle for read lock file */ FILE *fpIndexinfo; /* File pointer for index.info file */ int i, j; /* Loop variables */ char *cPtr; /* Running pointer */ long l; /* Long value */ /* * First make sure we know where the database should reside and which * identifier we should use. */ if (0 == dbDir) { const char *value = RatGetPathOption(interp, "dbase_dir"); if (NULL == value) { return TCL_ERROR; } dbDir = cpystr(value); } if (0 == ident) { gethostname(buf, sizeof(buf)); ident = (char*)ckalloc(strlen(buf)+16); snprintf(ident, strlen(buf)+16, "%s:%d", buf, (int)getpid()); } /* * Check if the database actually exists. If it doesn't then we * must create the needed directories and files. */ snprintf(buf, sizeof(buf), "%s/index", dbDir); if ( (0 != stat(dbDir, &sbuf) && ENOENT == errno) || (0 != stat(buf, &sbuf) && ENOENT == errno)) { if (0 != mkdir(dbDir, DIRMODE) && EEXIST != errno) { Tcl_AppendResult(interp, "error creating directory \"", dbDir, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } snprintf(buf, sizeof(buf), "%s/dbase", dbDir); if (0 != mkdir(buf, DIRMODE) && EEXIST != errno) { Tcl_AppendResult(interp, "error creating directory \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } Lock(interp); snprintf(buf, sizeof(buf), "%s/index", dbDir); if (0 > (fhIndex = open(buf, O_CREAT|O_WRONLY, FILEMODE)) || 0 != close(fhIndex)) { Unlock(interp); Tcl_AppendResult(interp, "error creating file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } snprintf(buf, sizeof(buf), "%s/index.info", dbDir); if (0 == (fpIndexinfo = fopen(buf, "w"))) { Unlock(interp); Tcl_AppendResult(interp, "error creating file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } if (0 > fprintf(fpIndexinfo, "%d 0\n", DBASE_VERSION)) { Unlock(interp); Tcl_AppendResult(interp, "error writing to file \"", buf, "\"", (char *) NULL); return TCL_ERROR; } if (0 != fclose(fpIndexinfo)) { Unlock(interp); Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } } else { Lock(interp); } /* * Create rlock file. */ snprintf(buf, sizeof(buf), "%s/rlock.%s", dbDir, ident); if (0 > (fhReadlock = open(buf, O_CREAT|O_WRONLY, FILEMODE)) || 0 != close(fhReadlock)) { Unlock(interp); Tcl_AppendResult(interp, "error creating file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } (void)Tcl_CreateTimerHandler(UPDATE_INTERVAL*1000,Update,(ClientData)NULL); /* * Read the index.info file */ snprintf(buf, sizeof(buf), "%s/index.info", dbDir); if (0 == (fpIndexinfo = fopen(buf, "r"))) { Tcl_AppendResult(interp, "error opening file (for reading)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); goto error; } if ( 2 != fscanf(fpIndexinfo, "%d %d", &version, &numRead)) { Tcl_SetResult(interp, "index.info file corrupt", TCL_STATIC); fclose(fpIndexinfo); goto error; } fclose(fpIndexinfo); /* * Check if this is the current version of the database. If not * complain! */ if (version != DBASE_VERSION && version != 3) { snprintf(buf, sizeof(buf), "wrong version of database got %d expected %d", version, DBASE_VERSION); Tcl_SetResult(interp, buf, TCL_VOLATILE); goto error; } /* * Read the indexfile and build internal data structures */ snprintf(buf, sizeof(buf), "%s/index", dbDir); if (0 != stat(buf, &sbuf)) { Tcl_AppendResult(interp, "error stating file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); goto error; } totSize = size = sbuf.st_size; if (size > 0) { char *indexPtr; /* Pointer to read version of the indexfile */ /* * We should not free this value since we never close the database. * Purify will note it as a leak, but we will have lots of * pointers into this data. */ if ( 0 == (indexPtr = (char*)ckalloc(size))) { Tcl_SetResult(interp, "failed to allocate memory for index", TCL_STATIC); goto error; } fhIndex = open(buf, O_RDONLY); if (size != SafeRead(fhIndex, indexPtr, size)) { Tcl_SetResult(interp, "error reading index", TCL_STATIC); close(fhIndex); goto error; } close(fhIndex); entryPtr = (RatDbEntry*)ckalloc((numRead+EXTRA_ENTRIES) * sizeof(RatDbEntry)); numAlloc = numRead+EXTRA_ENTRIES; cPtr = indexPtr; for (i=0; i &indexPtr[size]) { Tcl_SetResult(interp, "error in index-file", TCL_STATIC); ckfree(indexPtr); goto error; } *cPtr++ = '\0'; } totSize += atol(entryPtr[i].content[RSIZE]); /* * This is a KLUDGE to work around a bug which existed for a * short time /MaF 960218 */ if ('+' == entryPtr[i].content[EX_TYPE][0]) { char buf[64]; sprintf(buf, "%ld", atoi(entryPtr[i].content[EX_TYPE])*24*60*60+ atol(entryPtr[i].content[DATE])); entryPtr[i].content[EX_TIME] = cpystr(buf); entryPtr[i].content[EX_TYPE] = "backup"; } l = atol(entryPtr[i].content[DATE]); if (l > 0 && (l < firstDate || 0 == firstDate)) { firstDate = l; } if (l > 0 && (l > lastDate || 0 == lastDate)) { lastDate = l; } } } else { entryPtr = (RatDbEntry*)ckalloc(EXTRA_ENTRIES * sizeof(RatDbEntry)); numAlloc = EXTRA_ENTRIES; } isRead = 1; /* * Let's get up to date with any changes made. */ if (3 == version) { Sync(interp, 1); DbaseConvert3to4(interp); Unlock(interp); return Read(interp); } else { Sync(interp, 0); } Unlock(interp); return TCL_OK; error: snprintf(buf, sizeof(buf), "%s/rlock.%s", dbDir, ident); unlink(buf); Unlock(interp); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * Lock -- * * Exculsively lock the database. A lock like this must be obtained * before you do anything at all with the database. And while a * lock like this is active nobody but the owner of the lock may * do anything to the database. The lock obtained must be released * with a call to the Unlock() procedure. * * Results: * None. * * Side effects: * Creates a lockfile in the database directory. * *---------------------------------------------------------------------- */ static void Lock(Tcl_Interp *interp) { char buf[1024]; /* Scratch area */ int fhLock; /* Lockfile filehandle */ int msgPost = 0; /* True if message has been posted */ do { snprintf(buf, sizeof(buf), "%s/lock", dbDir); if (-1 == (fhLock = open(buf, O_CREAT|O_EXCL|O_WRONLY, FILEMODE))) { if (EEXIST == errno) { if (!msgPost) { RatLogF(interp, RAT_INFO, "waiting_dbase_lock", RATLOG_EXPLICIT); Tcl_Eval(interp, buf); msgPost = 1; } sleep(2); } else { RatLogF(interp, RAT_FATAL, "failed_to_create_file", RATLOG_TIME, buf, Tcl_PosixError(interp)); exit(1); } } } while (-1 == fhLock); if (-1 == safe_write(fhLock, ident, strlen(ident))) { fprintf(stderr, "Failed to write dbase lock\n"); } close(fhLock); if (msgPost) { RatLog(interp, RAT_INFO, "", RATLOG_TIME); } } /* *---------------------------------------------------------------------- * * Unlock -- * * Releases a lock previously obtained with the Lock() procedure. * * Results: * None. * * Side effects: * The lockfile is removed. * *---------------------------------------------------------------------- */ static void Unlock(Tcl_Interp *interp) { char buf[1024]; /* Scratch area */ snprintf(buf, sizeof(buf), "%s/lock", dbDir); if (0 != unlink(buf)) { RatLogF(interp, RAT_FATAL, "failed_to_unlink_file", RATLOG_TIME, buf, Tcl_PosixError(interp)); exit(1); } } /* *---------------------------------------------------------------------- * * Sync -- * * Make sure that the database in memory is consistent with the * master on disk. This is accomplished by reading the index.changes * file. This call assumes that we have an exclusive lock on the * database. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The internal list of entries may be affected as well as the * index-file on disk. * *---------------------------------------------------------------------- */ static int Sync(Tcl_Interp *interp, int force) { static int stale = 0; /* Number of stale entries */ char buf[1024]; /* Scratch area */ struct stat sbuf; /* Buffer for stat() calls */ FILE *fpChanges; /* index.changes file pointer */ FILE *fpIndex; /* Index file pointer */ char command; /* Command in changes file */ int cmdArg; /* Argument to command */ int i; /* Loop counter */ int doWrite = 0; /* 1 if we should write the changes to the disk */ int numEntries; /* How many entries there actually are */ char *indexBuf = NULL; /* New part of index file */ int indexOffset = 0; /* Offset of new part of index file */ char *cPtr; int size; long l; snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (0 > stat(buf, &sbuf)) { if (ENOENT != errno) { Tcl_AppendResult(interp, "error stating file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } if (!force) { return TCL_OK; } else { sbuf.st_size = 0; } } if (changeSize >= sbuf.st_size && !force) { return TCL_OK; } /* * Read and perform changes mentioned in index.changes file */ if (0 == (fpChanges = fopen(buf, "r"))) { Tcl_AppendResult(interp, "error opening file (for reading) \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } if (0 != fseek(fpChanges, changeSize, SEEK_SET)) { Tcl_AppendResult(interp, "error seeking in file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); fclose(fpChanges); return TCL_ERROR; } changeSize = sbuf.st_size; while(1) { if (2 != fscanf(fpChanges, "%c %d ", &command, &cmdArg) || ('d'!=command && 'a'!=command && 's'!=command && 'k'!=command) || (('s' == command || 'k' == command) && buf != fgets(buf, sizeof(buf), fpChanges))) { if (feof(fpChanges)) { break; } Tcl_SetResult(interp, "syntax error in changes file", TCL_STATIC); fclose(fpChanges); return TCL_ERROR; } numChanges++; if ('d' == command) { if (cmdArg < 0 || cmdArg >= numRead) { continue; } needRewrite = 1; entryPtr[cmdArg].content[FROM] = NULL; } else if ('s' == command) { if (cmdArg < 0 || cmdArg >= numRead) { continue; } needRewrite = 1; buf[strlen(buf)-1] = '\0'; if ( (int) strlen(buf) <= (int) strlen(entryPtr[cmdArg].content[STATUS])){ strcpy(entryPtr[cmdArg].content[STATUS], buf); } else { /* * This code may leak the memory occupied by the * previous status string. I believe this loss can * be lived with (it should be quite rare). */ entryPtr[cmdArg].content[STATUS] = (char *) ckalloc(strlen(buf)+1); strcpy(entryPtr[cmdArg].content[STATUS], buf); } } else if ('k' == command) { Tcl_Obj *line, **elemv, **indexes; int elemc, indexc, i, index; char *keywords, *ex_time, *ex_type; line = Tcl_NewStringObj(buf, -1); if (TCL_OK != Tcl_ListObjGetElements(interp, line, &elemc,&elemv) || elemc != 4 || TCL_OK != Tcl_ListObjGetElements(interp, elemv[0], &indexc, &indexes)) { continue; } keywords = cpystr(Tcl_GetString(elemv[1])); ex_time = cpystr(Tcl_GetString(elemv[2])); ex_type = cpystr(Tcl_GetString(elemv[3])); for (i=0; i lastDate || 0 == lastDate) { lastDate = l; } numRead++; } } fclose(fpChanges); /* * If the number of changes is at least 20 and we are the only agent * which have the database open we write the changes into the main * index. This also happens if we have read an older version of the * database. */ if (20 <= numChanges || force) { char myLock[1024]; /* Name of my rlock file */ snprintf(myLock, sizeof(myLock), "rlock.%s", ident); if (IsRlocked(myLock) && !force) { doWrite = 0; } else { doWrite = 1; } } if (doWrite) { FILE *fpIndexinfo; /* Filepointer to index info */ if (needRewrite || force) { char oldIndex[1024]; /* Name of old index file */ char newIndex[1024]; /* Name of new index file */ FILE *fpNewIndex; /* Filepointer to new index */ int j; /* Loop variable */ snprintf(oldIndex, sizeof(oldIndex), "%s/index", dbDir); snprintf(newIndex, sizeof(newIndex), "%s/index.new", dbDir); if (0 == (fpNewIndex = fopen(newIndex, "w"))) { Tcl_AppendResult(interp, "error creating file \"", newIndex, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } if (0 == (fpIndex = fopen(oldIndex, "r"))) { Tcl_AppendResult(interp, "error opening file (for reading)\"", oldIndex,"\": ", Tcl_PosixError(interp), (char*)NULL); (void)fclose(fpNewIndex); return TCL_ERROR; } totSize = 0; for (i=0, numEntries=0 ; i < numRead; i++) { if (0 != entryPtr[i].content[FROM]) { numEntries++; for (j=0; j fprintf(fpNewIndex, "%s\n", entryPtr[i].content[j])) { Tcl_AppendResult(interp,"error writing to file \"", newIndex, "\"", (char *) NULL); (void)fclose(fpNewIndex); (void)unlink(newIndex); (void)fclose(fpIndex); return TCL_ERROR; } } totSize += atol(entryPtr[i].content[RSIZE]); } else { snprintf(buf, sizeof(buf), "%s/dbase/%s", dbDir, entryPtr[i].content[FILENAME]); (void)unlink(buf); } } (void)fclose(fpIndex); totSize += ftell(fpNewIndex); if (0 != fclose(fpNewIndex)) { Tcl_AppendResult(interp,"error closing file \"", newIndex, "\": ", Tcl_PosixError(interp), (char *) NULL); (void)unlink(newIndex); return TCL_ERROR; } if (0 != rename(newIndex, oldIndex)) { Tcl_AppendResult(interp,"error moving file \"", newIndex, "\" -> \"", oldIndex, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } stale = numRead-numEntries; } else { numEntries = numRead-stale; } snprintf(buf, sizeof(buf), "%s/index.info", dbDir); if (0 == (fpIndexinfo = fopen(buf, "w"))) { Tcl_AppendResult(interp, "error opening file (for writing)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } if (0 > fprintf(fpIndexinfo, "%d %d\n", DBASE_VERSION, numEntries)) { Tcl_AppendResult(interp, "error writing to file \"", buf, "\"", (char *) NULL); return TCL_ERROR; } if (0 > fclose(fpIndexinfo)) { Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (0 != unlink(buf)) { Tcl_AppendResult(interp, "error unlinking file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } changeSize = 0; numChanges = 0; needRewrite = 0; version = DBASE_VERSION; } return TCL_OK; } /* *---------------------------------------------------------------------- * * Update -- * * This routine updates the read-lock we have on the database. This * must be done at least as often as is specified by the Read * rotine. Preferably more often. If the database hasn't been read * yet (or has been closed) this routine does nothing. * * Results: * None. * * Side effects: * The rlock-file will be touched (if the database is open). * *---------------------------------------------------------------------- */ static void Update(ClientData clientData) { char rlockName[1024]; /* Name of rlock file */ if (0 != isRead) { return; } snprintf(rlockName, sizeof(rlockName), "%s/rlock.%s", dbDir, ident); (void)utime(rlockName, (struct utimbuf*) NULL); (void)Tcl_CreateTimerHandler(UPDATE_INTERVAL*1000,Update,(ClientData)NULL); } /* *---------------------------------------------------------------------- * * IsRlocked -- * * Checks if any othe rprocess has an read lock on the database. * * Results: * True if any othe rprocess has. * * Side effects: * Stale rlock-files will be removed * *---------------------------------------------------------------------- */ static int IsRlocked(char *ignore) { time_t deadline; /* Lockfiles older than this can be ignored */ struct dirent *direntPtr; struct stat sbuf; int result = 0; DIR *dirPtr; char buf[1024]; deadline = time((time_t*) NULL) - RLOCK_TIMEOUT; dirPtr = opendir(dbDir); while (0 != (direntPtr = readdir(dirPtr))) { if (!strncmp("rlock.", (char*)direntPtr->d_name, 6) && (ignore && strcmp((char*)direntPtr->d_name, ignore))) { snprintf(buf, sizeof(buf),"%s/%s", dbDir,(char*)direntPtr->d_name); (void)stat(buf, &sbuf); if (deadline < sbuf.st_mtime) { result = 1; break; } else { unlink(buf); } } } closedir(dirPtr); return result; } /* *---------------------------------------------------------------------- * * RatDbInsert -- * * This procedure inserts a copy of the message, whose id is passed * in the mail parameter, into the database. An entry is made in the * index file. One of the arguments is the expiration date as a string. * This string is the number of days the message should stay in the * database until it expires. * * The algorithm is to update the index on disk and then let Sync() * insert the new value into the internal database. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The internal and external databases are updated. * *---------------------------------------------------------------------- */ int RatDbInsert (Tcl_Interp *interp, const char *to, const char *from, const char *cc, const char *msgid, const char *ref, const char *subject, long date, const char *flags, const char *keywords, long exDate, const char *exType, const char *fromline, const char *mail, int length) { static char *tobuf = NULL; /* Scratch area */ static int tobufsize = 0; /* Size of scratch area */ char fname[1024]; /* filename of new entry */ char buf[1024]; /* Scratch area */ char *dir = NULL; /* Message directory */ FILE *indexFP; /* File pointer to index file */ long indexPos; /* Start position in index file */ char *cPtr; /* Misc character pointer */ FILE *seqFP; /* Filepointer to seq file */ int seq; /* sequence number */ FILE *indchaFP; /* File pointer to the index.changes file */ Tcl_Channel data; /* Data channel */ ADDRESS *adrPtr; /* Address list */ int i; int fd; if (0 == isRead) { if (TCL_OK != Read(interp)) { return TCL_ERROR; } } Lock(interp); /* * Generate the filename we are to use for this entry in the database. * Create the directory it will be stored in too (if needed). */ adrPtr = NULL; if (to && *to) { if (tobufsize < strlen(to)+1) { tobufsize = strlen(to)+1; tobuf = (char*)ckrealloc(tobuf, tobufsize); } strlcpy(tobuf, to, tobufsize); rfc822_parse_adrlist(&adrPtr, tobuf, "not.used"); if (adrPtr && adrPtr->mailbox && *adrPtr->mailbox) { dir = cpystr(adrPtr->mailbox); } } if (!dir) { dir = cpystr("default"); } mail_free_address(&adrPtr); for (cPtr = dir; *cPtr; cPtr++) { if ('/' == *cPtr) { *cPtr = '_'; } } snprintf(fname, sizeof(fname), "%s/", dir); snprintf(buf, sizeof(buf), "%s/dbase/%s/.seq", dbDir, dir); if (NULL == (seqFP = fopen(buf, "r+"))) { snprintf(buf, sizeof(buf), "%s/dbase/%s", dbDir, dir); if (0 != mkdir(buf, DIRMODE) && EEXIST != errno) { Unlock(interp); Tcl_AppendResult(interp, "error creating directory \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); ckfree(dir); return TCL_ERROR; } seq = 0; snprintf(buf, sizeof(buf), "%s/dbase/%s/.seq", dbDir, dir); if (NULL == (seqFP = fopen(buf, "w"))) { Unlock(interp); Tcl_AppendResult(interp, "error opening (for writing)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); ckfree(dir); return TCL_ERROR; } } else { if (1 != fscanf(seqFP, "%d", &seq)) { (void)fclose(seqFP); Unlock(interp); Tcl_AppendResult(interp, "error parsing: \"", buf, "\"", (char*) NULL); ckfree(dir); return TCL_ERROR; } seq++; } ckfree(dir); rewind(seqFP); if (0 > fprintf(seqFP, "%d", seq)) { (void)fclose(seqFP); Unlock(interp); Tcl_AppendResult(interp, "error writing to \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } if (0 != fclose(seqFP)) { Unlock(interp); Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } sprintf(buf, "%d", seq); cPtr = fname + strlen(fname); for (i=strlen(buf)-1; i>=0; i--) { *cPtr++ = buf[i]; } *cPtr = '\0'; /* * Open the indexfile and remember where we are */ snprintf(buf, sizeof(buf), "%s/index", dbDir); if (NULL == (indexFP = fopen(buf, "a"))) { Unlock(interp); Tcl_AppendResult(interp, "error opening (for append)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } indexPos = ftell(indexFP); /* * Construct the entries... some are simple some others are more * complicated :-) The order and format of the entries is documented * at the top of this file. */ NoLFPrint(indexFP, to); NoLFPrint(indexFP, from); NoLFPrint(indexFP, cc); NoLFPrint(indexFP, msgid); NoLFPrint(indexFP, ref); NoLFPrint(indexFP, subject); fprintf(indexFP, "%ld\n", date); NoLFPrint(indexFP, (keywords ? keywords : "")); fprintf(indexFP, "%d\n", length); NoLFPrint(indexFP, flags); fprintf(indexFP, "%ld\n", exDate*24*60*60 + time((time_t*) NULL)); NoLFPrint(indexFP, exType); if (0 > NoLFPrint(indexFP, fname)) { goto losing; } if (0 != fclose(indexFP)) { Tcl_AppendResult(interp, "error closing index file :", Tcl_PosixError(interp), (char *) NULL); goto losing; } /* * Create the actual entry in the database. */ snprintf(buf, sizeof(buf), "%s/dbase/%s", dbDir, fname); if (0 > (fd = open(buf, O_WRONLY|O_CREAT|O_TRUNC, 0666)) || NULL == (data = Tcl_MakeFileChannel((ClientData)fd,TCL_WRITABLE))) { Tcl_AppendResult(interp, "error creating file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); goto losing; } /* The casts here are needed to build on tcl <8.4 */ Tcl_Write(data, (char*)fromline, strlen(fromline)); RatTranslateWrite(data, (char*)mail, length); if (TCL_OK != Tcl_Close(interp, data)) { Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); goto losing_but_nearly_got_it; } /* * Write an entry to the index.changes file and then update */ snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (NULL == (indchaFP = fopen(buf, "a"))) { Tcl_AppendResult(interp, "error opening file (for append)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); goto losing_but_nearly_got_it; } if (0 > fprintf(indchaFP, "a %ld\n", indexPos)) { Tcl_AppendResult(interp, "error writing to file \"", buf, "\"", (char *) NULL); goto losing_but_nearly_got_it; } if (0 != fclose(indchaFP)) { Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); goto losing_but_nearly_got_it; } Sync(interp, 0); Unlock(interp); return TCL_OK; losing_but_nearly_got_it: snprintf(buf, sizeof(buf), "%s/dbase/%s", dbDir, fname); (void)unlink(buf); losing: (void)snprintf(buf, sizeof(buf), "%s/index", dbDir); i = truncate(buf, indexPos); /* Ignore result */ Unlock(interp); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatDbSetStatus -- * * This procedure modifies the status of the given message. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The internal and external databases are updated. * *---------------------------------------------------------------------- */ int RatDbSetStatus(Tcl_Interp *interp, int index, char *status) { char buf[1024]; /* Name of index.changes file */ FILE *indexFP; /* FIle pointer to index.changes file */ /* * Check the index for validity. */ if (index >= numRead || index < 0) { Tcl_SetResult(interp, "error: the given index is invalid", TCL_STATIC); return TCL_ERROR; } /* * Check if we really need to do this */ if (!strcmp(status, entryPtr[index].content[STATUS])) { return TCL_OK; } Lock(interp); snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (NULL == (indexFP = fopen(buf, "a"))) { Tcl_AppendResult(interp, "error opening (for append)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); Unlock(interp); return TCL_ERROR; } if (0 > fprintf(indexFP, "s %d %s\n", index, status)){ Tcl_AppendResult(interp, "Failed to write to file \"", buf, "\"", (char*) NULL); (void)fclose(indexFP); Unlock(interp); return TCL_ERROR; } if (0 != fclose(indexFP)) { Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); Unlock(interp); return TCL_ERROR; } Sync(interp, 0); Unlock(interp); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatDbSearch -- * * Searches the database for entries matching the given expression. * The search expression is in the following form: * [interval] op exp [exp ...] * Where interval (which is optional) is defined as: * int start_time end_time * Where op is either "and" or "or". and exp is as follows: * [not] field value * Where field is one of "to", "from", "cc", "subject", "keywords", * "all", "time_from" and "time_to". if op is "and" the all following * expressions must be true but if it is "or" then only one has to bes * true. * * Results: * The number of items found is returned in numFoundPtr if it is * non zero a pointer to a list of found ones is put into *foundPtrPtr. * It is the callers resopnsibility to free this list with a * call to free(). The rotine normally returns TCL_OK except when * there is an error. In that case TCL_ERROR is returned and the cause * may be found in interp->error. * * Side effects: * None. * *---------------------------------------------------------------------- */ int RatDbSearch(Tcl_Interp *interp, Tcl_Obj *exp, int *numFoundPtr, int **foundPtrPtr, int *expError) { int or; /* Indicates the operation; 0 = and 1 = or */ int i, j, k; /* Loop counters */ int match, matchl; int numAlloc = 0; /* Number of entries allocated room for */ int expNumWords, objc; Tcl_Obj **expWords, **objv; long long_value; long time_start, time_end; int numExp; /* The number of subexpressions in the exp */ char fname[1024]; /* Filename of actual message */ int bodyfd; /* File descriptor to actual message */ char *message = NULL; /* Actual message */ int messageSize = 0; /* Size of message area */ struct stat sbuf; /* Buffer for stat calls */ ssize_t l; char *s; /* * The following three lists describes the search expression. Every * expression has one entry in each list. */ int *notPtr; RatDbEType *fieldPtr; Tcl_Obj **valuePtr; *numFoundPtr = 0; *foundPtrPtr = NULL; /* * Parse the expression and build the lists. * We start by being pessimistic and assume the expression is faulty:-) */ if (expError != NULL) { *expError = 1; } if (TCL_OK != Tcl_ListObjGetElements(interp, exp,&expNumWords,&expWords)) { return TCL_ERROR; } i=0; s = Tcl_GetString(expWords[i++]); if (!strcmp(s, "and") && !strcmp(s, "or") && !strcmp(s, "int")) { Tcl_SetResult(interp, "exp must start with 'and', 'or' or 'int'.", TCL_STATIC); return TCL_ERROR; } /* These might be sligthly larger than needed, but who cares:-) */ notPtr = (int*) ckalloc(sizeof(int) * (expNumWords/2)); fieldPtr = (RatDbEType*) ckalloc(sizeof(RatDbEType) * (expNumWords/2)); valuePtr = (Tcl_Obj**) ckalloc(sizeof(Tcl_Obj*) * (expNumWords/2)); if (!strcmp(s, "int")) { if (expNumWords < 4 || TCL_OK != Tcl_GetLongFromObj(interp, expWords[i+0], &time_start) || TCL_OK != Tcl_GetLongFromObj(interp, expWords[i+1], &time_end)){ Tcl_SetResult(interp, "syntax error in expression", TCL_STATIC); return TCL_ERROR; } i += 2; s = Tcl_GetString(expWords[i++]); } else { time_start = 0; time_end = 0; } if (!strcmp(s, "or")) { or = 1; } else { or = 0; } numExp = 0; while (i < expNumWords) { s = Tcl_GetString(expWords[i]); if (!strcmp(s, "not")) { notPtr[numExp] = 1; s = Tcl_GetString(expWords[++i]); } else { notPtr[numExp] = 0; } if (i > expNumWords-1) { Tcl_SetResult(interp, "Parse error in exp (to few words)", TCL_STATIC); goto losing; } if (!strcmp(s, "to")) { fieldPtr[numExp] = TO; } else if (!strcmp(s, "from")) { fieldPtr[numExp] = FROM; } else if (!strcmp(s, "cc")) { fieldPtr[numExp] = CC; } else if (!strcmp(s, "subject")) { fieldPtr[numExp] = SUBJECT; } else if (!strcmp(s, "keywords")) { fieldPtr[numExp] = KEYWORDS; } else if (!strcmp(s, "all")) { fieldPtr[numExp] = SEARCH_ALL; } else if (!strcmp(s, "all_addresses")) { fieldPtr[numExp] = SEARCH_ALL_ADDRESSES; } else if (!strcmp(s, "time_from")) { fieldPtr[numExp] = SEARCH_TIME_FROM; } else if (!strcmp(s, "time_to")) { fieldPtr[numExp] = SEARCH_TIME_TO; } else { Tcl_SetResult(interp, "Parse error in exp (illegal field value)", TCL_STATIC); goto losing; } i++; valuePtr[numExp++] = expWords[i++]; } /* * The expression was good... */ if (expError != NULL) { *expError = 0; } /* * Now we are ready to do the searching. First make sure that the * database is read and synced, then run through it. */ if (0 == isRead) { if (TCL_OK != Read(interp)) { goto losing; } } else { if (TCL_OK != Sync(interp, 0)) { goto losing; } } for (i=0; i < numRead; i++) { if (!entryPtr[i].content[FROM]) { /* Entry deleted */ continue; } if (time_start != 0) { long_value = atol(entryPtr[i].content[DATE]); if (long_value != 0 && ((long_value < time_start) || (long_value > time_end))) { continue; } } match = 0; for(j=0; j < numExp && !(j != 0 && or == match); j++) { Tcl_ListObjGetElements(interp, valuePtr[j], &objc, &objv); for (k=0, matchl=0; k (bodyfd = open(fname, O_RDONLY))) { Tcl_AppendResult(interp, "error opening file (for read)\"", fname, "\": ", Tcl_PosixError(interp), (char*)NULL); goto losing; } if (0 != fstat(bodyfd, &sbuf)) { Tcl_AppendResult(interp, "error stating file \"",fname, "\": ", Tcl_PosixError(interp), (char *) NULL); close(bodyfd); goto losing; } if (messageSize < sbuf.st_size+1) { ckfree(message); message = (char*)ckalloc(messageSize = sbuf.st_size+1); } l = SafeRead(bodyfd, message, sbuf.st_size); if (l > 0) { message[l] = '\0'; (void)close(bodyfd); matchl = RatSearch(Tcl_GetString(objv[k]), message); } } else if (fieldPtr[j] == SEARCH_ALL_ADDRESSES) { matchl = RatSearch(Tcl_GetString(objv[k]), entryPtr[i].content[TO]) || RatSearch(Tcl_GetString(objv[k]), entryPtr[i].content[CC]) || RatSearch(Tcl_GetString(objv[k]), entryPtr[i].content[FROM]); } else if (fieldPtr[j] == SEARCH_TIME_FROM) { Tcl_GetLongFromObj(interp, objv[k], &long_value); matchl = atol(entryPtr[i].content[DATE]) >= long_value; } else if (fieldPtr[j] == SEARCH_TIME_TO) { Tcl_GetLongFromObj(interp, objv[k], &long_value); matchl = atol(entryPtr[i].content[DATE]) <= long_value; } else { matchl = RatSearch(Tcl_GetString(objv[k]), entryPtr[i].content[fieldPtr[j]]); } if ((or && matchl) || (!or && !matchl)) { break; } } if (1 == notPtr[j]) { match = matchl ? 0 : 1; } else { match = matchl; } } if (match || (or && 0 == numExp)) { if (*numFoundPtr >= numAlloc) { numAlloc += EXTRA_ENTRIES; *foundPtrPtr =(int*)ckrealloc(*foundPtrPtr, numAlloc*sizeof(int)); } (*foundPtrPtr)[(*numFoundPtr)++] = i; } } ckfree((char*) notPtr); ckfree((char*) fieldPtr); ckfree((char*) valuePtr); if (messageSize > 0) { ckfree(message); } return TCL_OK; losing: ckfree((char*) expWords); ckfree((char*) notPtr); ckfree((char*) fieldPtr); ckfree((char*) valuePtr); if (messageSize > 0) { ckfree(message); } return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatDbGetEntry -- * * This routine retrieves an entry from the database. The pointer * returned is ONLY good until the next call to RatDbInsert(), * RatDbSetStatus(), RatDbSearch(). * * Results: * The routine returns a pointer to a RatDbEntry structure which * should be treated as read only. If the index is invalid or * points to a deleted entry a null pointer is returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ RatDbEntry* RatDbGetEntry(int index) { if (index<0 || index>=numRead || NULL == entryPtr[index].content[FROM]) { return NULL; } return &entryPtr[index]; } /* *---------------------------------------------------------------------- * * RatDbGetMessage -- * * This routine extracts a copy of a message in the database and * returns a MESSAGE structure. * * Results: * A pointer to a MESSAGE* structure. It alse fills in the pointer * to a buffer among the arguments with the address that needs to be * freed when the message is deleted. * * Side effects: * None. * *---------------------------------------------------------------------- */ MESSAGE* RatDbGetMessage(Tcl_Interp *interp, int index, char **buffer) { char fname[1024]; /* Filename of message */ int messfd; /* Message file descriptor */ struct stat sbuf; /* Buffer for stat call (to find out size of message)*/ char *message; /* Pointer to actual message */ ssize_t l; /* * Check the index for validity. */ if (index >= numRead || index < 0) { Tcl_SetResult(interp, "error: the given index is invalid", TCL_STATIC); return NULL; } if (NULL == entryPtr[index].content[FROM]) { Tcl_SetResult(interp, "error: the message is deleted", TCL_STATIC); return NULL; } Lock(interp); /* * Read the message into an array pointed to by 'message'. */ snprintf(fname, sizeof(fname), "%s/dbase/%s", dbDir, entryPtr[index].content[FILENAME]); if (0 > (messfd = open(fname, O_RDONLY))) { Unlock(interp); Tcl_AppendResult(interp, "error opening file (for read)\"", fname, "\": ", Tcl_PosixError(interp), (char*)NULL); return NULL; } if (0 != fstat(messfd, &sbuf)) { Unlock(interp); Tcl_AppendResult(interp, "error stating file \"", fname, "\": ", Tcl_PosixError(interp), (char *) NULL); close(messfd); return NULL; } *buffer = message = (char*)ckalloc(sbuf.st_size+1); l = SafeRead(messfd, message, sbuf.st_size); if (l < 0) { return NULL; } message[l] = '\0'; (void)close(messfd); Unlock(interp); return RatParseMsg(interp, (unsigned char*)message); } /* *---------------------------------------------------------------------- * * RatDbGetHeaders -- * * This routine extracts a copy of the headers of a message in * the database. * * Results: * A pointer to a static area containing the message headers * is returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ char* RatDbGetHeaders(Tcl_Interp *interp, int index) { static char *header = NULL; /* Static storage area */ static int headerSize = 0; /* Size of static storage area */ char fname[1024]; /* Filename of message */ char *hPtr; /* The header to return */ FILE *messFp; /* Message file pointer */ int length = 0; /* Length of header */ int c; /* * Check the index for validity. */ if (index >= numRead || index < 0) { Tcl_SetResult(interp, "error: the given index is invalid", TCL_STATIC); return NULL; } if (NULL == entryPtr[index].content[FROM]) { Tcl_SetResult(interp, "error: the message is deleted", TCL_STATIC); return NULL; } Lock(interp); /* * Read the message into an array pointed to by 'message'. */ snprintf(fname, sizeof(fname), "%s/dbase/%s", dbDir, entryPtr[index].content[FILENAME]); if (NULL == (messFp = fopen(fname, "r"))) { Unlock(interp); Tcl_AppendResult(interp, "error opening file (for read)\"", fname, "\": ", Tcl_PosixError(interp), (char*)NULL); return NULL; } while (c = fgetc(messFp), !feof(messFp)) { if (length >= headerSize-1) { headerSize += 1024; header = (char*)ckrealloc(header, headerSize); } if ('\n' == c && (length == 0 || header[length-1] != '\r')) { header[length++] = '\r'; } header[length++] = c; if (length > 4 && header[length-4] == '\r' && header[length-3] == '\n' && header[length-2] == '\r' && header[length-1] == '\n') { length -= 2; break; } } header[length] = '\0'; fclose(messFp); Unlock(interp); if (strncmp("From ", header, 5)) { hPtr = header; } else { hPtr = strchr(header, '\n')+1; if ('\r' == *hPtr) { hPtr++; } } return hPtr; } /* *---------------------------------------------------------------------- * * RatDbGetFrom -- * * This routine extracts a copy of the first line of the headers * * Results: * A pointer to a static area containing the from line * * Side effects: * None. * *---------------------------------------------------------------------- */ char* RatDbGetFrom(Tcl_Interp *interp, int index) { static char header[8192]; /* Static storage area */ char fname[1024]; /* Filename of message */ FILE *messFp; /* Message file pointer */ /* * Check the index for validity. */ if (index >= numRead || index < 0) { Tcl_SetResult(interp, "error: the given index is invalid", TCL_STATIC); return NULL; } if (NULL == entryPtr[index].content[FROM]) { Tcl_SetResult(interp, "error: the message is deleted", TCL_STATIC); return NULL; } Lock(interp); /* * Read the message into an array pointed to by 'message'. */ snprintf(fname, sizeof(fname), "%s/dbase/%s", dbDir, entryPtr[index].content[FILENAME]); if (NULL == (messFp = fopen(fname, "r"))) { Unlock(interp); Tcl_AppendResult(interp, "error opening file (for read)\"", fname, "\": ", Tcl_PosixError(interp), (char*)NULL); return NULL; } Unlock(interp); if (fgets(header, sizeof(header)-1, messFp)) { header[sizeof(header)-1] = '\0'; } else { header[0] = '\0'; } fclose(messFp); return header; } /* *---------------------------------------------------------------------- * * RatDbGetText -- * * This routine extracts a copy of the body of a message in * the database. * * Results: * A pointer to a static area containing the message body * is returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ char* RatDbGetText(Tcl_Interp *interp, int index) { static char *body = NULL; /* Static storage area */ static int bodySize = 0; /* Size of static storage area */ char fname[1024]; /* Filename of message */ FILE *messFp; /* Message file pointer */ int length = 0; /* Length of header */ char buf[2048]; /* Temporary holding area */ int c; /* * Check the index for validity. */ if (index >= numRead || index < 0) { Tcl_SetResult(interp, "error: the given index is invalid", TCL_STATIC); return NULL; } if (NULL == entryPtr[index].content[FROM]) { Tcl_SetResult(interp, "error: the message is deleted", TCL_STATIC); return NULL; } Lock(interp); /* * Read the message into an array pointed to by 'message'. */ snprintf(fname, sizeof(fname), "%s/dbase/%s", dbDir, entryPtr[index].content[FILENAME]); if (NULL == (messFp = fopen(fname, "r"))) { Unlock(interp); Tcl_AppendResult(interp, "error opening file (for read)\"", fname, "\": ", Tcl_PosixError(interp), (char*)NULL); return NULL; } while (fgets(buf, sizeof(buf), messFp) != NULL && !feof(messFp)) { if ('\n' == buf[0] || '\r' == buf[0]) { break; } } while (c = fgetc(messFp), !feof(messFp)) { if (length >= bodySize-1) { bodySize += 8192; body = (char*)ckrealloc(body, bodySize); } if ('\n' == c && (length == 0 || body[length-1] != '\r')) { body[length++] = '\r'; } body[length++] = c; } body[length] = '\0'; fclose(messFp); Unlock(interp); return body; } /* *---------------------------------------------------------------------- * * RatDbExpunge -- * * Deletes all entries marked for deletion (status contains D). * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * Both the internal and the disk copy of the database are affected. * Observer that if some caller has previously retrieved this entry * from the database with a call to RatDbGet() the RatDbEntry * obtained will be destroyed (filled with nulls). * *---------------------------------------------------------------------- */ int RatDbExpunge(Tcl_Interp *interp) { char buf[1024]; /* Name of index.changes file */ FILE *indexFP; /* File pointer to index.changes file */ int index, i; Lock(interp); snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (NULL == (indexFP = fopen(buf, "a"))) { Tcl_AppendResult(interp, "error opening (for append)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); Unlock(interp); return TCL_ERROR; } for (index=0; index < numRead; index++) { for (i=0; entryPtr[index].content[STATUS][i]; i++) { if ('D' == entryPtr[index].content[STATUS][i]) { fprintf(indexFP, "d %d\n", index); break; } } } if (0 != fclose(indexFP)) { Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); Unlock(interp); return TCL_ERROR; } Sync(interp, 0); Unlock(interp); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatDbDaysSinceExpire -- * * Finds ut how long it was since the database was expired last. * * Results: * An integer which is the number of days since the database was expired. * If the user has no database we return 0. * * Side effects: * None. * *---------------------------------------------------------------------- */ int RatDbDaysSinceExpire(Tcl_Interp *interp) { struct stat sbuf; char buf[1024]; /* * First make sure we know where the database should reside. */ if (0 == dbDir) { const char *value = RatGetPathOption(interp, "dbase_dir"); if (NULL == value) { return TCL_ERROR; } dbDir = cpystr(value); } snprintf(buf, sizeof(buf), "%s/expired", dbDir); if (stat(buf, &sbuf)) { snprintf(buf, sizeof(buf), "%s/dbase", dbDir); if (stat(buf, &sbuf)) { return 0; } } if (sbuf.st_mtime > time(NULL)) { return 0; } else { return (time(NULL)-sbuf.st_mtime)/(24*60*60); } } /* *---------------------------------------------------------------------- * * RatDbExpire -- * * Runs through the database and carries out any expiration that * should be done. This routine should be called periodically. * * Results: * If nothing went wrong TCL_OK is returned and in the result area is * a list containing 5 numbers {num_scanned num_delete, num_backup, * num_inbox num_custom}. Otherwise TCL_ERROR is returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ int RatDbExpire(Tcl_Interp *interp, char *infolder, char *backupDirectory) { char *compressProg, *compressSuffix, *statusId; const char *backupDir; int numScan = 0, numDelete = 0, numBackup = 0, numInbox = 0, numCustom = 0; int i, len, delete, fd, doBackup = 0, changed = 0, error = 0; int move; char buf[1024], buf2[1024]; FILE *indexFP = NULL; time_t t, now = time(NULL); struct tm *tmPtr; struct stat sbuf; struct dirent *direntPtr; Tcl_Obj *oPtr; DIR *dirPtr; if (0 == isRead) { if (TCL_OK != Read(interp)) { return TCL_ERROR; } } /* * Make sure the inbox directory exists. */ snprintf(buf, sizeof(buf), "%s/inbox", dbDir); if (-1 == stat(buf, &sbuf)) { if (mkdir(buf, DIRMODE)) { Tcl_AppendResult(interp, "error creating\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } } /* * Prepare backup */ if (-1 == stat(backupDirectory, &sbuf)) { if (mkdir(backupDirectory, DIRMODE)) { Tcl_AppendResult(interp, "error creating\"", backupDirectory, "\": ", Tcl_PosixError(interp), (char *) NULL); return TCL_ERROR; } } compressProg = getenv("COMPRESS"); compressSuffix = getenv("CSUFFIX"); backupDir = RatGetPathOption(interp, "dbase_backup"); if (!compressProg || !compressSuffix || !backupDir) { Tcl_AppendResult(interp, "Internal error: compressProg, ", "compressSuffix or option(dbase_backup) not defined", (char*) NULL); return TCL_ERROR; } RatLogF(interp, RAT_INFO, "db_expire", RATLOG_EXPLICIT); statusId = cpystr(Tcl_GetStringResult(interp)); Lock(interp); for (i=0; !error && i < numRead; i++) { if (!entryPtr[i].content[FROM]) { /* Entry deleted */ continue; } numScan++; t = atol(entryPtr[i].content[EX_TIME]); if (!t || t >now) { continue; } /* * If we get here the entry should be expired. * * Currently we only handle the backup, delete and inbox actions. * The rest are quitely ignored. */ delete = move = 0; if (!strcmp("delete", entryPtr[i].content[EX_TYPE])) { delete = 1; numDelete++; } else if (!strcmp("backup", entryPtr[i].content[EX_TYPE])) { move = 1; numBackup++; snprintf(buf, sizeof(buf), "%s/dbase/%s", dbDir, entryPtr[i].content[FILENAME]); RatGenIdCmd(NULL, interp, 0, NULL); snprintf(buf2, sizeof(buf2), "%s/message.%s", backupDirectory, Tcl_GetStringResult(interp)); } else if (!strcmp("incoming", entryPtr[i].content[EX_TYPE])) { move = 1; numInbox++; snprintf(buf, sizeof(buf), "%s/dbase/%s", dbDir, entryPtr[i].content[FILENAME]); RatGenIdCmd(NULL, interp, 0, NULL); snprintf(buf2, sizeof(buf2), "%s/inbox/%s", dbDir, Tcl_GetStringResult(interp)); } else if (!strncmp("custom", entryPtr[i].content[EX_TYPE], 6)) { numCustom++; } else if (!strcmp("none", entryPtr[i].content[EX_TYPE])) { continue; } else { /* * If we get here it is an unkown type and we just silently * deletes it (old versions of tkrat may have generated it. */ delete = 1; numDelete++; } if (move) { if (link(buf, buf2)) { int fdSrc, fdDst; /* * Sigh. the files are on different filesystems. We have to * copy them. */ fdSrc = open(buf, O_RDONLY); fdDst = open(buf2, O_WRONLY|O_TRUNC|O_CREAT, 0666); do { len = SafeRead(fdSrc, buf, sizeof(buf)); if (0 > safe_write(fdDst, buf, len)) { error = errno; len = 0; } } while (len); close(fdSrc); if (close(fdDst) || error) { RatLogF(interp, RAT_ERROR, "failed_to_move_to_file", RATLOG_TIME, buf2, Tcl_PosixError(interp)); delete = 0; } } } if (delete || move) { if (!indexFP) { changed = 1; snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (NULL == (indexFP = fopen(buf, "a"))) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "error opening (for append)\"", buf, "\":", Tcl_PosixError(interp), "\n", NULL); Unlock(interp); return TCL_ERROR; } } fprintf(indexFP, "d %d\n", i); } } if (changed) { fclose(indexFP); Sync(interp, 0); } Unlock(interp); /* * Compress the messages in the backup directory if we have enough * messages to make it meaningful. */ if (numBackup) { int chunkSize; Tcl_Obj *oPtr; oPtr = Tcl_GetVar2Ex(interp, "option", "chunksize", TCL_GLOBAL_ONLY); if (oPtr) { Tcl_GetIntFromObj(interp, oPtr, &chunkSize); } else { chunkSize = 100; } if (numBackup < chunkSize) { i = 0; dirPtr = opendir(backupDirectory); while (0 != (direntPtr = readdir(dirPtr))) { if (!strncmp(direntPtr->d_name, "message", 7)) { i++; } } closedir(dirPtr); doBackup = (i>=chunkSize); } else { doBackup = 1; } } if (doBackup) { Tcl_Channel backupChannel, inChannel; CONST84 char *argv[3], *error = NULL; ckfree(statusId); RatLogF(interp, RAT_INFO, "packing_backup", RATLOG_EXPLICIT); statusId = cpystr(Tcl_GetStringResult(interp)); tmPtr = localtime(&now); snprintf(buf2, sizeof(buf2), ">%s/backup_%04d%02d%02d.%s", backupDirectory, tmPtr->tm_year+1900, tmPtr->tm_mon+1, tmPtr->tm_mday+1, compressSuffix); argv[0] = compressProg; argv[1] = buf2; if (!(backupChannel = Tcl_OpenCommandChannel(interp, 2, argv, TCL_STDIN))) { Tcl_BackgroundError(interp); } if (backupChannel) { dirPtr = opendir(backupDirectory); while (!error && 0 != (direntPtr = readdir(dirPtr))) { if (strncmp(direntPtr->d_name, "message", 7)) { continue; } snprintf(buf, sizeof(buf), "%s/%s", backupDirectory, direntPtr->d_name); inChannel = Tcl_OpenFileChannel(interp, buf, "r", 0); do { len = Tcl_Read(inChannel, buf, sizeof(buf)); if (-1 == Tcl_Write(backupChannel, buf, len)) { error = Tcl_PosixError(interp); } } while (!error && !Tcl_Eof(inChannel)); Tcl_Write(backupChannel, "\n", 1); Tcl_Close(interp, inChannel); } if (TCL_OK != Tcl_Close(interp, backupChannel)) { error = Tcl_PosixError(interp); } if (error) { unlink(buf2); Tcl_SetResult(interp, (char*)error, TCL_STATIC); Tcl_BackgroundError(interp); } else { rewinddir(dirPtr); while (0 != (direntPtr = readdir(dirPtr))) { if (!strncmp(direntPtr->d_name, "message", 7)) { snprintf(buf, sizeof(buf), "%s/%s", backupDirectory, direntPtr->d_name); unlink(buf); } } } closedir(dirPtr); } } /* * Move messages to inbox */ if (numInbox) { char *data = NULL, *msg, *cPtr; int fd, allocated = 0; ssize_t l; snprintf(buf, sizeof(buf), "%s/inbox", dbDir); dirPtr = opendir(buf); while (0 != (direntPtr = readdir(dirPtr))) { snprintf(buf2, sizeof(buf2), "%s/%s", buf, direntPtr->d_name); if (stat(buf2, &sbuf) || !S_ISREG(sbuf.st_mode)) { continue; } if (allocated < sbuf.st_size+1) { allocated = sbuf.st_size+1; data = (char*)ckrealloc(data, allocated); } fd = open(buf2, O_RDONLY); l = SafeRead(fd, data, sbuf.st_size); close(fd); if (l <= 0) { continue; } data[sbuf.st_size] = '\0'; unlink(buf2); for (cPtr = data; *cPtr != '\n'; cPtr++); if (*(++cPtr) == '\r') { cPtr++; } msg = RatFrMessageCreate(interp, cPtr, sbuf.st_size, NULL); if (TCL_OK == Tcl_VarEval(interp, infolder, " insert ", msg,NULL)){ unlink(buf2); } } closedir(dirPtr); } /* * Mark that we have done expire */ snprintf(buf, sizeof(buf), "%s/expired", dbDir); (void)unlink(buf); if (0 <= (fd = open(buf, O_WRONLY|O_CREAT, 0666))) { close(fd); } Tcl_VarEval(interp, "RatClearLog ", statusId, "; update idletasks", (char*) NULL); ckfree(statusId); /* * Create result list and return */ oPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(numScan)); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(numDelete)); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(numBackup)); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(numInbox)); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(numCustom)); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatDbClose -- * * Closes the database on disk. * * Results: * None. * * Side effects: * The rlock file is removed and some of the internal data structures * are freed (but not all :-(). * *---------------------------------------------------------------------- */ void RatDbClose() { char buf[1024]; /* Scratch area */ if (1 == isRead) { ckfree(entryPtr); isRead = 0; snprintf(buf, sizeof(buf), "%s/rlock.%s", dbDir, ident); unlink(buf); } #ifdef MEM_DEBUG ckfree(dbDir); #endif /* MEM_DEBUG */ } /* *---------------------------------------------------------------------- * * RatDbBuildList -- * * Builds a list of the files in the database * * The algorithm is to open the directory and for all files do: * - If the file is ".seq" then we read it and remember the number * - If the name starts with a dot ('.') then we continue with the * next file. * - If it is a directory the we recursively call ourselves to check * that directory. * - If it is an ordinary file then we add it to the hastable (the * hash is computed from the prefix/filename). We also decode the * number of the file and remembers the highest number found. * When all files are checked we check if the highest number found * was bigger than the content of seq. If it is so then we * - Append a warning to the result area. * - write a new .seq file IF fix is true. * * Results: * May append diagnostic messages to the result area. * * Side effects: * Will probably add items to the given hashtable. * *---------------------------------------------------------------------- */ static void RatDbBuildList(Tcl_Interp *interp, Tcl_DString *dsPtr, char *prefix, char *dir, Tcl_HashTable *tablePtr, int fix) { char buf[1024], path[1024]; unsigned long seq = 0, maxnum = 0, num, fact; int i; Tcl_HashEntry *entryPtr; RatDbItem *itemPtr; struct dirent *entPtr; struct stat sbuf; DIR *dirPtr; FILE *fp; if (NULL == (dirPtr = opendir(dir))) { snprintf(buf, sizeof(buf), "Failed to open directory \"%s\": %s", dir, Tcl_PosixError(interp)); Tcl_DStringAppendElement(dsPtr, buf); return; } while (NULL != (entPtr = readdir(dirPtr))) { if (!strcmp(entPtr->d_name, ".seq")) { snprintf(path, sizeof(buf), "%s/.seq", dir); if (NULL == (fp = fopen(path, "r"))) { snprintf(buf, sizeof(buf), "Failed to open file \"%s\": %s", path, Tcl_PosixError(interp)); Tcl_DStringAppendElement(dsPtr, buf); if (fix) { if (unlink(path)) { snprintf(buf, sizeof(buf), "Failed to unlink file \"%s\": %s", path, Tcl_PosixError(interp)); Tcl_DStringAppendElement(dsPtr, buf); } } } else { if (1 != fscanf(fp, "%ld", &seq)) { seq = -1; } fclose(fp); } } if ('.' == entPtr->d_name[0]) { continue; } snprintf(path, sizeof(path), "%s/%s", dir, entPtr->d_name); if (stat(path, &sbuf)) { snprintf(buf, sizeof(buf), "Failed to stat file %s: %s\n", path, Tcl_PosixError(interp)); Tcl_DStringAppendElement(dsPtr, buf); continue; } if (S_IFREG == (sbuf.st_mode&S_IFMT)) { if (!(S_IRUSR & sbuf.st_mode)) { snprintf(buf, sizeof(buf), "\"%s\" is not readable by the owner", path); Tcl_DStringAppendElement(dsPtr, buf); if (fix) { if (chmod(path, sbuf.st_mode|S_IRUSR)) { snprintf(buf, sizeof(buf), "Failed to chmod \"%s\": %s", path, Tcl_PosixError(interp)); Tcl_DStringAppendElement(dsPtr, buf); continue; } } } if (0 == sbuf.st_size) { snprintf(buf, sizeof(buf), "Empty file \"%s\" found", path); if (fix) { if (unlink(path)) { snprintf(buf, sizeof(buf), "Failed to unlink \"%s\": %s", path, Tcl_PosixError(interp)); Tcl_DStringAppendElement(dsPtr, buf); } } continue; } if (*prefix) { snprintf(buf, sizeof(buf), "%s/%s", prefix, entPtr->d_name); } else { strlcpy(buf, entPtr->d_name, sizeof(buf)); } itemPtr = (RatDbItem*)ckalloc(sizeof(RatDbItem)); itemPtr->fileSize = sbuf.st_size; itemPtr->index = -1; for (i=0; ientry.content[i] = NULL; } entryPtr = Tcl_CreateHashEntry(tablePtr, buf, &i); Tcl_SetHashValue(entryPtr, (ClientData)itemPtr); /* Check sequence number */ num = 0; for (i=0, fact=1; isdigit(entPtr->d_name[i]); i++, fact *= 10) { num += (entPtr->d_name[i]-'0') * fact; } if (num > maxnum) { maxnum = num; } } else if (S_IFDIR == (sbuf.st_mode&S_IFMT)) { if (prefix && *prefix) { snprintf(path, sizeof(path), "%s/%s", prefix, entPtr->d_name); } else { strlcpy(path, entPtr->d_name, sizeof(path)); } snprintf(buf, sizeof(buf), "%s/%s", dir, entPtr->d_name); RatDbBuildList(interp, dsPtr, path, buf, tablePtr, fix); } else { snprintf(buf, sizeof(buf), "\"%s\" is not a file", path); Tcl_DStringAppendElement(dsPtr, buf); } } closedir(dirPtr); if (maxnum > seq) { snprintf(buf, sizeof(buf), "Bad sequence number was %ld but expected %ld", seq, maxnum); Tcl_DStringAppendElement(dsPtr, buf); if (fix) { snprintf(path, sizeof(buf), "%s/.seq", dir); if (NULL != (fp = fopen(path, "w"))) { fprintf(fp, "%ld", maxnum); fclose(fp); } } } } /* *---------------------------------------------------------------------- * * RatDbCheck -- * * Checks the database. * * Results: * A diagnostic string. * * Side effects: * The database on disk may be rewritten (depends on the fix argument). * *---------------------------------------------------------------------- */ #define EXP_NUM "[0-9]*" #define EXP_TYPE "^((none)|(remove)|(incoming)|(backup)|(custom.*))?$" #define EXP_FILE "[^/]+/[0-9]*" int RatDbCheck(Tcl_Interp *interp, int fix) { int numFound = 0, numMal = 0, numAlone = 0, numUnlinked = 0, size = 0, fd, lines, start, index, i, j, extraNum = 0, extraAlloc = 0, msgLen = 0, date = 0, listArgc, elemArgc, indexInfo = 0, numDel = 0; char buf[8092], *indexPtr = NULL, **linePtrPtr = NULL, *cPtr, *to, *from, *cc, *subject, *flags, *msgBuf = NULL; CONST84 char **listArgv, **elemArgv; Tcl_HashTable items, status; char **extraPtrPtr = NULL; Tcl_HashEntry *entryPtr; Tcl_HashSearch search; Tcl_DString reportDS; RatDbItem *itemPtr; struct stat sbuf; MESSAGECACHE elt; ssize_t l; struct tm tm; FILE *fp; /* * Initialize variables */ if (0 == dbDir) { const char *value = RatGetPathOption(interp, "dbase_dir"); if (NULL == value) { return TCL_ERROR; } dbDir = cpystr(value); } if (0 == ident) { gethostname(buf, sizeof(buf)); ident = (char*)ckalloc(strlen(buf)+16); snprintf(ident, strlen(buf)+16, "%s:%d", buf, (int)getpid()); } /* * Check that the database directory exists. If not we return zeros */ if (0 > stat(dbDir, &sbuf) || !S_ISDIR(sbuf.st_mode)) { Tcl_SetResult(interp, "0 0 0 0 0 {}", TCL_STATIC); return TCL_OK; } /* * Lock the database. We should also check that nobody else has * a read lock as well. */ Lock(interp); if (IsRlocked(NULL)) { Unlock(interp); Tcl_SetResult(interp, "Some other process has locked the database.", TCL_STATIC); return TCL_ERROR; } /* * Check index.info file */ snprintf(buf, sizeof(buf), "%s/index.info", dbDir); if (NULL == (fp = fopen(buf, "r"))) { Tcl_SetResult(interp, "Failed to open index.info file", TCL_STATIC); return TCL_ERROR; } else { if (2 != fscanf(fp, "%d %d", &i, &indexInfo)) { i = -1; } fclose(fp); if (i != DBASE_VERSION) { Tcl_SetResult(interp, "Wrong version of dbase", TCL_STATIC); Unlock(interp); return TCL_ERROR; } } /* * Initialize variables */ Tcl_DStringInit(&reportDS); Tcl_InitHashTable(&items, TCL_STRING_KEYS); Tcl_InitHashTable(&status, TCL_ONE_WORD_KEYS); /* * Get a list of messages actually stored */ snprintf(buf, sizeof(buf), "%s/dbase", dbDir); RatDbBuildList(interp, &reportDS, "", buf, &items, fix); /* * Check the changes file for flag changes and store them in the * status hash table. */ snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (NULL != (fp = fopen(buf, "r"))) { while (fgets(buf, sizeof(buf), fp) != NULL && !feof(fp)) { switch (buf[0]) { case 'a': indexInfo++; break; case 'd': indexInfo--; numDel++; break; case 's': if (extraNum == extraAlloc) { extraAlloc += 32; extraPtrPtr = (char**)ckrealloc(extraPtrPtr, extraAlloc*sizeof(char*)); } extraPtrPtr[extraNum] = (char*)ckalloc(strlen(buf)); sscanf(buf, "%*s %d %s", &index, extraPtrPtr[extraNum]); entryPtr = Tcl_CreateHashEntry(&status, (char*)index, &i); Tcl_SetHashValue(entryPtr, (ClientData)extraPtrPtr[extraNum]); extraNum++; break; } } fclose(fp); } /* * Check the index file */ snprintf(buf, sizeof(buf), "%s/index", dbDir); if (-1 != (fd = open(buf, O_RDONLY))) { /* * Read file and build pointers to the lines */ fstat(fd, &sbuf); indexPtr = (char*)ckalloc(sbuf.st_size+1); if (sbuf.st_size != SafeRead(fd, indexPtr, sbuf.st_size)) { close(fd); return TCL_ERROR; } close(fd); indexPtr[sbuf.st_size] = '\0'; for (lines = 0, cPtr = indexPtr; *cPtr; cPtr++) { if ('\n' == *cPtr) { lines++; } } linePtrPtr = (char**)ckalloc(sizeof(char*)*lines); for (cPtr = indexPtr, i = 0; cPtr && *cPtr && i < lines; i++) { linePtrPtr[i] = cPtr; if ((cPtr = strchr(cPtr, '\n'))) { *cPtr++ = '\0'; } } /* * Now we are ready to reconstruct the index. We do this one entry * a time. For each entry we read one line at a time and check the * content against what we expected. We expect the following lines * and contents: * to - any string * from - any string * cc - any string * message-id - any string * references - any string * subject - any string * date - a number * keywords - any string * size - a number * status - any string * extime - a number * exevent - one of none, remove, incoming, backup and custom * filename - (.*)/[0-9]+ * When we have found an index we check for the corresponding entry * in the list of files and fill it in. */ for (start = index = 0; start < lines; index++) { if (start > lines-RATDBETYPE_END) { break; } if (!Tcl_RegExpMatch(interp, linePtrPtr[start+DATE], EXP_NUM) || !Tcl_RegExpMatch(interp, linePtrPtr[start+RSIZE], EXP_NUM) || !Tcl_RegExpMatch(interp, linePtrPtr[start+EX_TIME], EXP_NUM) || !Tcl_RegExpMatch(interp, linePtrPtr[start+EX_TYPE], EXP_TYPE) || !Tcl_RegExpMatch(interp, linePtrPtr[start+FILENAME], EXP_FILE)){ sprintf(buf, "Entry %d is malformed", index); Tcl_DStringAppendElement(&reportDS, buf); numMal++; /* * We have found an malformed entry, first we search for the * filename which should be the last item. From that we go * backwards and try to collapse lines that somehow was * splitted. */ i=0; while (!Tcl_RegExpMatch(interp,linePtrPtr[start+i],EXP_FILE)) { if (start + (++i) == lines) { break; }; } i++; if (start+i >= lines) { /* * We have reached the end of the file */ break; } /* * Here we should collapse the lines but for now we * just continue with the next item. /MaF */ start += i; continue; } if (!(entryPtr = Tcl_FindHashEntry(&items, linePtrPtr[start+FILENAME]))) { numAlone++; snprintf(buf, sizeof(buf), "Entry %d has no associated file '%s'", index, linePtrPtr[start+FILENAME]); Tcl_DStringAppendElement(&reportDS, buf); start += 13; continue; } itemPtr = (RatDbItem*)Tcl_GetHashValue(entryPtr); for (i=0; ientry.content[i] = (char*)Tcl_GetHashValue(entryPtr); } else { itemPtr->entry.content[i] = linePtrPtr[start]; } } numFound++; } } /* * Check for unlinked messages * And calculate total size while we are at it. */ for (entryPtr = Tcl_FirstHashEntry(&items, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { itemPtr = (RatDbItem*)Tcl_GetHashValue(entryPtr); size += itemPtr->fileSize; if (itemPtr->entry.content[0]) { continue; } numUnlinked++; if (fix) { /* * Generate index entries for this message */ to = from = cc = subject = flags = NULL; date = 0; if (extraNum+8 >= extraAlloc) { extraAlloc += 32; extraPtrPtr = (char**)ckrealloc(extraPtrPtr, extraAlloc*sizeof(char*)); } if (msgLen < itemPtr->fileSize+1) { msgLen = itemPtr->fileSize+4096; msgBuf = (char*)ckrealloc(msgBuf, msgLen); } snprintf(buf, sizeof(buf), "%s/dbase/%s", dbDir, Tcl_GetHashKey(&items,entryPtr)); if (-1 == (fd = open(buf, O_RDONLY))) { continue; } l = SafeRead(fd, msgBuf, itemPtr->fileSize); if (l <= 0) { continue; } msgBuf[l] = '\0'; close(fd); if (NULL == (cPtr = strstr(msgBuf, "\n\n"))) { if (NULL == (cPtr = strstr(msgBuf, "\r\n\r"))) { cPtr = msgBuf + strlen(msgBuf); } } *(++cPtr) = '\0'; RatMessageGetHeader(interp, msgBuf); Tcl_SplitList(interp, Tcl_GetStringResult(interp), &listArgc, &listArgv); for (i=0; ientry.content[TO] = to ? to : ""; itemPtr->entry.content[FROM] = from ? from : ""; itemPtr->entry.content[CC] = cc ? cc : ""; itemPtr->entry.content[SUBJECT] = subject ? subject : ""; sprintf(buf, "%d", date); itemPtr->entry.content[DATE] = extraPtrPtr[extraNum++]=cpystr(buf); itemPtr->entry.content[KEYWORDS] = "LostMessage"; sprintf(buf, "%d", itemPtr->fileSize); itemPtr->entry.content[RSIZE] =extraPtrPtr[extraNum++]=cpystr(buf); itemPtr->entry.content[STATUS] = flags ? flags : ""; sprintf(buf, "%ld", time(NULL) + 60L*60L*24L*100L); itemPtr->entry.content[EX_TIME] = extraPtrPtr[extraNum++] = cpystr(buf); itemPtr->entry.content[EX_TYPE] = "backup"; itemPtr->entry.content[FILENAME] = Tcl_GetHashKey(&items,entryPtr); } } if (numUnlinked && fix) { Tcl_DStringAppendElement(&reportDS, "The unlinked messages has been inserted with the keyword 'LostMessage'"); } if (indexInfo != numFound+numUnlinked-numDel) { if (fix) { sprintf(buf, "Number of messages in index.info was wrong " "(was: %d is now: %d)", indexInfo, numFound+numUnlinked-numDel); } else { sprintf(buf, "Number of messages in index.info is wrong " "(was: %d should be: %d)", indexInfo, numFound+numUnlinked-numDel); } Tcl_DStringAppendElement(&reportDS, buf); } /* * Write new index if fixing and needing */ if (fix && (numMal || numAlone || numUnlinked || indexInfo != numFound+numUnlinked-numDel)) { snprintf(buf, sizeof(buf), "%s/index", dbDir); fp = fopen(buf, "w"); for (entryPtr = Tcl_FirstHashEntry(&items, &search), j=0; entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { itemPtr = (RatDbItem*)Tcl_GetHashValue(entryPtr); for (i=0; ientry.content[i]) { fputs(itemPtr->entry.content[i], fp); } fputc('\n', fp); } j++; } fclose(fp); snprintf(buf, sizeof(buf), "%s/index.info", dbDir); fp = fopen(buf, "w"); fprintf(fp, "%d %d\n", DBASE_VERSION, j); fclose(fp); snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); (void)unlink(buf); if (isRead) { isRead = 0; strlcpy(buf, "Popup $t(need_restart)", sizeof(buf)); Tcl_Eval(interp, buf); } } /* * Cleaning up */ Unlock(interp); for (entryPtr = Tcl_FirstHashEntry(&items, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { ckfree(Tcl_GetHashValue(entryPtr)); } Tcl_DeleteHashTable(&items); Tcl_DeleteHashTable(&status); ckfree(indexPtr); ckfree(linePtrPtr); Tcl_ResetResult(interp); sprintf(buf, "%d", numFound); Tcl_AppendElement(interp, buf); sprintf(buf, "%d", numMal); Tcl_AppendElement(interp, buf); sprintf(buf, "%d", numAlone); Tcl_AppendElement(interp, buf); sprintf(buf, "%d", numUnlinked); Tcl_AppendElement(interp, buf); sprintf(buf, "%d", size); Tcl_AppendElement(interp, buf); Tcl_AppendElement(interp, Tcl_DStringValue(&reportDS)); Tcl_DStringFree(&reportDS); if (extraAlloc) { for (i=0; i fprintf(fpNewIndex, "%s\n", s)) { return; } } } } fclose(fpNewIndex); rename(newIndex, oldIndex); snprintf(buf, sizeof(buf), "%s/index.info", dbDir); if (0 == (fpIndexinfo = fopen(buf, "w")) || (0 > fprintf(fpIndexinfo, "%d %d\n", DBASE_VERSION, numEntries)) || (0 > fclose(fpIndexinfo))) { return; } snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); unlink(buf); isRead = 0; ckfree(entryPtr[0].content[0]); ckfree(entryPtr); RatLog(interp, RAT_INFO, "", RATLOG_EXPLICIT); } /* *---------------------------------------------------------------------- * * RatDbInfoCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatDbaseInfoCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *robjv[4]; if (0 == isRead) { if (TCL_OK != Read(interp)) { goto losing; } } else { if (TCL_OK != Sync(interp, 0)) { goto losing; } } robjv[0] = Tcl_NewLongObj(numRead); robjv[1] = Tcl_NewLongObj(firstDate); robjv[2] = Tcl_NewLongObj(lastDate); robjv[3] = Tcl_NewLongObj(totSize); Tcl_SetObjResult(interp, Tcl_NewListObj(4, robjv)); return TCL_OK; losing: return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatDbKeywordsCmd -- * * See ../doc/interface for a descriptions of arguments and result. * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatDbaseKeywordsCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_HashTable keywords; Tcl_HashEntry *entry; Tcl_HashSearch search; int i, j, new, num, argc; Tcl_Obj *result, *robjv[2]; char *s, buf[1024]; const char **argv; Tcl_InitHashTable(&keywords, TCL_STRING_KEYS); /* Loop over messages */ for (i = 0; i= numRead || indexes[i] < 0) { Tcl_DecrRefCount(indlist); return TCL_ERROR; } Tcl_ListObjAppendElement(interp, indlist, Tcl_NewIntObj(indexes[i])); } /* * Prepare line */ lobjv[0] = indlist; lobjv[1] = keywords; lobjv[2] = ex_time; lobjv[3] = ex_type; line = Tcl_NewListObj(4, lobjv); /* * Write entry to index.changes */ Lock(interp); snprintf(buf, sizeof(buf), "%s/index.changes", dbDir); if (NULL == (indexFP = fopen(buf, "a"))) { Tcl_AppendResult(interp, "error opening (for append)\"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); Unlock(interp); return TCL_ERROR; } if (0 > fprintf(indexFP, "k 0 %s\n", Tcl_GetString(line))) { Tcl_AppendResult(interp, "Failed to write to file \"", buf, "\"", (char*) NULL); (void)fclose(indexFP); Unlock(interp); return TCL_ERROR; } if (0 != fclose(indexFP)) { Tcl_AppendResult(interp, "error closing file \"", buf, "\": ", Tcl_PosixError(interp), (char *) NULL); Unlock(interp); return TCL_ERROR; } Sync(interp, 0); Unlock(interp); return TCL_OK; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratDisFolder.c000066400000000000000000001641201137544547100213040ustar00rootroot00000000000000/* * ratDisFolder.c -- * * This file contains code which implements disconnected folders. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ /* * * One directory per folder * * In that directory we find the following files: * master - File containing information about master folder. * Contains the following entries one on each line: * name * folder_spec * state - State against master. * Contains the following entries one on each line: * uidvalidity * last known UID in master * mappings - File with mappings local_uid <> master_uid * folder - The local copy of the folder * changes - Changes which should be applied to the master once * we synchronize. This file should contain: * delete uid - message to delete * flag UID flag value - set flag to value * */ #include "ratFolder.h" #include "ratStdFolder.h" #include "mbx.h" /* * The uid map */ typedef struct { unsigned long *map; unsigned long size; unsigned long allocated; } RatUidMap; /* * This is the private part of a disconnected folder info structure. */ typedef struct DisFolderInfo { char *dir; /* Directory where local data is stored */ Tcl_HashTable map; /* Mappings local_uid > remote_uid */ int mapChanged; /* non null if mappings needs to be rewritten*/ MAILSTREAM *master; /* Mailstream, used only in online mode */ int error; /* Error indicator variable */ int diskfull; /* Disk full indicator */ MAILSTREAM *local; /* Mailstream of local folder */ char *spec; /* Name of master folder */ FolderHandlers handlers; /* Event handlers */ Tcl_Interp *interp; /* Needed if event-handlers */ RatFolderInfo *infoPtr; int exists, expunged; /* Used by event handlers (indexes) */ unsigned long lastUid; /* Uid of last message in master folder */ /* Original procs for local folder */ RatInitProc *initProc; RatCloseProc *closeProc; RatUpdateProc *updateProc; RatInsertProc *insertProc; RatSetFlagProc *setFlagProc; RatGetFlagProc *getFlagProc; RatInfoProc *infoProc; RatSetInfoProc *setInfoProc; RatCreateProc *createProc; } DisFolderInfo; /* * Used to collect changes */ typedef struct { unsigned long uid; int flag; int value; } changes_t; /* * Hashtable containing open disfolders * The dirname is the key and the infoPtr is the value */ Tcl_HashTable openDisFolders; /* * Procedures private to this module. */ static RatInitProc Dis_InitProc; static RatFinalProc Dis_FinalProc; static RatCloseProc Dis_CloseProc; static RatUpdateProc Dis_UpdateProc; static RatInsertProc Dis_InsertProc; static RatInfoProc Dis_InfoProc; static RatSetFlagProc Dis_SetFlagProc; static RatGetFlagProc Dis_GetFlagProc; static RatCreateProc Dis_CreateProc; static RatSetInfoProc Dis_SetInfoProc; static RatSyncProc Dis_SyncProc; static int CreateDir(char *dir); static Tcl_ObjCmdProc RatSyncDisconnectedCmd; static Tcl_ObjCmdProc RatDeleteDisconnectedCmd; static void Dis_FindAndSyncFolders(Tcl_Interp *interp, CONST84 char *dir); static int Dis_SyncFolder(Tcl_Interp *interp, CONST84 char *dir, off_t size, int force, MAILSTREAM **master); static unsigned long DisDownloadMsgs(Tcl_Interp *interp, MAILSTREAM *masterStream, MAILSTREAM *localStream, int *masterErrorPtr, int *diskFullPtr, CONST84 char *dir, Tcl_HashTable *mapPtr, FILE *mapFp, unsigned long startAfterUid, unsigned long stopBeforeUid); static unsigned long GetMasterUID(MAILSTREAM *s, Tcl_HashTable *mapPtr, int index); static void UpdateFolderFlag(Tcl_Interp *interp, DisFolderInfo *disPtr, int index, RatFlag flag, int value); static void ReadMappings(MAILSTREAM *s, const char *dir,Tcl_HashTable *mapPtr); static void ReadOldMappings(MAILSTREAM *s, Tcl_HashTable *mapPtr, char *buf, int buflen, FILE *fp); static RatUidMap *InitUidMap(MAILSTREAM *s); static void FreeUidMap(RatUidMap *uidMap); static unsigned long MsgNo(RatUidMap *uidMap, unsigned long uid); static void CheckDeletion(RatFolderInfoPtr infoPtr, Tcl_Interp *interp); static const char* PrepareDir(Tcl_Interp *interp, Tcl_Obj *defPtr); static unsigned long DisUploadMsg(MAILSTREAM *masterStream, MAILSTREAM *localStream, const char *subject, const char *in_reply_to, const char *message_id, char *envdate, unsigned long local_uid, Tcl_DString *message, char *date, char *flags, FILE *mapFP, Tcl_HashTable *mapPtr); static HandleExists Dis_HandleExists; static HandleExpunged Dis_HandleExpunged; static void WriteMappings(DisFolderInfo *disPtr); static void WriteState(DisFolderInfo *disPtr); /* *---------------------------------------------------------------------- * * RatDisFolderInit -- * * Initializes the disconnected folder command. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatDisFolderInit(Tcl_Interp *interp) { Tcl_InitHashTable(&openDisFolders, TCL_STRING_KEYS); Tcl_CreateObjCommand(interp, "RatSyncDisconnected", RatSyncDisconnectedCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatDeleteDisconnected", RatDeleteDisconnectedCmd, NULL, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatDisFolderCreate -- * * Creates a disconnected folder entity. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * A disconnected folder is created. * * *---------------------------------------------------------------------- */ RatFolderInfo* RatDisFolderCreate(Tcl_Interp *interp, int append_only, Tcl_Obj *defPtr) { const char *dir; Tcl_Obj **objv, *oPtr, *lPtr; RatFolderInfo *infoPtr; DisFolderInfo *disPtr; Tcl_HashEntry *entryPtr; int objc, unused, online; Tcl_ListObjGetElements(interp, defPtr, &objc, &objv); /* * Prepare directory */ dir = PrepareDir(interp, defPtr); if (!dir) { return NULL; } disPtr = (DisFolderInfo *) ckalloc(sizeof(*disPtr)); disPtr->dir = cpystr(dir); disPtr->spec = NULL; /* * Open filefolder */ lPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj("Base", 4)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj("file", 4)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewObj()); oPtr = Tcl_NewStringObj(disPtr->dir, -1); Tcl_AppendToObj(oPtr, "/folder", 7); Tcl_ListObjAppendElement(interp, lPtr, oPtr); Tcl_IncrRefCount(lPtr); infoPtr = RatStdFolderCreate(interp, 0, lPtr); Tcl_DecrRefCount(lPtr); if (NULL == infoPtr) { goto error; } /* * Read mappings */ Tcl_InitHashTable(&disPtr->map, TCL_ONE_WORD_KEYS); disPtr->mapChanged = 0; ReadMappings(((StdFolderInfo*)infoPtr->private)->stream, disPtr->dir, &disPtr->map); infoPtr->name = Tcl_GetString(objv[3]); if (!*infoPtr->name) { infoPtr->name = "INBOX"; } infoPtr->name = cpystr(infoPtr->name); infoPtr->type = "dis"; infoPtr->private2 = (ClientData) disPtr; disPtr->master = NULL; disPtr->local = ((StdFolderInfo*)infoPtr->private)->stream; disPtr->lastUid = 0; disPtr->handlers.state = (void*)disPtr; disPtr->handlers.exists = Dis_HandleExists; disPtr->handlers.expunged = Dis_HandleExpunged; disPtr->interp = interp; disPtr->infoPtr = infoPtr; disPtr->error = 0; disPtr->diskfull = 0; disPtr->initProc = infoPtr->initProc; disPtr->closeProc = infoPtr->closeProc; disPtr->updateProc = infoPtr->updateProc; disPtr->insertProc = infoPtr->insertProc; disPtr->setFlagProc = infoPtr->setFlagProc; disPtr->getFlagProc = infoPtr->getFlagProc; disPtr->infoProc = infoPtr->infoProc; disPtr->setInfoProc = infoPtr->setInfoProc; disPtr->createProc = infoPtr->createProc; infoPtr->initProc = Dis_InitProc; infoPtr->finalProc = NULL; infoPtr->closeProc = Dis_CloseProc; infoPtr->updateProc = Dis_UpdateProc; infoPtr->insertProc = Dis_InsertProc; infoPtr->setFlagProc = Dis_SetFlagProc; infoPtr->getFlagProc = Dis_GetFlagProc; infoPtr->infoProc = Dis_InfoProc; infoPtr->setInfoProc = Dis_SetInfoProc; infoPtr->createProc = Dis_CreateProc; infoPtr->syncProc = Dis_SyncProc; infoPtr->dbinfoGetProc = NULL; /* * Add to hash table */ entryPtr = Tcl_CreateHashEntry(&openDisFolders, disPtr->dir, &unused); Tcl_SetHashValue(entryPtr, (ClientData)infoPtr); /* * Maybe go online? */ oPtr = Tcl_GetVar2Ex(interp, "option", "online", TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &online); if (online && !append_only) { infoPtr->finalProc = Dis_FinalProc; } return infoPtr; error: ckfree(disPtr); return NULL; } /* *---------------------------------------------------------------------- * * Dis_FinalProc -- * * Do final initialization if we are going online * * Results: * None * * Side effects: * May update the folder * *---------------------------------------------------------------------- */ static void Dis_FinalProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; char buf[1024]; struct stat sbuf; snprintf(buf, sizeof(buf), "%s/master", disPtr->dir); stat(buf, &sbuf); Dis_SyncFolder(interp, disPtr->dir, sbuf.st_size, 1, &disPtr->master); } /* *---------------------------------------------------------------------- * * RatDisPrepareDir -- * * Prepares the directory for a disconnected folder * * Results: * A pointer to a static area holds the name of the directory or * NULL on errors; * * Side effects: * Updatesv the master-file * *---------------------------------------------------------------------- */ static const char* PrepareDir(Tcl_Interp *interp, Tcl_Obj *defPtr) { const char *dir, *name; struct stat sbuf; Tcl_DString ds; FILE *fp; Tcl_Obj **objv, *lPtr; int objc; /* * Find directory and make sure it exists */ if (!(dir = RatDisFolderDir(interp, defPtr))) { return NULL; } Tcl_ListObjGetElements(interp, defPtr, &objc, &objv); name = Tcl_GetString(objv[0]); if (!*name) { name = "INBOX"; } /* * Initialize state-file and create folder-file if it does not exist */ Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, dir, -1); Tcl_DStringAppend(&ds, "/state", 7); if (0 != stat(Tcl_DStringValue(&ds), &sbuf)) { fp = fopen(Tcl_DStringValue(&ds), "w"); if (NULL == fp) { Tcl_DStringFree(&ds); return NULL; } fprintf(fp, "0\n0\n"); fclose(fp); Tcl_DStringSetLength(&ds, strlen(dir)); Tcl_DStringAppend(&ds, "/folder", 7); mbx_create(NIL, Tcl_DStringValue(&ds)); } /* * Always update the master-file (the user may have changed some setting) */ Tcl_DStringSetLength(&ds, strlen(dir)); Tcl_DStringAppend(&ds, "/master", 7); fp = fopen(Tcl_DStringValue(&ds), "w"); if (NULL == fp) { Tcl_DStringFree(&ds); return NULL; } lPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj("Master", 6)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj("imap", 4)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewObj()); Tcl_ListObjAppendElement(interp, lPtr, objv[3]); Tcl_ListObjAppendElement(interp, lPtr, objv[4]); Tcl_IncrRefCount(lPtr); fprintf(fp, "%s\n%s\n", name, RatGetFolderSpec(interp, lPtr)); Tcl_DecrRefCount(lPtr); fclose(fp); Tcl_DStringFree(&ds); return dir; } /* *---------------------------------------------------------------------- * * Dis_InitProc -- * * See the documentation for initProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for initProc in ratFolder.h * * *---------------------------------------------------------------------- */ static void Dis_InitProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; /* Do nothing */ (*disPtr->initProc)(infoPtr, interp, index); } /* *---------------------------------------------------------------------- * * Dis_CloseProc -- * * See the documentation for closeProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for closeProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Dis_CloseProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int expunge) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; Tcl_HashEntry *entryPtr; int result; if (expunge) { CheckDeletion(infoPtr, interp); } result = (*disPtr->closeProc)(infoPtr, interp, expunge); entryPtr = Tcl_FindHashEntry(&openDisFolders, disPtr->dir); Tcl_DeleteHashEntry(entryPtr); WriteMappings(disPtr); Tcl_DeleteHashTable(&disPtr->map); ckfree(disPtr->dir); if (disPtr->master) { Std_StreamClose(interp, disPtr->master); disPtr->master = NULL; } ckfree(disPtr->spec); ckfree(disPtr); return result; } /* *---------------------------------------------------------------------- * * Dis_UpdateProc -- * * See the documentation for updateProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for updateProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Dis_UpdateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp,RatUpdateType mode) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; int lastKnown, result; if (disPtr->master && 0 == disPtr->error) { disPtr->exists = lastKnown = infoPtr->number; disPtr->expunged = 0; if (RAT_SYNC == mode) { mail_expunge(disPtr->master); } else { mail_check(disPtr->master); } if ((disPtr->master->nmsgs != lastKnown-disPtr->expunged || disPtr->diskfull) && 0 == disPtr->error) { MAILSTREAM *local = ((StdFolderInfo*)disPtr->infoPtr->private)->stream; char buf[1024]; FILE *fp; /* * Append new messages to local folder and uidmap */ snprintf(buf, sizeof(buf), "%s/mappings", disPtr->dir); fp = fopen(buf, "a"); if (NULL == fp) { return 0; } disPtr->lastUid = DisDownloadMsgs(disPtr->interp, disPtr->master, local, &disPtr->error, &disPtr->diskfull, disPtr->dir, &disPtr->map, fp, disPtr->lastUid, 0); fclose(fp); WriteState(disPtr); } if (0 != disPtr->error) { Std_StreamClose(interp, disPtr->master); disPtr->master = NULL; RatStdCheckNet(interp); } } if (RAT_SYNC == mode && 0 == disPtr->error) { CheckDeletion(infoPtr, interp); WriteMappings(disPtr); } result = (*disPtr->updateProc)(infoPtr, interp, mode); /* Sanity check */ if (RAT_SYNC == mode && disPtr->master && 0 == disPtr->diskfull && disPtr->master->nmsgs != disPtr->local->nmsgs) { char buf[1024]; snprintf(buf, sizeof(buf), "Message count bad, %ld != %ld, in %s", disPtr->local->nmsgs, disPtr->master->nmsgs, disPtr->master->original_mailbox); RatLog(interp, RAT_ERROR, buf, RATLOG_EXPLICIT); } return result; } /* *---------------------------------------------------------------------- * * Dis_InsertProc -- * * See the documentation for insertProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for insertProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Dis_InsertProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int argc, char *argv[]) { MAILSTREAM *local = ((StdFolderInfo*)infoPtr->private)->stream; DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; char flags[128], date[128], buf[1024]; Tcl_Obj *subject, *msgid; unsigned long localUid, us, ue; MessageInfo *msgPtr; Tcl_CmdInfo cmdInfo; Tcl_DString ds; int i, ret; FILE *fp; localUid = local->uid_last; ret = (*disPtr->insertProc)(infoPtr, interp, argc, argv); if (disPtr->master && argc) { Tcl_DStringInit(&ds); snprintf(buf, sizeof(buf), "%s/mappings", disPtr->dir); fp = fopen(buf, "a"); for (i=us=ue=0; imaster, local, Tcl_GetString(subject), NULL, Tcl_GetString(msgid), NULL, ++localUid, &ds, date, flags, fp, &disPtr->map); if (0 == i) { us = ue; } /* * The following is damage control. DisUploadMsg should * never return 0 as the UID but if it does then just * download from the latest known uid than from the first. */ if (0 == ue) { ue = us; } disPtr->mapChanged = 1; if (T != mail_ping(disPtr->master)) { disPtr->master = NULL; break; } Tcl_DStringSetLength(&ds, 0); } Tcl_DStringFree(&ds); if (disPtr->lastUid+1 < us) { DisDownloadMsgs(interp, disPtr->master, local, &disPtr->error, NULL, disPtr->dir, &disPtr->map, fp, disPtr->lastUid+1, us); } fclose(fp); disPtr->lastUid = ue; WriteState(disPtr); } return ret; } /* *---------------------------------------------------------------------- * * Dis_SetFlagProc -- * * See the documentation for setFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for setFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Dis_SetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int *ilist, int count, RatFlag flag, int value) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; FILE *fp = NULL; char buf[1024]; unsigned long uid; int i; for (i=0; iprivate)->stream, &disPtr->map, ilist[i]); if (uid && disPtr->master) { snprintf(buf, sizeof(buf), "%ld", uid); if (value) { mail_setflag_full(disPtr->master, buf, flag_name[flag].imap_name, ST_UID); } else { mail_clearflag_full(disPtr->master, buf, flag_name[flag].imap_name, ST_UID); } } else if (uid) { snprintf(buf, sizeof(buf), "%s/changes", disPtr->dir); if (NULL != (fp = fopen(buf, "a"))) { fprintf(fp, "flag %ld %d %d\n", uid, flag, value); fclose(fp); } } } return (*disPtr->setFlagProc)(infoPtr, interp, ilist, count, flag, value); } /* *---------------------------------------------------------------------- * * Dis_GetFlagProc -- * * See the documentation for getFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for getFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Dis_GetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, RatFlag flag) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; /* Do Nothing */ return (*disPtr->getFlagProc)(infoPtr, interp, index,flag); } /* *---------------------------------------------------------------------- * * Dis_InfoProc -- * * See the documentation for infoProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for infoProc in ratFolder.h * * *---------------------------------------------------------------------- */ Tcl_Obj* Dis_InfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index) { /* Do Nothing */ return Std_InfoProc(interp, clientData, type, index); } /* *---------------------------------------------------------------------- * * Dis_CreateProc -- * * See the documentation for createProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for createProc in ratFolder.h * * *---------------------------------------------------------------------- */ static char* Dis_CreateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; /* Do Nothing */ return (*disPtr->createProc)(infoPtr, interp, index); } /* *---------------------------------------------------------------------- * * Dis_SetInfoProc -- * * Sets information about a message * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void Dis_SetInfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index, Tcl_Obj *oPtr) { /* Do Nothing */ Std_SetInfoProc(interp, clientData, type, index, oPtr); } /* *---------------------------------------------------------------------- * * CreateDir -- * * Checks that a given directory exists and creates it and any * parent directories if they do not exist. This routine expects * a complete path starting with '/'. * * Results: * Non zero if failed and sets errno. * * Side effects: * None * * *---------------------------------------------------------------------- */ static int CreateDir(char *dir) { struct stat sbuf; char *cPtr; /* * First we check if it already exists. */ if (0 == stat(dir, &sbuf) && S_ISDIR(sbuf.st_mode)) { return 0; } /* * Go through all directories from the top and down and create those * which do not exist. */ for (cPtr = strchr(dir+1, '/'); cPtr; cPtr = strchr(cPtr+1, '/')) { *cPtr = '\0'; if (0 != stat(dir, &sbuf)) { if (mkdir(dir, 0700)) { return 1; } } else if (!S_ISDIR(sbuf.st_mode)) { errno = ENOTDIR; return 1; } *cPtr = '/'; } if (0 != stat(dir, &sbuf)) { if (mkdir(dir, 0700)) { return 1; } } else if (!S_ISDIR(sbuf.st_mode)) { errno = ENOTDIR; return 1; } return 0; } /* *---------------------------------------------------------------------- * * RatSyncDisconnectedCmd -- * * Synchronizes all disconnected folders * * Results: * None * * Side effects: * All disconnected folders are updated * * *---------------------------------------------------------------------- */ static int RatSyncDisconnectedCmd(ClientData op, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { CONST84 char *dirname; if (NULL == (dirname = RatGetPathOption(interp, "disconnected_dir"))) { return TCL_ERROR; } Dis_FindAndSyncFolders(interp, dirname); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatDeleteDisconnectedCmd -- * * Deletes the local copy of a disconnected folder * * Results: * None * * Side effects: * All disconnected folders are updated * * *---------------------------------------------------------------------- */ static int RatDeleteDisconnectedCmd(ClientData op, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *cmdv[4]; char *dir; int i; if (2 != objc) { Tcl_AppendResult(interp, "Usage: ", Tcl_GetString(objv[0]), \ " def", (char*) NULL); return TCL_ERROR; } if (!(dir = RatDisFolderDir(interp, objv[1]))) { return TCL_OK; } cmdv[0] = Tcl_NewStringObj("file", -1); cmdv[1] = Tcl_NewStringObj("delete", -1); cmdv[2] = Tcl_NewStringObj("-force", -1); cmdv[3] = Tcl_NewStringObj(dir, -1); for (i=0; i<4; i++) Tcl_IncrRefCount(cmdv[i]); Tcl_EvalObjv(interp, 4, cmdv, 0); for (i=0; i<4; i++) Tcl_DecrRefCount(cmdv[i]); return TCL_OK; } /* *---------------------------------------------------------------------- * * Dis_FindAndSyncFolders -- * * Recurses over a directory tree and calls Dis_SyncFolder for each * found folder. * * Results: * None * * Side effects: * All disconnected folders are updated * * *---------------------------------------------------------------------- */ static void Dis_FindAndSyncFolders(Tcl_Interp *interp, CONST84 char *dir) { struct stat sbuf; char buf[1024]; DIR *dirPtr; struct dirent *direntPtr; /* * Check if this is a folder directory (contains a master-file) */ strlcpy(buf, dir, sizeof(buf)-7); strlcat(buf, "/master", sizeof(buf)); if (0 == stat(buf, &sbuf) && S_ISREG(sbuf.st_mode)) { Dis_SyncFolder(interp, dir, sbuf.st_size, 0, NULL); return; } /* * Otherwise check all entries and call Dis_FindAndSyncFolders for all * descendants */ if (NULL == (dirPtr = opendir(dir))) { return; } while (dirPtr && 0 != (direntPtr = readdir(dirPtr))) { snprintf(buf, sizeof(buf), "%s/%s", dir, direntPtr->d_name); if (0 != stat(buf, &sbuf) || !S_ISDIR(sbuf.st_mode) || !strcmp(".", direntPtr->d_name) || !strcmp("..", direntPtr->d_name)) { continue; } Dis_FindAndSyncFolders(interp, buf); } closedir(dirPtr); } /* *---------------------------------------------------------------------- * * Dis_SyncFolder -- * * Synchronizes a specified folder. * * Results: * A standard tcl result. * * Side effects: * None * * *---------------------------------------------------------------------- */ static int Dis_SyncFolder(Tcl_Interp *interp, CONST84 char *dir, off_t size, int force, MAILSTREAM **master) { char buf[1024], *name, *spec, *data, *header, *body, localMailbox[1024], datebuf[128], *cPtr; MESSAGECACHE *elt; unsigned long uid, msgno, lastUid, uidvalidity, len, nmsgs; MAILSTREAM *masterStream, *localStream; Tcl_HashTable *mapPtr; RatFolderInfoPtr infoPtr = NULL; DisFolderInfo *disPtr = NULL; Tcl_HashEntry *entryPtr; Tcl_HashSearch search; Tcl_CmdInfo cmdInfo; ENVELOPE *envPtr; int fd, i, *masterErrorPtr, error; FILE *fp = NULL; Tcl_DString ds; RatUidMap *uidMap = NULL; Tcl_DStringInit(&ds); /* * Read and parse masterfile & statefile */ snprintf(buf, sizeof(buf), "%s/master", dir); if (-1 == (fd = open(buf, O_RDONLY)) || NULL == (data = (char*)ckalloc(size+1)) || size != SafeRead(fd, data, size) || 0 != close(fd)) { RatLog(interp, RAT_ERROR, "Failed to read masterfile", RATLOG_TIME); if (master) { *master = NULL; } return TCL_ERROR; } name = data; if (NULL == (spec = strchr(data, '\n'))) { if (master) { *master = NULL; } return TCL_ERROR; } *spec++ = '\0'; if (NULL == (cPtr = strchr(spec, '\n'))) { if (master) { *master = NULL; } return TCL_ERROR; } *cPtr = '\0'; snprintf(buf, sizeof(buf), "%s/state", dir); if (NULL == (fp = fopen(buf, "r")) || 2 != fscanf(fp, "%ld\n%ld", &uidvalidity, &lastUid) || 0 != fclose(fp)) { RatLog(interp, RAT_ERROR, "Failed to read statefile", RATLOG_TIME); if (master) { *master = NULL; } return TCL_ERROR; } if (!force && Tcl_GetCommandInfo(interp, "RatUP_NetsyncFolder", &cmdInfo)){ Tcl_Obj *oPtr = Tcl_NewObj(); int doSync = 0; Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewStringObj("RatUP_NetsyncFolder", -1)); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewStringObj(spec, -1)); Tcl_IncrRefCount(oPtr); if (TCL_OK != Tcl_EvalObjEx(interp, oPtr, TCL_EVAL_GLOBAL) || TCL_OK != Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp),&doSync) || 0 == doSync) { Tcl_DecrRefCount(oPtr); ckfree(data); if (master) { *master = NULL; } return TCL_ERROR; } Tcl_DecrRefCount(oPtr); } RatLogF(interp, RAT_INFO, "synchronizing", RATLOG_EXPLICIT, name); /* * Open connection * Check uidvalidity * Apply deletion commands and expunge * Apply flag commands * loop over messages in local folder * Check if has master uid * if YES check if still exists in master * if NO delete * if YES check flags and update from master * if NO then append to master, should update with masters uid * Loop over new messages in master and append them to local */ /* * Open connections */ if (NULL != (entryPtr = Tcl_FindHashEntry(&openDisFolders, dir))) { infoPtr = Tcl_GetHashValue(entryPtr); strlcpy(localMailbox, ((StdFolderInfo*)infoPtr->private)->stream->mailbox, sizeof(localMailbox)); disPtr = (DisFolderInfo*)infoPtr->private2; localStream = disPtr->local; masterStream = disPtr->master; masterErrorPtr = &disPtr->error; mapPtr = &disPtr->map; if (disPtr->lastUid) { lastUid = disPtr->lastUid; } } else { snprintf(localMailbox, sizeof(localMailbox), "%s/folder", dir); masterStream = NIL; localStream = mail_open(NIL, localMailbox, NIL); mapPtr = (Tcl_HashTable*)ckalloc(sizeof(*mapPtr)); Tcl_InitHashTable(mapPtr, TCL_ONE_WORD_KEYS); ReadMappings(localStream, dir, mapPtr); masterErrorPtr = &error; } if (!masterStream) { *masterErrorPtr = 0; masterStream = Std_StreamOpen(interp, spec, NIL, masterErrorPtr, (disPtr ? &disPtr->handlers : NULL)); } if (NULL == masterStream) { RatLog(interp, RAT_INFO, "", RATLOG_EXPLICIT); goto error; } if (disPtr && disPtr->spec) { spec = disPtr->spec; } else if (disPtr) { disPtr->spec = cpystr(spec); } if (!(0 == uidvalidity && 0 == lastUid) && uidvalidity != masterStream->uid_validity) { RatLogF(interp, RAT_ERROR, "uidvalidity_changed", RATLOG_TIME); goto error; } /* * Apply deletion commands and expunge */ RatLogF(interp, RAT_INFO, "uploading", RATLOG_EXPLICIT); snprintf(buf, sizeof(buf), "%s/changes", dir); if (NULL != (fp = fopen(buf, "r"))) { rat_sequence_t seq = RatSequenceInit(); buf[sizeof(buf)-1] = '\0'; while (fgets(buf, sizeof(buf)-1, fp) && !feof(fp)) { if (!strncmp(buf, "delete", 6)) { RatSequenceAdd(seq, atol(buf+7)); } } if (RatSequenceNotempty(seq)) { mail_setflag_full(masterStream, RatSequenceGet(seq), flag_name[RAT_DELETED].imap_name, ST_UID); mail_expunge(masterStream); } RatSequenceFree(seq); if (*masterErrorPtr) goto error; } /* * Build list of uids */ uidMap = InitUidMap(masterStream); if (*masterErrorPtr) goto error; /* * Apply flag commands (and remove changes file) * First we read the changes file and store all the changes in * a list. While doing this we eliminate extra changes to a flag * (we only keep the latest value). Then we build the sequences of * uids which needs flag changes and lastly we perform the * changes. * The goal is to minimize the amout of data which needs to be * sent over the wire. */ if (NULL != fp) { RatFlag flag; int f, value; rat_sequence_t seq[RAT_FLAG_END][2]; changes_t *changes = NULL; int changes_allocated = 0, changes_used = 0; fseek(fp, 0, SEEK_SET); while (fgets(buf, sizeof(buf)-1, fp) && !feof(fp)) { if (!strncmp(buf, "flag", 4)) { sscanf(buf+5, "%ld %d %d", &uid, &f, &value); flag = f; sprintf(buf, "%ld", uid); if (0 == (msgno = MsgNo(uidMap, uid))) { continue; /* Message was deleted */ } for (i=0; inmsgs; snprintf(buf, sizeof(buf), "%s/mappings", dir); fp = fopen(buf, "a"); lastUid = DisDownloadMsgs(interp, masterStream, localStream, masterErrorPtr, (disPtr ? &disPtr->diskfull : NULL), dir, mapPtr, fp, lastUid, 0); /* * Loop over messages and update */ RatLogF(interp, RAT_INFO, "downloading_flags", RATLOG_EXPLICIT); for (i = 1; i <= nmsgs && !*masterErrorPtr; i++) { if ((uid = GetMasterUID(localStream, mapPtr, i-1))) { msgno = MsgNo(uidMap, uid); if (0 == msgno) { if (disPtr) { UpdateFolderFlag(interp, disPtr, i, RAT_DELETED, 1); } else { sprintf(buf, "%d", i); mail_setflag(localStream, buf, flag_name[RAT_DELETED].imap_name); } continue; } /* * Update flags from master */ envPtr = mail_fetchenvelope(masterStream, MsgNo(uidMap, uid)); elt = mail_elt(masterStream, MsgNo(uidMap, uid)); if (disPtr) { UpdateFolderFlag(interp,disPtr,i,RAT_SEEN,elt->seen); UpdateFolderFlag(interp,disPtr,i,RAT_DELETED,elt->deleted); UpdateFolderFlag(interp,disPtr,i,RAT_FLAGGED,elt->flagged); UpdateFolderFlag(interp,disPtr,i,RAT_ANSWERED, elt->answered); UpdateFolderFlag(interp,disPtr,i,RAT_DRAFT,elt->draft); } else { MESSAGECACHE *lelt; lelt = mail_elt(localStream, i); sprintf(buf, "%d", i); if (elt->seen != lelt->seen) { if (elt->seen) { mail_setflag(localStream, buf, flag_name[RAT_SEEN].imap_name); } else { mail_clearflag(localStream, buf, flag_name[RAT_SEEN].imap_name); } } if (elt->deleted != lelt->deleted) { if (elt->deleted) { mail_setflag(localStream, buf, flag_name[RAT_DELETED].imap_name); } else { mail_clearflag(localStream, buf, flag_name[RAT_DELETED].imap_name); } } if (elt->flagged != lelt->flagged) { if (elt->flagged) { mail_setflag(localStream, buf, flag_name[RAT_FLAGGED].imap_name); } else { mail_clearflag(localStream, buf, flag_name[RAT_FLAGGED].imap_name); } } if (elt->answered != lelt->answered) { if (elt->answered) { mail_setflag(localStream, buf, flag_name[RAT_ANSWERED].imap_name); } else { mail_clearflag(localStream, buf, flag_name[RAT_ANSWERED].imap_name); } } if (elt->draft != lelt->draft) { if (elt->draft) { mail_setflag(localStream, buf, flag_name[RAT_DRAFT].imap_name); } else { mail_clearflag(localStream, buf, flag_name[RAT_DRAFT].imap_name); } } } } else { /* * Append the message to the master stream */ Tcl_DStringSetLength(&ds, 0); header = mail_fetchheader(localStream, i); Tcl_DStringAppend(&ds, header, -1); body = mail_fetchtext_full(localStream, i, &len, FT_PEEK); Tcl_DStringAppend(&ds, body, len); elt = mail_elt(localStream, i); mail_date(datebuf, elt); envPtr = mail_fetch_structure(localStream, i, NULL, 0); lastUid = DisUploadMsg(masterStream, localStream, envPtr->subject, envPtr->in_reply_to, envPtr->message_id, (char*)envPtr->date, mail_uid(localStream, i), &ds, datebuf, MsgFlags(elt), fp, mapPtr); if (disPtr) { disPtr->mapChanged = 1; } } } fclose(fp); /* * Update state file */ snprintf(buf, sizeof(buf), "%s/state", dir); fp = fopen(buf, "w"); fprintf(fp, "%ld\n%ld\n", masterStream->uid_validity, lastUid); fclose(fp); /* * Cleanup */ if (!disPtr) { mail_close(localStream); for (entryPtr = Tcl_FirstHashEntry(mapPtr, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { ckfree(Tcl_GetHashValue(entryPtr)); } Tcl_DeleteHashTable(mapPtr); ckfree(mapPtr); Std_StreamClose(interp, masterStream); masterStream = NULL; } else { Tcl_Obj *oPtr; int online; /* * If we are offline, then we should close the masterStream */ oPtr = Tcl_GetVar2Ex(interp, "option", "online", TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &online); if (!online) { Std_StreamClose(interp, disPtr->master); disPtr->master = NULL; } } RatLog(interp, RAT_INFO, "", RATLOG_EXPLICIT); ckfree(data); Tcl_DStringFree(&ds); /* * Trigger an update of the folder as well (if we are active) */ if (disPtr) { disPtr->lastUid = lastUid; RatUpdateFolder(interp, disPtr->infoPtr, RAT_UPDATE); } FreeUidMap(uidMap); if (master) { *master = masterStream; } return TCL_OK; error: RatLog(interp, RAT_INFO, "", RATLOG_EXPLICIT); if (!disPtr) { mail_close(localStream); for (entryPtr = Tcl_FirstHashEntry(mapPtr, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { ckfree(Tcl_GetHashValue(entryPtr)); } Tcl_DeleteHashTable(mapPtr); ckfree(mapPtr); } if (masterStream) { Std_StreamClose(interp, masterStream); if (disPtr) { disPtr->master = NULL; } } if (master) { *master = NULL; } if (uidMap) { FreeUidMap(uidMap); } return TCL_ERROR; } /* *---------------------------------------------------------------------- * * DisDownloadMsgs * * Downloads a new message from the master folder to the local * folder. * * Results: * Last uid * * Side effects: * The uidmap is updated (if one exists) * * *---------------------------------------------------------------------- */ static unsigned long DisDownloadMsgs(Tcl_Interp *interp, MAILSTREAM *masterStream, MAILSTREAM *localStream, int *masterErrorPtr, int *diskFullPtr, CONST84 char *dir, Tcl_HashTable *mapPtr, FILE *mapFp, unsigned long startAfterUid, unsigned long stopBeforeUid) { ENVELOPE *envPtr; MESSAGECACHE *elt; unsigned long len, uid; char *body, *header, datebuf[128], statebuf[1024], statetmp[1024]; Tcl_DString ds; STRING string; SEARCHPGM *pgm; int i, unused; FILE *stateFp; long mapPos; if (0 == masterStream->nmsgs) { return masterStream->uid_last; } snprintf(statebuf, sizeof(statebuf), "%s/state", dir); pgm = mail_newsearchpgm(); if (0 == stopBeforeUid) { stopBeforeUid = mail_uid(masterStream, masterStream->nmsgs)+1; } pgm->uid = mail_newsearchset(); pgm->uid->first = startAfterUid+1; pgm->uid->last = stopBeforeUid; searchResultNum = 0; mail_search_full(masterStream, NULL, pgm, SE_FREE); for (i = 0; i < searchResultNum; i++) { RatLogF(interp, RAT_INFO, "downloading", RATLOG_EXPLICIT, i+1, searchResultNum); envPtr = mail_fetchenvelope(masterStream, searchResultPtr[i]); if (*masterErrorPtr) goto done; elt = mail_elt(masterStream, searchResultPtr[i]); if (*masterErrorPtr) goto done; body = mail_fetchtext_full(masterStream, searchResultPtr[i], &len, FT_PEEK); if (*masterErrorPtr) goto done; header = mail_fetchheader(masterStream, searchResultPtr[i]); if (*masterErrorPtr) goto done; if (!body || !header) continue; /* * We must be careful here so we can undo what we do if any of * the later steps fails. */ uid = mail_uid(masterStream, searchResultPtr[i]); mapPos = ftell(mapFp); if (0 > fprintf(mapFp, "%ld %ld\n", uid, ++localStream->uid_last) || 0 != fflush(mapFp)) { goto disk_full; } snprintf(statetmp, sizeof(statetmp), "%s.tmp", statebuf); stateFp = fopen(statetmp, "w"); if (0>fprintf(stateFp, "%ld\n%ld\n", masterStream->uid_validity,uid)){ fclose(stateFp); unused = ftruncate(fileno(mapFp), mapPos); goto disk_full; } if (0 != fclose(stateFp)) { unused = ftruncate(fileno(mapFp), mapPos); unlink(statetmp); goto disk_full; } Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, header, -1); Tcl_DStringAppend(&ds, body, len); INIT(&string, mail_string, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); mail_date(datebuf, elt); if (T != mail_append_full(localStream, localStream->mailbox, RatPurgeFlags(MsgFlags(elt), 0), datebuf, &string)) { Tcl_DStringFree(&ds); unused = ftruncate(fileno(mapFp), mapPos); unlink(statetmp); goto disk_full; } Tcl_DStringFree(&ds); masterStream->uid_last = uid; rename(statetmp, statebuf); if (diskFullPtr) { *diskFullPtr = 0; } /* Move upwards */ if (mapPtr) { unsigned long *lPtr = (unsigned long*)ckalloc(sizeof(*lPtr)); Tcl_HashEntry *entryPtr; int unused; *lPtr = uid; entryPtr = Tcl_CreateHashEntry(mapPtr, (char*)(localStream->uid_last), &unused); Tcl_SetHashValue(entryPtr, lPtr); } } done: RatLog(interp, RAT_INFO, "", RATLOG_EXPLICIT); return masterStream->uid_last; disk_full: if (diskFullPtr && !*diskFullPtr) { *diskFullPtr = 1; RatLogF(interp, RAT_ERROR, "sync_failed_disk_full", RATLOG_TIME); } RatLog(interp, RAT_INFO, "", RATLOG_EXPLICIT); return startAfterUid; } /* *---------------------------------------------------------------------- * * GetMasterUID * * Returns the UID in the master folder of the specified message * * Results: * The UID is returned or zero if the message is not present * * Side effects: * None * * *---------------------------------------------------------------------- */ static unsigned long GetMasterUID(MAILSTREAM *s, Tcl_HashTable *mapPtr, int index) { Tcl_HashEntry *entryPtr; if ((entryPtr = Tcl_FindHashEntry(mapPtr, (char*)mail_uid(s, index+1)))) { return *((unsigned long*)Tcl_GetHashValue(entryPtr)); } else { return 0; } } /* *---------------------------------------------------------------------- * * UpdateFolderFlag -- * * Synchronizes a flag with master * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void UpdateFolderFlag(Tcl_Interp *interp, DisFolderInfo *disPtr, int index, RatFlag flag, int value) { int local, no; local = (*disPtr->getFlagProc)(disPtr->infoPtr, interp, index-1, flag); if (value == local) { return; } no = index-1; (*disPtr->setFlagProc)(disPtr->infoPtr, interp, &no, 1, flag, value); } /* *---------------------------------------------------------------------- * * ReadMappings -- * * Reads the mappings-file into the given hash-table * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void ReadMappings(MAILSTREAM *s, const char *dir, Tcl_HashTable *mapPtr) { Tcl_HashEntry *entryPtr; char buf[1024]; int unused; unsigned long *lPtr, uid; FILE *fp; snprintf(buf, sizeof(buf), "%s/mappings", dir); if (NULL != (fp = fopen(buf, "r"))) { buf[sizeof(buf)-1] = '\0'; while(fgets(buf, sizeof(buf)-1, fp) && !feof(fp)) { if (strchr(buf, '<')) { ReadOldMappings(s, mapPtr, buf, sizeof(buf)-1, fp); break; } buf[strlen(buf)-1] = '\0'; uid = atol(strchr(buf, ' ')); entryPtr = Tcl_CreateHashEntry(mapPtr, (char*)uid, &unused); lPtr = (unsigned long*)ckalloc(sizeof(unsigned long)); *lPtr = atol(buf); Tcl_SetHashValue(entryPtr, lPtr); } fclose(fp); } } /* *---------------------------------------------------------------------- * * ReadOldMappings -- * * Reads the old style mappings-file into the given hash-table * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void ReadOldMappings(MAILSTREAM *s, Tcl_HashTable *mapPtr, char *buf, int buflen, FILE *fp) { Tcl_HashTable tmap; Tcl_HashEntry *entryPtr; Tcl_HashSearch search; unsigned long *lPtr, l, uid; ENVELOPE *envPtr; int unused; /* * Read file into local hash-table tmap */ Tcl_InitHashTable(&tmap, TCL_STRING_KEYS); do { buf[strlen(buf)-1] = '\0'; entryPtr = Tcl_CreateHashEntry(&tmap, strchr(buf, '<'), &unused); lPtr = (unsigned long*)ckalloc(sizeof(unsigned long)); *lPtr = atol(buf); Tcl_SetHashValue(entryPtr, lPtr); } while (fgets(buf, buflen, fp) && !feof(fp)); /* * Loop through folder and add the new mappings to the real map */ for (l=1; l <= s->nmsgs; l++) { envPtr = mail_fetch_structure(s, l, NIL, 0); entryPtr = Tcl_FindHashEntry(&tmap, envPtr->message_id); if (entryPtr) { uid = *(unsigned long*)Tcl_GetHashValue(entryPtr); entryPtr = Tcl_CreateHashEntry(mapPtr, (char*)mail_uid(s, l), &unused); lPtr = (unsigned long*)ckalloc(sizeof(unsigned long)); *lPtr = uid; Tcl_SetHashValue(entryPtr, lPtr); } } /* * Free the temporary hashtable from memory */ for (entryPtr = Tcl_FirstHashEntry(&tmap, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { ckfree(Tcl_GetHashValue(entryPtr)); } Tcl_DeleteHashTable(&tmap); } /* *---------------------------------------------------------------------- * * RatDisFolderDir -- * * Calculates the directory name of a disconnected folder and * makes sure the directory exists. * * Results: * A pointer to a static area containign the name * * Side effects: * None * * *---------------------------------------------------------------------- */ char* RatDisFolderDir(Tcl_Interp *interp, Tcl_Obj *defPtr) { static Tcl_DString ds; static int initialized = 0; CONST84 char *dir; int objc, mobjc; Tcl_Obj **objv, **mobjv; if (!initialized) { Tcl_DStringInit(&ds); } else { Tcl_DStringSetLength(&ds, 0); } if (NULL == (dir = RatGetPathOption(interp, "disconnected_dir"))) { return NULL; } Tcl_ListObjGetElements(interp, defPtr, &objc, &objv); Tcl_ListObjGetElements(interp, Tcl_GetVar2Ex(interp, "mailServer", Tcl_GetString(objv[3]), TCL_GLOBAL_ONLY), &mobjc, &mobjv); Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, dir, -1); Tcl_DStringAppend(&ds, "/", 1); Tcl_DStringAppend(&ds,Tcl_GetString(mobjv[0]),Tcl_GetCharLength(mobjv[0])); Tcl_DStringAppend(&ds, ":", 1); if (Tcl_GetCharLength(mobjv[1])) { Tcl_DStringAppend(&ds, Tcl_GetString(mobjv[1]), Tcl_GetCharLength(mobjv[1])); } else { Tcl_DStringAppend(&ds, "143", 3); } Tcl_DStringAppend(&ds, "/", 1); if (Tcl_GetCharLength(objv[4])) { Tcl_DStringAppend(&ds, Tcl_GetString(objv[4]), Tcl_GetCharLength(objv[4])); } else { Tcl_DStringAppend(&ds, "INBOX", 5); } Tcl_DStringAppend(&ds, "+", 1); Tcl_DStringAppend(&ds,Tcl_GetString(mobjv[3]),Tcl_GetCharLength(mobjv[3])); Tcl_DStringAppend(&ds, "+imap", 5); if (CreateDir(Tcl_DStringValue(&ds))) { return NULL; } return Tcl_DStringValue(&ds); } /* *---------------------------------------------------------------------- * * Dis_SyncProc -- * * Synchronizes the specified folder * * Results: * None * * Side effects: * The folder may be modified * * *---------------------------------------------------------------------- */ static int Dis_SyncProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp) { DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; struct stat sbuf; char buf[1024]; snprintf(buf, sizeof(buf), "%s/master", disPtr->dir); stat(buf, &sbuf); return Dis_SyncFolder(interp, disPtr->dir, sbuf.st_size, 1, NULL); } /* *---------------------------------------------------------------------- * * InitUidMap -- * * initializes the uid map * * Results: * None * * Side effects: * The uidMap structure is initialized * *---------------------------------------------------------------------- */ static RatUidMap* InitUidMap(MAILSTREAM *s) { unsigned long i; RatUidMap *uidMap; uidMap = (RatUidMap*)ckalloc(sizeof(RatUidMap)); uidMap->allocated = s->nmsgs+32; uidMap->map = (unsigned long*) ckalloc(uidMap->allocated*sizeof(unsigned long)); uidMap->size = s->nmsgs; for (i=0; inmsgs; i++) { uidMap->map[i] = mail_uid(s, i+1); } return uidMap; } /* *---------------------------------------------------------------------- * * FreeUidMap -- * * Frees the uid map * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ static void FreeUidMap(RatUidMap *uidMap) { ckfree(uidMap->map); ckfree(uidMap); } /* *---------------------------------------------------------------------- * * MsgNo -- * * Lookup a message uid in the map and return the msgno * * Results: * The msgno for the given uid or 0 if there is no such message * * Side effects: * None * *---------------------------------------------------------------------- */ static unsigned long MsgNo(RatUidMap *uidMap, unsigned long uid) { unsigned long i; for (i=0; isize; i++) { if (uidMap->map[i] == uid) { return i+1; } } return 0; } /* *---------------------------------------------------------------------- * * CheckDeletion -- * * Checks which messages are going to be deleted before an expunge * * Results: * None * * Side effects: * Appends things to the changes file * *---------------------------------------------------------------------- */ static void CheckDeletion(RatFolderInfoPtr infoPtr, Tcl_Interp *interp) { MAILSTREAM *stream = ((StdFolderInfo*)infoPtr->private)->stream; DisFolderInfo *disPtr = (DisFolderInfo*)infoPtr->private2; Tcl_HashEntry *entryPtr; FILE *fp = NULL; char buf[1024]; unsigned long uid; int i; for (i = 0; i < infoPtr->number; i++) { if (0 != (*disPtr->getFlagProc)(infoPtr, interp, i, RAT_DELETED)) { if (NULL == fp) { snprintf(buf, sizeof(buf), "%s/changes", disPtr->dir); fp = fopen(buf, "a"); } uid = GetMasterUID(stream, &disPtr->map, i); if (uid && NULL != fp) { fprintf(fp, "delete %ld\n", uid); } entryPtr = Tcl_FindHashEntry(&disPtr->map, (char*) mail_uid(((StdFolderInfo*)infoPtr->private)->stream, i+1)); if (entryPtr) { disPtr->mapChanged = 1; ckfree(Tcl_GetHashValue(entryPtr)); Tcl_DeleteHashEntry(entryPtr); } } } if (NULL != fp) { fclose(fp); } } /* *---------------------------------------------------------------------- * * DisUploadMsg -- * * Uploads the given message to the master folder * * Results: * The uid of the newly uploaded messages * * Side effects: * Modifies master folder * * *---------------------------------------------------------------------- */ static unsigned long DisUploadMsg(MAILSTREAM *masterStream, MAILSTREAM *localStream, const char *subject, const char *in_reply_to, const char *message_id, char *envdate, unsigned long local_uid, Tcl_DString *message, char *date, char *flags, FILE *mapFP, Tcl_HashTable *mapPtr) { Tcl_HashEntry *entryPtr; SEARCHPGM *pgm; STRING string; unsigned long *lPtr, uid; int unused; uid = masterStream->uid_last; INIT(&string, mail_string, Tcl_DStringValue(message), Tcl_DStringLength(message)); RatPurgeFlags(flags, 0); mail_append_full(masterStream, masterStream->mailbox, flags, date,&string); pgm = mail_newsearchpgm(); if (subject && *subject) { pgm->subject = mail_newstringlist(); pgm->subject->text.data = (unsigned char*)cpystr(subject); pgm->subject->text.size = strlen(subject); } if (in_reply_to && *in_reply_to) { pgm->in_reply_to = mail_newstringlist(); pgm->in_reply_to->text.data = (unsigned char*)cpystr(in_reply_to); pgm->in_reply_to->text.size = strlen(in_reply_to); } if (message_id && *message_id) { pgm->message_id = mail_newstringlist(); pgm->message_id->text.data = (unsigned char*)cpystr(message_id); pgm->message_id->text.size = strlen(message_id); } pgm->uid = mail_newsearchset(); pgm->uid->first = uid+1; pgm->uid->last = 0; if (envdate && *envdate) { pgm->header = mail_newsearchheader("date", envdate); } searchResultNum = 0; mail_search_full(masterStream, NULL, pgm, SE_FREE|SE_UID); if (searchResultNum == 1) { fprintf(mapFP, "%ld %ld\n", searchResultPtr[0], local_uid); if (mapPtr) { lPtr = (unsigned long*)ckalloc(sizeof(*lPtr)); *lPtr = searchResultPtr[0]; entryPtr = Tcl_CreateHashEntry(mapPtr, (char*)local_uid, &unused); Tcl_SetHashValue(entryPtr, lPtr); } masterStream->uid_last = searchResultPtr[0]; return searchResultPtr[0]; } else { return 0; } } /* *---------------------------------------------------------------------- * * Handle* -- * * Handle events from mailbox * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void Dis_HandleExists(void *state, unsigned long nmsg) { DisFolderInfo *disPtr = (DisFolderInfo *) state; disPtr->exists = nmsg; } static void Dis_HandleExpunged(void *state, unsigned long index) { DisFolderInfo *disPtr = (DisFolderInfo *) state; disPtr->expunged++; } /* *---------------------------------------------------------------------- * * WriteMappings -- * * Writes the mappings-file * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void WriteMappings(DisFolderInfo *disPtr) { Tcl_HashEntry *entryPtr; Tcl_HashSearch search; unsigned long *lPtr; char buf[1024]; FILE *fp; if (!disPtr->mapChanged) { return; } snprintf(buf, sizeof(buf), "%s/mappings", disPtr->dir); fp = fopen(buf, "w"); for (entryPtr = Tcl_FirstHashEntry(&disPtr->map, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { lPtr = (unsigned long*)Tcl_GetHashValue(entryPtr); fprintf(fp, "%ld %ld\n", *lPtr, (unsigned long)Tcl_GetHashKey(&disPtr->map, entryPtr)); } fclose(fp); disPtr->mapChanged = 0; } /* *---------------------------------------------------------------------- * * WriteState -- * * Writes the state-file * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void WriteState(DisFolderInfo *disPtr) { char buf[1024]; FILE *fp; snprintf(buf, sizeof(buf), "%s/state", disPtr->dir); fp = fopen(buf, "w"); fprintf(fp, "%ld\n%ld\n", disPtr->master->uid_validity, disPtr->lastUid); fclose(fp); } /* *---------------------------------------------------------------------- * * RatDisOnOffTrans -- * * Handle transitions between online and offline state * * Results: * None * * Side effects: * Opens/closes folders * * *---------------------------------------------------------------------- */ int RatDisOnOffTrans(Tcl_Interp *interp, int newState) { Tcl_HashEntry *entryPtr; Tcl_HashSearch search; RatFolderInfo *infoPtr; DisFolderInfo *disPtr; char buf[1024]; struct stat sbuf; int count = 0, allfail = 1; for (entryPtr = Tcl_FirstHashEntry(&openDisFolders, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search), count++) { infoPtr = Tcl_GetHashValue(entryPtr); disPtr = (DisFolderInfo*)infoPtr->private2; if (newState && !disPtr->master) { /* Go online */ snprintf(buf, sizeof(buf), "%s/master", disPtr->dir); stat(buf, &sbuf); if (TCL_OK == Dis_SyncFolder(interp,disPtr->dir, sbuf.st_size, 1, &disPtr->master)) { allfail = 0; } } else if (!newState && disPtr->master) { /* Go offline */ Std_StreamClose(interp, disPtr->master); disPtr->master = NULL; allfail = 0; } } if (!newState) { /* * Force closing of all pending connections */ Std_StreamCloseAllCached(interp); } if (allfail && 0 != count) { return TCL_ERROR; } else { return TCL_OK; } } /* *---------------------------------------------------------------------- * * RatDisManageFolder -- * * Create or delete folders * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ void RatDisManageFolder(Tcl_Interp *interp, RatManagementAction op, Tcl_Obj *fPtr) { struct dirent *dirent; const char *dirname; char buf[1024]; DIR *dir; int i; if (NULL == (dirname = PrepareDir(interp, fPtr))) { return; } if (RAT_MGMT_DELETE == op) { if (NULL == (dir = opendir(dirname))) { return; } while (NULL != (dirent = readdir(dir))) { if (!strcmp(".", dirent->d_name) || !strcmp("..", dirent->d_name)){ continue; } snprintf(buf, sizeof(buf), "%s/%s", dirname, dirent->d_name); unlink(buf); } closedir(dir); i = rmdir(dirname); } } tkrat_2.2cvs20100105-dfsg.orig/lib/ratDummy.c000066400000000000000000000062341137544547100205250ustar00rootroot00000000000000/* * ratDummy.c -- * * Provides dummy routines so we can build the index with tclsh * * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include #ifndef CONST84 # define CONST84 #endif #if (TCL_MAJOR_VERSION >= 8) && (TCL_MINOR_VERSION >= 5) # define CONST85 CONST #else # define CONST85 #endif void Tk_UnmapWindow(Tk_Window tkwin) {} int Tk_ConfigureValue(Tcl_Interp * interp, Tk_Window tkwin, Tk_ConfigSpec * specs, char * widgRec, CONST84 char * argvName, int flags) { return 0; } void Tk_CreateEventHandler(Tk_Window token, unsigned long mask, Tk_EventProc * proc, ClientData clientData) {} Tk_Window Tk_CreateWindow(Tcl_Interp * interp, Tk_Window parent, CONST84 char * name, CONST84 char * screenName) { return NULL; } Status XQueryTree(Display *display, Window w, Window *root_return, Window *parent_return, Window **children_return, unsigned int *nchildren_return) { return 0; } Tk_Window Tk_MainWindow(Tcl_Interp * interp) { return NULL; } int Tk_RestackWindow(Tk_Window tkwin, int aboveBelow, Tk_Window other) { return 0; } int Tk_ConfigureInfo (Tcl_Interp * interp, Tk_Window tkwin, Tk_ConfigSpec * specs, char * widgRec, CONST84 char * argvName, int flags) { return 0; } int Tk_ConfigureWidget (Tcl_Interp * interp, Tk_Window tkwin, Tk_ConfigSpec *specs, int argc, CONST84 char ** argv, char * widgRec, int flags) { return 0; } void Tk_DefineCursor (Tk_Window window, Tk_Cursor cursor) {} void Tk_DeleteEventHandler (Tk_Window token, unsigned long mask, Tk_EventProc * proc, ClientData clientData) {} void Tk_DestroyWindow (Tk_Window tkwin) {} void Tk_FreeOptions (Tk_ConfigSpec * specs, char * widgRec, Display * display, int needFlags) {} void Tk_MakeWindowExist (Tk_Window tkwin) {} void Tk_ManageGeometry (Tk_Window tkwin, CONST85 Tk_GeomMgr * mgrPtr, ClientData clientData) {} void Tk_MapWindow (Tk_Window tkwin) {} void Tk_MoveResizeWindow (Tk_Window tkwin, int x, int y, int width, int height) {} Tk_Window Tk_NameToWindow (Tcl_Interp * interp, CONST84 char * pathName, Tk_Window tkwin) { return NULL; } void Tk_SetClass (Tk_Window tkwin, CONST84 char * className) {} void Tk_UndefineCursor (Tk_Window window) {} void Tk_HandleEvent (XEvent * eventPtr) {} int XFree(void *data) { return 0; } int XMapWindow(Display *display, Window w) { return 0; } int XConfigureWindow(Display *display, Window w, unsigned int value_mask, XWindowChanges *values) { return 0; } Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes) { return (Window)NULL; } Tk_PhotoHandle Tk_FindPhoto (Tcl_Interp *interp, CONST84 char *imageName) { return NULL; } int Tk_PhotoGetImage(Tk_PhotoHandle hd, Tk_PhotoImageBlock *blockPtr) { return 0; } int Ratdummy_Init(Tcl_Interp *interp) { return TCL_OK; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratExp.c000066400000000000000000000354401137544547100201670ustar00rootroot00000000000000/* * ratExp.c -- * * This file handles the expressions. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of my legal notices is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" /* * Private types */ typedef enum {TT_Field, TT_Operator, TT_Boolean,TT_Grouping,TT_Spec} TokenType; typedef enum {T_To, T_From, T_Subject, T_Sender, T_Cc, T_Reply_To, T_Size, T_Has, T_Is, T_Gt, T_Lt, T_And, T_Or, T_Not, T_Lparen, T_Rparen} Token; typedef struct { Token token; TokenType type; char *string; RatFolderInfoType info; } TokenList; typedef struct Expression { int negate; Token op; union { struct Expression *expPtr; RatFolderInfoType info; } arg1; union { struct Expression *expPtr; char *string; } arg2; } Expression; typedef struct ExpList { int id; Expression *expPtr; struct ExpList *next; } ExpList; /* * Static data */ static int expCounter = 0; static ExpList *expListPtr = NULL; static TokenList tokenList[] = { {T_To, TT_Field, "to", RAT_FOLDER_TO}, {T_From, TT_Field, "from", RAT_FOLDER_FROM}, {T_Subject, TT_Field, "subject", RAT_FOLDER_SUBJECT}, {T_Sender, TT_Field, "sender", RAT_FOLDER_SENDER}, {T_Cc, TT_Field, "cc", RAT_FOLDER_CC}, {T_Reply_To,TT_Field, "reply-to", RAT_FOLDER_REPLY_TO}, {T_Size, TT_Field, "size", RAT_FOLDER_SIZE}, {T_Has, TT_Operator, "has", 0}, {T_Is, TT_Operator, "is", 0}, {T_Gt, TT_Operator, ">", 0}, {T_Lt, TT_Operator, "<", 0}, {T_And, TT_Boolean, "and", 0}, {T_Or, TT_Boolean, "or", 0}, {T_Not, TT_Spec, "not", 0}, {T_Lparen, TT_Grouping, "(", 0}, {T_Rparen, TT_Grouping, ")", 0}, {0, 0, NULL, 0} }; /* * Local functions */ static TokenList *GetToken(char **sPtr); static char *GetString(char **sPtr); static void FreeExp(Expression *expPtr); static Expression *ParseExpression(char **sPtr, char **errPtr, int inParen); static void GetExpression(Tcl_Interp *interp, Tcl_Obj *ePtr, Expression *expPtr); static int RatExpMatchDo(Tcl_Interp *interp, Expression *expPtr, RatInfoProc *infoProc, ClientData clientData, int index); /* *---------------------------------------------------------------------- * * GetToken -- * * Extract the next token from the string. * to the interpreter. * * Results: * A pointer to a TokenList entity or nULL if no valid token is * found * * Side effects: * *sPtr will most probably be modified. * * *---------------------------------------------------------------------- */ static TokenList* GetToken(char **sPtr) { char *cPtr = *sPtr; int i; while (isspace((unsigned char)*cPtr)) { cPtr++; } *sPtr = cPtr; if (!*cPtr) { return NULL; } for (i=0; tokenList[i].string; i++) { if (!strncasecmp(cPtr, tokenList[i].string, strlen(tokenList[i].string))) { cPtr += strlen(tokenList[i].string); *sPtr = cPtr; return &tokenList[i]; } } return NULL; } /* *---------------------------------------------------------------------- * * GetString -- * * Extract the next string. * * Results: * A pointer to a copy of the found string. It is the callers * resposibility to eventually free this area. * * Side effects: * *sPtr will most probably be modified. * * *---------------------------------------------------------------------- */ static char* GetString(char **sPtr) { char quote = '\0', *cPtr = *sPtr, *result; int i; while (isspace((unsigned char)*cPtr)) { cPtr++; } if ('\'' == *cPtr || '"' == *cPtr || '{' == *cPtr) { quote = *cPtr++; } *sPtr = cPtr; if ('{' == quote) { quote = '}'; } result = (char*)ckalloc(strlen(cPtr)+1); i=0; while (*cPtr && !(quote == *cPtr || (!quote && isspace((unsigned char)*cPtr)))) { if ('\\' == *cPtr && cPtr[1]) { cPtr++; } if (isupper((unsigned char)*cPtr)) { result[i] = tolower((unsigned char)*cPtr); } else { result[i] = *cPtr; } i++; cPtr++; } result[i] = '\0'; if (quote && quote == *cPtr) { cPtr++; } *sPtr = cPtr; return result; } /* *---------------------------------------------------------------------- * * FreeExp -- * * Free an expression * * Results: * None * * Side effects: * The given expression will be free'ed. * * *---------------------------------------------------------------------- */ static void FreeExp(Expression *expPtr) { if (!expPtr) { return; } if (expPtr->op == T_And || expPtr->op == T_Or) { FreeExp(expPtr->arg1.expPtr); FreeExp(expPtr->arg2.expPtr); } else { ckfree(expPtr->arg2.string); } ckfree(expPtr); } /* *---------------------------------------------------------------------- * * ParseExpression -- * * Parse a given search expression * * Results: * A pointer to an expression, or NULL if an error was encountered. * * Side effects: * *sPtr will most probably be modified. * * *---------------------------------------------------------------------- */ static Expression* ParseExpression(char **sPtr, char **errPtr, int inParen) { TokenList *tokPtr; Expression *expPtr = NULL, *exp2Ptr; char *newString; int negated, l; while (**sPtr) { negated = 0; while (tokPtr = GetToken(sPtr), (tokPtr && tokPtr->token == T_Not)) { negated = negated ? 0 : 1; } if (!tokPtr) { if (**sPtr) { *errPtr = "Unparseable text"; } return expPtr; } switch (tokPtr->type) { case TT_Field: if (expPtr && expPtr->op != T_And && expPtr->op != T_Or) { *errPtr = "Expected boolean or ')'"; return expPtr; } exp2Ptr = (Expression*)ckalloc(sizeof(Expression)); exp2Ptr->negate = negated; exp2Ptr->arg1.info = tokPtr->info; tokPtr = GetToken(sPtr); if (tokPtr && tokPtr->token == T_Not) { exp2Ptr->negate = 1; tokPtr = GetToken(sPtr); } if (!tokPtr || tokPtr->type != TT_Operator) { *errPtr = "Expected operator"; ckfree(exp2Ptr); return expPtr; } exp2Ptr->op = tokPtr->token; exp2Ptr->arg2.string = GetString(sPtr); if (!exp2Ptr->arg2.string) { *errPtr = "String expected"; ckfree(exp2Ptr); return expPtr; } else if (T_Is == exp2Ptr->op) { exp2Ptr->op = T_Has; l = strlen(exp2Ptr->arg2.string)+3; newString = (char*)ckalloc(l); strlcpy(newString, "^", l); strlcat(newString, exp2Ptr->arg2.string, l); strlcat(newString, "$", l); ckfree(exp2Ptr->arg2.string); exp2Ptr->arg2.string = newString; } if (expPtr) { expPtr->arg2.expPtr = exp2Ptr; } else { expPtr = exp2Ptr; } break; case TT_Boolean: if (!expPtr) { *errPtr = "Must have a valid expression before a boolean"; return expPtr; } exp2Ptr = (Expression*)ckalloc(sizeof(Expression)); exp2Ptr->negate = negated; exp2Ptr->op = tokPtr->token; exp2Ptr->arg1.expPtr = expPtr; exp2Ptr->arg2.expPtr = NULL; expPtr = exp2Ptr; break; case TT_Grouping: if (T_Lparen == tokPtr->token) { if (expPtr && expPtr->op != T_And && expPtr->op != T_Or) { *errPtr = "Expected boolean, field or ')'"; return expPtr; } exp2Ptr = ParseExpression(sPtr, errPtr, 1); if (expPtr) { expPtr->arg2.expPtr = exp2Ptr; } else { expPtr = exp2Ptr; } if (*errPtr) { return expPtr; } } else { if (!inParen) { *errPtr = "Unexpected ')'."; return expPtr; } return expPtr; } break; case TT_Operator: /* fallthrough */ case TT_Spec: *errPtr = "Expected field or ("; return expPtr; } } if (!expPtr) { *errPtr = "Empty expression"; return NULL; } return expPtr; } /* *---------------------------------------------------------------------- * * RatParseExp -- * * See ../doc/interface * * Results: * A standard tcl result. * * Side effects: * Will probably add an expression to the local list. * * *---------------------------------------------------------------------- */ int RatParseExpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { char *error = NULL, *cPtr, *exp; Expression *expPtr; ExpList *elemPtr; if (objc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " expression\"", (char *) NULL); return TCL_ERROR; } exp = cPtr = Tcl_GetString(objv[1]); expPtr = ParseExpression(&cPtr, &error, 0); if (error) { char buf[32]; FreeExp(expPtr); sprintf(buf, "%d", cPtr-exp); Tcl_AppendElement(interp, buf); Tcl_AppendElement(interp, error); return TCL_ERROR; } elemPtr = (ExpList*)ckalloc(sizeof(ExpList)); elemPtr->id = expCounter; elemPtr->expPtr = expPtr; elemPtr->next = expListPtr; expListPtr = elemPtr; Tcl_SetObjResult(interp, Tcl_NewIntObj(expCounter++)); return TCL_OK; } /* *---------------------------------------------------------------------- * * GetExp -- * * Print one expression into the given DString. * * Results: * The given DString will be modified. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static void GetExpression(Tcl_Interp *interp, Tcl_Obj *ePtr, Expression *expPtr) { int opIndex, fIndex; Tcl_Obj *oPtr; for (opIndex=0; tokenList[opIndex].token != expPtr->op; opIndex++); if (expPtr->negate) { Tcl_ListObjAppendElement(interp, ePtr, Tcl_NewStringObj("not", 3)); } if (tokenList[opIndex].type == TT_Boolean) { oPtr = Tcl_NewObj(); GetExpression(interp, oPtr, expPtr->arg1.expPtr); Tcl_ListObjAppendElement(interp, ePtr, oPtr); Tcl_ListObjAppendElement(interp, ePtr, Tcl_NewStringObj(tokenList[opIndex].string, -1)); oPtr = Tcl_NewObj(); GetExpression(interp, oPtr, expPtr->arg2.expPtr); Tcl_ListObjAppendElement(interp, ePtr, oPtr); } else { for (fIndex=0; tokenList[fIndex].info != expPtr->arg1.info; fIndex++); Tcl_ListObjAppendElement(interp, ePtr, Tcl_NewStringObj(tokenList[fIndex].string, -1)); Tcl_ListObjAppendElement(interp, ePtr, Tcl_NewStringObj(tokenList[opIndex].string, -1)); Tcl_ListObjAppendElement(interp, ePtr, Tcl_NewStringObj(expPtr->arg2.string, -1)); } } /* *---------------------------------------------------------------------- * * RatGetExp -- * * See ../doc/interface * * Results: * The identified expression is returned as a string in the result area. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatGetExpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { ExpList *elemPtr; Tcl_Obj *rPtr; int id; if (objc < 2 || TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &id)) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " id\"", (char *) NULL); return TCL_ERROR; } for (elemPtr = expListPtr; elemPtr; elemPtr = elemPtr->next) { if (elemPtr->id == id) { rPtr = Tcl_NewObj(); GetExpression(interp, rPtr, elemPtr->expPtr); Tcl_SetObjResult(interp, rPtr); return TCL_OK; } } Tcl_AppendResult(interp, "No expression with id \"", Tcl_GetString(objv[1]), "\"", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatFreeExp -- * * See ../doc/interface * * Results: * A standard tcl result. * * Side effects: * Will probably remove an expression from the local list. * * *---------------------------------------------------------------------- */ int RatFreeExpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { ExpList **elemPtrPtr, *elemPtr; int id; if (objc < 2 || TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &id)) { Tcl_AppendResult(interp, "Illegal usage: should be \"", Tcl_GetString(objv[0]), " id\"", (char *) NULL); return TCL_ERROR; } for (elemPtrPtr = &expListPtr; *elemPtrPtr; elemPtrPtr=&(*elemPtrPtr)->next){ if ((*elemPtrPtr)->id == id) { elemPtr = *elemPtrPtr; FreeExp(elemPtr->expPtr); *elemPtrPtr = elemPtr->next; ckfree(elemPtr); return TCL_OK; } } return TCL_OK; } /* *---------------------------------------------------------------------- * * RatExpMatch -- * * Checks if a given expression matches to given message. * * Results: * True if it did match. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatExpMatch(Tcl_Interp *interp, int expId, RatInfoProc *infoProc, ClientData clientData, int index) { ExpList *elemPtr; for (elemPtr = expListPtr; elemPtr && elemPtr->id != expId; elemPtr = elemPtr->next); if (!elemPtr) { return 0; } return RatExpMatchDo(interp, elemPtr->expPtr, infoProc, clientData, index); } /* *---------------------------------------------------------------------- * * RatExpMatchDo -- * * Checks if a given expression matches to given message. * This routine actually does the checking and may call itself * recursively. * * Results: * True if it did match. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatExpMatchDo(Tcl_Interp *interp, Expression *expPtr, RatInfoProc *infoProc, ClientData clientData, int index) { char *sLower, *cPtr; int opIndex, val = 0; static Tcl_Obj *sPtr = NULL; Tcl_Obj *oPtr; for (opIndex=0; tokenList[opIndex].token != expPtr->op; opIndex++); if (TT_Boolean == tokenList[opIndex].type) { val = RatExpMatchDo(interp, expPtr->arg1.expPtr, infoProc, clientData, index); if (!((T_Or == tokenList[opIndex].token && val) || (T_And == tokenList[opIndex].token && !val))) { val = RatExpMatchDo(interp, expPtr->arg2.expPtr, infoProc, clientData, index); } } else { oPtr = (*infoProc)(interp, clientData, expPtr->arg1.info, index); if (!oPtr) { if (!sPtr) { sPtr = Tcl_NewObj(); Tcl_IncrRefCount(sPtr); } oPtr = sPtr; } if (T_Has == tokenList[opIndex].token || T_Is == tokenList[opIndex].token) { sLower = cpystr(Tcl_GetString(oPtr)); for (cPtr = sLower; *cPtr; cPtr++) { if (isupper((unsigned char)*cPtr)) { *cPtr = tolower((unsigned char)*cPtr); } } val = Tcl_RegExpMatch(interp, sLower, expPtr->arg2.string); ckfree(sLower); } else if (expPtr->arg1.info == RAT_FOLDER_SIZE) { Tcl_GetIntFromObj(interp, oPtr, &val); if (T_Gt == tokenList[opIndex].token) { val = val > atoi(expPtr->arg2.string); } else { val = val < atoi(expPtr->arg2.string); } } } if (expPtr->negate) { val = val ? 0 : 1; } return val; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratFolder.c000066400000000000000000002003171137544547100206430ustar00rootroot00000000000000/* * ratFolder.c -- * * This file contains basic support code for the folder commands. Each * folder type is created using an unique command. This command returns * a folder handler, which when invoked calls the RatFolderCmd() * procedure with a pointer to a RatFolderInfo structure as clientData. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notices is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" /* * This structure is used to hold the data while sorting */ typedef struct SortData { char *msgid; char *ref; char *subject; char *sender; time_t date; long size; struct SortData *nextPtr; int prev, next, child, parent; Tcl_Obj *tPtr; } SortData; /* * Global list of folders */ RatFolderInfo *ratFolderList = NULL; /* * The number of folders opened. This is used when making * the folder entities. */ static int numFolders = 0; /* * Sort order names */ struct { SortOrder order; int reverse; char *name; } sortNames[] = { {SORT_THREADED, 0, "threaded"}, {SORT_SUBJDATE, 0, "subject"}, {SORT_SENDERDATE, 0, "sender"}, {SORT_SUBJECT, 0, "subjectonly"}, {SORT_SENDER, 0, "senderonly"}, {SORT_DATE, 0, "date"}, {SORT_NONE, 0, "folder"}, {SORT_NONE, 1, "reverseFolder"}, {SORT_DATE, 1, "reverseDate"}, {SORT_SIZE, 0, "size"}, {SORT_SIZE, 1, "reverseSize"}, {0, 0, NULL} }; /* * Flag names * The entries in this list must be synchronized with the enum type RatFlag * defined in RatFolder.h */ flag_name_t flag_name[] = { { "\\Seen", "seen", 'R' }, { "\\Deleted", "deleted", 'D' }, { "\\Flagged", "flagged", 'F' }, { "\\Answered", "answered", 'A' }, { "\\Draft", "draft", 'T' }, { "\\Recent", "recent", '\0' }, { NULL, NULL, '\0'} }; /* * Global variable used to controll the sorting functions */ static SortData *baseSortDataPtr; /* * Global id to handle folder list updates */ static int folderChangeId = 0; /* * Possible flag values for remote hosts */ #ifdef HAVE_OPENSSL static char *cClientFlags[] = { "/notls", "/ssl", "/novalidate-cert", "/secure", NULL }; #endif /* HAVE_OPENSSL */ /* * Fluff to remove when canonalizing headers (case insensitive) */ static char *subjectFluff[] = { "re: ", "re ", "fwd: ", "fwd ", "ans: ", "ans ", "sv: ", "sv ", NULL }; static Tcl_ObjCmdProc RatOpenFolderCmd; static Tcl_ObjCmdProc RatGetOpenHandlerCmd; static Tcl_ObjCmdProc RatFolderCmd; static void RatFolderSort(Tcl_Interp *interp, RatFolderInfo *infoPtr); static int RatFolderSortCompareDate(const void *arg1, const void *arg2); static int RatFolderSortCompareSize(const void *arg1, const void *arg2); static int RatFolderSortCompareSubject(const void *arg1, const void *arg2); static int RatFolderSortCompareSender(const void *arg1, const void *arg2); static int IsChild(SortData *dataPtr, int child, int parent); static int RatFolderSortLinearize(int *p, int n, SortData *dataPtr, int first, int depth); static Tcl_ObjCmdProc RatCreateFolderCmd; static RatFlag RatFlagNameToInt(const char *name); static char *RatGetIdentDef(Tcl_Interp *interp, Tcl_Obj *defPtr); /* *---------------------------------------------------------------------- * * RatFolderInit -- * * Initializes the folder commands. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The folder creation commands are created in interp. And the * Std folder is initialized. * * *---------------------------------------------------------------------- */ int RatFolderInit(Tcl_Interp *interp) { RatInitMessages(); if (TCL_OK != RatStdFolderInit(interp)) { return TCL_ERROR; } if (TCL_OK != RatDbFolderInit(interp)) { return TCL_ERROR; } if (TCL_OK != RatDisFolderInit(interp)) { return TCL_ERROR; } Tcl_CreateObjCommand(interp, "RatOpenFolder", RatOpenFolderCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatGetOpenHandler", RatGetOpenHandlerCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatParseExp", RatParseExpCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatGetExp", RatGetExpCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatFreeExp", RatFreeExpCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatCreateFolder", RatCreateFolderCmd, (void*)RAT_MGMT_CREATE, NULL); Tcl_CreateObjCommand(interp, "RatCheckFolder", RatCreateFolderCmd, (void*)RAT_MGMT_CHECK, NULL); Tcl_CreateObjCommand(interp, "RatDeleteFolder", RatCreateFolderCmd, (void*)RAT_MGMT_DELETE, NULL); Tcl_CreateObjCommand(interp, "RatSubscribeFolder", RatCreateFolderCmd, (void*)RAT_MGMT_SUBSCRIBE, NULL); Tcl_CreateObjCommand(interp, "RatUnSubscribeFolder", RatCreateFolderCmd, (void*)RAT_MGMT_UNSUBSCRIBE, NULL); RatFolderUpdateTime((ClientData)interp); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatGetOpenFolder -- * * Search the list of open folders for a folder matching the given * definition. * * Results: * If a match is found then that folders reference count is incremented * and the infoPtr is returned. Otherwise NULL is returned. * * Side effects: * None. * * *---------------------------------------------------------------------- */ RatFolderInfo* RatGetOpenFolder(Tcl_Interp *interp, Tcl_Obj *defPtr, int append_only) { RatFolderInfo *infoPtr; char *def = RatGetIdentDef(interp, defPtr); for (infoPtr = ratFolderList; infoPtr; infoPtr = infoPtr->nextPtr) { if (!strcmp(infoPtr->ident_def, def) && (!infoPtr->append_only || append_only)) { break; } } if (infoPtr) { infoPtr->refCount++; } return infoPtr; } /* *---------------------------------------------------------------------- * * RatOpenFolderCmd -- * * See the INTERFACE specification * * Results: * The return value is normally TCL_OK and a foilder handle is left * in the result area; if something goes wrong TCL_ERROR is returned * and an error message will be left in the result area. * * Side effects: * The folder creation commands are created in interp. * * *---------------------------------------------------------------------- */ static int RatOpenFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { RatFolderInfo *infoPtr; if ((objc != 2 && objc != 3) || (objc == 3 && strcmp("append", Tcl_GetString(objv[1])))) { Tcl_AppendResult(interp, "wrong # args: should be \"?append?", Tcl_GetString(objv[0]), " folderdef\"", (char *) NULL); return TCL_ERROR; } if (2 == objc) { infoPtr = RatOpenFolder(interp, 0, objv[1]); } else { infoPtr = RatOpenFolder(interp, 1, objv[2]); } if (NULL == infoPtr) { Tcl_AppendResult(interp, ": Failed to create folder", NULL); return TCL_ERROR; } else { Tcl_SetResult(interp, infoPtr->cmdName, TCL_VOLATILE); return TCL_OK; } } /* *---------------------------------------------------------------------- * * RatOpenFolder -- * * See the INTERFACE specification * * Results: * The return value is normally TCL_OK and a foilder handle is left * in the result area; if something goes wrong TCL_ERROR is returned * and an error message will be left in the result area. * * Side effects: * The folder creation commands are created in interp. * * *---------------------------------------------------------------------- */ RatFolderInfo* RatOpenFolder(Tcl_Interp *interp, int append_only, Tcl_Obj *def) { RatFolderInfo *infoPtr; int i, fobjc, lobjc; CONST84 char *sortName = NULL; Tcl_Obj **fobjv, **lobjv, *role = NULL; /* * Check open folders */ if ((infoPtr = RatGetOpenFolder(interp, def, append_only))) { return infoPtr; } Tcl_ListObjGetElements(interp, def, &fobjc, &fobjv); if (!strcmp(Tcl_GetString(fobjv[1]), "dbase")) { infoPtr = RatDbFolderCreate(interp, append_only, def); } else if (!strcmp(Tcl_GetString(fobjv[1]), "dis")) { infoPtr = RatDisFolderCreate(interp, append_only, def); } else { infoPtr = RatStdFolderCreate(interp, append_only, def); } if (NULL == infoPtr) { return NULL; } Tcl_ListObjGetElements(interp, fobjv[2], &lobjc, &lobjv); for (i=0; i < lobjc; i+=2) { if (!strcmp("sort", Tcl_GetString(lobjv[i]))) { sortName = Tcl_GetString(lobjv[i+1]); } if (!strcmp("role", Tcl_GetString(lobjv[i]))) { role = lobjv[i+1]; } } infoPtr->ident_def = cpystr(RatGetIdentDef(interp, def)); infoPtr->append_only = append_only; ckfree(infoPtr->name); infoPtr->name = cpystr(Tcl_GetString(fobjv[0])); infoPtr->refCount = 1; if (!sortName || !strcmp("default", sortName)) { sortName = Tcl_GetVar2(interp, "option","folder_sort",TCL_GLOBAL_ONLY); } for (i=0; sortNames[i].name && strcmp(sortNames[i].name, sortName); i++); if (sortNames[i].name) { infoPtr->sortOrder = sortNames[i].order; infoPtr->reverse = sortNames[i].reverse; } else { infoPtr->sortOrder = SORT_NONE; infoPtr->reverse = 0; } if (!role || !strcmp("default", Tcl_GetString(role))) { role = Tcl_NewObj(); } infoPtr->role = role; Tcl_IncrRefCount(infoPtr->role); infoPtr->sortOrderChanged = 0; infoPtr->cmdName = ckalloc(16); infoPtr->allocated = infoPtr->number; infoPtr->msgCmdPtr = (char **) ckalloc(infoPtr->allocated*sizeof(char*)); infoPtr->privatePtr = (ClientData**)ckalloc( infoPtr->allocated*sizeof(ClientData)); for (i=0; iallocated; i++) { infoPtr->msgCmdPtr[i] = (char *) NULL; infoPtr->privatePtr[i] = (ClientData*) NULL; } (*infoPtr->initProc)(infoPtr, interp, -1); infoPtr->presentationOrder = (int*)ckalloc(infoPtr->allocated*sizeof(int)); infoPtr->flagsChanged = 0; infoPtr->nextPtr = ratFolderList; if (infoPtr->finalProc) { (*infoPtr->finalProc)(infoPtr, interp); } ratFolderList = infoPtr; sprintf(infoPtr->cmdName, "RatFolder%d", numFolders++); Tcl_CreateObjCommand(interp, infoPtr->cmdName, RatFolderCmd, (ClientData) infoPtr, (Tcl_CmdDeleteProc *) NULL); if (!append_only) { RatFolderSort(interp, infoPtr); Tcl_SetVar2Ex(interp, "folderExists", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->number), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderRecent", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->recent), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderUnseen", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->unseen), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderChanged", infoPtr->cmdName, Tcl_NewIntObj(++folderChangeId), TCL_GLOBAL_ONLY); } return infoPtr; } /* *---------------------------------------------------------------------- * * RatGetOpenHandlerCmd -- * * See the INTERFACE specification * * Results: * The return value is normally TCL_OK. If there is an open folder * for the given definition then the handler to that folder is * stored in the result area. Otherwise an empty string is resturned. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatGetOpenHandlerCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { RatFolderInfo *infoPtr; if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " folderdef\"", (char *) NULL); return TCL_ERROR; } if ((infoPtr = RatGetOpenFolder(interp, objv[1], 0))) { Tcl_SetObjResult(interp, Tcl_NewStringObj(infoPtr->cmdName, -1)); } else { Tcl_ResetResult(interp); } return TCL_OK; } /* *---------------------------------------------------------------------- * * RatFolderCmd -- * * Main folder entity procedure. This procedure implements the * folder commands mentioned in ../doc/interface. In order to make * this a tad easier it uses the procedures defined in the * RatFolderInfo structure :-) * * Results: * Depends on the input :-) * * Side effects: * The specified folder may be modified. * * *---------------------------------------------------------------------- */ int RatFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { RatFolderInfo *infoPtr = (RatFolderInfo*) clientData; Tcl_Obj *oPtr; int r; if (objc < 2) goto usage; if (!strcmp(Tcl_GetString(objv[1]), "update")) { RatUpdateType mode; if (objc != 3) goto usage; if (!strcmp(Tcl_GetString(objv[2]), "update")) { mode = RAT_UPDATE; } else if (!strcmp("checkpoint", Tcl_GetString(objv[2]))) { if (!infoPtr->flagsChanged) { Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); return TCL_OK; } mode = RAT_CHECKPOINT; infoPtr->flagsChanged = 0; } else if (!strcmp(Tcl_GetString(objv[2]), "sync")) { mode = RAT_SYNC; } else { goto usage; } return RatUpdateFolder(interp, infoPtr, mode); } else if (!strcmp(Tcl_GetString(objv[1]), "close")) { int force = 0; if (objc != 2 && (objc != 3 || TCL_OK != Tcl_GetBooleanFromObj(interp,objv[2],&force))) { goto usage; } r = RatFolderClose(interp, infoPtr, force); return r; } else if (!strcmp(Tcl_GetString(objv[1]), "setName")) { if (objc != 3) goto usage; ckfree(infoPtr->name); infoPtr->name = (char *) ckalloc(strlen(Tcl_GetString(objv[2]))+1); strcpy(infoPtr->name, Tcl_GetString(objv[2])); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "info")) { int infoArgc; CONST84 char *infoArgv[3]; char numberBuf[16], sizeBuf[16]; char *list; if (objc != 2) goto usage; sprintf(numberBuf, "%d", infoPtr->number); sprintf(sizeBuf, "%d", infoPtr->size); infoArgv[0] = infoPtr->name; infoArgv[1] = numberBuf; infoArgv[2] = sizeBuf; infoArgc = 3; list = Tcl_Merge(infoArgc, infoArgv); Tcl_SetResult(interp, list, TCL_DYNAMIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "list")) { ListExpression *exprPtr; Tcl_Obj *oPtr, *rPtr; int i; if (objc != 3) goto usage; if (NULL == (exprPtr = RatParseList(Tcl_GetString(objv[2]), NULL))) { Tcl_SetResult(interp, "Illegal list format", TCL_STATIC); goto error; } rPtr = Tcl_NewObj(); for (i=0; i < infoPtr->number; i++) { oPtr = RatDoList(interp, exprPtr, infoPtr->infoProc, (ClientData)infoPtr, infoPtr->presentationOrder[i]); Tcl_ListObjAppendElement(interp, rPtr, oPtr); } RatFreeListExpression(exprPtr); Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "get")) { int index; char *name; if (objc != 3 || TCL_OK != Tcl_GetIntFromObj(interp, objv[2],&index)) goto usage; if (index < 0 || index >= infoPtr->number) { Tcl_SetResult(interp, "Index is out of bounds", TCL_STATIC); goto error; } name = RatFolderCmdGet(interp, infoPtr, index); Tcl_SetResult(interp, name, TCL_VOLATILE); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "setFlag")) { int *ilist, value, i, ic; RatFlag flag; Tcl_Obj **iv; if (objc != 5 || TCL_OK != Tcl_GetBooleanFromObj(interp, objv[4], &value)) { goto usage; } Tcl_ListObjGetElements(interp, objv[2], &ic, &iv); ilist = (int*)ckalloc(ic*sizeof(int)); for (i=0; i= infoPtr->number) { Tcl_SetResult(interp, "Bad index", TCL_STATIC); ckfree(ilist); goto error; } } flag = RatFlagNameToInt(Tcl_GetString(objv[3])); RatFolderCmdSetFlag(interp, infoPtr, ilist, ic, flag, value); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "getFlag")) { int index; RatFlag flag; if (objc != 4 || TCL_OK != Tcl_GetIntFromObj(interp, objv[2],&index)) goto usage; if (index < 0 || index >= infoPtr->number) { Tcl_SetResult(interp, "Index is out of bounds", TCL_STATIC); goto error; } flag = RatFlagNameToInt(Tcl_GetString(objv[3])); Tcl_SetObjResult(interp, Tcl_NewIntObj( (*infoPtr->getFlagProc)(infoPtr, interp, infoPtr->presentationOrder[index],flag))); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "flagged")) { RatFlag flag; int i, v; if (objc != 4 || TCL_OK != Tcl_GetIntFromObj(interp, objv[3], &v)) goto usage; flag = RatFlagNameToInt(Tcl_GetString(objv[2])); oPtr = Tcl_NewObj(); Tcl_ResetResult(interp); for (i=0; inumber; i++) { if ((*infoPtr->getFlagProc)(infoPtr, interp, infoPtr->presentationOrder[i], flag) == v) { Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(i)); } } Tcl_SetObjResult(interp, oPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "insert")) { Tcl_CmdInfo cmdInfo; char **msgv; int i; if (objc < 3) goto usage; for(i=2; itype, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "find")) { int msgNo, i; if (objc != 3) goto usage; Tcl_SetObjResult(interp, Tcl_NewIntObj(-1)); for (msgNo=0; msgNo < infoPtr->number; msgNo++) { if (infoPtr->msgCmdPtr[msgNo] && !strcmp(infoPtr->msgCmdPtr[msgNo], Tcl_GetString(objv[2]))) { for (i=0; msgNo != infoPtr->presentationOrder[i]; i++); Tcl_SetObjResult(interp, Tcl_NewIntObj(i)); break; } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "match")) { int i, expId; if (objc != 3 || TCL_OK != Tcl_GetIntFromObj(interp, objv[2],&expId)) goto usage; oPtr = Tcl_NewObj(); for (i=0; inumber; i++) { if (RatExpMatch(interp, expId, infoPtr->infoProc, (ClientData)infoPtr, infoPtr->presentationOrder[i])) { Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(i)); } } Tcl_SetObjResult(interp, oPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "setSortOrder")) { CONST84 char *name = Tcl_GetString(objv[2]); int i, j; if (objc != 3) goto usage; if (!strcmp("default", name)) { name = Tcl_GetVar2(interp, "option","folder_sort",TCL_GLOBAL_ONLY); } for (i=0; sortNames[i].name && strcmp(sortNames[i].name, name); i++); if (sortNames[i].name) { /* * If the old sort order was threaded and the new one is not then * we should clear the threading info */ if (infoPtr->sortOrder == SORT_THREADED && sortNames[i].order != SORT_THREADED) { for (j=0; jnumber; j++) { (*infoPtr->setInfoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_THREADING, j, NULL); } } infoPtr->sortOrder = sortNames[i].order; infoPtr->reverse = sortNames[i].reverse; infoPtr->sortOrderChanged = 1; return TCL_OK; } else { Tcl_SetResult(interp, "No such sort order", TCL_STATIC); goto error; } } else if (!strcmp(Tcl_GetString(objv[1]), "getSortOrder")) { int i; for (i=0; sortNames[i].name; i++) { if (infoPtr->sortOrder == sortNames[i].order && infoPtr->reverse == sortNames[i].reverse) { Tcl_SetResult(interp, sortNames[i].name, TCL_STATIC); } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "netsync")) { if (!infoPtr->syncProc) { Tcl_AppendResult(interp, "Operation unsupported on this folder", (char *) NULL); goto error; } r = (*infoPtr->syncProc)(infoPtr, interp); return r; } else if (!strcmp(Tcl_GetString(objv[1]), "refcount")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(infoPtr->refCount)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "role")) { Tcl_SetObjResult(interp, infoPtr->role); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "dbinfo_get")) { if (infoPtr->dbinfoGetProc != NULL) { Tcl_Obj *oPtr = (*infoPtr->dbinfoGetProc)(infoPtr); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } else { return TCL_ERROR; } } else if (!strcmp(Tcl_GetString(objv[1]), "dbinfo_set")) { if (infoPtr->dbinfoSetProc != NULL) { if (objc != 6) goto usage; return (*infoPtr->dbinfoSetProc)(interp, infoPtr, objv[2], objv[3], objv[4], objv[5]); } else { return TCL_ERROR; } } usage: Tcl_AppendResult(interp, "Illegal usage of \"", Tcl_GetString(objv[0]), "\"", (char *) NULL); error: return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatFolderCmd* -- * * Implement various folder commands * * Results: * Varies * * Side effects: * Varies * * *---------------------------------------------------------------------- */ char* RatFolderCmdGet(Tcl_Interp *interp, RatFolderInfo *infoPtr, int index) { if (NULL == infoPtr->msgCmdPtr[infoPtr->presentationOrder[index]]) { infoPtr->msgCmdPtr[infoPtr->presentationOrder[index]] = (*infoPtr->createProc)(infoPtr, interp, infoPtr->presentationOrder[index]); } return infoPtr->msgCmdPtr[infoPtr->presentationOrder[index]]; } void RatFolderCmdSetFlag(Tcl_Interp *interp, RatFolderInfo *infoPtr, int *ilist, int count, RatFlag flag, int value) { int recent, unseen, i; for (i=0; ipresentationOrder[ilist[i]]; } recent = infoPtr->recent; unseen = infoPtr->unseen; (*infoPtr->setFlagProc)(infoPtr, interp, ilist, count, flag,value); infoPtr->flagsChanged = 1; if (infoPtr->recent != recent) { Tcl_SetVar2Ex(interp, "folderRecent", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->recent), TCL_GLOBAL_ONLY); } if (infoPtr->unseen != unseen) { Tcl_SetVar2Ex(interp, "folderUnseen", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->unseen), TCL_GLOBAL_ONLY); } } /* *---------------------------------------------------------------------- * * RatFolderSort -- * * Sorts the folder according to the users wishes. The user may * communicates their will via the folder_sort variable. Currently * The following methods are implemented: * subjectonly - Alphabetically on subject * sender - Alphabetically on sender name * folder - Sorts in native folder order * reverseFolder - The reverse of the above * date - By date sent * reverseDate - By reverse date sent * subject - Group messages with the same subject * and sort the groups by the earliest date * in each group. * * Results: * None. * * Side effects: * The presentation order member of the RatFolderInfo structure * is initialized. The size of the folder is updated. * * TODO, convert to Tcl_Obj * *---------------------------------------------------------------------- */ static void RatFolderSort(Tcl_Interp *interp, RatFolderInfo *infoPtr) { int i, j, k, pi, pj, numParm, *tmpPtr, first, last, *p=infoPtr->presentationOrder, needDate=0, needSubject=0, needSender=0, needIds = 0, needSize = 0, *uniqList, uniqListUsed, *subList, *lengthList, newEntry; Tcl_HashTable uniqTable; Tcl_HashEntry *uniqEntry; SortData *dataPtr, *dPtr; Tcl_Obj *oPtr; if (0 == infoPtr->number) { return; } switch(infoPtr->sortOrder) { case SORT_NONE: break; case SORT_SUBJECT: needSubject = 1; break; case SORT_THREADED: needIds = 1; needDate = 1; needSubject = 1; break; case SORT_SUBJDATE: needDate = 1; needSubject = 1; break; case SORT_SENDER: needSender = 1; break; case SORT_SENDERDATE: needDate = 1; needSender = 1; break; case SORT_DATE: needDate = 1; break; case SORT_SIZE: needSize = 1; break; } dataPtr = (SortData*)ckalloc(infoPtr->number*sizeof(*dataPtr)); infoPtr->size = 0; for (i=0; inumber; i++) { infoPtr->presentationOrder[i] = i; oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_TYPE, i); if (oPtr && !strcasecmp(Tcl_GetString(oPtr), "multipart/report")) { oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_PARAMETERS, i); if (!oPtr) { continue; } Tcl_ListObjLength(interp, oPtr, &numParm); } if ((oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_SIZE,i))) { Tcl_GetIntFromObj(interp, oPtr, &j); infoPtr->size += j; } if (needSubject) { oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_CANONSUBJECT, i); dataPtr[i].subject = Tcl_GetString(oPtr); } if (needSender) { oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_ANAME, i); dataPtr[i].sender = Tcl_GetString(oPtr); dataPtr[i].sender = cpystr(dataPtr[i].sender); lcase((unsigned char*)dataPtr[i].sender); } if (needDate) { long myLong; oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_DATE_N, i); Tcl_GetLongFromObj(interp, oPtr, &myLong); dataPtr[i].date = (time_t) myLong; } if (needSize) { oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_SIZE, i); Tcl_GetLongFromObj(interp, oPtr, &dataPtr[i].size); } if (needIds) { oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_MSGID, i); if (oPtr) { dataPtr[i].msgid = Tcl_GetString(oPtr); } else { dataPtr[i].msgid = ""; } oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_REF, i); if (oPtr) { dataPtr[i].ref = Tcl_GetString(oPtr); } else { dataPtr[i].ref = ""; } } } baseSortDataPtr = dataPtr; switch (infoPtr->sortOrder) { case SORT_NONE: for (i=0; inumber; i++) { p[i] = i; } break; case SORT_THREADED: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int),RatFolderSortCompareDate); /*for (i=0; inumber; i++) { printf("Msg: %d (really %d)\n", i, p[i]); printf(" Subj: %s\n", dataPtr[p[i]].subject); printf("MsgId: <%s>\n", dataPtr[p[i]].msgid); printf(" Ref: <%s>\n", (dataPtr[p[i]].ref ? dataPtr[p[i]].ref : "(NULL)")); }*/ /* * Start by sorting on hard references */ dataPtr[p[0]].prev = -1; dataPtr[p[0]].next = -1; dataPtr[p[0]].child = -1; dataPtr[p[0]].parent = -1; dataPtr[p[0]].tPtr = NULL; first = last = p[0]; for (i=1; inumber; i++) { pi = p[i]; dataPtr[pi].tPtr = NULL; dataPtr[pi].child = -1; dataPtr[pi].prev = last; dataPtr[last].next = pi; dataPtr[pi].next = -1; dataPtr[pi].child = -1; dataPtr[pi].parent = -1; last = pi; /* Find any replies to the current message */ for (j=i-1; j >= 0 && *dataPtr[pi].msgid; j--) { pj = p[j]; if (!strcmp(dataPtr[pj].ref, dataPtr[pi].msgid) && -1 == dataPtr[pj].parent) { /* Here 'pj' is considered a reply to 'pi' */ if (dataPtr[pj].prev != -1) { dataPtr[dataPtr[pj].prev].next = dataPtr[pj].next; } if (dataPtr[pj].next != -1) { dataPtr[dataPtr[pj].next].prev = dataPtr[pj].prev; } if (first == pj) first = dataPtr[pj].next; if (dataPtr[pi].child != -1) { dataPtr[dataPtr[pi].child].prev = pj; dataPtr[pj].next = dataPtr[pi].child; } else { dataPtr[pj].next = -1; } dataPtr[pi].child = pj; dataPtr[pj].prev = -1; dataPtr[pj].parent = pi; } } /* Find message which the current message is a reply to */ for (j=0; jnumber; i++) { pi = p[i]; if (-1 != dataPtr[pi].parent) continue; for (j=i-1; j>=0; j--) { pj = p[j]; if (!strcmp(dataPtr[pi].subject, dataPtr[pj].subject) && !IsChild(dataPtr, pj, pi)) { /* * 'pi' is later in the same thread as 'pj' * First we remove it from the list. */ dataPtr[dataPtr[pi].prev].next = dataPtr[pi].next; if (dataPtr[pi].next != -1) { dataPtr[dataPtr[pi].next].prev = dataPtr[pi].prev; } /* * If the parent to 'pj' also has the same subject * then we add this message after 'pj', otherwise * we add it under 'pj' */ for (k=pj; -1 != dataPtr[k].prev; k = dataPtr[k].prev); if (-1 != dataPtr[k].parent && !strcmp(dataPtr[pi].subject, dataPtr[dataPtr[k].parent].subject)) { dataPtr[pi].prev = pj; dataPtr[pi].next = dataPtr[pj].next; if (dataPtr[pj].next != -1) { dataPtr[dataPtr[pj].next].prev = pi; } dataPtr[pj].next = pi; } else { dataPtr[pi].parent = pj; dataPtr[pi].prev = -1; dataPtr[pi].next = dataPtr[pj].child; if (-1 != dataPtr[pj].child) { dataPtr[dataPtr[pj].child].prev = pi; dataPtr[dataPtr[pj].child].parent = -1; } dataPtr[pj].child = pi; } break; } } } /*printf("First: %d\n", first); for (i=0; inumber; i++) { printf("%d:\tprev: %2d next: %2d parent: %2d child: %2d\n", i, dataPtr[i].prev, dataPtr[i].next, dataPtr[i].parent, dataPtr[i].child); }*/ RatFolderSortLinearize(p, infoPtr->number, dataPtr, first, 0); for (i=0; inumber; i++) { (*infoPtr->setInfoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_THREADING, i, dataPtr[i].tPtr); } break; case SORT_SUBJDATE: case SORT_SENDERDATE: /* * This algorithm is complicated: * - First we build a list of unique subjects in uniqList. Each entry * in this list contains the index of the first message with this * subject. The messages are linked with the nextPtr field in * the SortData structs. * - Then we sort each found subject. This is done by placing the * indexes of the messages in subList. And sort that. When it * is sorted we rebuild the subject chains via the nextPtr; * - After that we sort the first message in each subject. This is done * by reusing the uniqList. We replace each entry in it with a * pointer to the first entry in the set. Actually we do this in * the preceding step. Then we sort this list. * - Finally we build to result array. */ uniqList = (int*)ckalloc(2*infoPtr->number*sizeof(*uniqList)); Tcl_InitHashTable(&uniqTable, TCL_STRING_KEYS); subList = &uniqList[infoPtr->number]; lengthList = &uniqList[2*infoPtr->number]; for (i=uniqListUsed=0; inumber; i++) { uniqEntry = Tcl_CreateHashEntry(&uniqTable, infoPtr->sortOrder == SORT_SUBJDATE ? dataPtr[i].subject : dataPtr[i].sender, &newEntry); if (newEntry) { dataPtr[i].nextPtr = NULL; uniqList[uniqListUsed++] = i; Tcl_SetHashValue(uniqEntry, &dataPtr[i]); } else { dPtr = Tcl_GetHashValue(uniqEntry); dataPtr[i].nextPtr = dPtr->nextPtr; dPtr->nextPtr = &dataPtr[i]; } } Tcl_DeleteHashTable(&uniqTable); for (i=0; inextPtr) { subList[j++] = dPtr-dataPtr; } qsort((void*)subList, j, sizeof(int),RatFolderSortCompareDate); for (k=0; knextPtr) { p[k++] = dPtr-baseSortDataPtr; } } ckfree(uniqList); break; case SORT_SENDER: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p,infoPtr->number,sizeof(int),RatFolderSortCompareSender); break; case SORT_SUBJECT: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int), RatFolderSortCompareSubject); break; case SORT_DATE: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int),RatFolderSortCompareDate); break; case SORT_SIZE: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int),RatFolderSortCompareSize); } if (infoPtr->reverse) { tmpPtr = (int*)ckalloc(infoPtr->number*sizeof(int)); for (i=infoPtr->number-1, j=0; i >= 0; i--) { tmpPtr[j++] = p[i]; } memcpy(p, tmpPtr, j*sizeof(int)); ckfree(tmpPtr); } else { for (i=j=0; i < infoPtr->number; i++) { p[j++] = p[i]; } } /* * Cleanup dataPtr */ for (i=0; inumber; i++) { if (needSender) { ckfree(dataPtr[i].sender); } } ckfree(dataPtr); } /* *---------------------------------------------------------------------- * * RatFolderSortCompare* -- * * This is the comparison functions used by RatFolderSort. They * expect to get pointers to integers as argumens. The integers * pointed at are indexes into a list of SortData structs which can be * found at the address in baseSortDataPtr. * * Results: * An integers describing the order of the compared objects. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatFolderSortCompareDate(const void *arg1, const void *arg2) { return baseSortDataPtr[*((int*)arg1)].date - baseSortDataPtr[*((int*)arg2)].date; } static int RatFolderSortCompareSubject(const void *arg1, const void *arg2) { return strcmp(baseSortDataPtr[*((int*)arg1)].subject, baseSortDataPtr[*((int*)arg2)].subject); } static int RatFolderSortCompareSender(const void *arg1, const void *arg2) { return strcmp(baseSortDataPtr[*((int*)arg1)].sender, baseSortDataPtr[*((int*)arg2)].sender); } static int RatFolderSortCompareSize(const void *arg1, const void *arg2) { return baseSortDataPtr[*((int*)arg1)].size - baseSortDataPtr[*((int*)arg2)].size; } /* *---------------------------------------------------------------------- * * RatFolderCanonalizeSubject -- * * Copy a subject line and remove certain constructs (the re:). * * Results: * A new object reference * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Obj* RatFolderCanonalizeSubject(const char *s) { Tcl_Obj *nPtr = Tcl_NewStringObj("", 0); const char *e; int i, len; if (s) { /* * We first try to find the start of the actual text, i.e. without any * leading fluff (Re: etc) and whitespaces. If the text starts with * [sometext] then also look for fluff immediately after it. * Then we find how long the text is (ignore trailing whitespaces) */ e = s+strlen(s)-1; while (*s) { while (*s && isspace((int)*s)) s++; for (i=0; subjectFluff[i]; i++) { if (!strncasecmp(subjectFluff[i], s, strlen(subjectFluff[i]))){ break; } } if (subjectFluff[i]) { s += strlen(subjectFluff[i]); } else if (*s == '[' && (e = strchr(s+1, ']'))) { Tcl_AppendToObj(nPtr, (CONST84 char*)s, e-s+1); s = e+1; } else { break; } } for (len = strlen(s)-1; len > 0 && isspace((int)s[len]); len--); Tcl_AppendToObj(nPtr, (CONST84 char*)s, len+1); len = Tcl_UtfToLower(Tcl_GetString(nPtr)); Tcl_SetObjLength(nPtr, len); } return nPtr; } /* *---------------------------------------------------------------------- * * IsChild -- * * See if one message has the other as one of its ancestors. * * Results: * A non-zero value if the child is related to the parent * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int IsChild(SortData *dataPtr, int child, int parent) { int i; for (i=child; i > -1 && i != parent;) { if (-1 != dataPtr[i].parent) { i = dataPtr[i].parent; } else { i = dataPtr[i].prev; } } return (i == parent); } /* *---------------------------------------------------------------------- * * RatFolderSortLinearize -- * * Linearizes the linked list of messages. The list is also sorted. * * Results: * Number of elements added to p * * Side effects: * Modifies the p array. * * *---------------------------------------------------------------------- */ static int RatFolderSortLinearize(int *p, int n, SortData *dataPtr, int first, int depth) { int *s = (int*)ckalloc(sizeof(int)*n); int i, j, k, o, ns; char *c; for (i=first, ns=0; -1 != i; i = dataPtr[i].next) { s[ns++] = i; } qsort((void*)s, ns, sizeof(int), RatFolderSortCompareDate); for (i=j=0; isubject,0), -1); break; case RAT_FOLDER_CANONSUBJECT: oPtr = RatFolderCanonalizeSubject( RatDecodeHeader(interp,envPtr->subject,0)); break; case RAT_FOLDER_MAIL_REAL: for (adrPtr = envPtr->from; adrPtr; adrPtr = adrPtr->next) { if (adrPtr->mailbox && adrPtr->host) { break; } } if (!adrPtr) { oPtr = Tcl_NewStringObj(NULL, 0); } else { oPtr = Tcl_NewStringObj(RatAddressMail(adrPtr), -1); } break; case RAT_FOLDER_ANAME: /* fallthrough */ case RAT_FOLDER_NAME: if (!envPtr->from) { oPtr = Tcl_NewObj(); } else if (type != RAT_FOLDER_ANAME && (RAT_ISME_YES == msgPtr->fromMe || (RAT_ISME_UNKOWN == msgPtr->fromMe && RatAddressIsMe(interp, envPtr->from, 1)))) { msgPtr->fromMe = RAT_ISME_YES; if (envPtr->to && envPtr->to->personal) { oPtr = Tcl_GetVar2Ex(interp, "t", "to", TCL_GLOBAL_ONLY); if (Tcl_IsShared(oPtr)) { oPtr = Tcl_DuplicateObj(oPtr); } Tcl_AppendToObj(oPtr, ": ", 2); Tcl_AppendToObj(oPtr, RatDecodeHeader(interp, envPtr->to->personal, 0), -1); break; } } else { if (type != RAT_FOLDER_ANAME) { msgPtr->fromMe = RAT_ISME_NO; } if (envPtr->from->personal) { oPtr = Tcl_NewStringObj(RatDecodeHeader(interp, envPtr->from->personal, 0), -1); break; } } /* fallthrough */ case RAT_FOLDER_MAIL: oPtr = Tcl_NewObj(); if (type != RAT_FOLDER_ANAME && (RAT_ISME_YES == msgPtr->fromMe || (RAT_ISME_UNKOWN == msgPtr->fromMe && RatAddressIsMe(interp, envPtr->from, 1)))) { msgPtr->fromMe = RAT_ISME_YES; adrPtr = envPtr->to; Tcl_AppendObjToObj(oPtr, Tcl_GetVar2Ex(interp, "t", "to", TCL_GLOBAL_ONLY)); Tcl_AppendToObj(oPtr, ": ", 2); } else { if (type != RAT_FOLDER_ANAME) { msgPtr->fromMe = RAT_ISME_NO; } adrPtr = envPtr->from; } for (; adrPtr; adrPtr = adrPtr->next) { if (adrPtr->mailbox && adrPtr->host) { break; } } if (!adrPtr) { Tcl_IncrRefCount(oPtr); Tcl_DecrRefCount(oPtr); oPtr = Tcl_NewObj(); } else { Tcl_AppendToObj(oPtr, RatAddressMail(adrPtr), -1); } break; case RAT_FOLDER_NAME_RECIPIENT: if (!envPtr->to) { oPtr = Tcl_NewObj(); break; } msgPtr->toMe = RAT_ISME_NO; if (envPtr->to->personal) { oPtr = Tcl_NewStringObj( RatDecodeHeader(interp, envPtr->to->personal, 0), -1); break; } /* fallthrough */ case RAT_FOLDER_MAIL_RECIPIENT: oPtr = Tcl_NewObj(); adrPtr = envPtr->to; for (; adrPtr; adrPtr = adrPtr->next) { if (adrPtr->mailbox && adrPtr->host) { break; } } if (!adrPtr) { Tcl_IncrRefCount(oPtr); Tcl_DecrRefCount(oPtr); oPtr = Tcl_NewObj(); } else { Tcl_AppendToObj(oPtr, RatAddressMail(adrPtr), -1); } break; case RAT_FOLDER_SIZE: oPtr = Tcl_NewIntObj(size); break; case RAT_FOLDER_SIZE_F: oPtr = RatMangleNumber(size); break; case RAT_FOLDER_DATE_F: /* fallthrough */ case RAT_FOLDER_DATE_N: if (envPtr->date && T == mail_parse_date(&dateElt, envPtr->date)) { dateEltPtr = &dateElt; } else { dateEltPtr = eltPtr; } tm.tm_sec = dateEltPtr->seconds; tm.tm_min = dateEltPtr->minutes; tm.tm_hour = dateEltPtr->hours; tm.tm_mday = dateEltPtr->day; tm.tm_mon = dateEltPtr->month - 1; tm.tm_year = dateEltPtr->year+70; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; /* time represents the time the message was sent, without * the time zone factor. So when rendered in gmt it gives * correct date/time. */ time = mktime(&tm); if (RAT_FOLDER_DATE_F == type) { oPtr = RatFormatDate(interp, &tm); } else { /* To get the real time of sending we must add the * time zone offset. */ zonediff = (dateEltPtr->zhours*60+dateEltPtr->zminutes)*60; if (!dateEltPtr->zoccident) { zonediff *= -1; } time += zonediff; oPtr = Tcl_NewObj(); Tcl_SetLongObj(oPtr, time); } break; case RAT_FOLDER_DATE_IMAP4: dateEltPtr = eltPtr; mail_date(buf, dateEltPtr); oPtr = Tcl_NewStringObj(buf, -1); break; case RAT_FOLDER_TYPE: oPtr = Tcl_NewObj(); Tcl_AppendStringsToObj(oPtr, body_types[bodyPtr->type], "/", bodyPtr->subtype, NULL); break; case RAT_FOLDER_PARAMETERS: oPtr = Tcl_NewObj(); for (parmPtr = bodyPtr->parameter; parmPtr; parmPtr = parmPtr->next) { pPtr[0] = Tcl_NewStringObj(parmPtr->attribute, -1); pPtr[1] = Tcl_NewStringObj(parmPtr->value, -1); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewListObj(2,pPtr)); } break; case RAT_FOLDER_TO: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->to, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->to); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_FROM: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->from, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->from); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_SENDER: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->sender, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->sender); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_CC: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->cc, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->cc); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_REPLY_TO: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->reply_to, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->reply_to); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_FLAGS: oPtr = Tcl_NewStringObj(MsgFlags(eltPtr), -1); break; case RAT_FOLDER_UNIXFLAGS: s = buf; if (eltPtr->seen) *s++ = 'R'; if (eltPtr->deleted) *s++ = 'D'; if (eltPtr->flagged) *s++ = 'F'; if (eltPtr->answered) *s++ = 'A'; oPtr = Tcl_NewStringObj(buf, s-buf); break; case RAT_FOLDER_MSGID: oPtr = RatExtractRef(envPtr->message_id); if (NULL == oPtr) { oPtr = Tcl_NewObj(); } break; case RAT_FOLDER_REF: oPtr = RatExtractRef(envPtr->in_reply_to); if (NULL == oPtr) { oPtr = RatExtractRef(envPtr->references); } if (NULL == oPtr) { oPtr = Tcl_NewObj(); } break; case RAT_FOLDER_STATUS: /*fallthrough */ case RAT_FOLDER_INDEX: /*fallthrough */ case RAT_FOLDER_THREADING: /*fallthrough */ case RAT_FOLDER_UID: /*fallthrough */ case RAT_FOLDER_END: oPtr = Tcl_NewObj(); break; } msgPtr->info[type] = oPtr; Tcl_IncrRefCount(oPtr); return oPtr; } /* *---------------------------------------------------------------------- * * MsgFlags -- * * Returns the flags of a message * * Results: * A poiter to a static area containing the flags * * Side effects: * None * * *---------------------------------------------------------------------- */ char* MsgFlags(MESSAGECACHE *eltPtr) { static Tcl_DString ds; static int initialized = 0; if (!initialized) { Tcl_DStringInit(&ds); initialized = 1; } Tcl_DStringSetLength(&ds, 0); if (eltPtr->seen) { Tcl_DStringAppend(&ds, flag_name[RAT_SEEN].imap_name, -1); } if (eltPtr->deleted) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_DELETED].imap_name, -1); } if (eltPtr->flagged) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_FLAGGED].imap_name, -1); } if (eltPtr->answered) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_ANSWERED].imap_name, -1); } if (eltPtr->draft) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_DRAFT].imap_name, -1); } if (eltPtr->recent) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_RECENT].imap_name, -1); } return Tcl_DStringValue(&ds); } /* *---------------------------------------------------------------------- * * RatParseFrom -- * * Parse the time in a 'From ' line. See ../imap/src/osdep/unix/unix.h * for details on how this line may look like. * * Results: * A poiter to a static area containing a MESSAGECACHE. * The only valid fields in this are the time-fields * * Side effects: * None * * *---------------------------------------------------------------------- */ MESSAGECACHE* RatParseFrom(const char *from) { static MESSAGECACHE elt; const char *cPtr; int i=0, found; /* * Start by finding the weekday name, if it is followed by one * space and a month-spec, then we assume we have found the date. */ for (cPtr = from+5, found=0; cPtr && !found; cPtr = strchr(cPtr, ' ')) { for (i=0; i<7 && strncmp(cPtr+1, dayName[i], 3); i++); if (i < 7) { for (i=0; i<12; i++) { if (!strncmp(cPtr+5, monthName[i], 3)) { found = 1; break; } } } } if (!found) { return NULL; } elt.month = i+1; for (cPtr+=8; isspace(*cPtr) && *cPtr; cPtr++); if (!*cPtr) return NULL; elt.day = atoi(cPtr); for (cPtr++; !isspace(*cPtr) && *cPtr; cPtr++); for (cPtr++; isspace(*cPtr) && *cPtr; cPtr++); if (!*cPtr) return NULL; elt.hours = atoi(cPtr); for (cPtr++; ':' != *cPtr && *cPtr; cPtr++); elt.minutes = atoi(cPtr+1); for (cPtr++; isdigit(*cPtr) && *cPtr; cPtr++); if (!*cPtr) return NULL; if (':' == *cPtr) { elt.seconds = atoi(cPtr+1); for (cPtr++; isdigit(*cPtr) && *cPtr; cPtr++); } else { elt.seconds = 0; } while (1) { for (cPtr++; isspace(*cPtr) && *cPtr; cPtr++); if (isdigit(cPtr[0]) && isdigit(cPtr[1]) && isdigit(cPtr[2]) && isdigit(cPtr[3])){ elt.year = atoi(cPtr)-BASEYEAR; break; } else { for (cPtr++; !isspace(*cPtr) && *cPtr; cPtr++); } if (!*cPtr) return NULL; } elt.zoccident = 0; elt.zhours = 0; elt.zminutes = 0; return &elt; } /* *---------------------------------------------------------------------- * * RatUpdateFolder -- * * Updates a folder * * Results: * The number of new messages is left in the tcl result-buffer. * A standard tcl-result is returned. * * Side effects: * None * * *---------------------------------------------------------------------- */ int RatUpdateFolder(Tcl_Interp *interp, RatFolderInfo *infoPtr, RatUpdateType mode) { int i, numNew, oldNumber, delta; oldNumber = infoPtr->number; numNew = (*infoPtr->updateProc)(infoPtr, interp, mode); if (numNew < 0) { return TCL_ERROR; } else if (numNew || oldNumber != infoPtr->number || infoPtr->sortOrderChanged) { if (infoPtr->number > infoPtr->allocated) { infoPtr->allocated = infoPtr->number; infoPtr->msgCmdPtr = (char **) ckrealloc(infoPtr->msgCmdPtr, infoPtr->allocated*sizeof(char*)); infoPtr->privatePtr = (ClientData**)ckrealloc(infoPtr->privatePtr, infoPtr->allocated*sizeof(ClientData*)); infoPtr->presentationOrder = (int *) ckrealloc( infoPtr->presentationOrder, infoPtr->allocated*sizeof(int)); } for (i=infoPtr->number-numNew; inumber; i++) { infoPtr->msgCmdPtr[i] = (char *) NULL; infoPtr->privatePtr[i] = (ClientData*) NULL; (*infoPtr->initProc)(infoPtr, interp, i); } RatFolderSort(interp, infoPtr); infoPtr->sortOrderChanged = 0; } delta = infoPtr->number - oldNumber; Tcl_SetObjResult(interp, Tcl_NewIntObj((delta>0 ? delta : 0))); if (delta) { Tcl_SetVar2Ex(interp, "folderExists", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->number), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderRecent", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->recent), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderUnseen", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->unseen), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderChanged", infoPtr->cmdName, Tcl_NewIntObj(++folderChangeId), TCL_GLOBAL_ONLY); } return TCL_OK; } /* *---------------------------------------------------------------------- * * RatFolderUpdateTime -- * * Updates a folder * * Results: * The number of new messages is left in the tcl result-buffer. * A standard tcl-result is returned. * * Side effects: * None * * *---------------------------------------------------------------------- */ void RatFolderUpdateTime(ClientData clientData) { static Tcl_TimerToken timer_token = NULL; Tcl_Interp *interp = (Tcl_Interp*)clientData; RatFolderInfo *infoPtr, *nextPtr; Tcl_Obj *oPtr; int interval; if (timer_token) { Tcl_DeleteTimerHandler(timer_token); } RatSetBusy(timerInterp); for (infoPtr = ratFolderList; infoPtr; infoPtr = nextPtr) { nextPtr = infoPtr->nextPtr; RatUpdateFolder(interp, infoPtr, RAT_UPDATE); } RatClearBusy(interp); oPtr = Tcl_GetVar2Ex(interp, "option", "watcher_time", TCL_GLOBAL_ONLY); if (!oPtr || TCL_OK != Tcl_GetIntFromObj(interp, oPtr, &interval)) { interval = 30; } else if (interval > 1000000) { interval = 1000000; } timer_token = Tcl_CreateTimerHandler(interval*1000, RatFolderUpdateTime, (ClientData)interp); } /* *---------------------------------------------------------------------- * * RatFolderClose -- * * Closes a folder * * Results: * A standard tcl result * * Side effects: * Many, all associated with cleaning up from the folder * * *---------------------------------------------------------------------- */ int RatFolderClose(Tcl_Interp *interp, RatFolderInfo *infoPtr, int force) { RatFolderInfo **rfiPtrPtr; int i, ret, expunge; char buf[1024]; Tcl_Obj *oPtr; oPtr = Tcl_GetVar2Ex(interp, "option", "expunge_on_close",TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &expunge); if (infoPtr->refCount-- != 1 && !force) { if (expunge && !infoPtr->append_only) { RatUpdateFolder(interp, infoPtr, RAT_SYNC); } return TCL_OK; } snprintf(buf, sizeof(buf), "foreach f [array names folderWindowList] {" " if {$folderWindowList($f) == \"%s\"} {" " FolderWindowClear $f" " }" "}", infoPtr->cmdName); Tcl_GlobalEval(interp, buf); for (rfiPtrPtr = &ratFolderList; infoPtr != *rfiPtrPtr; rfiPtrPtr = &(*rfiPtrPtr)->nextPtr); *rfiPtrPtr = infoPtr->nextPtr; ckfree(infoPtr->name); ckfree(infoPtr->ident_def); ret = (*infoPtr->closeProc)(infoPtr, interp, expunge); for(i=0; i < infoPtr->number; i++) { if (NULL != infoPtr->msgCmdPtr[i]) { (void)RatMessageDelete(interp, infoPtr->msgCmdPtr[i]); infoPtr->msgCmdPtr[i] = 0; } } Tcl_UnsetVar2(interp, "folderExists", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar2(interp, "folderUnseen", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar2(interp, "folderChanged", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar2(interp, "vFolderWatch", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar(interp, infoPtr->cmdName, TCL_GLOBAL_ONLY); (void)Tcl_DeleteCommand(interp, infoPtr->cmdName); ckfree(infoPtr->cmdName); ckfree(infoPtr->msgCmdPtr); ckfree(infoPtr->privatePtr); ckfree(infoPtr->presentationOrder); ckfree(infoPtr); return ret; } /* *---------------------------------------------------------------------- * * RatFolderInsert -- * * Insert messages into a folder * * Results: * A standard tcl result * * Side effects: * Messages gets added * * *---------------------------------------------------------------------- */ int RatFolderInsert(Tcl_Interp *interp, RatFolderInfo *infoPtr,int num,char **msgs) { int result; result = (*infoPtr->insertProc)(infoPtr, interp, num, msgs); RatUpdateFolder(interp, infoPtr, RAT_UPDATE); return result; } /* *---------------------------------------------------------------------- * * RatGetFolderSpec -- * * Return the mailbox spec for the given folder definition * * Results: * A pointer to a static area of memeory where the spec is stored. * This area will be overwritten by the next call. * * Side effects: * None * * *---------------------------------------------------------------------- */ char* RatGetFolderSpec(Tcl_Interp *interp, Tcl_Obj *def) { static Tcl_DString ds; static int initialized = 0; Tcl_Obj *oPtr, **objv, **fobjv, **mobjv, **sobjv; int objc, fobjc, mobjc, sobjc, port, i, j; char buf[64], *type, *c, *file; if (0 == initialized) { Tcl_DStringInit(&ds); } else { Tcl_DStringSetLength(&ds, 0); } Tcl_ListObjGetElements(interp, def, &objc, &objv); if (objc < 4) { return NULL; } type = Tcl_GetString(objv[1]); if (!strcmp(type, "file")) { file = cpystr(RatTranslateFileName(interp, Tcl_GetString(objv[3]))); if (NULL == file) { Tcl_DStringAppend(&ds, "invalid_file_specified", -1); } else { RatDecodeQP((unsigned char*)file); Tcl_DStringAppend(&ds, file, -1); c = Tcl_GetString(objv[3]); if ('/' == c[strlen(c)-1]) { Tcl_DStringAppend(&ds, "/", 1); } } } else if (!strcmp(type, "mh")) { Tcl_DStringAppend(&ds, "#mh/", 4); file = cpystr(Tcl_GetString(objv[3])); RatDecodeQP((unsigned char*)file); Tcl_DStringAppend(&ds, file, -1); ckfree(file); } else if (!strcmp(type, "dbase")) { if (objc < 6) { return NULL; } Tcl_DStringAppend(&ds, Tcl_GetString(objv[3]), -1); Tcl_DStringAppend(&ds, Tcl_GetString(objv[4]), -1); Tcl_DStringAppend(&ds, Tcl_GetString(objv[5]), -1); } else if (!strcmp(type, "imap") || !strcmp(type, "pop3") || !strcmp(type, "dis")) { oPtr = Tcl_GetVar2Ex(interp, "mailServer", Tcl_GetString(objv[3]), TCL_GLOBAL_ONLY); if (!oPtr) { return NULL; } Tcl_ListObjGetElements(interp, oPtr, &mobjc, &mobjv); Tcl_DStringAppend(&ds, "{", 1); Tcl_DStringAppend(&ds, Tcl_GetString(mobjv[0]), Tcl_GetCharLength(mobjv[0])); if (TCL_OK == Tcl_GetIntFromObj(interp, mobjv[1], &port) && port!=0) { snprintf(buf, sizeof(buf), ":%d", port); Tcl_DStringAppend(&ds, buf, -1); } if (!strcmp(type, "pop3")) { Tcl_DStringAppend(&ds, "/pop3", 5); } else { Tcl_DStringAppend(&ds, "/imap", 5); } Tcl_ListObjGetElements(interp, mobjv[2], &fobjc, &fobjv); #ifdef HAVE_OPENSSL /* * These flags must be in a specific order to match strings generated * by c-client. Also only include them if we have SSL available. */ for (i=0; cClientFlags[i]; i++) { for (j=0; j'))) { ls = s+1; le = e; } if (ls) { oPtr = Tcl_NewObj(); for (s=ls; sresult. * * int updateProc(RatFolderInfo *infoPtr, Tcl_Interp *interp,RatUpdateType mode) * * This procedure should update the folder. It return -1 on errors, * otherwise the number of new messages is returned. * * int insertProc(RatFolderInfo *infoPtr, Tcl_Interp *interp, int argc, * char *argv[]) * This function should add the messages passed in argc/argv to * the folder. The function can assume that msgCmdPtr has enough * room to hold all new messages, and that all arguments are valid * messages. * The return value should normally be TCL_OK, but in case of error * it should be TCL_ERROR and a message shpuld then be left in * interp->result. * * void setFlagProc(RatFolderInfo *infoPtr, Tcl_Interp *interp, int index, * RatFlag flag, int value) * * Sets the specified flag for message specified by 'index' to the * value passed (only boolean values allowed). * * int getFlagProc(RatFolderInfo *infoPtr, Tcl_Interp *interp, int index, * RatFlag flag) * * Gets the value of the specified flag for message specified by 'index'. * * Tcl_Obj *infoProc(Tcl_Interp *interp, ClientData clientData, * RatFolderInfoType type, int index) * * Returns information about a message. * * void setInfoProc(Tcl_Interp *interp, ClientData clientData, * RatFolderInfoType type, int index, Tcl_Obj *oPtr) * * Sets information about a message. * * char *createProc(RatFolderInfo *infoPtr, Tcl_Interp *interp, int index) * * This function should create a message command for the message * specified by "index". The message command is returned. * * int syncProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp) * Does a network synchronization for the given folder */ typedef void (RatInitProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index); typedef void (RatFinalProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp); typedef int (RatCloseProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int expunge); typedef int (RatUpdateProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp, RatUpdateType mode); typedef int (RatInsertProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int argc, char *argv[]); typedef int (RatSetFlagProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int *ilist, int count, RatFlag flag, int value); typedef int (RatGetFlagProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, RatFlag flag); typedef Tcl_Obj* (RatInfoProc) (Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index); typedef void (RatSetInfoProc) (Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index,Tcl_Obj *oPtr); typedef char* (RatCreateProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index); typedef int (RatSyncProc) (RatFolderInfoPtr infoPtr, Tcl_Interp *interp); typedef Tcl_Obj* (RatDbInfoGetProc) (RatFolderInfoPtr infoPtr); typedef int (RatDbInfoSetProc) (Tcl_Interp *interp, RatFolderInfoPtr infoPtr, Tcl_Obj *indexes, Tcl_Obj *keywords, Tcl_Obj *ex_date, Tcl_Obj *ex_type); /* * An instance of the structure below is created for each folder. It is then * used to hold the folder's internal state. */ typedef struct RatFolderInfo { char *cmdName; /* Name of the folder command */ char *name; /* Mailbox name (this is a pointer which * may be ckfree():ed) */ char *type; /* Type of folder */ char *ident_def; /* Definition id of folder */ int append_only; /* Is this open in appeld-only mode? */ int refCount; /* Reference count (<=0 means closing) */ SortOrder sortOrder; /* Sort order for folder */ Tcl_Obj *role; /* Role to use */ int sortOrderChanged; /* Non null if sort order has changed */ int reverse; /* If the sort order should be reversed */ int number; /* Number of messages in folder */ int recent; /* Number of recent messages in folder */ int unseen; /* Number of unseen messages in folder */ int size; /* Approximate size of folder, or -1 if the * folder type doesn't support size. */ int allocated; /* The number of messages that fits into the * following lists. */ char **msgCmdPtr; /* A list of pointers to the message commands, * or NULL's if the commands haven't been * created yet. All these strings will be * ckfree()ed sooner or later. */ ClientData **privatePtr; /* Pointer to folder private data */ int *presentationOrder; /* The order in which these messages should * be presented to the user. The first element * of this list is the index of the first * message to show etc. */ int flagsChanged; /* Non null if the flags has been changed * since the last checkpoint */ RatInitProc *initProc; RatFinalProc *finalProc; RatCloseProc *closeProc; RatUpdateProc *updateProc; RatInsertProc *insertProc; RatSetFlagProc *setFlagProc; RatGetFlagProc *getFlagProc; RatInfoProc *infoProc; RatSetInfoProc *setInfoProc; RatCreateProc *createProc; RatSyncProc *syncProc; RatDbInfoGetProc *dbinfoGetProc; RatDbInfoSetProc *dbinfoSetProc; ClientData private, private2; /* Data private for each folder type */ struct RatFolderInfo *nextPtr; /* Pointer to next folder (if any)*/ } RatFolderInfo; /* * Global list of folders */ extern RatFolderInfo *ratFolderList; /* * The different types of messages. These are for internal use only. */ typedef enum { RAT_CCLIENT_MESSAGE, RAT_DBASE_MESSAGE, RAT_FREE_MESSAGE } RatMessageType; /* * The state of the address is me check */ typedef enum { RAT_ISME_YES, RAT_ISME_NO, RAT_ISME_UNKOWN } RatIsMeStatus; /* * The ClientData for each message entity */ typedef struct BodyInfo BodyInfo; typedef struct MessageInfo { RatFolderInfo *folderInfoPtr; char name[16]; RatMessageType type; int msgNo; RatIsMeStatus fromMe; RatIsMeStatus toMe; BodyInfo *bodyInfoPtr; ClientData clientData; Tcl_Obj *info[RAT_FOLDER_END]; } MessageInfo; /* * The different signed statuses */ typedef enum { RAT_UNSIGNED, RAT_UNCHECKED, RAT_SIG_GOOD, RAT_SIG_BAD, RAT_SIG_ERR, RAT_PGP_ABORT} RatSigStatus; /* * The ClientData for each bodypart entity */ struct BodyInfo { char *cmdName; MessageInfo *msgPtr; RatMessageType type; BODY *bodyPtr; BodyInfo *firstbornPtr; BodyInfo *nextPtr; char *containedEntity; RatSigStatus sigStatus; Tcl_DString *pgpOutput; int encoded; BodyInfo *secPtr; BodyInfo *altPtr; Tcl_DString *decodedTextPtr; ClientData clientData; }; /* * The different operations that can be made on messages and bodyparts * which are specific for every type. */ typedef char* (RatGetHeadersProc) (Tcl_Interp *interp, MessageInfo *msgPtr); typedef char* (RatGetEnvelopeProc) (Tcl_Interp *interp, MessageInfo *msgPtr); typedef BodyInfo* (RatCreateBodyProc) (Tcl_Interp *interp, MessageInfo *msgPtr); typedef char* (RatFetchTextProc) (Tcl_Interp *interp, MessageInfo *msgPtr); typedef ENVELOPE* (RatEnvelopeProc) (MessageInfo *msgPtr); typedef void (RatMsgDeleteProc) (MessageInfo *msgPtr); typedef void (RatMakeChildrenProc) (Tcl_Interp *interp, BodyInfo *bodyPtr); typedef char* (RatFetchBodyProc) (BodyInfo *bodyPtr, unsigned long *lengthPtr); typedef void (RatBodyDeleteProc) (BodyInfo *bodyPtr); typedef MESSAGECACHE* (RatGetInternalDateProc) (Tcl_Interp *interp, MessageInfo *msgPtr); typedef Tcl_Obj* (RatMsgDbInfoProc) (MessageInfo *msgPtr); /* * The following structure defines which functions to call for to * perform certain message type operations. */ typedef struct { RatGetHeadersProc *getHeadersProc; RatGetEnvelopeProc *getEnvelopeProc; RatInfoProc *getInfoProc; RatCreateBodyProc *createBodyProc; RatFetchTextProc *fetchTextProc; RatEnvelopeProc *envelopeProc; RatMsgDeleteProc *msgDeleteProc; RatMakeChildrenProc *makeChildrenProc; RatFetchBodyProc *fetchBodyProc; RatBodyDeleteProc *bodyDeleteProc; RatGetInternalDateProc *getInternalDateProc; RatMsgDbInfoProc *dbinfoGetProc; } MessageProcInfo; /* * This structure holds a parsed list expression */ typedef struct { int size; /* How many items the lists below has */ char **preString; /* Any characters that should be inserted befor the next data part. */ RatFolderInfoType *typeList;/* The type of the data */ int *fieldWidth; /* How wide this field should be (0=variable)*/ int *leftJust; /* True if it should be left justified */ char *postString; /* Any character sthat should be appended. */ } ListExpression; /* * Folder management operations */ typedef enum { RAT_MGMT_CREATE, RAT_MGMT_CHECK, RAT_MGMT_DELETE, RAT_MGMT_SUBSCRIBE, RAT_MGMT_UNSUBSCRIBE } RatManagementAction; /* ratFolder.c (note that this file also exports functions in rat.h) */ extern RatFolderInfo *RatGetOpenFolder(Tcl_Interp *interp, Tcl_Obj *defPtr, int append_only); extern RatFolderInfo* RatOpenFolder(Tcl_Interp *interp, int append_only, Tcl_Obj *def); extern char* RatFolderCmdGet(Tcl_Interp *interp, RatFolderInfo *infoPtr, int index); extern void RatFolderCmdSetFlag(Tcl_Interp *interp, RatFolderInfo *infoPtr, int *ilist, int count,RatFlag flag, int value); extern Tcl_Obj *RatFolderCanonalizeSubject (const char *s); extern Tcl_Obj *RatGetMsgInfo(Tcl_Interp *interp, RatFolderInfoType type, MessageInfo *msgPtr, ENVELOPE *envPtr, BODY *bodyPtr, MESSAGECACHE *eltPtr, int size); extern char* MsgFlags(MESSAGECACHE *eltPtr); extern MESSAGECACHE *RatParseFrom(const char *from); extern int RatFolderClose(Tcl_Interp *interp, RatFolderInfo *infoPtr, int force); extern int RatFolderInsert(Tcl_Interp *interp, RatFolderInfo *infoPtr, int num, char **msgs); extern int RatUpdateFolder(Tcl_Interp *interp, RatFolderInfo *infoPtr, RatUpdateType mode); extern char *RatGetFolderSpec(Tcl_Interp *interp, Tcl_Obj *def); extern Tcl_Obj *RatExtractRef(CONST84 char *text); /* ratStdFolder.c */ extern int RatStdFolderInit(Tcl_Interp *interp); extern RatFolderInfo *RatStdFolderCreate(Tcl_Interp *interp, int append_only, Tcl_Obj *defPtr); extern int OpenStdFolder(Tcl_Interp *interp, char *spec, void *voidPtr, int append_only, MAILSTREAM **stream_out); extern void CloseStdFolder(Tcl_Interp *interp, MAILSTREAM *stream); extern int RatStdManageFolder(Tcl_Interp *interp, RatManagementAction op, int mbx, Tcl_Obj *fptr); void RatStdCheckNet(Tcl_Interp *interp); /* ratDbFolder.c */ extern int RatDbFolderInit (Tcl_Interp *interp); extern RatFolderInfo *RatDbFolderCreate(Tcl_Interp *interp, int append_only, Tcl_Obj *defPtr); extern RatInfoProc Db_InfoProc; extern Tcl_Obj* Db_InfoProcInt(Tcl_Interp *interp, RatFolderInfo *infoPtr, RatFolderInfoType type, int rIndex); /* ratDisFolder.c */ extern int RatDisFolderInit (Tcl_Interp *interp); extern RatFolderInfo *RatDisFolderCreate(Tcl_Interp *interp, int append_only, Tcl_Obj *defPtr); extern char* RatDisFolderDir(Tcl_Interp *interp, Tcl_Obj *defPtr); extern int RatDisOnOffTrans(Tcl_Interp *interp, int newState); extern void RatDisManageFolder(Tcl_Interp *interp, RatManagementAction op, Tcl_Obj *fptr); /* ratMessage.c */ extern void RatInitMessages (void); extern Tcl_ObjCmdProc RatMessageCmd; extern void RatMessageGetContent(Tcl_Interp *interp, MessageInfo *msgPtr, char **header, char **body); extern BodyInfo *CreateBodyInfo(Tcl_Interp *interp, MessageInfo *msgPtr, BODY *bodyPtr); extern Tcl_ObjCmdProc RatBodyCmd; extern int RatMessageDelete (Tcl_Interp *interp, char *msgCmd); extern void RatMessageGet (Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_DString *ds, char *flags, size_t flaglen, char *date, size_t datelen); extern Tcl_ObjCmdProc RatInsertCmd; extern int RatInsertMsg (Tcl_Interp *interp, MessageInfo *msgPtr, char *keywords, char *exDate, char *exType); extern Tcl_Obj *RatMsgInfo(Tcl_Interp *interp, MessageInfo *msgPtr, RatFolderInfoType type); extern int RatBodySave(Tcl_Interp *interp,Tcl_Channel channel, BodyInfo *bodyInfoPtr, int encoded, int convertNL); extern Tcl_Obj *RatBodyType(BodyInfo *bodyInfoPtr); extern Tcl_Obj *RatBodyData(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, int encoded, char *charset); extern MESSAGECACHE *RatMessageInternalDate(Tcl_Interp *interp, MessageInfo *msgPtr); extern char *RatPurgeFlags(char *flags, int level); extern size_t RatHeaderSize(ENVELOPE *env,BODY *body); /* ratMsgList.c */ extern ListExpression *RatParseList(const char *format, char *error); extern void RatFreeListExpression(ListExpression *exprPtr); extern Tcl_Obj *RatDoList(Tcl_Interp *interp, ListExpression *exprPtr, RatInfoProc *infoProc, ClientData clientData, int index); extern Tcl_ObjCmdProc RatCheckListFormatCmd; /* ratDbMessage.c */ extern char *RatDbMessageCreate (Tcl_Interp *interp, RatFolderInfoPtr infoPtr, int index, int dbIndex); extern void RatDbMessagesInit (MessageProcInfo* procInfo); /* ratFrMessage.c */ extern void RatFrMessagesInit (MessageProcInfo* procInfo); extern Tcl_ObjCmdProc RatCreateMessageCmd; extern BodyInfo* Fr_CreateBodyProc(Tcl_Interp *interp, MessageInfo *msgPtr); extern char* RatFrMessageCreate(Tcl_Interp *interp, char *data, int length, MessageInfo **msgPtrPtr); extern int RatFrMessagePGP(Tcl_Interp *interp, MessageInfo *msgPtr, int sign, int encrypt, char *role, char *signer, Tcl_Obj *rcpts); extern int RatFrMessageRemoveInternal(Tcl_Interp *interp, MessageInfo *msgPtr); /* ratStdMessage.c */ extern void RatStdMessagesInit (MessageProcInfo* procInfo); extern int RatStdEasyCopyingOK(Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_Obj *defPtr); extern int RatStdMessageCopy (Tcl_Interp *interp, MessageInfo *msgPtr, char *destination); /* ratExp.c */ extern Tcl_ObjCmdProc RatParseExpCmd; extern Tcl_ObjCmdProc RatGetExpCmd; extern Tcl_ObjCmdProc RatFreeExpCmd; extern int RatExpMatch(Tcl_Interp *interp, int expId, RatInfoProc *infoProc, ClientData clientData, int index); /* ratMailcap.c */ int RatMcapFindCmd(Tcl_Interp *interp, BodyInfo *bodyInfoPtr); #ifdef MEM_DEBUG void ratStdMessageCleanup(void); void ratMessageCleanup(void); void ratStdFolderCleanup(void); void ratAddressCleanup(void); void ratCodeCleanup(void); #endif /* MEM_DEBUG */ #endif /* _RATFOLDER */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratFrMessage.c000066400000000000000000000736371137544547100213210ustar00rootroot00000000000000/* * ratFrMessage.c -- * * This file contains code which implements free messages. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" #include "ratPGP.h" /* * The ClientData for each message entity */ typedef struct FrMessageInfo { MESSAGE *messagePtr; char *from; char *headers; char *msgData; unsigned char *bodyData; } FrMessageInfo; /* * The ClientData for each bodypart entity */ typedef struct FrBodyInfo { unsigned char *text; } FrBodyInfo; /* * The number of message entities created. This is used to create new * unique command names. */ static int numFrMessages = 0; /* * Used when doing output to internal string */ typedef struct { unsigned int used; unsigned int allocated; char *data; } DynamicString; static ENVELOPE* RatFrCreateEnvelope(Tcl_Interp *interp, char *role, Tcl_Obj *envelope_data, Tcl_DString *extraHeaders); static void RatFrCreateBody(BODY *b, Tcl_Interp *interp, char *role, Tcl_Obj *body_data, Tcl_DString *extraHeaders); static RatGetHeadersProc Fr_GetHeadersProc; static RatGetEnvelopeProc Fr_GetEnvelopeProc; static RatFetchTextProc Fr_FetchTextProc; static RatEnvelopeProc Fr_EnvelopeProc; static RatMsgDeleteProc Fr_MsgDeleteProc; static RatMakeChildrenProc Fr_MakeChildrenProc; static RatFetchBodyProc Fr_FetchBodyProc; static RatBodyDeleteProc Fr_BodyDeleteProc; static RatInfoProc Fr_GetInfoProc; static RatGetInternalDateProc Fr_GetInternalDateProc; static long RatStringSoutr(void *stream_x, char *string); /* *---------------------------------------------------------------------- * * RatFrMessagesInit -- * * Initializes the given MessageProcInfo entry for a free message * * Results: * None. * * Side effects: * The given MessageProcInfo is initialized. * * *---------------------------------------------------------------------- */ void RatFrMessagesInit(MessageProcInfo *messageProcInfoPtr) { messageProcInfoPtr->getHeadersProc = Fr_GetHeadersProc; messageProcInfoPtr->getEnvelopeProc = Fr_GetEnvelopeProc; messageProcInfoPtr->getInfoProc = Fr_GetInfoProc; messageProcInfoPtr->createBodyProc = Fr_CreateBodyProc; messageProcInfoPtr->fetchTextProc = Fr_FetchTextProc; messageProcInfoPtr->envelopeProc = Fr_EnvelopeProc; messageProcInfoPtr->msgDeleteProc = Fr_MsgDeleteProc; messageProcInfoPtr->makeChildrenProc = Fr_MakeChildrenProc; messageProcInfoPtr->fetchBodyProc = Fr_FetchBodyProc; messageProcInfoPtr->bodyDeleteProc = Fr_BodyDeleteProc; messageProcInfoPtr->getInternalDateProc = Fr_GetInternalDateProc; messageProcInfoPtr->dbinfoGetProc = NULL; } /* *---------------------------------------------------------------------- * * RatFrMessageCreate -- * * Creates a free message entity * * Results: * The name of the new message entity. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatFrMessageCreate(Tcl_Interp *interp, char *data, int length, MessageInfo **msgPtrPtr) { FrMessageInfo *frMsgPtr=(FrMessageInfo*)ckalloc(sizeof(FrMessageInfo)); MessageInfo *msgPtr=(MessageInfo*)ckalloc(sizeof(MessageInfo)); char *msgData, *cPtr; int headerLength, j, fromLength; for (headerLength = 0; data[headerLength]; headerLength++) { if (data[headerLength] == '\n' && data[headerLength+1] == '\n') { headerLength++; break; } if (data[headerLength]=='\r' && data[headerLength+1]=='\n' && data[headerLength+2]=='\r' && data[headerLength+3]=='\n') { headerLength += 2; break; } } msgData = (char*)ckalloc(length+1); memcpy(msgData, data, length); msgData[length] = '\0'; msgPtr->folderInfoPtr = NULL; msgPtr->type = RAT_FREE_MESSAGE; msgPtr->bodyInfoPtr = NULL; msgPtr->msgNo = 0; msgPtr->fromMe = RAT_ISME_UNKOWN; msgPtr->toMe = RAT_ISME_UNKOWN; msgPtr->clientData = (ClientData)frMsgPtr; for (j=0; jinfo)/sizeof(*msgPtr->info); j++) { msgPtr->info[j] = NULL; } frMsgPtr->msgData = msgData; frMsgPtr->messagePtr = RatParseMsg(interp, (unsigned char*)msgData); frMsgPtr->bodyData = frMsgPtr->messagePtr->text.text.data + frMsgPtr->messagePtr->text.offset; frMsgPtr->headers = (char*)ckalloc(headerLength+1); strlcpy(frMsgPtr->headers, data, headerLength+1); if (!strncmp("From ", data, 5) && (cPtr = strchr(data, '\n'))) { fromLength = cPtr-data; frMsgPtr->from = (char*)ckalloc(fromLength+1); strlcpy(frMsgPtr->from, frMsgPtr->headers, fromLength); } else { frMsgPtr->from = NULL; } if (msgPtrPtr) { *msgPtrPtr = msgPtr; } sprintf(msgPtr->name, "RatFrMsg%d", numFrMessages++); Tcl_CreateObjCommand(interp, msgPtr->name, RatMessageCmd, (ClientData)msgPtr, NULL); return msgPtr->name; } /* *---------------------------------------------------------------------- * * RatCreateMessageCmd -- * * Creates a free message entity from tcl * * Results: * The name of the new message entity. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatCreateMessageCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { FrMessageInfo *frMsgPtr=(FrMessageInfo*)ckalloc(sizeof(FrMessageInfo)); MessageInfo *msgPtr=(MessageInfo*)ckalloc(sizeof(MessageInfo)); DynamicString ds; ENVELOPE *env; BODY *body; char *msgData = NULL; Tcl_Obj **aobjv; int i, aobjc, len; Tcl_DString extraHeaders; if (3 != objc || TCL_OK != Tcl_ListObjGetElements(interp, objv[2], &aobjc, &aobjv) || 2 != aobjc) { Tcl_AppendResult(interp, "bad args: should be \"", Tcl_GetString(objv[0]), " role {envelope body}\"", NULL); return TCL_ERROR; } Tcl_DStringInit(&extraHeaders); Tcl_DStringAppend(&extraHeaders, "Status: R\r\n", -1); env = RatFrCreateEnvelope(interp, Tcl_GetString(objv[1]), aobjv[0], &extraHeaders); body = mail_newbody(); RatFrCreateBody(body, interp, Tcl_GetString(objv[1]), aobjv[1], &extraHeaders); rfc822_encode_body_8bit(env, body); msgPtr->folderInfoPtr = NULL; msgPtr->type = RAT_FREE_MESSAGE; msgPtr->bodyInfoPtr = NULL; msgPtr->msgNo = 0; msgPtr->fromMe = RAT_ISME_UNKOWN; msgPtr->toMe = RAT_ISME_UNKOWN; msgPtr->clientData = (ClientData)frMsgPtr; for (i=0; iinfo)/sizeof(*msgPtr->info); i++) { msgPtr->info[i] = NULL; } frMsgPtr->msgData = msgData; frMsgPtr->messagePtr = mail_newmsg(); frMsgPtr->messagePtr->env = env; frMsgPtr->messagePtr->body = body; frMsgPtr->from = NULL; len = RatHeaderSize(env, body) + Tcl_DStringLength(&extraHeaders); frMsgPtr->headers = (char*)ckalloc(len); rfc822_header(frMsgPtr->headers, env, body); frMsgPtr->headers[strlen(frMsgPtr->headers)-2] = '\0'; strlcat(frMsgPtr->headers, Tcl_DStringValue(&extraHeaders), len); ds.used = ds.allocated = 0; ds.data = NULL; rfc822_output_body(body, RatStringSoutr, (void*)&ds); if (NULL != ds.data) { /* Skip the last two which are extra \r\n added by c-client */ ds.data[ds.used-2] = '\0'; } else { ds.data = cpystr(""); } frMsgPtr->bodyData = (unsigned char*)ds.data; sprintf(msgPtr->name, "RatFrMsg%d", numFrMessages++); Tcl_CreateObjCommand(interp, msgPtr->name, RatMessageCmd, (ClientData)msgPtr, NULL); Tcl_SetResult(interp, msgPtr->name, TCL_VOLATILE); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatFrCreateEnvelope -- * * Creates an envelope structure from a list * * Results: * The name of the new message entity. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static ENVELOPE* RatFrCreateEnvelope(Tcl_Interp *interp, char *role, Tcl_Obj *envelope_data, Tcl_DString *ehp) { ENVELOPE *e = mail_newenvelope(); Tcl_Obj **objv, **ev; int objc, ec, i; char host[1024], buf[8192]; if (TCL_OK != Tcl_ListObjGetElements(interp, envelope_data, &objc, &objv)){ return e; } strlcpy(host, RatGetCurrent(interp, RAT_HOST, role), sizeof(host)); for (i=0; idate = (unsigned char*)cpystr(Tcl_GetString(ev[1])); } else if (!strcasecmp(Tcl_GetString(ev[0]), "subject")) { e->subject = cpystr(RatEncodeHeaderLine(interp, ev[1], 7)); } else if (!strcasecmp(Tcl_GetString(ev[0]), "in_reply_to")) { e->in_reply_to = cpystr(Tcl_GetString(ev[1])); } else if (!strcasecmp(Tcl_GetString(ev[0]), "message_id")) { e->message_id = cpystr(Tcl_GetString(ev[1])); } else if (!strcasecmp(Tcl_GetString(ev[0]), "newsgroups")) { e->newsgroups = cpystr(Tcl_GetString(ev[1])); } else if (!strcasecmp(Tcl_GetString(ev[0]), "followup_to")) { e->followup_to = cpystr(Tcl_GetString(ev[1])); } else if (!strcasecmp(Tcl_GetString(ev[0]), "references")) { e->references = cpystr(Tcl_GetString(ev[1])); } else if (!strcasecmp(Tcl_GetString(ev[0]), "from")) { strlcpy(buf, Tcl_GetString(ev[1]), sizeof(buf)); rfc822_parse_adrlist(&e->from, buf, host); RatEncodeAddresses(interp, e->from); } else if (!strcasecmp(Tcl_GetString(ev[0]), "sender")) { strlcpy(buf, Tcl_GetString(ev[1]), sizeof(buf)); rfc822_parse_adrlist(&e->sender, buf, host); RatEncodeAddresses(interp, e->sender); } else if (!strcasecmp(Tcl_GetString(ev[0]), "reply_to")) { strlcpy(buf, Tcl_GetString(ev[1]), sizeof(buf)); rfc822_parse_adrlist(&e->reply_to, buf, host); RatEncodeAddresses(interp, e->reply_to); } else if (!strcasecmp(Tcl_GetString(ev[0]), "to")) { strlcpy(buf, Tcl_GetString(ev[1]), sizeof(buf)); rfc822_parse_adrlist(&e->to, buf, host); RatEncodeAddresses(interp, e->to); } else if (!strcasecmp(Tcl_GetString(ev[0]), "cc")) { strlcpy(buf, Tcl_GetString(ev[1]), sizeof(buf)); rfc822_parse_adrlist(&e->cc, buf, host); RatEncodeAddresses(interp, e->cc); } else if (!strcasecmp(Tcl_GetString(ev[0]), "bcc")) { strlcpy(buf, Tcl_GetString(ev[1]), sizeof(buf)); rfc822_parse_adrlist(&e->bcc, buf, host); RatEncodeAddresses(interp, e->bcc); } else if (!strncmp(Tcl_GetString(ev[0]), "X-", 2)) { Tcl_DStringAppend(ehp, Tcl_GetString(ev[0]), -1); Tcl_DStringAppend(ehp, ": ", 2); Tcl_DStringAppend(ehp, Tcl_GetString(ev[1]), -1); Tcl_DStringAppend(ehp, "\r\n", 2); } else { /* Perhaps handle this error? */ fprintf(stderr, "Env: unknown envelope header '%s'\n", Tcl_GetString(ev[0])); } } return e; } /* *---------------------------------------------------------------------- * * RatFrCreateBody -- * * Creates the body structure from a body entity * * Results: * The name of the new message entity. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static void RatFrCreateBody(BODY *b, Tcl_Interp *interp, char *role, Tcl_Obj *body_data, Tcl_DString *ehp) { DynamicString ds; Tcl_DString tds; PARAMETER *p; Tcl_Obj **objv, **av, **pv; int objc, ac, pc, i; char *charset = "us-ascii"; if (TCL_OK != Tcl_ListObjGetElements(interp, body_data, &objc, &objv) || 8 != objc) { return; } /* Content-type & parameters */ for (i=0; i < TYPEMAX-1 && body_types[i] && strcasecmp(body_types[i], Tcl_GetString(objv[0])); i++); b->type = i; b->subtype = (char*)ucase((unsigned char*)cpystr(Tcl_GetString(objv[1]))); if (TCL_OK == Tcl_ListObjGetElements(interp, objv[2], &ac, &av)) { for (i=ac-1; i>=0; i--) { if (TCL_OK != Tcl_ListObjGetElements(interp, av[i], &pc, &pv)) { continue; } p = mail_newbody_parameter(); p->attribute = (char*)ucase((unsigned char*) cpystr(Tcl_GetString(pv[0]))); p->value = cpystr(Tcl_GetString(pv[1])); p->next = b->parameter; b->parameter = p; if (!strcmp("CHARSET", p->attribute)) { charset = p->value; } } RatEncodeParameters(interp, b->parameter); } for (i=0; i < ENCMAX-1 && body_encodings[i] && strcasecmp(body_encodings[i],Tcl_GetString(objv[3])); i++); b->encoding = i; /* Disposition & parameters */ if (strlen(Tcl_GetString(objv[4]))) { b->disposition.type = (char*)ucase((unsigned char*)cpystr(Tcl_GetString(objv[4]))); if (TCL_OK == Tcl_ListObjGetElements(interp, objv[5], &ac, &av)) { for (i=0; iattribute = (char*)ucase((unsigned char*)cpystr(Tcl_GetString(pv[0]))); p->value = cpystr(Tcl_GetString(pv[1])); p->next = b->disposition.parameter; b->disposition.parameter = p; } } RatEncodeParameters(interp, b->disposition.parameter); } /* Headers */ if (TCL_OK == Tcl_ListObjGetElements(interp, objv[6], &ac, &av)) { for (i=0; iid = cpystr(RatEncodeHeaderLine(interp, pv[1], 10)); } else if (!strcasecmp(Tcl_GetString(pv[0]), "content_description")) { b->description = cpystr(RatEncodeHeaderLine(interp, pv[1],19)); } else if (!strncmp(Tcl_GetString(pv[0]), "X-", 2) && ehp) { Tcl_DStringAppend(ehp, Tcl_GetString(pv[0]), -1); Tcl_DStringAppend(ehp, ": ", 2); Tcl_DStringAppend(ehp, Tcl_GetString(pv[1]), -1); Tcl_DStringAppend(ehp, "\r\n", 2); } else { fprintf(stderr, "Env: unknown body header '%s'\n", Tcl_GetString(pv[0])); } } } if (TYPEMESSAGE == b->type) { int len; char *tmp; b->nested.msg = mail_newmsg(); Tcl_ListObjGetElements(interp, objv[7], &ac, &av); if (2 == ac && !strcmp("file", Tcl_GetString(av[0]))) { b->contents.text.data = (unsigned char*) RatReadAndCanonify(interp, Tcl_GetString(av[1]), &b->contents.text.size, 0); } else { b->nested.msg->env = RatFrCreateEnvelope(interp, role, av[0],NULL); b->nested.msg->body = mail_newbody(); RatFrCreateBody(b->nested.msg->body, interp, role, av[1], NULL); ds.used = ds.allocated = 0; ds.data = NULL; len = RatHeaderSize(b->nested.msg->env, b->nested.msg->body); tmp = (char*)ckalloc(len+1); rfc822_output(tmp, b->nested.msg->env, b->nested.msg->body, RatStringSoutr, (void*)&ds, 0); b->contents.text.data = (unsigned char*)ds.data; b->contents.text.size = ds.used; } } else if (TYPEMULTIPART == b->type) { PART **p = &b->nested.part; Tcl_ListObjGetElements(interp, objv[7], &ac, &av); for (i=0; ibody, interp, role, av[i], NULL); p = &(*p)->next; } } else { Tcl_ListObjGetElements(interp, objv[7], &ac, &av); if (!strcasecmp(Tcl_GetString(av[0]), "utfblob")) { Tcl_DStringInit(&tds); Tcl_UtfToExternalDString(RatGetEncoding(interp, charset), Tcl_GetString(av[1]), -1, &tds); RatCanonalize(&tds); b->contents.text.data = (unsigned char*)cpystr(Tcl_DStringValue(&tds)); b->contents.text.size = Tcl_DStringLength(&tds); Tcl_DStringFree(&tds); } else if (!strcasecmp(Tcl_GetString(av[0]), "file")) { int canon = 0; if (TYPETEXT == b->type) { canon = 1; } b->contents.text.data = (unsigned char*) RatReadAndCanonify(interp, Tcl_GetString(av[1]), &b->contents.text.size, canon); } } } /* *---------------------------------------------------------------------- * * Fr_GetHeadersProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Fr_GetHeadersProc(Tcl_Interp *interp, MessageInfo *msgPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; return frMsgPtr->headers; } /* *---------------------------------------------------------------------- * * Fr_GetEnvelopeProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Fr_GetEnvelopeProc(Tcl_Interp *interp, MessageInfo *msgPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; static char buf[1024]; MESSAGECACHE elt; ADDRESS *adrPtr; time_t date; struct tm tm, *tmPtr; if (frMsgPtr->messagePtr->env->return_path) { adrPtr = frMsgPtr->messagePtr->env->sender; } else if (frMsgPtr->messagePtr->env->sender) { adrPtr = frMsgPtr->messagePtr->env->sender; } else { adrPtr = frMsgPtr->messagePtr->env->from; } if (!strcmp(Tcl_GetHostName(), adrPtr->host)) { snprintf(buf, sizeof(buf), "From %s", adrPtr->mailbox); } else { strlcpy(buf, "From ", sizeof(buf)); if (RatAddressSize(adrPtr, 0) > sizeof(buf)-32) { snprintf(buf+5, sizeof(buf)-5, "ridiculously@long.address"); } else { rfc822_write_address_full(buf+5, adrPtr, NULL); } } if (T == mail_parse_date(&elt, frMsgPtr->messagePtr->env->date)) { tm.tm_sec = elt.seconds; tm.tm_min = elt.minutes; tm.tm_hour = elt.hours; tm.tm_mday = elt.day; tm.tm_mon = elt.month - 1; tm.tm_year = elt.year+69; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; date = (int)mktime(&tm); } else { date = 0; } tmPtr = gmtime(&date); snprintf(buf + strlen(buf), sizeof(buf)-strlen(buf), " %s %s %2d %02d:%02d GMT %04d\n", dayName[tmPtr->tm_wday], monthName[tmPtr->tm_mon], tmPtr->tm_mday, tmPtr->tm_hour, tmPtr->tm_min, tmPtr->tm_year+1900); return buf; } /* *---------------------------------------------------------------------- * * Fr_CreateBodyProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ BodyInfo* Fr_CreateBodyProc(Tcl_Interp *interp, MessageInfo *msgPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; FrBodyInfo *frBodyInfoPtr = (FrBodyInfo*)ckalloc(sizeof(FrBodyInfo)); msgPtr->bodyInfoPtr = CreateBodyInfo(interp, msgPtr, frMsgPtr->messagePtr->body); msgPtr->bodyInfoPtr->clientData = (ClientData)frBodyInfoPtr; frBodyInfoPtr->text = frMsgPtr->bodyData; return msgPtr->bodyInfoPtr; } /* *---------------------------------------------------------------------- * * Fr_FetchTextProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Fr_FetchTextProc(Tcl_Interp *interp, MessageInfo *msgPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; return (char*)frMsgPtr->bodyData; } /* *---------------------------------------------------------------------- * * Fr_EnvelopeProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static ENVELOPE* Fr_EnvelopeProc(MessageInfo *msgPtr) { return ((FrMessageInfo*)msgPtr->clientData)->messagePtr->env; } /* *---------------------------------------------------------------------- * * Fr_MsgDeleteProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static void Fr_MsgDeleteProc(MessageInfo *msgPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; mail_free_envelope(&frMsgPtr->messagePtr->env); mail_free_body(&frMsgPtr->messagePtr->body); ckfree(frMsgPtr->messagePtr); ckfree(frMsgPtr->headers); ckfree(frMsgPtr->msgData); ckfree(frMsgPtr); } /* *---------------------------------------------------------------------- * * Fr_MakeChildrenProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static void Fr_MakeChildrenProc(Tcl_Interp *interp, BodyInfo *bodyInfoPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)bodyInfoPtr->msgPtr->clientData; BODY *bodyPtr = bodyInfoPtr->bodyPtr; BodyInfo *partInfoPtr, **partInfoPtrPtr; FrBodyInfo *frPartInfoPtr; PART *partPtr; if (!bodyInfoPtr->firstbornPtr) { partInfoPtrPtr = &bodyInfoPtr->firstbornPtr; for (partPtr = bodyPtr->nested.part; partPtr; partPtr = partPtr->next) { frPartInfoPtr = (FrBodyInfo*)ckalloc(sizeof(FrBodyInfo)); partInfoPtr = CreateBodyInfo(interp, bodyInfoPtr->msgPtr, &partPtr->body); *partInfoPtrPtr = partInfoPtr; partInfoPtrPtr = &partInfoPtr->nextPtr; partInfoPtr->clientData = (ClientData)frPartInfoPtr; frPartInfoPtr->text = frMsgPtr->bodyData + partPtr->body.contents.offset; } } } /* *---------------------------------------------------------------------- * * Fr_FetchBodyProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static char* Fr_FetchBodyProc(BodyInfo *bodyInfoPtr, unsigned long *lengthPtr) { FrBodyInfo *frBodyInfoPtr = (FrBodyInfo*)bodyInfoPtr->clientData; if (bodyInfoPtr->decodedTextPtr) { *lengthPtr = Tcl_DStringLength(bodyInfoPtr->decodedTextPtr); return Tcl_DStringValue(bodyInfoPtr->decodedTextPtr); } *lengthPtr = bodyInfoPtr->bodyPtr->contents.text.size; return (char*)frBodyInfoPtr->text; } /* *---------------------------------------------------------------------- * * Fr_BodyDeleteProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static void Fr_BodyDeleteProc(BodyInfo *bodyInfoPtr) { FrBodyInfo *frBodyInfoPtr = (FrBodyInfo*)bodyInfoPtr->clientData; ckfree(frBodyInfoPtr); } /* *---------------------------------------------------------------------- * * Fr_GetInternalDateProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static MESSAGECACHE* Fr_GetInternalDateProc(Tcl_Interp *interp, MessageInfo *msgPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; static MESSAGECACHE elt; if (frMsgPtr->from) { return RatParseFrom(frMsgPtr->from); } else { mail_parse_date(&elt, frMsgPtr->messagePtr->env->date); return &elt; } } /* *---------------------------------------------------------------------- * * Fr_GetInfoProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ static Tcl_Obj* Fr_GetInfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index) { static char buf[128]; MessageInfo *msgPtr = (MessageInfo*)clientData; FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; MESSAGECACHE elt; Tcl_Obj *oPtr = NULL; char *cPtr; int i, f; if (msgPtr->info[type]) { return msgPtr->info[type]; } switch (type) { case RAT_FOLDER_SUBJECT: /* fallthrough */ case RAT_FOLDER_CANONSUBJECT: /* fallthrough */ case RAT_FOLDER_ANAME: /* fallthrough */ case RAT_FOLDER_NAME: /* fallthrough */ case RAT_FOLDER_MAIL_REAL: /* fallthrough */ case RAT_FOLDER_MAIL: /* fallthrough */ case RAT_FOLDER_NAME_RECIPIENT: /* fallthrough */ case RAT_FOLDER_MAIL_RECIPIENT: /* fallthrough */ case RAT_FOLDER_TYPE: /* fallthrough */ case RAT_FOLDER_TO: /* fallthrough */ case RAT_FOLDER_FROM: /* fallthrough */ case RAT_FOLDER_SENDER: /* fallthrough */ case RAT_FOLDER_CC: /* fallthrough */ case RAT_FOLDER_REPLY_TO: /* fallthrough */ case RAT_FOLDER_MSGID: /* fallthrough */ case RAT_FOLDER_REF: /* fallthrough */ case RAT_FOLDER_PARAMETERS: return RatGetMsgInfo(interp, type,msgPtr,frMsgPtr->messagePtr->env, frMsgPtr->messagePtr->body, NULL, 0); case RAT_FOLDER_SIZE: /* fallthrough */ case RAT_FOLDER_SIZE_F: return RatGetMsgInfo(interp, type, msgPtr, NULL, NULL, NULL, frMsgPtr->messagePtr->header.text.size + frMsgPtr->messagePtr->text.text.size); case RAT_FOLDER_DATE_F: /* fallthrough */ case RAT_FOLDER_DATE_N: /* fallthrough */ case RAT_FOLDER_DATE_IMAP4: if (T != mail_parse_date(&elt, frMsgPtr->messagePtr->env->date)) { rfc822_date(buf); mail_parse_date(&elt, (unsigned char*)buf); } return RatGetMsgInfo(interp, type, msgPtr, frMsgPtr->messagePtr->env, NULL, &elt, 0); case RAT_FOLDER_STATUS: cPtr = frMsgPtr->headers; do { if (!strncasecmp(cPtr, "status:", 7)) { int seen, deleted, marked, answered; ADDRESS *addressPtr; seen = deleted = marked = answered = 0; for (i=7; cPtr[i]; i++) { switch (cPtr[i]) { case 'R': seen = 1; break; case 'D': deleted = 1; break; case 'F': marked = 1; break; case 'A': answered = 1; break; } } if (RAT_ISME_UNKOWN == msgPtr->toMe) { msgPtr->toMe = RAT_ISME_NO; for (addressPtr = frMsgPtr->messagePtr->env->to; addressPtr; addressPtr = addressPtr->next) { if (RatAddressIsMe(interp, addressPtr, 1)) { msgPtr->toMe = RAT_ISME_YES; break; } } } i = 0; if (!seen) { buf[i++] = 'N'; } if (deleted) { buf[i++] = 'D'; } if (marked) { buf[i++] = 'F'; } if (answered) { buf[i++] = 'A'; } if (RAT_ISME_YES == msgPtr->toMe) { buf[i++] = '+'; } else { buf[i++] = ' '; } buf[i] = '\0'; oPtr = Tcl_NewStringObj(buf, -1); break; } } while ((cPtr = strchr(cPtr, '\n')) && cPtr++ && *cPtr); break; case RAT_FOLDER_FLAGS: cPtr = frMsgPtr->headers; buf[0] = '\0'; do { if (!strncasecmp(cPtr, "status:", 7)) { for (i=7; cPtr[i] != '\n' && cPtr[i]; i++) { for (f=0; flag_name[f].imap_name; f++) { if (flag_name[f].unix_char == cPtr[i]) { strlcat(buf, " ", sizeof(buf)); strlcat(buf, flag_name[f].imap_name, sizeof(buf)); break; } } } if (*buf) { oPtr = Tcl_NewStringObj(buf+1, -1); } else { oPtr = Tcl_NewObj(); } break; } } while ((cPtr = strchr(cPtr, '\n')) && cPtr++ && *cPtr); break; case RAT_FOLDER_UNIXFLAGS: cPtr = frMsgPtr->headers; buf[0] = '\0'; do { if (!strncasecmp(cPtr, "status:", 7)) { for (cPtr += 7; isspace(*cPtr); cPtr++); oPtr = Tcl_NewStringObj(cPtr, -1); break; } } while ((cPtr = strchr(cPtr, '\n')) && cPtr++ && *cPtr); break; case RAT_FOLDER_INDEX: oPtr = Tcl_NewIntObj(1); break; case RAT_FOLDER_UID: oPtr = Tcl_NewIntObj(1); break; case RAT_FOLDER_THREADING: oPtr = Tcl_NewObj(); break; case RAT_FOLDER_END: break; } if (!oPtr) { oPtr = Tcl_NewObj(); } msgPtr->info[type] = oPtr; Tcl_IncrRefCount(oPtr); return oPtr; } /* *---------------------------------------------------------------------- * * RatStringSoutr -- * * APpend data to dynamic string * * Results: * None. * * Side effects: * Affects the passed dynamic string * * *---------------------------------------------------------------------- */ static long RatStringSoutr(void *stream_x, char *string) { DynamicString *ds = (DynamicString*)stream_x; int len = strlen(string); if (ds->used + len > ds->allocated) { ds->allocated = ds->used + len + 8192; ds->data = (char*)ckrealloc(ds->data, ds->allocated); } strcpy(ds->data+ds->used, string); ds->used += len; return(1L); /* T for c-client */ } /* *---------------------------------------------------------------------- * * RatStdMessagePGP -- * * Do pgp operation on message * * Results: * A standard tcl result * * Side effects: * The message is modified and interaction with the user may be * required. * * *---------------------------------------------------------------------- */ int RatFrMessagePGP(Tcl_Interp *interp, MessageInfo *msgPtr, int sign, int encrypt, char *role, char *signer, Tcl_Obj *rcpts) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; DynamicString ds; char *old, *x; int r, len; if (encrypt) { r = RatPGPEncrypt(interp, frMsgPtr->messagePtr->env, &frMsgPtr->messagePtr->body, (sign ? signer : NULL), rcpts); } else if (sign) { r = RatPGPSign(interp, frMsgPtr->messagePtr->env, &frMsgPtr->messagePtr->body, signer); } else { return TCL_OK; } if (TCL_OK == r) { len = RatHeaderSize(frMsgPtr->messagePtr->env, frMsgPtr->messagePtr->body); /* XXX * Find old X-headers. We assume they are collected at the end * of the headers (which they always are for generated free messages */ old = frMsgPtr->headers; if ((x = strstr(old, "\nX-"))) { x++; len += strlen(x); } frMsgPtr->headers = (char*)ckalloc(len); rfc822_header(frMsgPtr->headers, frMsgPtr->messagePtr->env, frMsgPtr->messagePtr->body); frMsgPtr->headers[strlen(frMsgPtr->headers)-2] = '\0'; if (x) { strlcat(frMsgPtr->headers, x, len); } ckfree(old); ds.used = ds.allocated = 0; ds.data = NULL; rfc822_output_body(frMsgPtr->messagePtr->body, RatStringSoutr, (void*)&ds); if (NULL != ds.data) { /* Skip the last two which are extra \r\n added by c-client */ ds.data[ds.used-2] = '\0'; } else { ds.data = cpystr(""); } ckfree(frMsgPtr->bodyData); frMsgPtr->bodyData = (unsigned char*)ds.data; } return r; } /* *---------------------------------------------------------------------- * * RatFrMessageRemoveInternal -- * * Remove the internal TkRat header fields * * Results: * A standard tcl result * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatFrMessageRemoveInternal(Tcl_Interp *interp, MessageInfo *msgPtr) { FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData; char *s, *e; while (NULL != (s = strstr(frMsgPtr->headers, "X-TkRat-Internal"))) { if (NULL != (e = strchr(s, '\n'))) { memmove(s, e+1, strlen(e+1)+1); } else { *s = '\0'; } } return TCL_OK; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratHold.c000066400000000000000000000156541137544547100203260ustar00rootroot00000000000000/* * ratHold.c -- * * Manages different holds of messages. * * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include #include #include "rat.h" #include static int RatoldAppInit(Tcl_Interp *interp); static Tcl_ObjCmdProc RatHoldCmd; static int RatHoldList(Tcl_Interp *interp, const char *dir, Tcl_Obj *fileListPtr); static int RatHoldExtract(Tcl_Interp *interp, const char *prefix, Tcl_Obj *usedArraysPtr, Tcl_Obj *filesPtr); int Ratold_Init(Tcl_Interp *interp) { RatoldAppInit(interp); return Tcl_PkgProvide(interp, "ratatosk_old", VERSION); } int Ratold_SafeInit(Tcl_Interp *interp) { RatoldAppInit(interp); return Tcl_PkgProvide(interp, "ratatosk_old", VERSION); } /* *---------------------------------------------------------------------- * * RatAppInit -- * * This procedure performs application-specific initialization. * Most applications, especially those that incorporate additional * packages, will have their own version of this procedure. * * Results: * Returns a standard Tcl completion code, and leaves an error * message in the result if an error occurs. * * Side effects: * Depends on the startup script. * *---------------------------------------------------------------------- */ static int RatoldAppInit(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "RatHold", RatHoldCmd, NULL, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatHoldCmd -- * * See ../doc/interface * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatHoldCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { static Tcl_Obj *fileListPtr = NULL; char buf[1024]; const char *holdDir; Tcl_Obj *oPtr; int index; if (objc < 3) goto usage; holdDir = RatTranslateFileName(interp, Tcl_GetString(objv[1])); if (!strcmp(Tcl_GetString(objv[2]), "list")) { if (fileListPtr) { Tcl_DecrRefCount(fileListPtr); } fileListPtr = Tcl_NewObj(); return RatHoldList(interp, holdDir, fileListPtr); } else if (!strcmp(Tcl_GetString(objv[2]), "extract")) { if (objc != 4 || TCL_OK != Tcl_GetIntFromObj(interp, objv[3], &index)) { goto usage; } else if (!fileListPtr) { Tcl_SetResult(interp,"You must list the content of the hold first", TCL_STATIC); return TCL_ERROR; } else { Tcl_ListObjIndex(interp, fileListPtr, index, &oPtr); snprintf(buf, sizeof(buf), "%s/%s", holdDir, Tcl_GetString(oPtr)); return RatHoldExtract(interp, buf, NULL, NULL); } } usage: Tcl_AppendResult(interp, "Usage error of \"", Tcl_GetString(objv[0]), "\"", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatHoldList -- * * List the content of the specified hold directory. * * Results: * A standard Tcl result. * * Side effects: * The fileListPtr argument will be used to hold the return list * * *---------------------------------------------------------------------- */ static int RatHoldList(Tcl_Interp *interp, const char *dir, Tcl_Obj *fileListPtr) { struct dirent *direntPtr; char buf[1024]; DIR *dirPtr; FILE *fPtr; Tcl_Obj *oPtr = Tcl_NewObj(); int l; if (NULL == (dirPtr = opendir(dir))) { snprintf(buf, sizeof(buf), "Failed to open %s: %s", dir, Tcl_PosixError(interp)); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_ERROR; } while (0 != (direntPtr = readdir(dirPtr))) { l = strlen(direntPtr->d_name); if ( 'd' == direntPtr->d_name[l-4] && 'e' == direntPtr->d_name[l-3] && 's' == direntPtr->d_name[l-2] && 'c' == direntPtr->d_name[l-1]) { snprintf(buf, sizeof(buf), "%s/%s", dir, direntPtr->d_name); fPtr = fopen(buf, "r"); if (!fgets(buf, sizeof(buf), fPtr)) { buf[0] = '\0'; } fclose(fPtr); buf[strlen(buf)-1] = '\0'; Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewStringObj(buf, -1)); snprintf(buf, sizeof(buf), "%s", direntPtr->d_name); if (fileListPtr) { Tcl_ListObjAppendElement(interp, fileListPtr, Tcl_NewStringObj(buf, strlen(buf)-5)); } } } closedir(dirPtr); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatHoldExtract -- * * Extract a specified held message. * * Results: * A standard Tcl result. The handler of the new message will be left * in the result area. * * Side effects: * The content of holdId will be modified. * * *---------------------------------------------------------------------- */ static int RatHoldExtract(Tcl_Interp *interp, const char *prefix, Tcl_Obj *usedArraysPtr, Tcl_Obj *filesPtr) { static int holdId = 0; Tcl_Channel ch; char buf[1024], *cPtr; int i, oldId; Tcl_Obj *fPtr = Tcl_NewObj(), *oPtr; /* * We start by reading the file. This is complicated by the fact that * the file is encoded in utf-8. */ if (NULL == (ch = Tcl_OpenFileChannel(interp, (char*)prefix, "r", 0))) { return TCL_ERROR; } Tcl_SetChannelOption(interp, ch, "-encoding", "utf-8"); i = Tcl_Seek(ch, 0, SEEK_END); Tcl_Seek(ch, 0, SEEK_SET); Tcl_ReadChars(ch, fPtr, i, 0); Tcl_Close(interp, ch); /* * Now we should eval the data, first we set the holdId variable * so that the file can generate unique handlers, after the read * we remember a new value of holdId. Then we get the right entry * in the list of files. */ oldId = holdId; sprintf(buf, "%d", holdId); Tcl_SetVar(interp, "holdId", buf, 0); Tcl_IncrRefCount(fPtr); Tcl_EvalObjEx(interp, fPtr, TCL_EVAL_DIRECT); Tcl_DecrRefCount(fPtr); sprintf(buf, "hold%d", holdId); if (NULL == Tcl_GetVar2Ex(interp, buf, "role", 0)) { oPtr = Tcl_GetVar2Ex(interp, "option", "default_role",TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, buf, "role", oPtr, 0); } Tcl_SetResult(interp, buf, TCL_VOLATILE); oPtr = Tcl_GetVar2Ex(interp, "holdId", NULL, 0); Tcl_GetIntFromObj(interp, oPtr, &holdId); if (usedArraysPtr) { for (i=oldId; ibodyPtr->type]; *cPtr; cPtr++){ if (strchr("|<>%*?\"`'", *cPtr)) { Tcl_DStringAppend(&ds, " ", 1); } else { Tcl_DStringAppend(&ds, cPtr, 1); } } Tcl_DStringAppend(&ds, "/", 1); for (cPtr = bodyInfoPtr->bodyPtr->subtype; *cPtr; cPtr++) { if (strchr("|<>%*?\"`'", *cPtr)) { Tcl_DStringAppend(&ds, " ", 1); } else { Tcl_DStringAppend(&ds, cPtr, 1); } } srcPtr++; continue; } if ('{' != *srcPtr++) { if (filePtr) { *filePtr = NULL; } return NULL; } for (cPtr = srcPtr, l = 0; *srcPtr && '}' != *srcPtr; srcPtr++, l++); if (*srcPtr) { srcPtr++; } for (parmPtr = bodyInfoPtr->bodyPtr->parameter; parmPtr; parmPtr = parmPtr->next) { if (!strncasecmp(cPtr, parmPtr->attribute, l)) { break; } } if (!parmPtr) { if (filePtr) { *filePtr = NULL; } return NULL; } /* * Copy the parameter value and in the process we remove any * chanacters that might be used to slip a trojan horse in * through our gates. */ for (cPtr = parmPtr->value; *cPtr; cPtr++) { if (strchr("|<>%*?\"`'", *cPtr)) { Tcl_DStringAppend(&ds, " ", 1); } else { Tcl_DStringAppend(&ds, cPtr, 1); } } } return Tcl_DStringValue(&ds); } /* *---------------------------------------------------------------------- * * RatMcapFindCmd -- * * Find a matching mailcap entry for a bodypart * * Results: * See ../doc/interface * * Side effects: * The mailcap files may be loaded. * * *---------------------------------------------------------------------- */ int RatMcapFindCmd(Tcl_Interp *interp, BodyInfo *bodyInfoPtr) { char *cmd, *file, *s; Tcl_Channel channel; int i; Tcl_Obj *rPtr; /* * We start by making sure that the mailcap files have been loaded */ if (0 == tableId) { MailcapReload(interp); } /* * Loop through all entries and check them. * - First we check the type/subtype for match. * - If they matched we check eventual test commands */ for (i=0; ibodyPtr->type]) || ('*' != *tablePtr[i].subtype && strcasecmp(tablePtr[i].subtype, bodyInfoPtr->bodyPtr->subtype))) { continue; } if (tablePtr[i].test) { if (!(cmd = ExpandString(interp, bodyInfoPtr, tablePtr[i].test, &file))) { continue; } if (file) { channel = Tcl_OpenFileChannel(interp, file, "w", 0666); RatBodySave(interp, channel, bodyInfoPtr, 0, 1); Tcl_Close(interp, channel); } if (system(cmd)) { if (file) { unlink(file); } continue; } if (file) { unlink(file); } } rPtr = Tcl_NewObj(); s = ExpandString(interp, bodyInfoPtr, tablePtr[i].view, NULL); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewStringObj(s, -1)); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewBooleanObj(tablePtr[i].needsterminal)); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewBooleanObj(tablePtr[i].copiousoutput)); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewStringObj(tablePtr[i].description,-1)); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewStringObj(tablePtr[i].bitmap, -1)); Tcl_SetObjResult(interp, rPtr); return TCL_OK; } Tcl_SetResult(interp, "{} 0 0 {} {}", TCL_STATIC); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatMailcapReloadCmd -- * * This is just a wrapper which calls MailcapReload * * Results: * A standard tcl result. * * Side effects: * The mailcap files will be loaded. * * *---------------------------------------------------------------------- */ int RatMailcapReloadCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { MailcapReload(interp); return TCL_OK; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratMessage.c000066400000000000000000002102311137544547100210100ustar00rootroot00000000000000/* * ratMessage.c -- * * This file contains code which implements the message entities. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include #include #include "ratStdFolder.h" #include "ratPGP.h" #include "osdep.h" /* * An array of commands. It contains one entry for each internal message * type (as defined by RatMessageType). */ static MessageProcInfo *messageProcInfo = NULL; /* * The number of replies created. This is used to create new unique * message handlers. */ static int numReplies = 0; /* * The number of message entities created. This is used to create new * unique command names. */ static int numBodies = 0; static void RatCreateBody(Tcl_Interp *interp, MessageInfo *msgPtr); static void RatCreateChildren(Tcl_Interp *interp, BodyInfo *bodyInfoPtr); static void RatBodyDelete(Tcl_Interp *interp, BodyInfo *bodyInfoPtr); static BodyInfo *RatFindFirstText(BodyInfo *bodyInfoPtr); static CONST84 char *RatGetCitation(Tcl_Interp *interp, MessageInfo *msgPtr); static void RatCiteMessage(Tcl_Interp *interp, Tcl_Obj *dstObjPtr, CONST84 char *src, CONST84 char *myCitation); static int RatMessageDeleteAttachments(Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_Obj *attachments); static int RatDeleteAttachment(Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_DString *ds, Tcl_Obj *spec); static char* RatFindAttachment(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, char *text, Tcl_Obj *spec, int spec_index, char **boundary); extern long unix_create (MAILSTREAM *stream,char *mailbox); /* *---------------------------------------------------------------------- * * RatInitMessages -- * * Initialize the message data structures. * * Results: * None. * * Side effects: * The messageProcInfo array is allocated and initialized. * * *---------------------------------------------------------------------- */ void RatInitMessages() { messageProcInfo = (MessageProcInfo*)ckalloc(3*sizeof(MessageProcInfo)); RatStdMessagesInit(&messageProcInfo[RAT_CCLIENT_MESSAGE]); RatDbMessagesInit(&messageProcInfo[RAT_DBASE_MESSAGE]); RatFrMessagesInit(&messageProcInfo[RAT_FREE_MESSAGE]); } /* *---------------------------------------------------------------------- * * RatMessageCmd -- * * Main std mail entity procedure. This routine implements the mail * commands mentioned in ../INTERFACE. * * Results: * A standard tcl result. * * Side effects: * many. * * *---------------------------------------------------------------------- */ int RatMessageCmd(ClientData clientData,Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { MessageInfo *msgPtr = (MessageInfo*) clientData; Tcl_Obj *rPtr; if (objc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " option ?arg?\"", (char *) NULL); return TCL_ERROR; } if (!strcmp(Tcl_GetString(objv[1]), "headers")) { if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " headers\"", (char *) NULL); return TCL_ERROR; } return RatMessageGetHeader(interp, (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr)); } else if (!strcmp(Tcl_GetString(objv[1]), "body")) { if (!msgPtr->bodyInfoPtr) { RatCreateBody(interp, msgPtr); } Tcl_SetResult(interp, msgPtr->bodyInfoPtr->cmdName, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "rerunPGP")) { RatPGPBodyCheck(interp, messageProcInfo, &msgPtr->bodyInfoPtr); Tcl_CreateObjCommand(interp, msgPtr->bodyInfoPtr->cmdName, RatBodyCmd, (ClientData) msgPtr->bodyInfoPtr, NULL); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "rawText")) { rPtr = Tcl_NewObj(); Tcl_AppendToObj(rPtr, (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr), -1); Tcl_AppendToObj(rPtr, "\r\n", 2); Tcl_AppendToObj(rPtr, (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr), -1); Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "get")) { ENVELOPE *env = (*messageProcInfo[msgPtr->type].envelopeProc)(msgPtr); int i; if (objc < 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " get fields\"", (char *) NULL); return TCL_ERROR; } for (i=2; ireturn_path); } else if (!strcasecmp(Tcl_GetString(objv[i]), "from")) { RatInitAddresses(interp, env->from); } else if (!strcasecmp(Tcl_GetString(objv[i]), "sender")) { RatInitAddresses(interp, env->sender); } else if (!strcasecmp(Tcl_GetString(objv[i]), "reply_to")) { RatInitAddresses(interp, env->reply_to); } else if (!strcasecmp(Tcl_GetString(objv[i]), "to")) { RatInitAddresses(interp, env->to); } else if (!strcasecmp(Tcl_GetString(objv[i]), "cc")) { RatInitAddresses(interp, env->cc); } else if (!strcasecmp(Tcl_GetString(objv[i]), "bcc")) { RatInitAddresses(interp, env->bcc); } else { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "bad field \"",Tcl_GetString(objv[i]), "\": must be one of return_path, from, sender, ", "reply_to, to, cc or bcc", (char*)NULL); return TCL_ERROR; } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "reply")) { /* * Construct a reply to a message. We should really handle * different character sets here. /MaF */ ENVELOPE *env = (*messageProcInfo[msgPtr->type].envelopeProc)(msgPtr); char handler[32], *cPtr; BodyInfo *bodyInfoPtr; unsigned long bodylength; ADDRESS *adrPtr; char *dataPtr, *role; Tcl_DString ds; Tcl_Obj *oPtr, *vPtr; Tcl_DStringInit(&ds); if (objc != 4) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " reply to role\"", (char *) NULL); return TCL_ERROR; } role = Tcl_GetString(objv[3]); sprintf(handler, "reply%d", numReplies++); if (!strcasecmp(Tcl_GetString(objv[2]), "sender")) { /* * We should construct a reply which should go only to one person. * We look for the address in the following fields: * Reply-To:, From:. Sender:, From * As sson as an address is found the search stops. */ if (env->reply_to) { adrPtr = env->reply_to; } else if (env->from) { adrPtr = env->from; } else if (env->sender) { adrPtr = env->sender; } else { adrPtr = env->return_path; } for (;adrPtr; adrPtr = adrPtr->next) { RatAddressTranslate(interp, adrPtr); if (adrPtr->mailbox) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, ", ", -1); } Tcl_DStringAppend(&ds, RatAddressFull(interp, adrPtr, role),-1); } } Tcl_SetVar2(interp, handler, "to", Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY); } else { /* * We should construct a reply which goes to everybody who has * recieved this message. This is done by first collecting all * addresses found in: Reply-To:, From:, Sender:, To: and Cc:. * Then go though this list and eliminate myself and any * duplicates. Now we use the first element of the list as To: * and the rest as Cc:. */ ADDRESS **recipientPtrPtr =(ADDRESS**)ckalloc(16*sizeof(ADDRESS*)); int numAllocated = 16; int numRecipients = 0; int inList = 0; ADDRESS *to = NULL; int i, j; #define SCANLIST(x) for (adrPtr = (x); adrPtr; adrPtr = adrPtr->next) { \ if (numRecipients == numAllocated) { \ numAllocated += 16; \ recipientPtrPtr = (ADDRESS**)ckrealloc( \ recipientPtrPtr, \ numAllocated*sizeof(ADDRESS*)); \ } \ recipientPtrPtr[numRecipients++] = adrPtr; \ } SCANLIST(env->reply_to); if (NULL == env->reply_to) { SCANLIST(env->from); } SCANLIST(env->to); SCANLIST(env->cc); for (i=0; ihost) { inList = (inList)? 0 : 1; continue; } if (RatAddressIsMe(interp, adrPtr, 1)) { continue; } RatAddressTranslate(interp, adrPtr); for (j=0; jsubject && *env->subject) { int match; cPtr = RatDecodeHeader(interp, env->subject, 0); Tcl_DStringSetLength(&ds, 0); Tcl_DStringAppendElement(&ds, "regexp"); Tcl_DStringAppendElement(&ds, "-nocase"); Tcl_DStringAppendElement(&ds, Tcl_GetVar2(interp, "option","re_regexp",TCL_GLOBAL_ONLY)); Tcl_DStringAppendElement(&ds, cPtr); Tcl_DStringAppendElement(&ds, "reply_match"); if (TCL_OK == Tcl_EvalEx(interp, Tcl_DStringValue(&ds), -1, TCL_EVAL_DIRECT) && NULL != (oPtr = Tcl_GetObjResult(interp)) && TCL_OK == Tcl_GetBooleanFromObj(interp, oPtr, &match) && match) { CONST84 char *s = Tcl_GetVar(interp, "reply_match", 0); if (!strncmp(s, cPtr, strlen(s))) { cPtr += strlen(s); while (isspace(*cPtr)) cPtr++; } } oPtr = Tcl_NewStringObj("Re: ", 4); Tcl_AppendToObj(oPtr, cPtr, -1); Tcl_SetVar2Ex(interp, handler,"subject", oPtr,TCL_GLOBAL_ONLY); } else { Tcl_SetVar2(interp, handler, "subject", Tcl_GetVar2(interp, "option","no_subject",TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY); } if (env->references || env->in_reply_to) { Tcl_DStringSetLength(&ds, 0); if (env->references) { Tcl_DStringAppend(&ds, env->references, -1); } else { Tcl_DStringAppend(&ds, env->in_reply_to, -1); } if (env->message_id) { Tcl_DStringAppend(&ds, " ", 1); Tcl_DStringAppend(&ds, env->message_id, -1); } Tcl_SetVar2(interp, handler, "references", Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY); } else if (env->message_id) { Tcl_SetVar2(interp, handler, "references", env->message_id, TCL_GLOBAL_ONLY); } if (env->message_id) { Tcl_SetVar2(interp, handler, "in_reply_to", env->message_id, TCL_GLOBAL_ONLY); } bodyInfoPtr = RatFindFirstText(msgPtr->bodyInfoPtr); if (bodyInfoPtr && (NULL != (dataPtr = (*messageProcInfo[bodyInfoPtr->msgPtr->type].fetchBodyProc) (bodyInfoPtr, &bodylength)))) { CONST84 char *alias, *attrFormat, *charset = "us-ascii", *citation; ListExpression *exprPtr; PARAMETER *parameter; Tcl_DString *decBPtr; BODY *bodyPtr; int wrap; bodyPtr = bodyInfoPtr->bodyPtr; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if ( 0 == strcasecmp("charset", parameter->attribute)) { charset = parameter->value; } } if ((alias = Tcl_GetVar2(interp, "charsetAlias", (char*)charset, TCL_GLOBAL_ONLY))) { charset = alias; } decBPtr = RatDecode(interp, bodyPtr->encoding, dataPtr, bodylength, charset); attrFormat = Tcl_GetVar2(interp, "option", "attribution", TCL_GLOBAL_ONLY); if (attrFormat && strlen(attrFormat) && (exprPtr = RatParseList(attrFormat, NULL))) { oPtr = RatDoList(interp, exprPtr, messageProcInfo[msgPtr->type].getInfoProc, (ClientData)msgPtr, 0); RatFreeListExpression(exprPtr); Tcl_AppendToObj(oPtr, "\n", 1); } else { oPtr = Tcl_NewObj(); } Tcl_IncrRefCount(oPtr); citation = RatGetCitation(interp, msgPtr); RatCiteMessage(interp, oPtr, Tcl_DStringValue(decBPtr), citation); Tcl_DStringFree(decBPtr); vPtr = Tcl_GetVar2Ex(interp,"option","wrap_cited",TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, vPtr, &wrap); if (wrap) { Tcl_Obj *nPtr; nPtr = RatWrapMessage(interp, oPtr); oPtr = nPtr; } Tcl_SetVar2Ex(interp, handler, "data", oPtr, TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, handler, "data_tags", "Cited noWrap no_spell", TCL_GLOBAL_ONLY); Tcl_DecrRefCount(oPtr); } Tcl_SetResult(interp, handler, TCL_VOLATILE); Tcl_DStringFree(&ds); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "copy")) { char flags[128], date[128], *spec, *name; Tcl_Obj *defPtr, **dobjv, **eobjv, *oPtr; MAILSTREAM *stream = NULL; struct stat sbuf; Tcl_DString ds, specBuf; STRING string; int dobjc, eobjc, result, i, freeListObjv = 0, specBufUse = 0; RatFolderInfo *infoPtr; if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0])," copy vfolder_def\"", NULL); return TCL_ERROR; } defPtr = objv[2]; infoPtr = RatGetOpenFolder(interp, defPtr, 1); if (infoPtr) { name = msgPtr->name; result = RatFolderInsert(interp, infoPtr, 1, &name); RatFolderClose(interp, infoPtr, 0); return result; } Tcl_ListObjGetElements(interp, defPtr, &dobjc, &dobjv); /* * If the destination is dbase then we call RatInsert */ if (!strcmp("dbase", Tcl_GetString(dobjv[1]))) { Tcl_ListObjGetElements(interp, dobjv[5], &eobjc, &eobjv); for (i=0; itype].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_MAIL_REAL, 0); if (oPtr) { name = Tcl_GetString(oPtr); } if (!name) { struct passwd *pwPtr = GetPw(); name = pwPtr->pw_name; } defPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, defPtr, dobjv[0]); Tcl_IncrRefCount(dobjv[0]); Tcl_ListObjAppendElement(interp, defPtr, Tcl_NewStringObj("file", 4)); Tcl_ListObjAppendElement(interp, defPtr, dobjv[2]); Tcl_IncrRefCount(dobjv[2]); oPtr = Tcl_DuplicateObj(dobjv[3]); Tcl_AppendToObj(oPtr, "/", 1); for (i=0; name[i] && name[i] != '@'; i++); Tcl_AppendToObj(oPtr, name, i); Tcl_ListObjAppendElement(interp, defPtr, oPtr); freeListObjv = 1; Tcl_ListObjGetElements(interp, defPtr, &dobjc, &dobjv); } /* * Try to create nonexisting files */ if (!strcmp("file", Tcl_GetString(dobjv[1]))) { name = RatGetFolderSpec(interp, defPtr); if (!strcmp(name, "INBOX")) { name = sysinbox(); } if (0 != stat(name, &sbuf)) { unix_create(NIL, name); } } /* * Try the case where both source and destination are c-client * messages of sufficiently same type. */ if (RAT_CCLIENT_MESSAGE == msgPtr->type && RatStdEasyCopyingOK(interp, msgPtr, defPtr)) { result = RatStdMessageCopy(interp, msgPtr, RatGetFolderSpec(interp, defPtr)); goto end_copy; } /* * Open a folder and get the stream */ spec = RatGetFolderSpec(interp, defPtr); if (TCL_OK == OpenStdFolder(interp, spec, NULL, 1, &stream)) { Tcl_DStringInit(&ds); RatMessageGet(interp, msgPtr, &ds, flags, sizeof(flags), date, sizeof(date)); if ('\n' != Tcl_DStringValue(&ds)[Tcl_DStringLength(&ds)-1]) { Tcl_DStringAppend(&ds, "\r\n", 2); } INIT(&string, mail_string, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); RatPurgeFlags(flags, 1); if (!mail_append_full(stream, spec, flags, date, &string)) { CloseStdFolder(interp, stream); Tcl_SetResult(interp, "mail_append failed", TCL_STATIC); result = TCL_ERROR; Tcl_DStringFree(&ds); goto end_copy; } if (infoPtr) { RatFolderClose(interp, infoPtr, 0); } else { CloseStdFolder(interp, stream); } Tcl_DStringFree(&ds); result = TCL_OK; } else { result = TCL_ERROR; } end_copy: if (specBufUse) { Tcl_DStringFree(&specBuf); } if (freeListObjv) { Tcl_DecrRefCount(defPtr); } return result; } else if (!strcmp(Tcl_GetString(objv[1]), "list")) { ListExpression *exprPtr; Tcl_Obj *oPtr; if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " list format\"", (char *) NULL); return TCL_ERROR; } if (NULL == (exprPtr = RatParseList(Tcl_GetString(objv[2]), NULL))) { Tcl_SetResult(interp, "Illegal list format", TCL_STATIC); return TCL_ERROR; } oPtr = RatDoList(interp, exprPtr, messageProcInfo[msgPtr->type].getInfoProc, (ClientData)msgPtr, 0); Tcl_SetObjResult(interp, oPtr); RatFreeListExpression(exprPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "pgp")) { int sign, encrypt; if (objc != 7) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " pgp sign encrypt role signer enc_rcpts\"", (char *) NULL); return TCL_ERROR; } if (msgPtr->type != RAT_FREE_MESSAGE) { Tcl_SetResult(interp, "pgp only works on internal messages", TCL_STATIC); return TCL_ERROR; } Tcl_GetBooleanFromObj(interp, objv[2], &sign); Tcl_GetBooleanFromObj(interp, objv[3], &encrypt); return RatFrMessagePGP(interp, msgPtr, sign, encrypt, Tcl_GetString(objv[4]), Tcl_GetString(objv[5]), objv[6]); } else if (!strcmp(Tcl_GetString(objv[1]), "remove_internal")) { if (msgPtr->type != RAT_FREE_MESSAGE) { Tcl_SetResult(interp, "remove_internal only works on internal " "messages", TCL_STATIC); return TCL_ERROR; } return RatFrMessageRemoveInternal(interp, msgPtr); } else if (!strcmp(Tcl_GetString(objv[1]), "duplicate")) { char *cmd; Tcl_Obj *oPtr = Tcl_NewObj(); Tcl_AppendToObj(oPtr, (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr), -1); Tcl_AppendToObj(oPtr, "\r\n", 2); Tcl_AppendToObj(oPtr, (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr), -1); Tcl_IncrRefCount(oPtr); cmd = RatFrMessageCreate(interp, Tcl_GetString(oPtr), strlen(Tcl_GetString(oPtr)), NULL); Tcl_DecrRefCount(oPtr); Tcl_SetResult(interp, cmd, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "delete_attachments")) { if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), "delete_attachments" " attachments\"", (char*)NULL); return TCL_ERROR; } /* Create body if needed */ if (!msgPtr->bodyInfoPtr) { RatCreateBody(interp, msgPtr); } if (msgPtr->bodyInfoPtr->bodyPtr->type != TYPEMULTIPART) { Tcl_AppendResult(interp, "not a multipart message", NULL); return TCL_ERROR; } return RatMessageDeleteAttachments(interp, msgPtr, objv[2]); } else if (!strcmp(Tcl_GetString(objv[1]), "dbinfo_get")) { if (messageProcInfo[msgPtr->type].dbinfoGetProc != NULL) { Tcl_Obj *oPtr; oPtr = (*messageProcInfo[msgPtr->type].dbinfoGetProc)(msgPtr); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } else { return TCL_ERROR; } } else { Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[1]), "\": must be one of header, body, rawText reply, get" ", pgp, remove_internal, duplicate or " "delete_attachments", (char*)NULL); return TCL_ERROR; } } /* *---------------------------------------------------------------------- * * RatCreateBody -- * * Create the body of a message * * Results: * None * * Side effects: * msgPtr->bodyInfoPtr is filled in a command is created * * *---------------------------------------------------------------------- */ static void RatCreateBody(Tcl_Interp *interp, MessageInfo *msgPtr) { msgPtr->bodyInfoPtr = (*messageProcInfo[msgPtr->type].createBodyProc)(interp, msgPtr); RatPGPBodyCheck(interp, messageProcInfo, &msgPtr->bodyInfoPtr); Tcl_CreateObjCommand(interp, msgPtr->bodyInfoPtr->cmdName, RatBodyCmd, (ClientData) msgPtr->bodyInfoPtr, NULL); } /* *---------------------------------------------------------------------- * * RatMessageGetContent -- * * Gets the content of the message * * Results: * Is left in header and body. * * Side effects: * None. * * *---------------------------------------------------------------------- */ void RatMessageGetContent(Tcl_Interp *interp, MessageInfo *msgPtr, char **header, char **body) { *header = (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr); *body = (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr); } /* *---------------------------------------------------------------------- * * RatMessageGetHeader -- * * Gets the header of a message * * Results: * The header is returned as a list in the result area. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatMessageGetHeader(Tcl_Interp *interp, char *srcHeader) { char *header, *listArgv[2]; char *dstPtr, *srcPtr = srcHeader, *cPtr, *tPtr; Tcl_Obj *oPtr = Tcl_NewObj(), *fPtr[2]; int adr; if (!srcHeader) { RatLog(interp, RAT_FATAL, Tcl_GetStringResult(interp), RATLOG_TIME); exit(1); } header = (char*) ckalloc (strlen(srcHeader)+2); if (!strncmp("From ", srcPtr, 5)) { while ('\n' != *srcPtr) { srcPtr++; } if ('\r' == *(++srcPtr)) { srcPtr++; } } while (*srcPtr) { dstPtr = header; listArgv[0] = dstPtr = header; while (*srcPtr && ':' != *srcPtr && ' ' != *srcPtr) { *dstPtr++ = *srcPtr++; } *dstPtr = '\0'; fPtr[0] = Tcl_NewStringObj(header, -1); cPtr = ++dstPtr; if (*srcPtr) { do { srcPtr++; } while (' ' == *srcPtr || '\t' == *srcPtr); } do { for (; *srcPtr && '\n' != *srcPtr; srcPtr++) { if ('\r' != *srcPtr) { *dstPtr++ = *srcPtr; } } while ('\n' == *srcPtr || '\r' == *srcPtr) { srcPtr++; } } while (*srcPtr && (' ' == *srcPtr || '\t' == *srcPtr)); *dstPtr = '\0'; tPtr = cPtr; if (0 == strncasecmp("resent-", tPtr, 7)) { tPtr += 7; } if (!strcasecmp(tPtr, "to") || !strcasecmp(tPtr, "cc") || !strcasecmp(tPtr, "bcc") || !strcasecmp(tPtr, "from") || !strcasecmp(tPtr, "sender") || !strcasecmp(tPtr, "reply-to")) { adr = 1; } else { adr = 0; } fPtr[1] = Tcl_NewStringObj(RatDecodeHeader(interp, cPtr, adr), -1); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewListObj(2, fPtr)); } ckfree(header); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatMessageDelete -- * * Deletes the given message. * * Results: * None. * * Side effects: * The message and all its bodyparts are deleted from the interpreter * and all the structures are freed. * * *---------------------------------------------------------------------- */ int RatMessageDelete(Tcl_Interp *interp, char *msgName) { Tcl_CmdInfo cmdInfo; MessageInfo *msgPtr; char buf[256]; int i; if (0 == Tcl_GetCommandInfo(interp, msgName, &cmdInfo)) { Tcl_AppendResult(interp, "No such message: ", msgName, NULL); return TCL_ERROR; } msgPtr = (MessageInfo*)cmdInfo.objClientData; (*messageProcInfo[msgPtr->type].msgDeleteProc)(msgPtr); if (msgPtr->bodyInfoPtr) { if (msgPtr->bodyInfoPtr->altPtr) { RatBodyDelete(interp, msgPtr->bodyInfoPtr->altPtr); } if (msgPtr->bodyInfoPtr->decodedTextPtr) { Tcl_DStringFree(msgPtr->bodyInfoPtr->decodedTextPtr); ckfree(msgPtr->bodyInfoPtr->decodedTextPtr); } if (msgPtr->bodyInfoPtr->secPtr) { RatBodyDelete(interp, msgPtr->bodyInfoPtr->secPtr); } else { RatBodyDelete(interp, msgPtr->bodyInfoPtr); } } snprintf(buf, sizeof(buf), "msgInfo_%s", msgPtr->name); Tcl_UnsetVar(interp, buf, TCL_GLOBAL_ONLY); Tcl_DeleteCommand(interp, msgName); for (i=0; iinfo)/sizeof(*msgPtr->info); i++) { if (msgPtr->info[i]) { Tcl_DecrRefCount(msgPtr->info[i]); } } ckfree(msgPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * CreateBodyInfo -- * * Create and somewhat initialize a BodyInfo structure. * * Results: * A pointer to a BodyInfo structure. * * Side effects: * None. * * *---------------------------------------------------------------------- */ BodyInfo* CreateBodyInfo(Tcl_Interp *interp, MessageInfo *msgPtr, BODY *bodyPtr) { BodyInfo *bodyInfoPtr; int pad = sizeof(char*) - sizeof(BodyInfo)%sizeof(char*); if (sizeof(char*) == pad) { pad = 0; } bodyInfoPtr = (BodyInfo*)ckalloc(sizeof(BodyInfo)+pad+16); bodyInfoPtr->cmdName = (char*)bodyInfoPtr + pad + sizeof(BodyInfo); sprintf(bodyInfoPtr->cmdName, "RatBody%d", numBodies++); bodyInfoPtr->firstbornPtr = NULL; bodyInfoPtr->nextPtr = NULL; bodyInfoPtr->containedEntity = NULL; bodyInfoPtr->bodyPtr = bodyPtr; bodyInfoPtr->type = msgPtr->type; bodyInfoPtr->msgPtr = msgPtr; bodyInfoPtr->secPtr = NULL; bodyInfoPtr->altPtr = NULL; bodyInfoPtr->decodedTextPtr = NULL; bodyInfoPtr->encoded = 0; bodyInfoPtr->sigStatus = RAT_UNSIGNED; bodyInfoPtr->pgpOutput = NULL; RatDecodeParameters(interp, bodyPtr->parameter); RatDecodeParameters(interp, bodyPtr->disposition.parameter); return bodyInfoPtr; } /* *---------------------------------------------------------------------- * * RatBodyCmd -- * * Main bodypart entity procedure. This routine implements the * bodypart commands mentioned in ../INTERFACE. * * Results: * A standard tcl result. * * Side effects: * many. * * *---------------------------------------------------------------------- */ int RatBodyCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { BodyInfo *bodyInfoPtr = (BodyInfo*) clientData; BODY *bodyPtr = bodyInfoPtr->bodyPtr; unsigned long length; Tcl_Obj *ov[2], *rPtr; if (objc < 2) { goto usage; } if (!strcmp(Tcl_GetString(objv[1]), "children")) { BodyInfo *partInfoPtr; if (TYPEMULTIPART != bodyPtr->type) { return TCL_OK; } if (!bodyInfoPtr->firstbornPtr) { RatCreateChildren(interp, bodyInfoPtr); } rPtr = Tcl_NewObj(); for (partInfoPtr = bodyInfoPtr->firstbornPtr; partInfoPtr; partInfoPtr = partInfoPtr->nextPtr) { Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewStringObj(partInfoPtr->cmdName, -1)); } Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "message")) { char *body; if (!bodyInfoPtr->containedEntity) { if (TYPEMESSAGE != bodyPtr->type && !strcasecmp(bodyPtr->subtype, "rfc822")) { Tcl_SetResult(interp, "Not an message/rfc822 bodypart", TCL_STATIC); return TCL_ERROR; } body = (*messageProcInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length); if (body && *body) { bodyInfoPtr->containedEntity = RatFrMessageCreate(interp, body, length, NULL); Tcl_SetResult(interp, bodyInfoPtr->containedEntity,TCL_STATIC); } else { Tcl_SetResult(interp,"Failed to fetch mail body. " "The message is damaged", TCL_STATIC); return TCL_ERROR; } } else { Tcl_SetResult(interp, bodyInfoPtr->containedEntity, TCL_STATIC); } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "type")) { Tcl_SetObjResult(interp, RatBodyType(bodyInfoPtr)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "params")) { PARAMETER *parameter; rPtr = Tcl_NewObj(); for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { ov[0] = Tcl_NewStringObj(parameter->attribute, -1); ov[1] = Tcl_NewStringObj(parameter->value, -1); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewListObj(2, ov)); } Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "parameter")) { PARAMETER *parameter; if (objc != 3) goto usage; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if (0 == strcasecmp(Tcl_GetString(objv[2]),parameter->attribute)) { Tcl_SetResult(interp, parameter->value, TCL_VOLATILE); break; } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "disp_type")) { Tcl_SetResult(interp, bodyPtr->disposition.type, TCL_VOLATILE); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "disp_parm")) { PARAMETER *parameter; rPtr = Tcl_NewObj(); for (parameter = bodyPtr->disposition.parameter; parameter; parameter = parameter->next) { ov[0] = Tcl_NewStringObj(parameter->attribute, -1); ov[1] = Tcl_NewStringObj(parameter->value, -1); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewListObj(2, ov)); } Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "id")) { if (bodyPtr->id) { Tcl_SetResult(interp, bodyPtr->id, TCL_VOLATILE); } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "description")) { if (bodyPtr->description) { char *desc = RatDecodeHeader(interp, bodyPtr->description, 0); Tcl_SetResult(interp, desc, TCL_VOLATILE); } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "size")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(bodyPtr->size.bytes)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "lines")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(bodyPtr->size.lines)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "encoding")) { char *enc; switch(bodyPtr->encoding) { case ENC7BIT: enc = "7bit"; break; case ENC8BIT: enc = "8bit"; break; case ENCBINARY: enc = "binary"; break; case ENCBASE64: enc = "base64"; break; case ENCQUOTEDPRINTABLE:enc = "quoted-printable"; break; default: enc = "unkown"; break; } Tcl_SetResult(interp, enc, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "isGoodCharset")) { PARAMETER *parameter; CONST84 char *charset = "us-ascii", *alias; int b; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if ( 0 == strcasecmp("charset", parameter->attribute)) { charset = parameter->value; break; } } if ((alias = Tcl_GetVar2(interp, "charsetAlias", charset, TCL_GLOBAL_ONLY))) { charset = alias; } b = RatGetEncoding(interp, charset) ? 1 : 0; Tcl_SetObjResult(interp, Tcl_NewBooleanObj(b)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "data")) { char *isCharset; int encoded; if (3 != objc && 4 != objc) goto usage; if (TCL_OK != Tcl_GetBooleanFromObj(interp, objv[2], &encoded)) { return TCL_ERROR; } if (objc == 4) { isCharset = Tcl_GetString(objv[3]); } else { isCharset = NULL; } Tcl_SetObjResult(interp, RatBodyData(interp, bodyInfoPtr, encoded, isCharset)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "saveData")) { int encoded, convertNL; Tcl_Channel channel; if (5 != objc) goto usage; if (NULL==(channel=Tcl_GetChannel(interp,Tcl_GetString(objv[2]),NULL)) || TCL_OK != Tcl_GetBooleanFromObj(interp, objv[3], &encoded) || TCL_OK != Tcl_GetBooleanFromObj(interp, objv[4], &convertNL)) { goto usage; } return RatBodySave(interp, channel, bodyInfoPtr, encoded, convertNL); } else if (!strcmp(Tcl_GetString(objv[1]), "getShowCharset")) { CONST84 char *c_charset = "us-ascii", *alias; PARAMETER *parmPtr; char *charset; if (TYPETEXT != bodyPtr->type) { Tcl_AppendElement(interp, "good"); Tcl_AppendElement(interp, "us-ascii"); return TCL_OK; } for (parmPtr = bodyPtr->parameter; parmPtr; parmPtr = parmPtr->next) { if (!strcasecmp(parmPtr->attribute, "charset")) { c_charset = parmPtr->value; break; } } charset = cpystr(c_charset); lcase((unsigned char*)charset); /* * - See if this charset is an alias and resolve that if so. * - Check if this is a charset we do have a font for * return good and the charset in that case. * - Check if this is a character set we know about and convert. * return lose if that is the case. * - return none. */ if (NULL != ( alias = Tcl_GetVar2(interp, "charsetAlias", charset, TCL_GLOBAL_ONLY))) { ckfree(charset); charset = cpystr(alias); } if (Tcl_GetVar2(interp, "fontEncoding", charset, TCL_GLOBAL_ONLY)) { ov[0] = Tcl_NewStringObj("good", 4); ov[1] = Tcl_NewStringObj("charset", -1); Tcl_SetObjResult(interp, Tcl_NewListObj(2, ov)); ckfree(charset); return TCL_OK; } /* * This converting part is not implemented yet */ ov[0] = Tcl_NewStringObj("none", 4); ov[1] = Tcl_NewStringObj("", 0); Tcl_SetObjResult(interp, Tcl_NewListObj(2, ov)); ckfree(charset); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "findShowCommand")) { return RatMcapFindCmd(interp, bodyInfoPtr); } else if (!strcmp(Tcl_GetString(objv[1]), "filename")) { PARAMETER *parmPtr; char *filename = NULL, *delim, *c; int gen = 0; if (objc == 3 && !strcmp("gen_if_empty", Tcl_GetString(objv[2]))) { gen = 1; } for (parmPtr = bodyPtr->disposition.parameter; parmPtr; parmPtr = parmPtr->next) { if (!strcasecmp(parmPtr->attribute, "filename") || !strcasecmp(parmPtr->attribute, "name")) { filename = parmPtr->value; break; } } for (parmPtr = bodyPtr->parameter; !filename && parmPtr; parmPtr = parmPtr->next) { if (!strcasecmp(parmPtr->attribute, "filename") || !strcasecmp(parmPtr->attribute, "name")) { filename = parmPtr->value; break; } } if (!filename && bodyPtr->description && !strchr(bodyPtr->description, ' ')) { filename = bodyPtr->description; } if (!filename && gen) { filename = RatGenId(); } for (c=filename; c && *c; c++) { if (!isalnum((int)*c) && NULL == strchr("_.,-=+", *c)) { *c = '_'; } } if (filename) { delim = strrchr(filename, '/'); if (delim) { Tcl_SetResult(interp, delim+1, TCL_VOLATILE); } else { Tcl_SetResult(interp, filename, TCL_VOLATILE); } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "encoded")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(bodyInfoPtr->encoded)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "sigstatus")) { char *status = NULL; switch (bodyInfoPtr->sigStatus) { case RAT_UNSIGNED: status = "pgp_none"; break; case RAT_UNCHECKED: status = "pgp_unchecked"; break; case RAT_SIG_GOOD: status = "pgp_good"; break; case RAT_SIG_BAD: status = "pgp_bad"; break; case RAT_SIG_ERR: status = "pgp_err"; break; case RAT_PGP_ABORT: status = "pgp_abort"; break; } Tcl_SetResult(interp, status, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "checksig")) { RatPGPChecksig(interp, messageProcInfo, bodyInfoPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "getPGPOutput")) { if (bodyInfoPtr->pgpOutput) { Tcl_SetResult(interp, Tcl_DStringValue(bodyInfoPtr->pgpOutput), TCL_VOLATILE); } else { Tcl_ResetResult(interp); } return TCL_OK; } usage: Tcl_AppendResult(interp, "Illegal argument string", NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatCreateChildren -- * * Creates the children of a multipart body * * Results: * None. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static void RatCreateChildren(Tcl_Interp *interp, BodyInfo *bodyInfoPtr) { BodyInfo **partInfoPtrPtr; (*messageProcInfo[bodyInfoPtr->type].makeChildrenProc) (interp, bodyInfoPtr); for (partInfoPtrPtr = &bodyInfoPtr->firstbornPtr; *partInfoPtrPtr; partInfoPtrPtr = &(*partInfoPtrPtr)->nextPtr) { RatPGPBodyCheck(interp, messageProcInfo, partInfoPtrPtr); Tcl_CreateObjCommand(interp, (*partInfoPtrPtr)->cmdName, RatBodyCmd, (ClientData)(*partInfoPtrPtr), NULL); } } /* *---------------------------------------------------------------------- * * RatBodyDelete -- * * Deletes the given body. * * Results: * None. * * Side effects: * The bodypart and all its siblings are deleted from the interpreter * and all the structures are freed. * * *---------------------------------------------------------------------- */ static void RatBodyDelete(Tcl_Interp *interp, BodyInfo *bodyInfoPtr) { BodyInfo *siblingInfoPtr, *nextSiblingInfoPtr; Tcl_DeleteCommand(interp, bodyInfoPtr->cmdName); siblingInfoPtr = bodyInfoPtr->firstbornPtr; (*messageProcInfo[bodyInfoPtr->type].bodyDeleteProc)(bodyInfoPtr); while (siblingInfoPtr) { nextSiblingInfoPtr = siblingInfoPtr->nextPtr; RatBodyDelete(interp, siblingInfoPtr); siblingInfoPtr = nextSiblingInfoPtr; } if (bodyInfoPtr->containedEntity) { RatMessageDelete(interp, bodyInfoPtr->containedEntity); } if (bodyInfoPtr->pgpOutput) { Tcl_DStringFree(bodyInfoPtr->pgpOutput); ckfree(bodyInfoPtr->pgpOutput); } ckfree(bodyInfoPtr); } /* *---------------------------------------------------------------------- * * RatMessageGet -- * * Retrieves a message in textual form. The text is placed in the * supplied Tcl_DString. * * Results: * No result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ void RatMessageGet(Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_DString *ds, char *flags, size_t flaglen, char *date, size_t datelen) { char *data, *status, *status_end; int seen; Tcl_Obj *oPtr; data = (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr); status = strstr(data, "\r\nStatus: "); if (status) { status += 2; Tcl_DStringAppend(ds, data, status-data); status_end = strstr(status, "\r\n"); if (status_end && strlen(status_end+2)) { Tcl_DStringAppend(ds, status_end+2, -1); } } else { Tcl_DStringAppend(ds, data, -1); } if (msgPtr->folderInfoPtr) { seen = (*msgPtr->folderInfoPtr->getFlagProc)(msgPtr->folderInfoPtr, interp, msgPtr->msgNo, RAT_SEEN); } else { seen = 1; } Tcl_DStringAppend(ds, "\r\n", 2); data = (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr); Tcl_DStringAppend(ds, data, strlen(data)); if (!seen) { (*msgPtr->folderInfoPtr->setFlagProc)(msgPtr->folderInfoPtr, interp, &msgPtr->msgNo, 1, RAT_SEEN, 0); } if (flags) { oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_FLAGS, 0); strlcpy(flags, Tcl_GetString(oPtr), flaglen); oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_DATE_IMAP4, 0); strlcpy(date, Tcl_GetString(oPtr), datelen); } } /* *---------------------------------------------------------------------- * * RatInsertCmd -- * * Inserts the given message into the database * * Results: * A standard Tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatInsertCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_CmdInfo cmdInfo; MessageInfo *msgPtr; if (objc != 5) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " msgId keywords exDate exType\"", (char *) NULL); return TCL_ERROR; } if (0 == Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo)) { Tcl_AppendResult(interp, "No such message: ", Tcl_GetString(objv[1]), NULL); return TCL_ERROR; } msgPtr = (MessageInfo*)cmdInfo.objClientData; return RatInsertMsg(interp, msgPtr, Tcl_GetString(objv[2]), Tcl_GetString(objv[3]), Tcl_GetString(objv[4])); } /* *---------------------------------------------------------------------- * * RatInsertMsg -- * * Inserts the given message into the database * * Results: * A standard Tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatInsertMsg (Tcl_Interp *interp, MessageInfo *msgPtr, char *keywords, char *exDate, char *exType) { char *to, *from, *cc, *subject, *msgid, *ref, *flags, *key, *value; MESSAGECACHE elt; int listObjc, elemObjc; char *eFrom, *header, *body, *s, *e, *d; Tcl_DString dString; int result, i; struct tm tm; time_t date = 0, exTime; Tcl_Obj *oPtr, **listObjv, **elemObjv; to = from = cc = subject = msgid = ref = flags = NULL; if (TCL_OK != RatMessageGetHeader(interp, (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr))) { return TCL_ERROR; } oPtr = Tcl_GetObjResult(interp); Tcl_ListObjGetElements(interp, oPtr, &listObjc, &listObjv); for (i=0; i'))) { ref = (char*)ckalloc(e-s+1); strlcpy(ref, s, e-s+1); } else if (!strcasecmp(key, "in-reply-to") && (s = strchr(value, '<')) && (e = strchr(s, '>'))) { ckfree(ref); ref = (char*)ckalloc(e-s+1); strlcpy(ref, s, e-s+1); ref = cpystr(value); } else if (!strcasecmp(key, "status") || !strcasecmp(key, "x-status")) { if (flags) { flags = (char*)ckrealloc(flags, strlen(flags)+strlen(value)+1); strcpy(&flags[strlen(flags)], value); } else { flags = cpystr(value); } } else if (!strcasecmp(key, "date")) { if (T == mail_parse_date(&elt, (unsigned char*)value)) { tm.tm_sec = elt.seconds; tm.tm_min = elt.minutes; tm.tm_hour = elt.hours; tm.tm_mday = elt.day; tm.tm_mon = elt.month - 1; tm.tm_year = elt.year+70; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; date = (int)mktime(&tm); } else { date = 0; } } } if (flags) { for (s = d = flags; *s; s++) { if ('D' != *s && 'F' != *s) { *d++ = *s; } } *d = '\0'; } else { oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_UNIXFLAGS, 0); flags = cpystr(Tcl_GetString(oPtr)); } if (0 == date) { long myLong = 0; oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_DATE_N, 0); Tcl_GetLongFromObj(interp, oPtr, &myLong); date = (time_t) myLong; } Tcl_DStringInit(&dString); eFrom = (*messageProcInfo[msgPtr->type].getEnvelopeProc)(interp, msgPtr); header = (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr); Tcl_DStringAppend(&dString, header, strlen(header)); Tcl_DStringAppend(&dString, "\r\n", 2); body = (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr); Tcl_DStringAppend(&dString, body, strlen(body)); Tcl_ResetResult(interp); exTime = atol(exDate); if (!strcmp("none", exType)) { exTime = 0; } result = RatDbInsert(interp, to, from, cc, msgid, ref, subject, date, flags, keywords, exTime, exType, eFrom, Tcl_DStringValue(&dString), Tcl_DStringLength(&dString)); Tcl_DStringFree(&dString); ckfree(to); ckfree(from); ckfree(cc); ckfree(msgid); ckfree(ref); ckfree(subject); ckfree(flags); return result; } /* *---------------------------------------------------------------------- * * RatMsgInfo -- * * get information about a message * * Results: * A tcl object * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Obj* RatMsgInfo(Tcl_Interp *interp, MessageInfo *msgPtr, RatFolderInfoType type) { return (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, type, 0); } /* *---------------------------------------------------------------------- * * RatBodySave -- * * Save a bodypart to an open channel. * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatBodySave(Tcl_Interp *interp,Tcl_Channel channel, BodyInfo *bodyInfoPtr, int encoded, int convertNL) { BODY *bodyPtr = bodyInfoPtr->bodyPtr; char *body; int result = 0, i; unsigned long length; Tcl_DString *dsPtr = NULL; if (NULL == (body = (*messageProcInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length))) { Tcl_SetResult(interp, "[Body not available]\n", TCL_STATIC); return TCL_OK; } if (!encoded) { dsPtr = RatDecode(interp, bodyPtr->encoding, body, length, NULL); body =Tcl_DStringValue(dsPtr); length = Tcl_DStringLength(dsPtr); } if (convertNL) { /* * This isn't really elegant but since the channel is buffered * we shouldn't suffer too badly. */ for (i=0; inextPtr) { if (TYPETEXT == bodyInfoPtr->bodyPtr->type) { return bodyInfoPtr; } if (bodyInfoPtr->firstbornPtr && (NULL != (b2Ptr = RatFindFirstText(bodyInfoPtr->firstbornPtr)))) { return b2Ptr; } } return NULL; } /* *---------------------------------------------------------------------- * * RatGetCitation -- * * Get the citation to use * * Results: * A pointer to a citation string to use. This pointer will * remain valid until the next call to this function. * are found. * * Side effects: * May call userproc. * * *---------------------------------------------------------------------- */ static CONST84 char* RatGetCitation(Tcl_Interp *interp, MessageInfo *msgPtr) { Tcl_CmdInfo cmdInfo; static char citation[80]; if (0 != Tcl_GetCommandInfo(interp, "RatUP_Citation", &cmdInfo)) { if (TCL_OK != Tcl_VarEval(interp,"RatUP_Citation ",msgPtr->name,NULL)){ RatLog(interp, RAT_ERROR, Tcl_GetStringResult(interp), RATLOG_EXPLICIT); return ""; } if (79 < strlen(Tcl_GetStringResult(interp))) { RatLog(interp, RAT_ERROR, "Too long citation", RATLOG_EXPLICIT); return ""; } strlcpy(citation, Tcl_GetStringResult(interp), sizeof(citation)); return citation; } return Tcl_GetVar2(interp, "option", "reply_lead", TCL_GLOBAL_ONLY); } /* *---------------------------------------------------------------------- * * RatCiteMessage -- * * Copy a message and add citation. * * Results: * The cited text is appended to dstObjPtr * * Side effects: * Modifies *dstObjPtr * *---------------------------------------------------------------------- */ static void RatCiteMessage(Tcl_Interp *interp, Tcl_Obj *dstObjPtr, CONST84 char *src, CONST84 char *myCitation) { int i, skipSig, addCitBlank, myCitLength; Tcl_Obj *oPtr; CONST84 char *srcPtr; /* * Initialize and find out desired behaviour */ myCitLength = strlen(myCitation); if (' ' == myCitation[myCitLength-1]) { addCitBlank = 1; myCitLength--; } else { addCitBlank = 0; } oPtr = Tcl_GetVar2Ex(interp, "option", "skip_sig", TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &skipSig); /* * Go over the text and add citation */ for (srcPtr = src; *srcPtr;) { /* * Stop when encoutering signature (if that option is true) */ if (skipSig && '-'== srcPtr[0] && '-' == srcPtr[1] && ' ' == srcPtr[2] && '\n' == srcPtr[3]) { break; } Tcl_AppendToObj(dstObjPtr, myCitation, myCitLength); if ('>' != *srcPtr && addCitBlank) { Tcl_AppendToObj(dstObjPtr, " ", 1); } for (i=0; '\n' != srcPtr[i] && srcPtr[i]; i++); Tcl_AppendToObj(dstObjPtr, srcPtr, i); srcPtr += i; if ('\n' == *srcPtr) { Tcl_AppendToObj(dstObjPtr, "\n", 1); srcPtr++; } } } /* *---------------------------------------------------------------------- * * RatCitELength -- * * Calculate the effective length of a citation. Assuming * tab-stops are placed 8 chars apart. * * Results: * The effective length * * Side effects: * None. * *---------------------------------------------------------------------- */ static unsigned int RatCitELength(const char *cit, unsigned int citLength) { unsigned int l; const char *cPtr; for (l=0, cPtr=cit; cPtr < cit+citLength; cPtr = Tcl_UtfNext(cPtr)) { if ('\t' == *cPtr) { l = (l/8+1)*8; } else { l++; } } return l; } /* *---------------------------------------------------------------------- * * RatWrapMessage -- * * Wraps the text of a message * * Results: * An object containing the wrapped text is returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj* RatWrapMessage(Tcl_Interp *interp, Tcl_Obj *textPtr) { int wrapLength, l, citLength, citLength2, overflow, i, add, mark, broken; int delta; CONST84 char *s, *e, *cPtr, *lineStartPtr, *startPtr, *citPtr = NULL; Tcl_RegExp citexp, bullexp; Tcl_Obj *nPtr = Tcl_NewObj(), *oPtr; unsigned char citbuf[80]; Tcl_IncrRefCount(nPtr); oPtr = Tcl_GetVar2Ex(interp, "option", "wrap_length", TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &wrapLength); s = Tcl_GetVar2(interp, "option", "citexp", TCL_GLOBAL_ONLY); citexp = Tcl_RegExpCompile(interp, s); if (NULL == citexp) { RatLogF(interp, RAT_ERROR, "illegal_regexp", RATLOG_EXPLICIT, Tcl_GetStringResult(interp)); } s = Tcl_GetVar2(interp, "option", "bullexp", TCL_GLOBAL_ONLY); bullexp = Tcl_RegExpCompile(interp, s); if (NULL == bullexp) { RatLogF(interp, RAT_ERROR, "illegal_regexp", RATLOG_EXPLICIT, Tcl_GetStringResult(interp)); } cPtr = Tcl_GetString(textPtr); while (*cPtr) { /* * Check if this line needs to be wrapped */ startPtr = cPtr; for (l=0; l < wrapLength && '\n' != *cPtr && *cPtr; cPtr = Tcl_UtfNext(cPtr)) { if ('\t' == *cPtr) { l = (l/8+1)*8; } else { l++; } } if (l < wrapLength) { Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); if ('\n' == *cPtr) { Tcl_AppendToObj(nPtr, "\n", 1); cPtr++; } continue; } /* * If it contains no letters after the wrap-point we keep it unwrapped */ for (s=cPtr; *s && '\n' != *s && !isalpha(*s); s = Tcl_UtfNext(s)); if (!*s || '\n' == *s) { Tcl_AppendToObj(nPtr, startPtr, s-startPtr); cPtr = s; if ('\n' == *cPtr) { Tcl_AppendToObj(nPtr, "\n", 1); cPtr++; } continue; } /* * It should be wrapped, find citation */ if (citexp && Tcl_RegExpExec(interp, citexp, startPtr, startPtr) && (Tcl_RegExpRange(citexp, 0, &s, &e), s == startPtr)) { citLength = e-s; citPtr = startPtr; } else { citLength = 0; } /* * Does it contain a bullet after the citation? * If so create a modified citation for the following lines. */ if (citPtr && Tcl_RegExpExec(interp, bullexp, citPtr+citLength, citPtr+citLength) && (Tcl_RegExpRange(bullexp, 1, &s, &e), 1) && e-citPtr < sizeof(citbuf)) { strncpy((char*)citbuf, citPtr, e-citPtr); for (i=citLength; i startPtr+citLength; cPtr--); for (s = startPtr+citLength; s < cPtr && isspace(*s); s++); if (s == cPtr) { for (; !isspace(*cPtr) && *cPtr; cPtr++); Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); continue; } /* * Add first part of line, linebreak and citation */ Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); Tcl_AppendToObj(nPtr, "\n", 1); mark = nPtr->length; Tcl_AppendToObj(nPtr, citPtr, citLength); /* * Continue adding the following lines. * Keep doing that until we find either: * An empty line (citation does not count) * A line whose citation differs in any non LWSP-character * A line whose indention is longer than the curent one, and * where the difference does not match a bullet expression */ lineStartPtr = startPtr = ++cPtr; l = RatCitELength(citPtr, citLength); broken = 1; while (*cPtr) { /* Found end of line? */ if ('\n' == *cPtr) { /* Skip trailing LWSP */ for (e = cPtr; isspace(*e) && e > startPtr; e--); if (e >= startPtr) e++; Tcl_AppendToObj(nPtr, startPtr, e-startPtr); cPtr++; /* Find length of citation */ if (citexp && Tcl_RegExpExec(interp, citexp, cPtr, cPtr) && (Tcl_RegExpRange(citexp, 0, &s, &e), s == cPtr)) { citLength2 = e-s; } else { citLength2 = 0; } add = 0; /* Check for empty line */ for (s=cPtr+citLength2; isspace(*s) && '\n' != *s && *s;s++); if (*s != '\n' && (isalnum(*s) || '\'' == *s || '"' == *s || '(' == *s)) { /* Is citation identical? */ delta = RatCitELength(citPtr, citLength) - RatCitELength(cPtr, citLength2); if (0 == delta) { add = 1; } else if (delta > 0) { /* * We have found a line with shorter citation. * Change data already inserted into dest as well * as remembered citation */ /* Data already there */ oPtr = Tcl_NewStringObj( nPtr->bytes+mark+citLength, nPtr->length-mark-citLength); Tcl_IncrRefCount(oPtr); Tcl_SetObjLength(nPtr, mark+citLength2); Tcl_AppendObjToObj(nPtr, oPtr); Tcl_DecrRefCount(oPtr); l -= citLength-citLength2; citLength = citLength2; add = 1; } else if (delta < 0 && Tcl_RegExpExec(interp, bullexp, citPtr+citLength, citPtr+citLength) && (Tcl_RegExpRange(bullexp, 0, &s, &e), 1) && citLength + e - s == citLength2) { /* Citation is longer and bullet exp matches */ /* Data already there */ oPtr = Tcl_NewStringObj( nPtr->bytes+mark+citLength, nPtr->length-mark-citLength); Tcl_IncrRefCount(oPtr); Tcl_SetObjLength(nPtr, mark); Tcl_AppendToObj(nPtr, cPtr, citLength2); Tcl_AppendObjToObj(nPtr, oPtr); Tcl_DecrRefCount(oPtr); l += citLength2-citLength; add = 1; citPtr = cPtr; citLength = citLength2; } } if (add && broken) { Tcl_AppendToObj(nPtr, " ", 1); l++; cPtr += citLength2; startPtr = cPtr; broken = 0; continue; } else { Tcl_AppendToObj(nPtr, "\n", 1); l = 0; startPtr = cPtr; break; } } else if (l >= wrapLength) { broken = 1; for (; !isspace(*cPtr) && cPtr > startPtr; cPtr--); l = overflow = 0; if (cPtr == startPtr && startPtr == lineStartPtr) { while (!isspace(*cPtr)) cPtr++; overflow = 1; } Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); Tcl_AppendToObj(nPtr, "\n", 1); if (startPtr != cPtr) { cPtr++; } lineStartPtr = startPtr = cPtr; if (overflow) break; mark = nPtr->length; Tcl_AppendToObj(nPtr, citPtr, citLength); l += RatCitELength(citPtr, citLength); } else { l++; cPtr = Tcl_UtfNext(cPtr); } } if (startPtr < cPtr) { Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); Tcl_AppendToObj(nPtr, "\n", 1); } } return nPtr; } /* *---------------------------------------------------------------------- * * RatBodyType -- * * Gets the types of a bodypart * * Results: * A list object containing two strings, the first is the major * type and the second is the subtype. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj* RatBodyType(BodyInfo *bodyInfoPtr) { BODY *bodyPtr = bodyInfoPtr->bodyPtr; Tcl_Obj *oPtr[2]; oPtr[0] = Tcl_NewStringObj(body_types[bodyPtr->type], -1); if (bodyPtr->subtype) { oPtr[1] = Tcl_NewStringObj(bodyPtr->subtype, -1); } else { oPtr[1] = Tcl_NewStringObj("", 0); } return Tcl_NewListObj(2, oPtr); } /* *---------------------------------------------------------------------- * * RatBodyData -- * * Gets the content of a bodypart * * Results: * An object containing the data. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj* RatBodyData(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, int encoded, char *charset) { BODY *bodyPtr = bodyInfoPtr->bodyPtr; Tcl_Obj *oPtr; char *body; CONST84 char *isCharset = NULL, *alias; PARAMETER *parameter; unsigned long length; if (charset) { isCharset = charset; } else if (TYPETEXT == bodyPtr->type){ isCharset = "us-ascii"; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if ( 0 == strcasecmp("charset", parameter->attribute)) { isCharset = parameter->value; } } if ((alias = Tcl_GetVar2(interp, "charsetAlias", isCharset, TCL_GLOBAL_ONLY))) { isCharset = alias; } } body = (*messageProcInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length); if (body) { if (encoded) { Tcl_Encoding enc; Tcl_DString ds; Tcl_DStringInit(&ds); if (ENC8BIT == bodyPtr->encoding) { enc = RatGetEncoding(interp, isCharset); Tcl_ExternalToUtfDString(enc, body, length, &ds); } else { Tcl_DStringAppend(&ds, body, length); } oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); Tcl_DStringFree(&ds); } else { Tcl_DString *dsPtr = RatDecode(interp, bodyPtr->encoding, body, length, isCharset); oPtr = Tcl_NewStringObj(Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr)); Tcl_DStringFree(dsPtr); ckfree(dsPtr); } } else { oPtr = Tcl_NewStringObj("[Body not available]\n", -1); } return oPtr; } /* *---------------------------------------------------------------------- * * RatMessageInternalDate -- * * Gets the internal date of a message * * Results: * A pointer to a MESSAGECACHE entry where only the date-fields may * be used. May return NULL on errors. * * Side effects: * None. * *---------------------------------------------------------------------- */ MESSAGECACHE* RatMessageInternalDate(Tcl_Interp *interp, MessageInfo *msgPtr) { return (*messageProcInfo[msgPtr->type].getInternalDateProc)(interp,msgPtr); } /* *---------------------------------------------------------------------- * * RatPurgeFlags -- * * Purge Flagged, Deleted and Recent flags * * Results: * None. * * Side effects: * May modify the buffer passed as argument. However the result is * never bigger than the argument. * *---------------------------------------------------------------------- */ char* RatPurgeFlags(char *flags, int level) { char *cPtr, *toPurge[4]; int i, l; i = 0; if (1 == level) { toPurge[i++] = flag_name[RAT_FLAGGED].imap_name; toPurge[i++] = flag_name[RAT_DELETED].imap_name; toPurge[i++] = flag_name[RAT_RECENT].imap_name; } else { toPurge[i++] = flag_name[RAT_RECENT].imap_name; } toPurge[i] = NULL; for (i=0; '\0' != toPurge[i]; i++) { if (NULL != (cPtr = strstr(flags, toPurge[i]))) { l = strlen(toPurge[i]); if (flags == cPtr) { if (' ' == cPtr[l]) { l++; } } else { cPtr--; l++; } strcpy(cPtr, cPtr+l); } } return flags; } /* *---------------------------------------------------------------------- * * RatHeaderSize -- * * Calculate size of header * * Results: * Maximum size of header * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatHeaderLineSize(char *name, ENVELOPE *env, char *text); static int RatHeaderAddressSize(char *name, ENVELOPE *env, ADDRESS *adr); static int RatHeaderLineSize(char *name, ENVELOPE *env, char *text) { if (text) { return (env->remail ? 7 : 0) + strlen(name) + 2 + strlen(text) + 2; } else { return 0; } } static int RatHeaderAddressSize(char *name, ENVELOPE *env, ADDRESS *adr) { if (adr) { return (env->remail?7:0) + strlen(name) + 2 + RatAddressSize(adr, 1)+2; } else { return 0; } } size_t RatHeaderSize(ENVELOPE *env,BODY *body) { size_t len = 0; if (env->remail) len += strlen(env->remail); len += RatHeaderLineSize("Newsgroups", env, env->newsgroups); len += RatHeaderLineSize("Date", env, (char*)env->date); len += RatHeaderAddressSize("From", env, env->from); len += RatHeaderAddressSize("Sender", env, env->sender); len += RatHeaderAddressSize("Reply-To", env, env->reply_to); len += RatHeaderLineSize("Subject", env, env->subject); if (env->bcc && !(env->to || env->cc)) { len += strlen("To: undisclosed recipients: ;\015\012"); } len += RatHeaderAddressSize("To", env, env->to); len += RatHeaderAddressSize("cc", env, env->cc); len += RatHeaderLineSize("In-Reply-To", env, env->in_reply_to); len += RatHeaderLineSize("Message-ID", env, env->message_id); len += RatHeaderLineSize("Followup-to", env, env->followup_to); len += RatHeaderLineSize("References", env, env->references); if (body && !env->remail) { /* not if remail or no body structure */ /* * TODO: Fix this correctly * Here we assume that the body headers will never become longer * than 8192 bytes */ len += 8192; } len += 2; return len; } /* *---------------------------------------------------------------------- * * RatMessageDeleteAttachments -- * * See ../doc/interface * * Results: * A standard tcl result * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatMessageDeleteAttachments(Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_Obj *attachments) { char flags[128], date[128], *name; Tcl_DString ds; int i, length; Tcl_Obj *oPtr; /* Get message text */ Tcl_DStringInit(&ds); RatMessageGet(interp, msgPtr, &ds, flags, sizeof(flags), date, sizeof(date)); if ('\n' != Tcl_DStringValue(&ds)[Tcl_DStringLength(&ds)-1]) { Tcl_DStringAppend(&ds, "\r\n", 2); } /* Delete attachments */ Tcl_ListObjLength(interp, attachments, &length); for (i=0; ibodyInfoPtr, Tcl_DStringValue(ds), spec, 0, &boundary); if (!start) { return TCL_ERROR; } strlcpy(buf, "--", sizeof(buf)); strlcat(buf, boundary, sizeof(buf)); end = strstr(start+1, buf); if (!end) { Tcl_SetResult(interp, "Attachment end not found", TCL_STATIC); return TCL_ERROR; } start += strlen(buf)+2; /* Create replacement data */ text = Tcl_GetVar2(interp, "t", "deleted_attachment", TCL_GLOBAL_ONLY); snprintf(buf, sizeof(buf), "Content-Type: TEXT/PLAIN; CHARSET=us-ascii\r\n" "\r\n%s\r\n", text); /* Make sure there is room */ if (end-start < strlen(buf)) { Tcl_DStringSetLength(ds,Tcl_DStringLength(ds)+strlen(buf)-(end-start)); } /* Move data */ memmove(start+strlen(buf), end, strlen(end)+1); memmove(start, buf, strlen(buf)); /* Set new length */ if (end-start > strlen(buf)) { Tcl_DStringSetLength(ds,Tcl_DStringLength(ds)+strlen(buf)-(end-start)); } return TCL_OK; } static char* RatFindAttachment(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, char *text, Tcl_Obj *spec, int spec_index, char **boundary) { char buf[1024]; BodyInfo *child; PARAMETER *param; Tcl_Obj *oPtr; int index, i, length; /* Find boundary */ if (bodyInfoPtr->bodyPtr->type != TYPEMULTIPART) { Tcl_SetResult(interp, "Not a multipart message", TCL_STATIC); return NULL; } for (param = bodyInfoPtr->bodyPtr->parameter; param && strcasecmp(param->attribute, "BOUNDARY"); param = param->next) { } if (!param) { Tcl_SetResult(interp, "No boundary found", TCL_STATIC); return NULL; } *boundary = param->value; strlcpy(buf, "--", sizeof(buf)); strlcat(buf, param->value, sizeof(buf)); /* Make sure children exist */ if (!bodyInfoPtr->firstbornPtr) { RatCreateChildren(interp, bodyInfoPtr); } /* Extract index of child we are interested in */ if (TCL_OK != Tcl_ListObjIndex(interp, spec, spec_index, &oPtr) || TCL_OK != Tcl_GetIntFromObj(interp, oPtr, &index)) { Tcl_SetResult(interp, "Failed to extract index", TCL_STATIC); return NULL; } /* Find the location of the child */ text = strstr(text+1, buf); for (i=0, child = bodyInfoPtr->firstbornPtr; text && i < index && child->nextPtr; i++, child = child->nextPtr) { text = strstr(text+1, buf); } if (i < index || !text) { Tcl_SetResult(interp, "Failed to locate child", TCL_STATIC); return NULL; } /* Are we done or do we need to recurse? */ Tcl_ListObjLength(interp, spec, &length); if (spec_index < length-1) { return RatFindAttachment(interp, child, text, spec, spec_index+1, boundary); } else { return text; } } #ifdef MEM_DEBUG void ratMessageCleanup() { ckfree(messageProcInfo); } #endif /* MEM_DEBUG */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratMsgList.c000066400000000000000000000155771137544547100210260ustar00rootroot00000000000000/* * ratMsgList.c -- * * This file contains code which handles message listing * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" /* *---------------------------------------------------------------------- * * RatParseList -- * * Parse a list expression (almost like a printf format string) * * Results: * A structure representing the parsed expression, or null if * there is a syntax error in the format string. * * Side effects: * None. * * *---------------------------------------------------------------------- */ ListExpression* RatParseList(const char *format, char *error) { ListExpression *expPtr; int i, w, index, bufLen, num; char buf[1024]; for(i=num=0; '\0' != format[i]; i++) { if ('%' == format[i] && format[i+1] && '%' != format[i+1]) { while (format[++i] && ('-' == format[i] || isdigit((unsigned char)format[i]))); if (!strchr("scnNmrRbBdDSitMu", format[i])) { if (error != NULL) { *error = format[i]; } return NULL; } num++; } } expPtr = (ListExpression*)ckalloc(sizeof(ListExpression)); expPtr->preString = (char**)ckalloc(num*sizeof(char*)); expPtr->typeList = (RatFolderInfoType*)ckalloc(num*sizeof(RatFolderInfoType)); expPtr->fieldWidth = (int*)ckalloc(num*sizeof(int)); expPtr->leftJust = (int*)ckalloc(num*sizeof(int)); for (i = index = bufLen = 0; format[i]; i++) { if ('%' == format[i] && format[i+1]) { if ('%' == format[++i]) { buf[bufLen++] = format[i]; continue; } buf[bufLen] = '\0'; expPtr->preString[index] = cpystr(buf); if ('-' == format[i]) { expPtr->leftJust[index] = 1; i++; } else { expPtr->leftJust[index] = 0; } w=0; while (isdigit((unsigned char)format[i])) { w = w*10+format[i++]-'0'; } if (!format[i]) { break; } expPtr->fieldWidth[index] = w; switch(format[i]) { case 's': expPtr->typeList[index++] = RAT_FOLDER_SUBJECT; break; case 'c': expPtr->typeList[index++] = RAT_FOLDER_CANONSUBJECT; break; case 'n': expPtr->typeList[index++] = RAT_FOLDER_NAME; break; case 'N': expPtr->typeList[index++] = RAT_FOLDER_ANAME; break; case 'm': expPtr->typeList[index++] = RAT_FOLDER_MAIL; break; case 'r': expPtr->typeList[index++] = RAT_FOLDER_NAME_RECIPIENT; break; case 'R': expPtr->typeList[index++] = RAT_FOLDER_MAIL_RECIPIENT; break; case 'b': expPtr->typeList[index++] = RAT_FOLDER_SIZE; break; case 'B': expPtr->typeList[index++] = RAT_FOLDER_SIZE_F; break; case 'd': expPtr->typeList[index++] = RAT_FOLDER_DATE_F; break; case 'D': expPtr->typeList[index++] = RAT_FOLDER_DATE_N; break; case 'S': expPtr->typeList[index++] = RAT_FOLDER_STATUS; break; case 'i': expPtr->typeList[index++] = RAT_FOLDER_INDEX; break; case 't': expPtr->typeList[index++] = RAT_FOLDER_THREADING;break; case 'M': expPtr->typeList[index++] = RAT_FOLDER_MSGID; break; case 'u': expPtr->typeList[index++] = RAT_FOLDER_UID; break; } bufLen = 0; } else { buf[bufLen++] = format[i]; } } expPtr->size = index; if (bufLen) { buf[bufLen] = '\0'; expPtr->postString = cpystr(buf); } else { expPtr->postString = NULL; } return expPtr; } /* *---------------------------------------------------------------------- * * RatFreeListExpression -- * * Frees all memory associated with a list expression. * * Results: * None. * * Side effects: * Some memory is freed. * * *---------------------------------------------------------------------- */ void RatFreeListExpression(ListExpression *exPtr) { int i; for (i=0; isize; i++) { ckfree(exPtr->preString[i]); } ckfree(exPtr->preString); ckfree(exPtr->typeList); ckfree(exPtr->fieldWidth); ckfree(exPtr->leftJust); ckfree(exPtr->postString); ckfree(exPtr); } /* *---------------------------------------------------------------------- * * RatDoList -- * * Print the list information about a message. * * Results: * A tcl object * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Obj* RatDoList(Tcl_Interp *interp, ListExpression *exprPtr, RatInfoProc *infoProc, ClientData clientData, int index) { Tcl_Obj *oPtr = Tcl_NewObj(), *iPtr; char *str; unsigned char *s2 = NULL; int i, j, slen, length; for (i=0; isize; i++) { if (exprPtr->preString[i]) { Tcl_AppendToObj(oPtr, exprPtr->preString[i], -1); } iPtr = (*infoProc)(interp, clientData, exprPtr->typeList[i], index); if (!iPtr) { for (j=0; jfieldWidth[i]; j++) { Tcl_AppendToObj(oPtr, " ", 1); } continue; } str = Tcl_GetStringFromObj(iPtr, &slen); for (j=0; j ' '; j++); if (j < slen) { s2 = (unsigned char*)cpystr(str); for (j=0; jfieldWidth[i]) { length = Tcl_NumUtfChars(str, slen); if (length > exprPtr->fieldWidth[i]) { j = Tcl_UtfAtIndex(str, exprPtr->fieldWidth[i]) - str; Tcl_AppendToObj(oPtr, str, j); } else { if (exprPtr->leftJust[i]) { Tcl_AppendToObj(oPtr, str, slen); for (j=length; jfieldWidth[i]; j++) { Tcl_AppendToObj(oPtr, " ", 1); } } else { for (j=length; jfieldWidth[i]; j++) { Tcl_AppendToObj(oPtr, " ", 1); } Tcl_AppendToObj(oPtr, str, slen); } } } else { Tcl_AppendToObj(oPtr, str, slen); } if (s2) { ckfree(s2); s2 = NULL; } } if (exprPtr->postString) { Tcl_AppendToObj(oPtr, exprPtr->postString, -1); } return oPtr; } /* *---------------------------------------------------------------------- * * RatCheckListFormatCmd -- * * CHeck if the given list format is correct * * Results: * A tcl object * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatCheckListFormatCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { ListExpression *list; char error, buf[1024]; Tcl_Obj *msg; if (objc != 2) { Tcl_AppendResult(interp, "Missing parameter", TCL_STATIC); return TCL_ERROR; } list = RatParseList(Tcl_GetString(objv[1]), &error); if (list != NULL) { Tcl_SetResult(interp, "ok", TCL_STATIC); RatFreeListExpression(list); } else { msg = Tcl_GetVar2Ex(interp, "t","illegal_list_format",TCL_GLOBAL_ONLY); snprintf(buf, sizeof(buf), Tcl_GetString(msg), error); Tcl_SetResult(interp, buf, TCL_VOLATILE); } return TCL_OK; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratPGP.c000066400000000000000000000172551137544547100200650ustar00rootroot00000000000000/* * ratPGPprog.c -- * * This file contains compatibility functions. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" #include "ratPGP.h" /* * Cached pass phrase */ volatile static char pgpPass[MAXPASSLENGTH]; static int pgpPassValid = 0; static Tcl_TimerToken pgpPassToken; /* *---------------------------------------------------------------------- * * ClearPGPPass -- * * Clear the pgp pass phrase * * Results: * None. * * Side effects: * The pass phrase is cleared. * * *---------------------------------------------------------------------- */ void ClearPGPPass(ClientData unused) { int i; for (i=0; isigStatus = RAT_UNSIGNED; if ((*bodyInfoPtrPtr)->bodyPtr->type == TYPEMULTIPART && !strcasecmp("encrypted", (*bodyInfoPtrPtr)->bodyPtr->subtype)) { enc = 0; for (parPtr = (*bodyInfoPtrPtr)->bodyPtr->parameter; parPtr; parPtr = parPtr->next) { if (!strcasecmp(parPtr->attribute, "protocol") && !strcasecmp(parPtr->value,"application/pgp-encrypted")){ enc = 1; break; } } if (enc) { RatPGPDecrypt(interp, procInfo, bodyInfoPtrPtr); (*bodyInfoPtrPtr)->encoded = 1; } } else if ((*bodyInfoPtrPtr)->bodyPtr->type == TYPEMULTIPART && !strcasecmp("signed", (*bodyInfoPtrPtr)->bodyPtr->subtype)) { for (parPtr = (*bodyInfoPtrPtr)->bodyPtr->parameter; parPtr && (strcasecmp(parPtr->attribute, "protocol") || strcasecmp(parPtr->value, "application/pgp-signature")); parPtr = parPtr->next); if (parPtr) { BodyInfo *bodyInfoPtr; (*procInfo[(*bodyInfoPtrPtr)->type].makeChildrenProc)(interp, *bodyInfoPtrPtr); if (NULL == (*bodyInfoPtrPtr)->firstbornPtr) { /* * Corrupt message */ return; } bodyInfoPtr = *bodyInfoPtrPtr; *bodyInfoPtrPtr = (*bodyInfoPtrPtr)->firstbornPtr; (*bodyInfoPtrPtr)->sigStatus = RAT_UNCHECKED; (*bodyInfoPtrPtr)->secPtr = bodyInfoPtr; } } else if ((*bodyInfoPtrPtr)->bodyPtr->type == TYPETEXT || ((*bodyInfoPtrPtr)->bodyPtr->type == TYPEAPPLICATION && !strcasecmp("pgp", (*bodyInfoPtrPtr)->bodyPtr->subtype))) { text = (*procInfo[(*bodyInfoPtrPtr)->type].fetchBodyProc) (*bodyInfoPtrPtr, &length); if (text && (((start = RatPGPStrFind(text,length,"BEGIN PGP SIGNED",1)) && (middle = RatPGPStrFind(start, length - (start-text), "BEGIN PGP SIGNATURE",1)) && (end = RatPGPStrFind(middle, length - (middle-text), "END PGP",1))) || ((start = RatPGPStrFind(text, length,"BEGIN PGP MESSAGE",1)) && (end = RatPGPStrFind(start, length - (start-text), "END PGP",1))))) { RatPGPHandleOld(interp, *bodyInfoPtrPtr, text, start, end+1); } } } /* *---------------------------------------------------------------------- * * RatPGPCmd -- * * Handle ratPGP command. * * Results: * A standard tcl result. * * Side effects: * Depends on the arguments * *---------------------------------------------------------------------- */ int RatPGPCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc < 2) goto usage; if (!strcmp(Tcl_GetString(objv[1]), "listkeys")) { if (objc != 3 && objc != 2) goto usage; if (objc == 3) { return RatPGPListKeys(interp, Tcl_GetString(objv[2])); } else { return RatPGPListKeys(interp, NULL); } } else if (!strcmp(Tcl_GetString(objv[1]), "extract")) { if (objc != 3 && objc != 4) goto usage; if (objc == 4) { return RatPGPExtractKey(interp, Tcl_GetString(objv[2]), Tcl_GetString(objv[3])); } else { return RatPGPExtractKey(interp, Tcl_GetString(objv[2]), NULL); } } else if (!strcmp(Tcl_GetString(objv[1]), "add")) { if (objc != 3 && objc != 4) goto usage; if (objc == 4) { return RatPGPAddKeys(interp, Tcl_GetString(objv[2]), Tcl_GetString(objv[3])); } else { return RatPGPAddKeys(interp, Tcl_GetString(objv[2]), NULL); } } usage: Tcl_AppendResult(interp, "Illegal usage of \"", Tcl_GetString(objv[0]), "\"", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatPGPStrFind -- * * Find a PGP string in a message * * Results: * A pointer to the start of the string. * * Side effects: * None. * * *---------------------------------------------------------------------- */ char* RatPGPStrFind(char *haystack, long straws, char *needle, int linestart) { long i, j, end; int needleSize = strlen(needle); end = straws-strlen(needle); for (i=0; i<=end; i+= 5) { if ('-' == haystack[i]) { for (j=i; j>0 && j>i-5 && '-' == haystack[j]; j--); if ((j >= end-5) || (linestart && j>0 && '\n' != haystack[j])) { continue; } if (j > 0) { j++; } if (!strncmp("-----", haystack+i, 5-(i-j)) && !strncmp(needle, haystack+j+5, needleSize)) { return haystack+j; } } } return NULL; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratPGP.h000066400000000000000000000031551137544547100200640ustar00rootroot00000000000000/* * ratStdFolder.h -- * * Declarations of functions used in the Std folder and messages * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #ifndef _RATPGP #define _RATPGP /* * Maximaum length of pass phrase (plus two) */ #define MAXPASSLENGTH 1024 /* ratPGP.c */ extern volatile char *RatPGPPhrase(Tcl_Interp *interp, volatile char *phrase, int phraseSize); extern char *RatSenderPGPPhrase(Tcl_Interp *interp); extern Tcl_TimerProc ClearPGPPass; extern void RatPGPBodyCheck(Tcl_Interp *interp, MessageProcInfo *procInfo, BodyInfo **bodyInfoPtrPtr); extern Tcl_ObjCmdProc RatPGPCmd; extern char *RatPGPStrFind(char *haystack, long straws, char *needle, int linestart); /* ratPGPprog.c */ extern int RatPGPEncrypt(Tcl_Interp *interp, ENVELOPE *env, BODY **body, char *signer, Tcl_Obj *rcpts); extern int RatPGPSign(Tcl_Interp *interp, ENVELOPE *env, BODY **body, const char *signer); extern void RatPGPChecksig(Tcl_Interp *interp, MessageProcInfo *procInfo, BodyInfo *bodyInfoPtr); extern void RatPGPDecrypt(Tcl_Interp *interp, MessageProcInfo *procInfo, BodyInfo **bodyInfoPtrPtr); extern int RatPGPListKeys(Tcl_Interp *interp, char *keyring); extern int RatPGPExtractKey(Tcl_Interp *interp, char *id, char *keyring); extern int RatPGPAddKeys(Tcl_Interp *interp, char *keys, char *keyring); extern void RatPGPHandleOld(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, char *text, char *start, char *end); #endif /* _RATPGP */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratPGPprog.c000066400000000000000000001552631137544547100207570ustar00rootroot00000000000000/* * ratPGPprog.c -- * * This file contains compatibility functions. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" #include "ratPGP.h" #include /* * The contents of the first bodypart of nested bodyparts */ #define ENCFIRST "Version: 1\r\n" /* * List of keys on keyring */ typedef struct { Tcl_Obj *keyid; Tcl_Obj *addresses; Tcl_Obj *subjects; Tcl_Obj *descr; Tcl_Obj *sign; Tcl_Obj *encrypt; } RatPGPKey; typedef struct { RatPGPKey *keys; unsigned int keyCount; unsigned int keyAlloc; Tcl_Obj *title; char *name; time_t mtime; int secring; } RatPGPKeyring; static RatPGPKeyring *keyring = NULL; /* * Maximum number of accepted fields in pgp key list output. Extra * fields will be ignored. */ #define MAX_FIELDS 20 /* * Local functions */ static int RatRunPGP(Tcl_Interp *interp, int nopass, char *cmd, char *args, int *toPGP, char **outFile, int *errPGP, int *statusPGP); static Tcl_DString *RatPGPRunOld(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, char *text, char *start, char *end); static int RatUpdatePGPKeys(Tcl_Interp *interp, RatPGPKeyring *k); static void ParsePGPListFormat(Tcl_Interp *interp, FILE *fp, Tcl_RegExp exp_id, Tcl_RegExp axp_addr, int blankiscont, RatPGPKeyring *k); static void ParseGPGListFormat(Tcl_Interp *interp, FILE *fp, RatPGPKeyring *k); static void AddKey(Tcl_Interp *interp, RatPGPKeyring *k, Tcl_Obj *ids, Tcl_DString *descr); static void AddKeyNew(Tcl_Interp *interp, RatPGPKeyring *k, Tcl_Obj *id, Tcl_Obj *addresses, Tcl_Obj *subjects, Tcl_Obj *descr, Tcl_Obj *sign, Tcl_Obj *encrypt); static void RatPGPFreeKeyring(RatPGPKeyring *k); static RatPGPKeyring* RatPGPNewKeyring(Tcl_Interp *interp, const char *name, int secring); /* *---------------------------------------------------------------------- * * RatRunPGP -- * * Run the pgp command * * Results: * Returns the pid of the pgp program on success and a negative * value on failure. The toPGP and fromPGP integers will be modified. * * Side effects: * forks. * * *---------------------------------------------------------------------- */ static int RatRunPGP(Tcl_Interp *interp, int nopass, char *command, char *args, int *toPGP, char **outFile, int *errPGP, int *statusPGP) { int toPipe[2], errPipe[2], statusPipe[2], argc, pid, i, out; Tcl_DString cmd; char cmdbuf[1024]; CONST84 char *opt_args, *pgp_path, **argv, *tmp; struct rlimit rlim; static char name[1024]; /* * Setup command arrays */ pgp_path = RatGetPathOption(interp, "pgp_path"); opt_args = Tcl_GetVar2(interp, "option", "pgp_args", TCL_GLOBAL_ONLY); if (pgp_path && strlen(pgp_path)) { snprintf(cmdbuf, sizeof(cmdbuf), "%s/%s", pgp_path, command); } else { snprintf(cmdbuf, sizeof(cmdbuf), "%s", command); } Tcl_DStringInit(&cmd); Tcl_DStringAppend(&cmd, cmdbuf, -1); if (opt_args) { Tcl_DStringAppend(&cmd, " ", 1); Tcl_DStringAppend(&cmd, opt_args, -1); } Tcl_DStringAppend(&cmd, " ", 1); if (statusPGP != NULL) { char *printed; int size = strlen(args)+16; if (pipe(statusPipe)) return 0; printed = (char*)ckalloc(size); snprintf(printed, size, args, statusPipe[1]); Tcl_DStringAppend(&cmd, printed, -1); ckfree(printed); } else { Tcl_DStringAppend(&cmd, args, -1); } Tcl_SplitList(interp, Tcl_DStringValue(&cmd), &argc, &argv); /*fprintf(stderr, "Exec: %s %s\n", cmdbuf, Tcl_DStringValue(&cmd));*/ Tcl_DStringFree(&cmd); /* * Open outfile and create the pgp subprocess. */ tmp = Tcl_GetVar(interp, "rat_tmp", TCL_GLOBAL_ONLY); tmp = RatTranslateFileName(interp, tmp); snprintf(name, sizeof(name), "%s/pgptmp.%d", tmp, getpid()); if ( 0 > (out = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0600))) { return 0; } if (pipe(toPipe)) return 0; if (pipe(errPipe)) { close(toPipe[0]); close(toPipe[1]); return 0; } if (0 == (pid = fork())) { getrlimit(RLIMIT_NOFILE, &rlim); for (i=0; i safe_write(STDERR_FILENO, buf, strlen(buf))) exit(-1); } exit(-1); /* notreached */ } close(toPipe[0]); close(out); close(errPipe[1]); if (NULL != statusPGP) { close(statusPipe[1]); *statusPGP = statusPipe[0]; } ckfree(argv); *toPGP = toPipe[1]; *outFile = name; *errPGP = errPipe[0]; return pid; } /* *---------------------------------------------------------------------- * * RatPGPEncrypt -- * * Encrypt a bodypart. Optionally also sign it. * * Results: * A multipart/encrypted body * * Side effects: * Will call pgp. * * *---------------------------------------------------------------------- */ int RatPGPEncrypt(Tcl_Interp *interp, ENVELOPE *env, BODY **body, char *signer, Tcl_Obj *rcpts) { int toPGP, fromPGP, errPGP, length, all_ok, pid, result, status, i, j, objc; char *hdrPtr, *from, buf[MAILTMPLEN], *command; volatile char passPhrase[MAXPASSLENGTH]; CONST84 char *version; Tcl_DString cmdDS, encDS; BODY *multiPtr; PARAMETER *parmPtr; PART *partPtr; char *recipSep; Tcl_Obj **objv; Tcl_DStringInit(&cmdDS); Tcl_DStringInit(&encDS); rfc822_encode_body_8bit(env, *body); /* * Create command to run */ version = Tcl_GetVar2(interp, "option", "pgp_version", TCL_GLOBAL_ONLY); if (!strcmp("gpg-1", version)) { command = "gpg"; Tcl_DStringAppend(&cmdDS, "-eatq --no-secmem-warning --passphrase-fd 0 --batch", -1); if (signer) { Tcl_DStringAppend(&cmdDS, " -s ", -1); } recipSep = " -r "; } else if (!strcmp("2", version)) { command = "pgp"; Tcl_DStringAppend(&cmdDS, "+BATCHMODE +VERBOSE=0 -eaf", -1); if (signer) { Tcl_DStringAppend(&cmdDS, "s", 1); } recipSep=" "; } else if (!strcmp("5", version)) { command = "pgpe"; if (signer) { Tcl_DStringAppend(&cmdDS, "-s ", -1); } Tcl_DStringAppend(&cmdDS, "-at -f +batchmode=1 -r", -1); recipSep=" "; } else if (!strcmp("6", version)) { command = "pgp"; Tcl_DStringAppend(&cmdDS, "+BATCHMODE +VERBOSE=0 +force -eaf", -1); if (signer) { Tcl_DStringAppend(&cmdDS, "s", 1); } recipSep=" "; } else { Tcl_SetResult(interp, "Unkown pgp version", TCL_STATIC); return TCL_ERROR; } if (signer) { Tcl_DStringAppend(&cmdDS, " -u ", 4); Tcl_DStringAppendElement(&cmdDS, signer); } Tcl_ListObjGetElements(interp, rcpts, &objc, &objv); for (i=0; i safe_write(toPGP, (char*)passPhrase, strlen((char*)passPhrase))){ return TCL_ERROR; } for (i=0; i safe_write(toPGP, buf, strlen(buf))) return TCL_ERROR; RatInitDelayBuffer(); rfc822_output_body(*body, RatDelaySoutr, (void*)toPGP); close(toPGP); do { result = waitpid(pid, &status, 0); } while(-1 == result && EINTR == errno); /* * Read result */ fromPGP = open(from, O_RDONLY); Tcl_DStringSetLength(&encDS, 0); do { length = SafeRead(fromPGP, buf, sizeof(buf)); for (i=0; i < length; i += j) { for (j=0; buf[i+j] != '\n' && i+j 0); close(fromPGP); unlink(from); /* * Check for errors */ if (pid != result || WEXITSTATUS(status)) { Tcl_DStringSetLength(&cmdDS, 0); Tcl_DStringAppendElement(&cmdDS, "RatPGPError"); Tcl_DStringStartSublist(&cmdDS); do { if (0 < (length = SafeRead(errPGP, buf, sizeof(buf)))) { Tcl_DStringAppend(&cmdDS, buf, length); } } while (length > 0); Tcl_DStringEndSublist(&cmdDS); Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdDS)); if (!strncmp("ABORT", Tcl_GetStringResult(interp), 5)) { close(errPGP); Tcl_DStringFree(&encDS); return TCL_ERROR; } all_ok = 0; } else { all_ok = 1; } close(errPGP); } while(0 == all_ok); Tcl_DStringFree(&cmdDS); mail_free_body(body); /* * Build encrypted multipart */ multiPtr = mail_newbody(); multiPtr->type = TYPEMULTIPART; multiPtr->subtype = cpystr("encrypted"); multiPtr->parameter = parmPtr = mail_newbody_parameter(); parmPtr->attribute = cpystr("protocol"); parmPtr->value = cpystr("application/pgp-encrypted"); parmPtr->next = mail_newbody_parameter(); parmPtr = parmPtr->next; parmPtr->attribute = cpystr("BOUNDARY"); snprintf(buf, sizeof(buf), "%ld-%ld-%ld=:%ld",(long)gethostid(),random(), time(NULL), (long)getpid()); parmPtr->value = cpystr(buf); parmPtr->next = NULL; multiPtr->encoding = ENC7BIT; multiPtr->id = NULL; multiPtr->description = NULL; multiPtr->nested.part = partPtr = mail_newbody_part(); partPtr->body.type = TYPEAPPLICATION; partPtr->body.subtype = cpystr("pgp-encrypted"); partPtr->body.encoding = ENC7BIT; partPtr->body.contents.text.data = (unsigned char*)cpystr(ENCFIRST); partPtr->body.size.bytes = strlen(ENCFIRST); partPtr->next = mail_newbody_part(); partPtr = partPtr->next; partPtr->body.type = TYPEAPPLICATION; partPtr->body.subtype = cpystr("octet-stream"); partPtr->body.encoding = ENC7BIT; partPtr->body.contents.text.data = (unsigned char*)cpystr(Tcl_DStringValue(&encDS)); partPtr->body.size.bytes = Tcl_DStringLength(&encDS); Tcl_DStringFree(&encDS); partPtr->next = NULL; *body = multiPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * * RatPGPSign -- * * Sign a bodypart. * * Results: * A multipart/signed body * * Side effects: * Will call pgp. * * *---------------------------------------------------------------------- */ int RatPGPSign(Tcl_Interp *interp, ENVELOPE *env, BODY **body, const char *signer) { int toPGP, fromPGP, errPGP, length, all_ok, pid, status, result, i, j; char *hdrPtr, *outfile, *cmd, buf[MAILTMPLEN]; volatile char passPhrase[MAXPASSLENGTH]; CONST84 char *version; Tcl_DString sigDS, cmdDS; BODY *multiPtr; PARAMETER *parmPtr; PART *partPtr; int write_failed; version = Tcl_GetVar2(interp, "option", "pgp_version", TCL_GLOBAL_ONLY); Tcl_DStringInit(&sigDS); Tcl_DStringInit(&cmdDS); do { /* * Run command */ write_failed = 0; rfc822_encode_body_7bit(NIL, *body); Tcl_DStringSetLength(&cmdDS, 0); if (!strcmp("gpg-1", version)) { cmd = "gpg"; Tcl_DStringAppend(&cmdDS, "--detach-sign --armor " "--no-secmem-warning --passphrase-fd 0 " "--batch", -1); } else if (!strcmp("2", version)) { cmd = "pgp"; Tcl_DStringAppend(&cmdDS, "+BATCHMODE +VERBOSE=0 -satbf", -1); } else if (!strcmp("5", version)) { cmd = "pgps"; Tcl_DStringAppend(&cmdDS, "-abf", -1); } else if (!strcmp("6", version)) { cmd = "pgp"; Tcl_DStringAppend(&cmdDS,"+BATCHMODE +VERBOSE=0 +force -satbf",-1); } else { Tcl_SetResult(interp, "Unkown pgp version", TCL_STATIC); return TCL_ERROR; } Tcl_DStringAppend(&cmdDS, " -u ", -1); Tcl_DStringAppendElement(&cmdDS, signer); pid = RatRunPGP(interp, 0, cmd, Tcl_DStringValue(&cmdDS), &toPGP, &outfile, &errPGP, NULL); if (NULL == RatPGPPhrase(interp, passPhrase, sizeof(passPhrase))) { return TCL_ERROR; } if (0 > safe_write(toPGP,(char*)passPhrase,strlen((char*)passPhrase))) { return TCL_ERROR; } for (i=0; i safe_write(toPGP, "\n", 1)) return TCL_ERROR; hdrPtr = buf; buf[0] = '\0'; rfc822_write_body_header(&hdrPtr, *body); strlcat(buf, "\015\012", sizeof(buf)); if (strlen(buf) != safe_write(toPGP, buf, strlen(buf))) { write_failed = 1; } RatInitDelayBuffer(); if (!rfc822_output_body(*body, RatDelaySoutr, (void*)toPGP)) { write_failed = 1; } close(toPGP); /*i = open("/tmp/sigdump", O_CREAT | O_TRUNC | O_WRONLY, FILEMODE); safe_write(i, buf, strlen(buf)); RatInitDelayBuffer(); rfc822_output_body(*body, RatDelaySoutr, (void*)i); close(i);*/ do { result = waitpid(pid, &status, 0); } while(-1 == result && EINTR == errno); /* * Read result */ fromPGP = open(outfile, O_RDONLY); Tcl_DStringSetLength(&sigDS, 0); do { length = SafeRead(fromPGP, buf, sizeof(buf)); for (i=0; i < length; i += j) { for (j=0; buf[i+j] != '\n' && i+j 0); close(fromPGP); unlink(outfile); /* * Check for errors */ if (pid != result || WEXITSTATUS(status) || write_failed) { /* We do not know why it failed, but it may be due to a * bad passphrase. So to be on teh safe side we clear the * cached passphrase here. */ ClearPGPPass(NULL); Tcl_DStringSetLength(&cmdDS, 0); Tcl_DStringAppendElement(&cmdDS, "RatPGPError"); Tcl_DStringStartSublist(&cmdDS); do { if (0 < (length = SafeRead(errPGP, buf, sizeof(buf)))) { Tcl_DStringAppend(&cmdDS, buf, length); } } while (length > 0); Tcl_DStringEndSublist(&cmdDS); Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdDS)); if (!strncmp("ABORT", Tcl_GetStringResult(interp), 5)) { close(errPGP); Tcl_DStringFree(&sigDS); return TCL_ERROR; } all_ok = 0; } else { all_ok = 1; } close(errPGP); } while(0 == all_ok); Tcl_DStringFree(&cmdDS); /* * Build signature multipart */ multiPtr = mail_newbody(); multiPtr->type = TYPEMULTIPART; multiPtr->subtype = cpystr("signed"); multiPtr->parameter = parmPtr = mail_newbody_parameter(); parmPtr->attribute = cpystr("micalg"); if (!strcmp("gpg-1", version)) parmPtr->value = cpystr("pgp-sha1"); else parmPtr->value = cpystr("pgp-md5"); parmPtr->next = mail_newbody_parameter(); parmPtr = parmPtr->next; parmPtr->attribute = cpystr("protocol"); parmPtr->value = cpystr("application/pgp-signature"); parmPtr->next = mail_newbody_parameter(); parmPtr = parmPtr->next; parmPtr->attribute = cpystr("BOUNDARY"); snprintf(buf, sizeof(buf), "%ld-%ld-%ld=:%ld",(long)gethostid(),random(), time(NULL), (long)getpid()); parmPtr->value = cpystr(buf); parmPtr->next = NULL; multiPtr->encoding = ENC7BIT; multiPtr->id = NULL; multiPtr->description = NULL; multiPtr->nested.part = partPtr = mail_newbody_part(); memcpy(&partPtr->body, *body, sizeof(partPtr->body)); partPtr->next = mail_newbody_part(); partPtr = partPtr->next; partPtr->body.type = TYPEAPPLICATION; partPtr->body.subtype = cpystr("pgp-signature"); partPtr->body.encoding = ENC7BIT; partPtr->body.contents.text.data = (unsigned char*)cpystr(Tcl_DStringValue(&sigDS)); partPtr->body.size.bytes = Tcl_DStringLength(&sigDS); Tcl_DStringFree(&sigDS); partPtr->next = NULL; *body = multiPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * * FindBoundary -- * * Find the boundary in a message string. * * Results: * Pointer to the start of the boundary. * * Side effects: * None * * *---------------------------------------------------------------------- */ static char* FindBoundary(char *text, char*boundary) { char *cPtr = text; int l = strlen(boundary); if (NULL == text) return NULL; do { if ('-' == cPtr[0] && '-' == cPtr[1] && !strncmp(cPtr+2, boundary, l)){ return cPtr; } cPtr = strchr(cPtr, '\n'); } while (cPtr++); return NULL; } /* *---------------------------------------------------------------------- * * RatPGPChecksig -- * * Check the signature of a bodypart * * Results: * None * * Side effects: * Updates the bodyInfoPtr->sigStatus * * *---------------------------------------------------------------------- */ void RatPGPChecksig(Tcl_Interp *interp, MessageProcInfo* procInfo, BodyInfo *bodyInfoPtr) { unsigned char *text; unsigned long length; char *command; CONST84 char *version; version = Tcl_GetVar2(interp, "option", "pgp_version", TCL_GLOBAL_ONLY); /* * Check if PGP/MIME message or old style PGP. * The algorithms kind of differ:-) */ if (bodyInfoPtr->secPtr) { char *boundary, *start, *end, *from; char buf[2048], textfile[1024], sigfile[1024]; int fd, toPGP, fromPGP, errPGP, pid, status, result; CONST84 char *tmp; PARAMETER *parPtr; Tcl_DString *resultDS = (Tcl_DString*)ckalloc(sizeof(Tcl_DString)); /* * Generate filenames */ tmp = Tcl_GetVar(interp, "rat_tmp", TCL_GLOBAL_ONLY); tmp = RatTranslateFileName(interp, tmp); RatGenIdCmd(NULL, interp, 0, NULL); snprintf(textfile, sizeof(textfile), "%s/rat.%s", tmp, Tcl_GetStringResult(interp)); strlcpy(sigfile, textfile, sizeof(sigfile)); strlcat(sigfile, ".sig", sizeof(sigfile)); /* * Save text and signature in files */ boundary = NULL; text = (unsigned char*)(*procInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr->secPtr, &length); for (parPtr = bodyInfoPtr->secPtr->bodyPtr->parameter; parPtr; parPtr = parPtr->next) { if (!strcasecmp(parPtr->attribute, "boundary")) { boundary = parPtr->value; break; } } if (!boundary || NULL == (start = FindBoundary((char*)text,boundary))){ goto fail; } start += strlen(boundary) + 4; if (NULL == (end = FindBoundary(start, boundary))) { goto fail; } end -= 2; fd = open(textfile, O_CREAT | O_TRUNC | O_WRONLY, FILEMODE); if (0 > safe_write(fd, start, end-start)) goto fail; close(fd); text = (unsigned char*)(*procInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr->secPtr->firstbornPtr->nextPtr, &length); fd = open(sigfile, O_CREAT | O_TRUNC | O_WRONLY, FILEMODE); if (text) { if (0 > safe_write(fd, text, length)) goto fail; } close(fd); /* * Run PGP command */ if (!strcmp("gpg-1", version)) { command = "gpg"; snprintf(buf, sizeof(buf), "--verify --no-secmem-warning --batch %s %s", sigfile, textfile); } else if (!strcmp("2", version)) { command = "pgp"; snprintf(buf, sizeof(buf), "+batchmode +verbose=0 %s %s", sigfile, textfile); } else if (!strcmp("5", version)) { command = "pgpv"; snprintf(buf, sizeof(buf), "+batchmode=1 %s -o %s", sigfile, textfile); } else if (!strcmp("6", version)) { command = "pgp"; snprintf(buf, sizeof(buf), "+batchmode +verbose=0 +force %s %s", sigfile, textfile); } else { Tcl_SetResult(interp, "Unkown pgp version", TCL_STATIC); return; } pid = RatRunPGP(interp, 1, command, buf, &toPGP, &from, &errPGP, NULL); close(toPGP); do { result = waitpid(pid, &status, 0); } while(-1 == result && EINTR == errno); fromPGP = open(from, O_RDONLY); Tcl_DStringInit(resultDS); while (0 < (length = SafeRead(errPGP, buf, sizeof(buf)))) { Tcl_DStringAppend(resultDS, buf, length); } while (0 < (length = SafeRead(fromPGP, buf, sizeof(buf)))) { Tcl_DStringAppend(resultDS, buf, length); } close(fromPGP); unlink(from); close(errPGP); if (pid != result || WEXITSTATUS(status)) { bodyInfoPtr->sigStatus = RAT_SIG_BAD; } else { bodyInfoPtr->sigStatus = RAT_SIG_GOOD; } /* * pgp-6.5.1i does not produce usable exit codes :-( */ if (!strcmp("6", version)) { bodyInfoPtr->sigStatus = RAT_UNCHECKED; } bodyInfoPtr->pgpOutput = resultDS; /* * Clean up */ unlink(textfile); unlink(sigfile); /*fprintf(stderr, "textfile: %s\n", textfile);*/ /*fprintf(stderr, " sigfile: %s\n", sigfile);*/ } else { Tcl_DString *bodyDSPtr; char *start, *end; text = (unsigned char*)(*procInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length); if (text) { start = RatPGPStrFind((char*)text, length, "BEGIN PGP", 1); if (NULL == start) { Tcl_ResetResult(interp); return; } end = RatPGPStrFind(start,length-(start-(char*)text),"END PGP ",1); bodyDSPtr=RatPGPRunOld(interp,bodyInfoPtr,(char*)text,start,end+1); Tcl_DStringFree(bodyDSPtr); ckfree(bodyDSPtr); } } if (bodyInfoPtr->pgpOutput && 1pgpOutput)){ Tcl_SetResult(interp, Tcl_DStringValue(bodyInfoPtr->pgpOutput), TCL_VOLATILE); } else { Tcl_ResetResult(interp); } return; fail: bodyInfoPtr->sigStatus = RAT_SIG_BAD; } /* *---------------------------------------------------------------------- * * RatPGPDecrypt -- * * Decryt a bodypart. * * Results: * None * * Side effects: * The BodyInfo structure will be modified if the decryption is ok. * * *---------------------------------------------------------------------- */ void RatPGPDecrypt(Tcl_Interp *interp, MessageProcInfo *procInfo, BodyInfo **bodyInfoPtrPtr) { int toPGP, fromPGP, errPGP, result, pid, retry, status, i; char *text, buf[1024], *from; volatile char passPhrase[MAXPASSLENGTH]; CONST84 char *version; BodyInfo *origPtr = *bodyInfoPtrPtr, *partInfoPtr; MessageInfo *msgPtr; unsigned long length, textLength, passLength; Tcl_DString bodyDS, *errDSPtr = (Tcl_DString*)ckalloc(sizeof(Tcl_DString)); RatLog(interp, RAT_PARSE, "decrypting", RATLOG_EXPLICIT); version = Tcl_GetVar2(interp, "option", "pgp_version", TCL_GLOBAL_ONLY); /* * Decode the bodypart */ Tcl_DStringInit(&bodyDS); (*procInfo[(*bodyInfoPtrPtr)->type].makeChildrenProc) (interp, *bodyInfoPtrPtr); text = (*procInfo[(*bodyInfoPtrPtr)->type].fetchBodyProc) ((*bodyInfoPtrPtr)->firstbornPtr->nextPtr, &textLength); retry = 1; while (retry && text) { if (NULL == RatPGPPhrase(interp, passPhrase, sizeof(passPhrase))) { goto failed; } if (!strcmp("gpg-1", version)) { pid = RatRunPGP(interp, 0, "gpg", "--decrypt -atq --no-secmem-warning " "--passphrase-fd 0 --batch", &toPGP, &from, &errPGP, NULL); } else if (!strcmp("2", version)) { pid = RatRunPGP(interp, 0, "pgp", "+BATCHMODE +VERBOSE=0 -f", &toPGP, &from, &errPGP, NULL); } else if (!strcmp("5", version)) { pid = RatRunPGP(interp, 0, "pgpv", "+batchmode=1 -f", &toPGP, &from, &errPGP, NULL); } else if (!strcmp("6", version)) { pid = RatRunPGP(interp, 0, "pgp", "+BATCHMODE +VERBOSE=0 +force -f", &toPGP, &from, &errPGP, NULL); } else { Tcl_SetResult(interp, "Unkown pgp version", TCL_STATIC); for (i=0; icmdName); (*bodyInfoPtrPtr)->containedEntity = RatFrMessageCreate(interp, Tcl_DStringValue(&bodyDS), Tcl_DStringLength(&bodyDS), &msgPtr); Tcl_DStringFree(&bodyDS); *bodyInfoPtrPtr = Fr_CreateBodyProc(interp, msgPtr); msgPtr->bodyInfoPtr = NULL; if (WEXITSTATUS(status)) { (*bodyInfoPtrPtr)->sigStatus = RAT_UNSIGNED; } else { (*bodyInfoPtrPtr)->sigStatus = RAT_SIG_GOOD; } (*bodyInfoPtrPtr)->pgpOutput = errDSPtr; (*bodyInfoPtrPtr)->altPtr = origPtr; RatLog(interp, RAT_PARSE, "", RATLOG_EXPLICIT); failed: /* * Create ordinary parts for body */ for (partInfoPtr = (*bodyInfoPtrPtr)->firstbornPtr; partInfoPtr; partInfoPtr = partInfoPtr->nextPtr) { Tcl_CreateObjCommand(interp, partInfoPtr->cmdName, RatBodyCmd, (ClientData) partInfoPtr, NULL); } RatLog(interp, RAT_PARSE, "", RATLOG_EXPLICIT); } /* *---------------------------------------------------------------------- * * RatPGPListKeys -- * * Lists the keys on a keyring * * Results: * See ../doc/interface. * * Side effects: * Runs the pgp command * * *---------------------------------------------------------------------- */ int RatPGPListKeys(Tcl_Interp *interp, char *keyringName) { struct stat sbuf; Tcl_DString ck; RatPGPKeyring *k = NULL; Tcl_Obj **list, **l = NULL, *l3[6]; int i; CONST84 char *value, *name; int secring = 0; Tcl_DStringInit(&ck); if (keyringName && !strcmp("PubRing", keyringName)) { Tcl_DStringAppend(&ck, "", 0); } else if (keyringName && !strcmp("SecRing", keyringName)) { Tcl_DStringAppend(&ck, "", 0); secring = 1; } else if (keyringName) { switch (keyringName[0]) { case '/': Tcl_DStringAppend(&ck, keyringName, -1); break; case '~': name = RatTranslateFileName(interp, keyringName); Tcl_DStringAppend(&ck, name, -1); break; default: Tcl_DStringAppend(&ck, Tcl_GetVar2(interp, "env", "HOME", TCL_GLOBAL_ONLY), -1); Tcl_DStringAppend(&ck, "/.pgp/", -1); Tcl_DStringAppend(&ck, keyringName, -1); break; } } else { if (NULL == (value = RatGetPathOption(interp, "pgp_keyring"))) { return TCL_ERROR; } Tcl_DStringAppend(&ck, value, -1); } /* * Check that we really need to do this */ if ((keyring && !strcmp(keyring->name, Tcl_DStringValue(&ck)))) { k = keyring; if (0 != stat(k->name, &sbuf) || sbuf.st_mtime != k->mtime) { RatPGPFreeKeyring(keyring); k = NULL; keyring = k = RatPGPNewKeyring(interp, Tcl_DStringValue(&ck), secring); if (TCL_OK != RatUpdatePGPKeys(interp, k)) { return TCL_ERROR; } } } if (!k) { k = RatPGPNewKeyring(interp, Tcl_DStringValue(&ck), secring); if (TCL_OK != RatUpdatePGPKeys(interp, k)) { return TCL_ERROR; } } if (!keyringName) { keyring = k; } Tcl_DStringFree(&ck); if (0 < k->keyCount) { list = (Tcl_Obj**)ckalloc(sizeof(Tcl_Obj*)*k->keyCount); for (i=0; ikeyCount; i++) { l3[0] = k->keys[i].keyid; l3[1] = k->keys[i].addresses; l3[2] = k->keys[i].descr; l3[3] = k->keys[i].subjects; l3[4] = k->keys[i].sign; l3[5] = k->keys[i].encrypt; list[i] = Tcl_NewListObj(6, l3); } l3[0] = k->title; l3[1] = Tcl_NewListObj(k->keyCount, list); Tcl_SetObjResult(interp, Tcl_NewListObj(2, l3)); ckfree(list); ckfree(l); } else { Tcl_ResetResult(interp); } if (keyring != k) { RatPGPFreeKeyring(k); } return TCL_OK; } /* *---------------------------------------------------------------------- * * RatPGPExtractKey -- * * Extracts a key from a keyring * * Results: * See ../doc/interface. * * Side effects: * Runs the pgp command * * *---------------------------------------------------------------------- */ int RatPGPExtractKey(Tcl_Interp *interp, char *id, char *keyringName) { int toPGP, fromPGP, errPGP, pid, status, length, ret; Tcl_DString cmd, ck; char buf[1024], *cPtr, *command, *from; CONST84 char *value, *version, *name, *keyring_arg; Tcl_Obj *rPtr; Tcl_DStringInit(&ck); if (keyringName) { switch (keyringName[0]) { case '/': Tcl_DStringAppend(&ck, keyringName, -1); break; case '~': name = RatTranslateFileName(interp, keyringName); Tcl_DStringAppend(&ck, name, -1); break; default: Tcl_DStringAppend(&ck, Tcl_GetVar2(interp, "env", "HOME", TCL_GLOBAL_ONLY), -1); Tcl_DStringAppend(&ck, "/.pgp/", -1); Tcl_DStringAppend(&ck, keyringName, -1); break; } } else { if (NULL != (value = RatGetPathOption(interp, "pgp_keyring"))) { Tcl_DStringAppend(&ck, value, -1); } } Tcl_DStringInit(&cmd); rPtr = Tcl_NewObj(); version = Tcl_GetVar2(interp, "option", "pgp_version", TCL_GLOBAL_ONLY); if (!strcmp("gpg-1", version)) { command = "gpg"; keyring_arg = "--keyring "; Tcl_DStringAppend(&cmd, "--no-secmem-warning --export -aqt ", -1); } else if (!strcmp("2", version)) { command = "pgp"; keyring_arg = "+PubRing="; Tcl_DStringAppend(&cmd, "-kxaf +BATCHMODE +VERBOSE=0 ", -1); } else if (!strcmp("5", version)) { command = "pgpk"; keyring_arg = "+PubRing="; Tcl_DStringAppend(&cmd, "+batchmode=1 -x ", -1); } else if (!strcmp("6", version)) { command = "pgp"; keyring_arg = "+PubRing="; Tcl_DStringAppend(&cmd, "-kxaf +BATCHMODE +VERBOSE=0 +force ", -1); } else { Tcl_SetResult(interp, "Unkown pgp version", TCL_STATIC); return TCL_ERROR; } if (Tcl_DStringLength(&ck)) { Tcl_DStringAppend(&cmd, keyring_arg, -1); Tcl_DStringAppend(&cmd, Tcl_DStringValue(&ck), Tcl_DStringLength(&ck)); } Tcl_DStringAppend(&cmd, " \"", 2); for (cPtr = id; *cPtr; cPtr++) { if ('"' == *cPtr) { Tcl_DStringAppend(&cmd, "\\\"", 2); } else { Tcl_DStringAppend(&cmd, cPtr, 1); } } Tcl_DStringAppend(&cmd, "\"", 1); pid = RatRunPGP(interp, 1, command, Tcl_DStringValue(&cmd), &toPGP, &from, &errPGP, NULL); Tcl_DStringFree(&cmd); close(toPGP); do { ret = waitpid(pid, &status, 0); } while(-1 == ret && EINTR == errno); /* * Read output */ fromPGP = open(from, O_RDONLY); do { if (0 < (length = SafeRead(fromPGP, buf, sizeof(buf)))) { Tcl_AppendToObj(rPtr, buf, length); } } while (length > 0); close(fromPGP); unlink(from); /* * Check for errors? */ if (pid != ret || (WEXITSTATUS(status) != 0 && WEXITSTATUS(status) != 1)) { Tcl_SetStringObj(rPtr, NULL, 0); do { if (0 < (length = SafeRead(errPGP, buf, sizeof(buf)))) { Tcl_AppendToObj(rPtr, buf, length); } } while (length > 0); close(errPGP); Tcl_SetObjResult(interp, rPtr); return TCL_ERROR; } close(errPGP); Tcl_SetObjResult(interp, rPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatPGPAddKeys -- * * Adds keys to a keyring * * Results: * See ../doc/interface. * * Side effects: * Runs the pgp command * * *---------------------------------------------------------------------- */ int RatPGPAddKeys(Tcl_Interp *interp, char *keys, char *keyring) { Tcl_DString cmd; int result; /* * Setup and execute command */ Tcl_DStringInit(&cmd); Tcl_DStringAppendElement(&cmd, "RatPGPAddKeys"); Tcl_DStringAppendElement(&cmd, keys); if (keyring) { Tcl_DStringAppendElement(&cmd, keyring); } result = Tcl_Eval(interp, Tcl_DStringValue(&cmd)); Tcl_DStringFree(&cmd); return result; } /* *---------------------------------------------------------------------- * * RatPGPRunOld -- * * Handle an bodypart generated by pgp (which is not PGP/MIME). * * Results: * None * * Side effects: * The BodyInfo structure will be modified. * * *---------------------------------------------------------------------- */ static Tcl_DString* RatPGPRunOld(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, char *text, char *start, char *end) { Tcl_DString *errDSPtr = (Tcl_DString*)ckalloc(sizeof(Tcl_DString)); Tcl_DString *bodyDSPtr = (Tcl_DString*)ckalloc(sizeof(Tcl_DString)); Tcl_DString *dsPtr = NULL; Tcl_DString statusDS; int needPhrase, toPGP, fromPGP, errPGP, statusPGP = -1, length, preamble; int pid, status, result, retry, i, failed = 0; char *ePtr, *cPtr, buf[1024], *from; volatile char passPhrase[MAXPASSLENGTH]; CONST84 char *version; FILE *fp; version = Tcl_GetVar2(interp, "option", "pgp_version", TCL_GLOBAL_ONLY); /* * Prepare text part */ Tcl_DStringInit(bodyDSPtr); RatDStringApendNoCRLF(bodyDSPtr, text, start-text); preamble = start-text; /* * Setup and run pgp command */ needPhrase = strncmp(start, "-----BEGIN PGP SIGNED", 21); do { if (needPhrase) { if (NULL == RatPGPPhrase(interp, passPhrase, sizeof(passPhrase))) { RatDStringApendNoCRLF(bodyDSPtr, start, -1); ckfree(errDSPtr); bodyInfoPtr->pgpOutput = NULL; bodyInfoPtr->sigStatus = RAT_PGP_ABORT; return bodyDSPtr; } } else { passPhrase[0] = '\0'; } if (!strcmp("gpg-1", version)) { pid = RatRunPGP(interp, 0, "gpg", "--decrypt -atq " "--passphrase-fd 0 --no-secmem-warning --batch " "--status-fd=%d", &toPGP, &from, &errPGP, &statusPGP); } else if (!strcmp("2", version)) { pid = RatRunPGP(interp, 0, "pgp", "+BATCHMODE +VERBOSE=0 -f", &toPGP, &from, &errPGP, NULL); } else if (!strcmp("5", version)) { pid = RatRunPGP(interp, 0, "pgpv", "+batchmode=1 -f", &toPGP, &from, &errPGP, NULL); } else if (!strcmp("6", version)) { pid = RatRunPGP(interp, 0, "pgp", "+BATCHMODE +VERBOSE=0 +force -f", &toPGP, &from, &errPGP, NULL); } else { Tcl_SetResult(interp, "Unkown pgp version", TCL_STATIC); for (i=0; i safe_write(toPGP,(char*)passPhrase,strlen((char*)passPhrase))) { return NULL; } for (i=0; i safe_write(toPGP, "\n", 1)) return NULL; fp = fdopen(toPGP, "w"); /* * Undo any encoding ant buggy MTA may have applied. Since we have this * code here it means that the "BEGIN PGP" stuff must be visible in * the raw (undecoded) text, so the only encoding we actually can * handle is quoted-printable. I can live with this restriction and * it is better than the performance penalty to decode all bodyparts. */ if (ENC7BIT != bodyInfoPtr->bodyPtr->encoding) { dsPtr = RatDecode(interp, bodyInfoPtr->bodyPtr->encoding, start, end-start, NULL); start = Tcl_DStringValue(dsPtr); ePtr = start + Tcl_DStringLength(dsPtr); } else { if (!(ePtr = strchr(end, '\n'))) { ePtr = end + strlen(end); } } /* Convert to local newline conventions */ for (cPtr = start; cPtr < ePtr; cPtr++) { if ('\r' == cPtr[0] && '\n' == cPtr[1]) cPtr++; fputc(*cPtr, fp); } fclose(fp); if (dsPtr) { Tcl_DStringFree(dsPtr); } /* * Read output descriptors */ Tcl_DStringInit(errDSPtr); Tcl_DStringInit(&statusDS); Tcl_DStringAppend(&statusDS, "\n", 1); while (errPGP != -1 || statusPGP != -1) { fd_set readfds; int nfds = errPGP > statusPGP ? errPGP : statusPGP; FD_ZERO(&readfds); if (errPGP != -1) FD_SET(errPGP, &readfds); if (statusPGP != -1) FD_SET(statusPGP, &readfds); if (-1 == select(nfds+1, &readfds, NULL, NULL, NULL)) { if (EINTR == errno) { continue; } break; } if (errPGP > -1 && FD_ISSET(errPGP, &readfds)) { length = SafeRead(errPGP, buf, sizeof(buf)); if (length <= 0) { close(errPGP); errPGP = -1; } else { Tcl_DStringAppend(errDSPtr, buf, length); } } if (statusPGP > -1 && FD_ISSET(statusPGP, &readfds)) { length = SafeRead(statusPGP, buf, sizeof(buf)); if (length <= 0) { close(statusPGP); statusPGP = -1; } else { Tcl_DStringAppend(&statusDS, buf, length); } } } if (errPGP != -1) close(errPGP); if (statusPGP != -1) close(statusPGP); /* Wait for PGP process to finish */ do { result = waitpid(pid, &status, 0); } while(-1 == result && EINTR == errno); /* * Read output file */ fromPGP = open(from, O_RDONLY); while (0 < (length = SafeRead(fromPGP, buf, sizeof(buf)))) { Tcl_DStringAppend(bodyDSPtr, buf, length); } close(fromPGP); unlink(from); /* fprintf(stderr, "%s:%d BODY <<%s>>\n", __FILE__, __LINE__, */ /* Tcl_DStringValue(bodyDSPtr)); */ /* fprintf(stderr, "%s:%d ERR <<%s>>\n", __FILE__, __LINE__, */ /* Tcl_DStringValue(errDSPtr)); */ /* fprintf(stderr, "%s:%d STATUS <<%s>>\n", __FILE__, __LINE__, */ /* Tcl_DStringValue(&statusDS)); */ /* * Check for errors? */ if (pid != result) { failed = 1; } else if (0 == Tcl_DStringLength(&statusDS)) { if ((WEXITSTATUS(status) != 0 && WEXITSTATUS(status) != 1)) { failed = 1; } } else { char *status = Tcl_DStringValue(&statusDS); failed = !strstr(status, "\n[GNUPG:] "); } if (failed) { Tcl_DString error; ClearPGPPass(NULL); Tcl_DStringInit(&error); Tcl_DStringAppend(&error, "RatPGPError", -1); Tcl_DStringAppendElement(&error, Tcl_DStringValue(errDSPtr)); if (TCL_OK != Tcl_Eval(interp, Tcl_DStringValue(&error)) || !strcmp("ABORT", Tcl_GetStringResult(interp))) { close(errPGP); Tcl_DStringFree(&error); Tcl_DStringFree(&statusDS); RatLog(interp, RAT_PARSE, "", RATLOG_EXPLICIT); RatDStringApendNoCRLF(bodyDSPtr, start, ePtr-start); RatDStringApendNoCRLF(bodyDSPtr, end, -1); bodyInfoPtr->pgpOutput = errDSPtr; bodyInfoPtr->sigStatus = RAT_PGP_ABORT; return bodyDSPtr; } else { retry = 1; } } else { retry = 0; } } while (0 != retry); if (Tcl_DStringLength(&statusDS) > 0) { char *status = Tcl_DStringValue(&statusDS); if (strstr(status, "\n[GNUPG:] GOODSIG")) { bodyInfoPtr->sigStatus = RAT_SIG_GOOD; } else if (strstr(status, "\n[GNUPG:] ERRSIG")) { bodyInfoPtr->sigStatus = RAT_SIG_ERR; } else if (strstr(status, "\n[GNUPG:] BADSIG")) { bodyInfoPtr->sigStatus = RAT_SIG_BAD; } else { bodyInfoPtr->sigStatus = RAT_UNSIGNED; } } else if (WEXITSTATUS(status)) { if (needPhrase) { bodyInfoPtr->sigStatus = RAT_UNSIGNED; } else { bodyInfoPtr->sigStatus = RAT_SIG_BAD; } } else { bodyInfoPtr->sigStatus = RAT_UNCHECKED; } bodyInfoPtr->pgpOutput = errDSPtr; Tcl_DStringFree(&statusDS); return bodyDSPtr; } /* *---------------------------------------------------------------------- * * RatPGPHandleOld -- * * Handle an bodypart generated by pgp (which is not PGP/MIME). * * Results: * None * * Side effects: * The BodyInfo structure will be modified. * * *---------------------------------------------------------------------- */ void RatPGPHandleOld(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, char *text, char *start, char *end) { if (strncmp(start, "-----BEGIN PGP SIGNED", 21)) { char *cPtr; CONST84 char *t; bodyInfoPtr->decodedTextPtr = RatPGPRunOld(interp, bodyInfoPtr, text, start, end); if (!(cPtr = strchr(end, '\n'))) { cPtr = end + strlen(end); } if (*cPtr) { RatDStringApendNoCRLF(bodyInfoPtr->decodedTextPtr, cPtr, -1); } if (bodyInfoPtr->pgpOutput && 1 < Tcl_DStringLength(bodyInfoPtr->pgpOutput)) { Tcl_DString cmd; Tcl_DStringInit(&cmd); Tcl_DStringAppendElement(&cmd, "RatText"); t = Tcl_GetVar2(interp, "t", "pgp_output", TCL_GLOBAL_ONLY); Tcl_DStringAppendElement(&cmd, t); Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(bodyInfoPtr->pgpOutput)); Tcl_Eval(interp, Tcl_DStringValue(&cmd)); Tcl_DStringFree(&cmd); } } else { bodyInfoPtr->sigStatus = RAT_UNCHECKED; } } /* *---------------------------------------------------------------------- * * RatUpdatePGPKeys -- * * Update the internal list of pgp keys (if needed) * * Results: * None * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatUpdatePGPKeys(Tcl_Interp *interp, RatPGPKeyring *k) { int toPGP, errPGP, pid, status, length, ret, blankiscont = 0; char buf[1024], *command, *from; CONST84 char *version, *keyring_arg; Tcl_DString cmd; struct stat sbuf; Tcl_RegExp exp_id = NULL, exp_addr = NULL; FILE *fp; Tcl_Obj *rPtr; stat(k->name, &sbuf); k->mtime = sbuf.st_mtime; /* * Launch PGP command */ Tcl_DStringInit(&cmd); version = Tcl_GetVar2(interp, "option", "pgp_version", TCL_GLOBAL_ONLY); if (!strcmp("gpg-1", version)) { command = "gpg"; keyring_arg = "--keyring "; if (k->secring) { Tcl_DStringAppend(&cmd, "--list-secret-keys ", -1); } else { Tcl_DStringAppend(&cmd, "--list-public-keys ", -1); } Tcl_DStringAppend(&cmd, "--no-secmem-warning " "--with-colons --fixed-list-mode ", -1); } else if (!strcmp("2", version)) { /* XXX Handle private-public keyrings */ command = "pgp"; keyring_arg = "+PubRing="; Tcl_DStringAppend(&cmd, "-kv +BATCHMODE +VERBOSE=0 ", -1); exp_id = Tcl_RegExpCompile(interp, "[0-9]/([0-9A-F]{8})"); exp_addr = Tcl_RegExpCompile(interp, "<[a-zA-Z.+@-]+>"); blankiscont = 1; } else if (!strcmp("5", version)) { /* XXX Handle private-public keyrings */ command = "pgpk"; keyring_arg = "+PubRing="; Tcl_DStringAppend(&cmd, "-l +batchmode=1", -1); exp_id = Tcl_RegExpCompile(interp, ".ub.+0x([0-9A-F]{8}) "); exp_addr = Tcl_RegExpCompile(interp, "<[a-zA-Z.+@-]+>"); blankiscont = 0; } else if (!strcmp("6", version)) { /* XXX Handle private-public keyrings */ command = "pgp"; keyring_arg = "+PubRing="; Tcl_DStringAppend(&cmd, "-kv +BATCHMODE +VERBOSE=0 +force ", -1); exp_id = Tcl_RegExpCompile(interp, "0x([0-9A-F]{8})"); exp_addr = Tcl_RegExpCompile(interp, "<[a-zA-Z.+@-]+>"); blankiscont = 1; } else { Tcl_SetResult(interp, "Unkown pgp version", TCL_STATIC); return TCL_ERROR; } if (k->name && *k->name) { Tcl_DStringAppend(&cmd, keyring_arg, -1); Tcl_DStringAppend(&cmd, k->name, -1); } pid = RatRunPGP(interp, 1, command, Tcl_DStringValue(&cmd), &toPGP, &from, &errPGP, NULL); Tcl_DStringFree(&cmd); close(toPGP); do { ret = waitpid(pid, &status, 0); } while(-1 == ret && EINTR == errno); fp = fopen(from, "r"); if (exp_id) { ParsePGPListFormat(interp, fp, exp_id, exp_addr, blankiscont, k); } else { ParseGPGListFormat(interp, fp, k); } fclose(fp); unlink(from); /* * Check for errors? */ if (pid != ret || (WEXITSTATUS(status) != 0 && WEXITSTATUS(status) != 1)) { rPtr = Tcl_NewObj(); do { if ( 0 < (length = SafeRead(errPGP, buf, sizeof(buf)))) { Tcl_AppendToObj(rPtr, buf, length); } } while (length > 0); close(errPGP); Tcl_SetObjResult(interp, rPtr); return TCL_ERROR; } close(errPGP); return TCL_OK; } /* *---------------------------------------------------------------------- * * ParsePGPListFormat -- * * Parse the old format key list * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void ParsePGPListFormat(Tcl_Interp *interp, FILE *fp, Tcl_RegExp exp_id, Tcl_RegExp exp_addr, int blankiscont, RatPGPKeyring *k) { int preamble; char buf[1024], title[1024], idbuf[1024]; CONST84 char *start, *end, *last; Tcl_DString tmp; Tcl_Obj *ids = NULL; Tcl_DStringInit(&tmp); preamble = 1; buf[sizeof(buf)-1] = '\0'; while (fgets(buf, sizeof(buf)-1, fp) && !feof(fp)) { if (buf[0]) { buf[strlen(buf)-1] = '\0'; } if (blankiscont && !isspace(buf[0]) && ids) { AddKey(interp, k, ids, &tmp); Tcl_DStringSetLength(&tmp, 0); ids = NULL; } if (Tcl_RegExpExec(interp, exp_id, buf, buf) || Tcl_RegExpExec(interp, exp_addr, buf, buf)) { if (preamble) { preamble = 0; if (k->title) { Tcl_DecrRefCount(k->title); } k->title = Tcl_NewStringObj(title, -1); Tcl_IncrRefCount(k->title); } if (Tcl_DStringLength(&tmp)) { Tcl_DStringAppend(&tmp, "\n", 1); } Tcl_DStringAppend(&tmp, buf, -1); last = buf; do { if (NULL == ids) { ids = Tcl_NewObj(); } if (Tcl_RegExpExec(interp, exp_id, buf, buf)) { Tcl_RegExpRange(exp_id, 1, &start, &end); last = end; strlcpy(idbuf, "0x", sizeof(idbuf)); strlcpy(idbuf+2, start, end-start+1); Tcl_ListObjAppendElement( interp, ids, Tcl_NewStringObj(idbuf, 2+end-start)); } if (Tcl_RegExpExec(interp, exp_addr, buf, buf)) { Tcl_RegExpRange(exp_addr, 0, &start, &end); last = end; Tcl_ListObjAppendElement( interp, ids, Tcl_NewStringObj(start, end-start)); } } while (Tcl_RegExpExec(interp, exp_id, last, buf) || Tcl_RegExpExec(interp, exp_addr, last, buf)); } else { if (ids) { AddKey(interp, k, ids, &tmp); Tcl_DStringSetLength(&tmp, 0); ids = NULL; } if (preamble && buf[0] && buf[0] != '-') { strlcpy(title, buf, sizeof(title)); } } } if (ids) { AddKey(interp, k, ids, &tmp); } Tcl_DStringFree(&tmp); } /* *---------------------------------------------------------------------- * * ParseGPGListFormat -- * * Parse the gpg format key list * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void ParseGPGListFormat(Tcl_Interp *interp, FILE *fp, RatPGPKeyring *k) { char buf[1024], buf2[1024], buf3[16], *fields[MAX_FIELDS], *s, *e, *alg; int num_fields, expect_uid = 0, is_subkey, is_key; Tcl_Obj *id = NULL; Tcl_Obj *addresses = NULL; Tcl_Obj *subjects = NULL; Tcl_Obj *descr = NULL; int sign = 0, encrypt = 0; Tcl_Obj *oPtr; struct tm *tm; time_t t; buf[sizeof(buf)-1] = '\0'; do { if (!fgets(buf, sizeof(buf)-1, fp) || feof(fp)) { buf[0] = '\0'; } else if (buf[0]) { buf[strlen(buf)-1] = '\0'; } for (num_fields=0, s=buf; s && *s && num_fields < MAX_FIELDS;) { fields[num_fields++] = s; if (NULL != (s = strchr(s, ':'))) { *s++ = '\0'; } } if (expect_uid) { if (num_fields && !strcmp("uid", fields[0])) { oPtr = Tcl_NewStringObj(fields[9], -1); Tcl_ListObjAppendElement(interp, subjects, oPtr); if (NULL != (s = strchr(fields[9], '<')) && NULL != (e = strchr(++s, '>'))) { oPtr = Tcl_NewStringObj(s, e-s); } Tcl_ListObjAppendElement(interp, addresses, oPtr); continue; } else { AddKeyNew(interp, k, id, addresses, subjects, descr, Tcl_NewBooleanObj(sign), Tcl_NewBooleanObj(encrypt)); expect_uid = 0; } } is_subkey = is_key = 0; if (num_fields && (!strcmp("sub", fields[0]) || !strcmp("ssb", fields[0]))) { is_subkey = 1; } else if (num_fields && (!strcmp("pub", fields[0]) || !strcmp("sec", fields[0]))) { is_key = 1; } if (is_subkey || is_key) { id = Tcl_NewStringObj(fields[4], -1); switch (atoi(fields[3])) { case 1: alg = "RSA"; sign = 1; encrypt = 1; break; case 16: alg = "ElGamal (encrypt only)"; sign = 0; encrypt = 1; break; case 17: alg = "DSA (sign only)"; sign = 1; encrypt = 0; break; case 20: alg = "ElGamal (sign and encrypt)"; sign = 1; encrypt = 1; break; default: alg = "unknown"; sign = 1; encrypt = 1; break; } snprintf(buf2, sizeof(buf2), "%s %s %s", fields[0], fields[2], alg); if (fields[6][0]) { /* Expire date */ strlcat(buf2, " expires ", sizeof(buf)); t = atol(fields[6]); tm = localtime(&t); strftime(buf3, sizeof(buf3), "%x", tm); strlcat(buf2, buf3, sizeof(buf2)); } descr = Tcl_NewStringObj(buf2, -1); if (fields[11][0]) { sign = (int)strchr(fields[11], 's'); encrypt = (int)strchr(fields[11], 'e'); } if (is_subkey) { AddKeyNew(interp, k, id, addresses, subjects, descr, Tcl_NewBooleanObj(sign), Tcl_NewBooleanObj(encrypt)); } else { expect_uid = 1; addresses = Tcl_NewObj(); subjects = Tcl_NewObj(); } } } while (!feof(fp)); /* 0x986343A5 {pub 2048R/986343A5 2002-03-26 AppGate support } 0xA2646D35 {pub 1024D/A2646D35 2003-11-10 Martin Forssen sub 2048g/63A7B927 2003-11-10 [expires: 2004-11-09]} */ /* Fields to handle, pub, uid, sub, sec, ssb */ } /* *---------------------------------------------------------------------- * * AddKey -- * * Add key(s) to current keyring * * TODO, usage, trust, expiration date, alg/size * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void AddKey(Tcl_Interp *interp, RatPGPKeyring *k, Tcl_Obj *ids, Tcl_DString *descr) { unsigned int i; int idc; Tcl_Obj **idv; RatPGPKey *key; Tcl_Obj *addresses = Tcl_NewObj(); char *s; if (TCL_OK != Tcl_ListObjGetElements(interp, ids, &idc, &idv) || idc == 0 || !strcmp("0x00000000", Tcl_GetString(idv[0])) || strncmp("0x", Tcl_GetString(idv[0]), 2)) { return; } Tcl_IncrRefCount(ids); for (i = 0; i < idc; i++) { if ('<' == *Tcl_GetString(idv[i])) { s = Tcl_GetString(idv[i])+1; Tcl_ListObjAppendElement(interp, addresses, Tcl_NewStringObj(s, strlen(s)-1)); } } for(i = 0; ikeyCount == k->keyAlloc) { k->keyAlloc += 256; k->keys = (RatPGPKey*) ckrealloc(k->keys, sizeof(RatPGPKey)*k->keyAlloc); } key = &k->keys[k->keyCount++]; key->keyid = idv[i]; Tcl_IncrRefCount(key->keyid); key->addresses = addresses; key->subjects = NULL; Tcl_IncrRefCount(key->subjects); key->descr = Tcl_NewStringObj( Tcl_DStringValue(descr), Tcl_DStringLength(descr)); Tcl_IncrRefCount(key->descr); key->sign = Tcl_NewBooleanObj(1); key->encrypt = key->sign; Tcl_IncrRefCount(key->sign); Tcl_IncrRefCount(key->encrypt); } } Tcl_DecrRefCount(ids); } /* *---------------------------------------------------------------------- * * AddKeyNew -- * * Add key(s) to current keyring. Thsi is the new interface. * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void AddKeyNew(Tcl_Interp *interp, RatPGPKeyring *k, Tcl_Obj *id, Tcl_Obj *addresses, Tcl_Obj *subjects, Tcl_Obj *descr, Tcl_Obj *sign, Tcl_Obj *encrypt) { RatPGPKey *key; if (k->keyCount == k->keyAlloc) { k->keyAlloc += 256; k->keys = (RatPGPKey*) ckrealloc(k->keys, sizeof(RatPGPKey)*k->keyAlloc); } key = &k->keys[k->keyCount++]; key->keyid = id; key->addresses = addresses; key->subjects = subjects; key->descr = descr; key->sign = sign; key->encrypt = encrypt; Tcl_IncrRefCount(key->keyid); Tcl_IncrRefCount(key->addresses); Tcl_IncrRefCount(key->subjects); Tcl_IncrRefCount(key->descr); Tcl_IncrRefCount(key->sign); Tcl_IncrRefCount(key->encrypt); } /* *---------------------------------------------------------------------- * * RatPGPFreeKeyring -- * * Deallocates a keyring * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void RatPGPFreeKeyring(RatPGPKeyring *k) { int i; for (i=0; i < k->keyCount; i++) { Tcl_DecrRefCount(k->keys[i].keyid); Tcl_DecrRefCount(k->keys[i].addresses); Tcl_DecrRefCount(k->keys[i].subjects); Tcl_DecrRefCount(k->keys[i].descr); Tcl_DecrRefCount(k->keys[i].sign); Tcl_DecrRefCount(k->keys[i].encrypt); } ckfree(k->keys); if (k->title) { Tcl_DecrRefCount(k->title); } ckfree(k->name); k->keyCount = 0; ckfree(k); } /* *---------------------------------------------------------------------- * * RatPGPNewKeyring -- * * Allocate a new keyring * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static RatPGPKeyring* RatPGPNewKeyring(Tcl_Interp *interp, const char *name, int secring) { RatPGPKeyring *k; k = (RatPGPKeyring*)ckalloc(sizeof(RatPGPKeyring)); k->keys = NULL; k->keyCount = 0; k->keyAlloc = 0; k->title = Tcl_GetVar2Ex(interp, "t", (secring ? "secring" : "pubring"), TCL_GLOBAL_ONLY); Tcl_IncrRefCount(k->title); k->name = cpystr(name); k->mtime = 0; k->secring = secring; return k; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratPrint.c000066400000000000000000001127551137544547100205340ustar00rootroot00000000000000/* * ratPrint.c -- * * This file contains the code for a simple prettyprinter. * Unfortunately it is currently limited to iso8859-1 characters. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" #include #include typedef enum { FONT_SMALL, FONT_NORMAL, FONT_BOLD, FONT_BIG } RatFont; /* * Misc defines and variables describing the size of the page and * other related data */ #define PS_LEFT_MARGIN 50 #define PS_RIGHT_MARGIN 25 #define PS_TOP_MARGIN 25 #define PS_BOTTOM_MARGIN 25 static int ps_xsize, ps_ysize; static int portrait; static int fontsize; static int resolution; static char *font, *boldfont; static int *font_wx, *boldfont_wx; static int yPos; static int pagenum; static int last_font = -1; #define DATEWIDTH fontsize*7 #define PAGENUMWIDTH fontsize*4 #define SPACE 5 #define HINDENT 20 #define CHECK_NEWPAGE(x, y) \ {if (yPos < SPACE) Newpage(x, y, NULL, NULL, NULL);} /* * PostScript prolog, partly stolen from the tk 8.2b1 one */ static char *prolog = "\n\ %%BeginProlog\n\ % Define the array ISOLatin1Encoding (which specifies how characters are\n\ % encoded for ISO-8859-1 fonts), if it isn't already present (Postscript\n\ % level 2 is supposed to define it, but level 1 doesn't).\n\ \n\ systemdict /ISOLatin1Encoding known not {\n\ /ISOLatin1Encoding [\n\ /space /space /space /space /space /space /space /space\n\ /space /space /space /space /space /space /space /space\n\ /space /space /space /space /space /space /space /space\n\ /space /space /space /space /space /space /space /space\n\ /space /exclam /quotedbl /numbersign /dollar /percent /ampersand\n\ /quoteright\n\ /parenleft /parenright /asterisk /plus /comma /minus /period /slash\n\ /zero /one /two /three /four /five /six /seven\n\ /eight /nine /colon /semicolon /less /equal /greater /question\n\ /at /A /B /C /D /E /F /G\n\ /H /I /J /K /L /M /N /O\n\ /P /Q /R /S /T /U /V /W\n\ /X /Y /Z /bracketleft /backslash /bracketright /asciicircum \ /underscore\n\ /quoteleft /a /b /c /d /e /f /g\n\ /h /i /j /k /l /m /n /o \n\ /p /q /r /s /t /u /v /w\n\ /x /y /z /braceleft /bar /braceright /asciitilde /space\n\ /space /space /space /space /space /space /space /space\n\ /space /space /space /space /space /space /space /space\n\ /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent\n\ /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron\n\ /space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n\ /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen\n\ /registered /macron\n\ /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph\n\ /periodcentered\n\ /cedillar /onesuperior /ordmasculine /guillemotright /onequarter\n\ /onehalf /threequarters /questiondown\n\ /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n\ /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex\n\ /Idieresis\n\ /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis \ /multiply\n\ /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn\n\ /germandbls\n\ /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n\ /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex\n\ /idieresis\n\ /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n\ /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn\n\ /ydieresis\n\ ] def\n\ } if\n\ \n\ % font ISOEncode font \n\ % This procedure changes the encoding of a font from the default\n\ % Postscript encoding to ISOLatin1. It's typically invoked just\n\ % before invoking \"setfont\". The body of this procedure comes from\n\ % Section 5.6.1 of the Postscript book.\n\ \n\ /ISOEncode {\n\ dup length dict begin\n\ {1 index /FID ne {def} {pop pop} ifelse} forall\n\ /Encoding ISOLatin1Encoding def\n\ currentdict\n\ end \n\ \n\ % I'm not sure why it's necessary to use \"definefont\" on this new\n\ % font, but it seems to be important; just use the name \"Temporary\"\n\ % for the font. \n\ \n\ /Temporary exch definefont \n\ } bind def \n\ "; /* * Width table for Times-Roman with the iso8859-1 encoding */ static int tir_wx[256] = { 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 408, 500, 500, 833, 778, 333, 333, 333, 500, 564, 250, 564, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 564, 564, 564, 444, 921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389, 722, 611, 889, 722, 722, 556, 722, 667, 556, 611, 722, 722, 944, 722, 722, 611, 333, 278, 333, 469, 500, 333, 444, 500, 444, 500, 444, 333, 500, 500, 278, 278, 500, 278, 778, 500, 500, 500, 500, 333, 389, 278, 500, 500, 722, 500, 500, 444, 480, 200, 480, 541, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 278, 333, 333, 333, 333, 333, 333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 250, 333, 500, 500, 500, 500, 200, 500, 333, 760, 276, 500, 564, 333, 760, 333, 400, 564, 300, 300, 333, 500, 453, 250, 333, 300, 310, 500, 750, 750, 750, 444, 722, 722, 722, 722, 722, 722, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 722, 722, 722, 722, 722, 722, 564, 722, 722, 722, 722, 722, 722, 556, 500, 444, 444, 444, 444, 444, 444, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 500, 500, 500, 500, 500, 500, 564, 500, 500, 500, 500, 500, 500, 500, 500 }; /* * Width table for Times-Bold with the iso8859-1 encoding */ static int tib_wx[256] = { 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 555, 500, 500, 1000, 833, 333, 333, 333, 500, 570, 250, 570, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500, 778, 667, 944, 722, 778, 611, 778, 722, 556, 667, 722, 722, 1000, 722, 722, 667, 333, 278, 333, 581, 500, 333, 500, 556, 444, 556, 444, 333, 500, 556, 278, 333, 556, 278, 833, 556, 500, 556, 556, 444, 389, 333, 556, 500, 722, 500, 500, 444, 394, 220, 394, 520, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 278, 333, 333, 333, 333, 333, 333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 250, 333, 500, 500, 500, 500, 220, 500, 333, 747, 300, 500, 570, 333, 747, 333, 400, 570, 300, 300, 333, 556, 540, 250, 333, 300, 330, 500, 750, 750, 750, 500, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722, 778, 778, 778, 778, 778, 570, 778, 722, 722, 722, 722, 722, 611, 556, 500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 500, 556, 500 }; /* * Width table for Helvetica with the iso8859-1 encoding */ static int hv_wx[256] = { 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584, 278, 584, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 222, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 333, 333, 333, 333, 333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500 }; /* * Width table for Helvetica-Bold with the iso8859-1 encoding */ static int hvb_wx[256] = { 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 278, 333, 333, 389, 584, 278, 584, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 278, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 333, 333, 333, 333, 333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556 }; /* * Width table for Courier with the iso8859-1 encoding */ static int co_wx[256] = { 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600 }; /* * Width table for Courier-Bold with the iso8859-1 encoding */ static int cob_wx[256] = { 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600 }; /* * Local functions */ static void InitPrintData(Tcl_Interp *interp); static float GetStringLength(RatFont font, const char *string, int length); static void Newpage(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, const char *subjectArg, MESSAGECACHE *elt); static void Startpage(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, const char *subject, MESSAGECACHE *elt, int pagenum); static void Endpage(Tcl_Channel channel); static void PsPrintString(Tcl_Interp *interp, Tcl_Channel channel, RatFont font, Tcl_Encoding enc, float lm, float hm, const char *string, int length); static void PrintHeaders(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, char *hs, MessageInfo *msgPtr); static void PsPrintAddrList(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, int x, Tcl_Obj *addrList); static void PrintBody(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, BodyInfo *bodyInfoPtr); static int PrintBodyText(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, BodyInfo *bodyInfoPtr); static int PrintBodyImage(Tcl_Interp *interp, Tcl_Channel channel, BodyInfo *bodyInfoPtr); /* *---------------------------------------------------------------------- * * RatPrettyPrintMsgCmd -- * * Print a message prettily * * Results: * A standard tcl result * * Side effects: * Adds data to the passed channel * *---------------------------------------------------------------------- */ int RatPrettyPrintMsgCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { MESSAGECACHE *elt; Tcl_Channel channel; Tcl_CmdInfo cmdInfo; MessageInfo *msgPtr; char *subject, *hs, buf[1024]; Tcl_Obj *oPtr, **bv; Tcl_Encoding enc; int bc, i; if (5 != objc) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " channel header_set msg bodys\"", (char *) NULL); return TCL_ERROR; } /* * Get data from options */ channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); hs = Tcl_GetString(objv[2]); if (0 == Tcl_GetCommandInfo(interp, Tcl_GetString(objv[3]), &cmdInfo)) { oPtr = Tcl_GetVar2Ex(interp, "t", "message_deleted", TCL_GLOBAL_ONLY); Tcl_SetObjResult(interp, oPtr); return TCL_ERROR; } msgPtr = (MessageInfo*)cmdInfo.objClientData; oPtr = RatMsgInfo(interp, msgPtr, RAT_FOLDER_SUBJECT); subject = Tcl_GetString(oPtr); elt = RatMessageInternalDate(interp, msgPtr); /* * Init print data */ InitPrintData(interp); pagenum = 0; enc = Tcl_GetEncoding(interp, "iso8859-1"); /* Print prelude & prolog */ Tcl_WriteChars(channel, "%!PS-Adobe-3.0\n" "%%Createor: TkRat\n" "%%Pages: (atend)\n" "%%DocumentData: Clean7Bit\n", -1); snprintf(buf, sizeof(buf), "%%%%Orientation: %s\n" "%%%%DocumentNeededResources: font %s\n%%%%+ font %s\n", (portrait ? "Portrait" : "Landscape"), font, boldfont); Tcl_WriteChars(channel, buf, -1); Tcl_WriteChars(channel, "%%EndComments\n", -1); Tcl_WriteChars(channel, prolog, -1); snprintf(buf, sizeof(buf), "/smallfont /%s findfont %.2f scalefont ISOEncode def\n", font, fontsize/2.0); Tcl_WriteChars(channel, buf, -1); snprintf(buf, sizeof(buf), "/textfont /%s findfont %d scalefont ISOEncode def\n", font, fontsize); Tcl_WriteChars(channel, buf, -1); snprintf(buf, sizeof(buf), "/boldfont /%s findfont %d scalefont ISOEncode def\n", boldfont, fontsize); Tcl_WriteChars(channel, buf, -1); snprintf(buf,sizeof(buf), "/bigfont /%s findfont %d scalefont ISOEncode def\n", boldfont, fontsize*2); Tcl_WriteChars(channel, buf, -1); Tcl_WriteChars(channel, "%%EndProlog\n", -1); /* Print page borders etc */ Newpage(interp, channel, enc, subject, elt); /* Print headers */ PrintHeaders(interp, channel, enc, hs, msgPtr); /* Print bodyparts */ Tcl_ListObjGetElements(interp, objv[4], &bc, &bv); for (i=0; i= 0; i--) { Tcl_ListObjIndex(interp, oPtr, i, &o2Ptr); Tcl_ListObjIndex(interp, o2Ptr, 0, &o3Ptr); if (!strcmp(s, Tcl_GetString(o3Ptr))) { break; } } Tcl_ListObjIndex(interp, o2Ptr, 1, &o3Ptr); Tcl_ListObjIndex(interp, o3Ptr, 0, &o2Ptr); Tcl_GetIntFromObj(interp, o2Ptr, &ps_xsize); Tcl_ListObjIndex(interp, o3Ptr, 1, &o2Ptr); Tcl_GetIntFromObj(interp, o2Ptr, &ps_ysize); ps_xsize -= PS_LEFT_MARGIN + PS_RIGHT_MARGIN; ps_ysize -= PS_TOP_MARGIN + PS_BOTTOM_MARGIN; /* * Orientation */ s = Tcl_GetVar2(interp, "option", "print_orientation", TCL_GLOBAL_ONLY); if (!strcmp("portrait", s)) { portrait = 1; } else { portrait = 0; i = ps_xsize; ps_xsize = ps_ysize; ps_ysize = i; } /* * Fonts */ f = Tcl_GetVar2(interp, "option", "print_fontfamily", TCL_GLOBAL_ONLY); if (!strcasecmp("helvetica", f)) { font = "Helvetica"; boldfont = "Helvetica-Bold"; font_wx = hv_wx; boldfont_wx = hvb_wx; } else if (!strcasecmp("courier", f)) { font = "Courier"; boldfont = "Courier-Bold"; font_wx = co_wx; boldfont_wx = cob_wx; } else { font = "Times-Roman"; boldfont = "Times-Bold"; font_wx = tir_wx; boldfont_wx = tib_wx; } /* * Rest of the variables */ oPtr = Tcl_GetVar2Ex(interp, "option", "print_fontsize", TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &fontsize); oPtr = Tcl_GetVar2Ex(interp, "option", "print_resolution",TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &resolution); } /* *---------------------------------------------------------------------- * * GetStringLength -- * * Get the length of a given string in poscript points * * Results: * the length of the string * * Side effects: * None * *---------------------------------------------------------------------- */ static float GetStringLength(RatFont font, const char *string, int length) { int *wx = tir_wx, i; float l, size = fontsize; if (-1 == length) { length = strlen((char*)string); } switch(font) { case FONT_SMALL: wx = font_wx; size /= 2; break; case FONT_NORMAL: wx = font_wx; break; case FONT_BOLD: wx = boldfont_wx; break; case FONT_BIG: wx = boldfont_wx; size *= 2; break; } for (i=l=0; i 0) { Endpage(channel); } Startpage(interp, channel, enc, subject, elt, ++pagenum); } /* *---------------------------------------------------------------------- * * Startpage -- * * Writes start of page data * * Results: * None * * Side effects: * Adds data to the passed channel * *---------------------------------------------------------------------- */ static void Startpage(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, const char *subject, MESSAGECACHE *elt, int pagenum) { char buf[1024]; unsigned char *cPtr; CONST84 char *s; float x, y, w, h, l; int objc; Tcl_Obj **objv, *oPtr; Tcl_DString ds; /* * prepare */ snprintf(buf, sizeof(buf), "%%%%Page: %d %d\n" "save\n" "%d %d translate\n", pagenum, pagenum, PS_LEFT_MARGIN, PS_BOTTOM_MARGIN); Tcl_WriteChars(channel, buf, -1); /* * Background */ x = SPACE/2; y = ps_ysize+SPACE/2; w = ps_xsize-SPACE; h=fontsize*2.6+SPACE; snprintf(buf, sizeof(buf), ".8 setgray %.2f %.2f %.2f %.2f rectfill\n" "0 setgray %.2f %.2f %.2f %.2f rectstroke\n", x, y-h, w, h, x, y-h, w, h); Tcl_WriteChars(channel, buf, -1); /* * Date */ x = SPACE; y = ps_ysize; w = fontsize*2.4; h = fontsize*2.6; snprintf(buf, sizeof(buf), "%.2f %.2f moveto %.2f 0 rlineto 0 -%.2f " "rlineto -%.2f 0 rlineto closepath stroke\n", x, y, w, h, w); Tcl_WriteChars(channel, buf, -1); sprintf(buf, "%d", elt->day); l = GetStringLength(FONT_BOLD, buf, -1); sprintf(buf, "%.2f %.2f moveto boldfont setfont (%d) show\n", x+w/2-l/2, y-fontsize*1.1, elt->day); Tcl_WriteChars(channel, buf, -1); oPtr = Tcl_GetVar2Ex(interp, "t", "months", TCL_GLOBAL_ONLY), Tcl_ListObjGetElements(interp, oPtr, &objc, &objv); s = Tcl_GetString(objv[elt->month-1]); l = GetStringLength(FONT_SMALL, s, -1); snprintf(buf, sizeof(buf),"%.2f %.2f moveto smallfont setfont (%s) show\n", x+w/2-l/2, y-fontsize*1.7, s); Tcl_WriteChars(channel, buf, -1); sprintf(buf, "%d", elt->year+BASEYEAR); l = GetStringLength(FONT_SMALL, buf, -1); sprintf(buf, "%.2f %.2f moveto smallfont setfont (%d) show\n", x+w/2-l/2, y-fontsize*2.3, elt->year+BASEYEAR); Tcl_WriteChars(channel, buf, -1); x += fontsize*3; y -= fontsize/2; s = Tcl_GetVar2(interp, "t", "received", TCL_GLOBAL_ONLY); snprintf(buf, sizeof(buf),"%.2f %.2f moveto smallfont setfont (%s) show\n", x, y, s); Tcl_WriteChars(channel, buf, -1); sprintf(buf, "%.2f %.2f moveto textfont setfont (%02d:%02d) show\n", x, y-fontsize*1.2, elt->hours, elt->minutes); Tcl_WriteChars(channel, buf, -1); /* * Subject */ x = SPACE+DATEWIDTH; y = ps_ysize-1.7*fontsize; yPos = ps_ysize-fontsize*4; Tcl_WriteChars(channel, buf, -1); snprintf(buf, sizeof(buf),"boldfont setfont %.2f %.2f moveto\n(", x+2, y); Tcl_WriteChars(channel, buf, -1); Tcl_UtfToExternalDString(enc, subject, -1, &ds); for (cPtr = (unsigned char*)Tcl_DStringValue(&ds); *cPtr; cPtr++) { if ('(' == *cPtr || ')' == *cPtr || '\\' == *cPtr) { Tcl_WriteChars(channel, "\\", 1); } if (*cPtr >= 32 && *cPtr < 127) { Tcl_WriteChars(channel, cPtr, 1); } else { snprintf(buf, sizeof(buf), "\\%o", *cPtr); Tcl_WriteChars(channel, buf, -1); } } Tcl_DStringFree(&ds); Tcl_WriteChars(channel, ") show\n", -1); /* * Page number */ s = Tcl_GetVar2(interp, "t", "page", TCL_GLOBAL_ONLY); x = ps_xsize - PAGENUMWIDTH - GetStringLength(FONT_SMALL, s, -1); y = ps_ysize-fontsize; snprintf(buf, sizeof(buf),"%.2f %.2f moveto smallfont setfont (%s) show\n", x, y, s); Tcl_WriteChars(channel, buf, -1); sprintf(buf, "%d", pagenum); x = ps_xsize-PAGENUMWIDTH/2 - GetStringLength(FONT_BIG, buf, -1)/2; y = ps_ysize-2*fontsize; sprintf(buf, "bigfont setfont %.2f %.2f moveto (%d) show\n", x, y,pagenum); Tcl_WriteChars(channel, buf, -1); /* * Can not set this to FONT_BIG because then tab_width will not be * calculated in PsPrintString(). */ last_font = -1; } /* *---------------------------------------------------------------------- * * Endpage -- * * Writes end of page data * * Results: * None * * Side effects: * Adds data to the passed channel * *---------------------------------------------------------------------- */ static void Endpage(Tcl_Channel channel) { Tcl_WriteChars(channel, "restore\nshowpage\n", -1); } /* *---------------------------------------------------------------------- * * PsPrintString -- * * Prints the given string with the given margins. May break the * line if it does not fit, in that case yPos is updated to point at * the last line. * * Results: * None * * Side effects: * Adds data to the passed channel and may modify yPos and * initialize a new page. * *---------------------------------------------------------------------- */ static void PsPrintString(Tcl_Interp *interp, Tcl_Channel channel, RatFont font, Tcl_Encoding enc, float lm, float hm, const char *string, int length) { static float tab_width; char buf[1024], *fn = ""; const unsigned char *cPtr; float ll, l; Tcl_DString ds; if (font != last_font) { switch (font) { case FONT_SMALL: fn = "smallfont"; break; case FONT_NORMAL: fn = "textfont"; break; case FONT_BOLD: fn = "boldfont"; break; case FONT_BIG: fn = "bigfont"; break; } snprintf(buf, sizeof(buf), "%s setfont ", fn); Tcl_WriteChars(channel, buf, -1); last_font = font; tab_width = GetStringLength(font, "XXXXXXXX", 8); } Tcl_UtfToExternalDString(enc, string, length, &ds); snprintf(buf, sizeof(buf), "%.2f %d moveto\n(", lm, yPos); Tcl_WriteChars(channel, buf, -1); for (cPtr = (unsigned char*)Tcl_DStringValue(&ds), ll=lm; *cPtr; cPtr++) { if ('\t' == *cPtr) { ll = floor((ll-lm+tab_width)/tab_width)*tab_width+lm; snprintf(buf, sizeof(buf), ") show\n%.2f %d moveto (", ll, yPos); Tcl_WriteChars(channel, buf, -1); } else { ll += (l = GetStringLength(font, (char*)cPtr, 1)); if (ll > ps_xsize-hm) { Tcl_WriteChars(channel, ") show\n", -1); yPos -= fontsize*1.1; CHECK_NEWPAGE(interp, channel); snprintf(buf, sizeof(buf), "%.2f %d moveto\n(", lm, yPos); Tcl_WriteChars(channel, buf, -1); ll = lm; } if ('(' == *cPtr || ')' == *cPtr || '\\' == *cPtr) { Tcl_WriteChars(channel, "\\", 1); } if (*cPtr >= 32 && *cPtr < 127) { Tcl_WriteChars(channel, (char*)cPtr, 1); } else { snprintf(buf, sizeof(buf), "\\%o", *cPtr); Tcl_WriteChars(channel, buf, -1); } } } Tcl_WriteChars(channel, ") show\n", -1); Tcl_DStringFree(&ds); } /* *---------------------------------------------------------------------- * * PrintHeaders -- * * Prints the selected message headers * * Results: * None * * Side effects: * Adds data to the passed channel * *---------------------------------------------------------------------- */ static void PrintHeaders(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc, char *hs, MessageInfo *msgPtr) { Tcl_Obj **fhv, **phv, *oPtr, *tPtr, *o2Ptr, **thv, *headers; char buf[1024], *s; int i, j, l, fhc, phc, *address; float maxX, *lengths; if (!strcmp("none", hs)) { return; } /* * Get headers to print */ snprintf(buf, sizeof(buf), "%s headers", msgPtr->name); Tcl_Eval(interp, buf); headers = Tcl_GetObjResult(interp); Tcl_IncrRefCount(headers); Tcl_ListObjGetElements(interp, headers, &fhc, &fhv); if (!strcmp("selected", hs)) { oPtr = Tcl_GetVar2Ex(interp, "option", "show_header_selection", TCL_GLOBAL_ONLY); Tcl_ListObjLength(interp, oPtr, &l); phv = (Tcl_Obj**)ckalloc(sizeof(Tcl_Obj*)*l); for (i=phc=0; i maxX) { maxX = lengths[i]; } } maxX += GetStringLength(FONT_BOLD, ": ", 2); /* * Print headers */ for (i=0; i=0; i--) { Tcl_DecrRefCount(objv[i]); } if (TCL_OK != r) { return TCL_ERROR; } namePtr = Tcl_GetObjResult(interp); Tcl_IncrRefCount(namePtr); ph = Tk_FindPhoto(interp, Tcl_GetString(namePtr)); Tk_PhotoGetImage(ph, &block); /* * Print it */ psw = (block.width*72)/resolution; psh = (block.height*72)/resolution; if (yPos < psh+SPACE) { Newpage(interp, channel, NULL, NULL, NULL); } yPos -= psh; sprintf(buf, "gsave\n/picstr %d string def\n", block.width*3); Tcl_WriteChars(channel, buf, -1); sprintf(buf, "%d %d translate\n", ps_xsize/2-psw/2, yPos); Tcl_WriteChars(channel, buf, -1); sprintf(buf, "%d %d scale\n", psw, psh); Tcl_WriteChars(channel, buf, -1); sprintf(buf, "%d %d 8 [%d 0 0 -%d 0 %d]\n", block.width, block.height, block.width, block.height, block.height); Tcl_WriteChars(channel, buf, -1); Tcl_WriteChars(channel, "{currentfile picstr readhexstring pop} false 3 colorimage\n", -1); for (l=y=0, p = block.pixelPtr; y < block.height; y++, p = bp+block.pitch) { bp = p; for (x=0; x=0; i--) { Tcl_DecrRefCount(objv[i]); } return TCL_OK; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratPwCache.c000066400000000000000000000203031137544547100207350ustar00rootroot00000000000000/* * RatPwCache.c -- * * This file contains password caching routines * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include #include #include "rat.h" #include "env_unix.h" /* * For the memory cache */ typedef struct CachedPasswd { int onDisk; char *spec; char *passwd; struct CachedPasswd *next; Tcl_TimerToken token; } CachedPasswd; static CachedPasswd *cache = NULL; static int initialized = 0; static char *filename = NULL; /* * Local functions */ static char *Canonify(const char *spec); static void ReadDisk(Tcl_Interp *interp); static void WriteDisk(Tcl_Interp *interp); static void TouchEntry(Tcl_Interp *interp, CachedPasswd *cp); static Tcl_TimerProc ErasePasswd; /* *---------------------------------------------------------------------- * * Canonify -- * * Convert foler specification to canonical form * * Results: * A pointer to a static buffer containing the canonic form. * This buffer will be rewritten by the next call. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static char* Canonify(const char *spec) { static char *cSpec = NULL; static int size = 0; char *c; if (strlen(spec)+1 > size) { size = strlen(spec)+64; cSpec = (char*)realloc(cSpec, size); } strlcpy(cSpec, spec, size); if (NULL != (c = strstr(cSpec, "/debug"))) { memmove(c, c+6, strlen(c+6)+1); } if (NULL != (c = strchr(cSpec, '}'))) { c[1] = '\0'; } return cSpec; } /* *---------------------------------------------------------------------- * * ReadDisk -- * * Read cached passwords from disk * * Results: * None. * * Side effects: * Updates the local cached list * * *---------------------------------------------------------------------- */ void ReadDisk(Tcl_Interp *interp) { CONST84 char **argv, *spec = NULL, *passwd = NULL; const char *name; CachedPasswd *cp; char buf[1024]; int argc; FILE *fp; if (NULL == (name = RatGetPathOption(interp, "pwcache_file"))) { return; } filename = cpystr(name); initialized = 1; if (NULL == (fp = fopen(filename, "r"))) { return; } while (fgets(buf, sizeof(buf), fp) && !feof(fp)) { if (TCL_OK != Tcl_SplitList(interp, buf, &argc, &argv) || (argc != 2 && argc != 5)) { continue; } if (2 == argc) { /* {spec passwd} */ spec = argv[0]; passwd = argv[1]; } else if (5 == argc) { /* {host port user service passwd} */ snprintf(buf, sizeof(buf), "{%s:%s/user=%s%s}", argv[0], argv[1], argv[2], (strcmp("imap", argv[3]) ? "/pop3" : "")); spec = buf; passwd = argv[4]; } cp = (CachedPasswd*)ckalloc(sizeof(CachedPasswd) +strlen(spec)+1+strlen(passwd)+1); cp->onDisk = 1; cp->spec = (char*)cp + sizeof(CachedPasswd); strcpy(cp->spec, spec); cp->passwd = cp->spec + strlen(cp->spec)+1; strcpy(cp->passwd, passwd); cp->next = cache; cache = cp; ckfree(argv); } fclose(fp); return; } /* *---------------------------------------------------------------------- * * WriteDisk -- * * Write the cache to disk. Only those entries marked to be stored on * disk are actually output. * * Results: * None. * * Side effects: * Rewrites the disk cache. * * *---------------------------------------------------------------------- */ void WriteDisk(Tcl_Interp *interp) { CachedPasswd *cp; char c; FILE *fp; struct stat sbuf; int i, fd; Tcl_DString ds; if (-1 < (fd = open(filename, O_WRONLY))) { fstat(fd, &sbuf); c = 0; for (i=0; i safe_write(fd, &c, 1)) break; } close(fd); unlink(filename); } if (NULL == (fp = fopen(filename, "w"))) { return; } fchmod(fileno(fp), 0600); Tcl_DStringInit(&ds); for (cp = cache; cp; cp = cp->next) { if (cp->onDisk) { Tcl_DStringAppendElement(&ds, cp->spec); Tcl_DStringAppendElement(&ds, cp->passwd); fprintf(fp, "%s\n", Tcl_DStringValue(&ds)); Tcl_DStringSetLength(&ds, 0); } } fclose(fp); Tcl_DStringFree(&ds); return; } /* *---------------------------------------------------------------------- * * TouchEntry -- * * Touch an entry in the cache. This only affects entries that are * going to timeout. * * Results: * None. * * Side effects: * May rewrite the cached password file * * *---------------------------------------------------------------------- */ void TouchEntry(Tcl_Interp *interp, CachedPasswd *cp) { int timeout; Tcl_Obj *oPtr; if (cp->onDisk) { return; } Tcl_DeleteTimerHandler(cp->token); oPtr = Tcl_GetVar2Ex(interp, "option", "cache_passwd_timeout", TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &timeout); if (timeout) { cp->token = Tcl_CreateTimerHandler(timeout*1000, ErasePasswd, (ClientData)cp); } } /* *---------------------------------------------------------------------- * * ErasePasswd -- * * Earase a password from the cache * * Results: * None. * * Side effects: * Modifies the in-memory cache * * *---------------------------------------------------------------------- */ static void ErasePasswd(ClientData clientData) { CachedPasswd **tcp, *cp = (CachedPasswd*)clientData; Tcl_DeleteTimerHandler(cp->token); memset(cp->passwd, 0, strlen(cp->passwd)); for (tcp = &cache; *tcp != cp; tcp = &(*tcp)->next); *tcp = cp->next; ckfree(cp); } /* *---------------------------------------------------------------------- * * RatGetCachedPassword -- * * get a cached password * * Results: * Returns a pointer to a static area containing the password, * or NULL if no suitable cached password was found. * * Side effects: * may read the cached password file * * *---------------------------------------------------------------------- */ char* RatGetCachedPassword(Tcl_Interp *interp, const char *spec) { CachedPasswd *cp; char *cSpec = Canonify(spec); if (0 == initialized) { ReadDisk(interp); } for (cp = cache; cp; cp = cp->next) { if (!strcmp(cp->spec, cSpec)) { TouchEntry(interp, cp); return cp->passwd; } } return NULL; } /* *---------------------------------------------------------------------- * * RatCachePassword -- * * Cache a password * * Results: * None. * * Side effects: * May rewrite the cached password file * * *---------------------------------------------------------------------- */ void RatCachePassword(Tcl_Interp *interp, const char *spec, const char *passwd, int store) { CachedPasswd *cp; char *cSpec = Canonify(spec); if (0 == initialized) { ReadDisk(interp); } cp = (CachedPasswd*)ckalloc(sizeof(CachedPasswd) + strlen(cSpec) + 1 + strlen(passwd) + 1); cp->onDisk = store; cp->spec = (char*)cp + sizeof(CachedPasswd); strcpy(cp->spec, cSpec); cp->passwd = cp->spec+strlen(cSpec)+1; strcpy(cp->passwd, passwd); cp->next = cache; cp->token = NULL; cache = cp; if (store) { WriteDisk(interp); } else { TouchEntry(interp, cp); } return; } /* *---------------------------------------------------------------------- * * RatPasswdCachePurge -- * * Purge the password cache. If disk_also is true the both the disk and * memory caches are purged. If disk_also is false, the only the * memory cache is purged. * * Results: * None. * * Side effects: * May rewrite the cached password file * * *---------------------------------------------------------------------- */ void RatPasswdCachePurge(Tcl_Interp *interp, int disk_also) { CachedPasswd *cp, *cpn; if (0 == initialized) { ReadDisk(interp); } for (cp = cache; cp; cp = cpn) { cpn = cp->next; memset(cp->passwd, 0, strlen(cp->passwd)); Tcl_DeleteTimerHandler(cp->token); ckfree(cp); } cache = NULL; if (disk_also) { WriteDisk(interp); } return; } int RatPasswdCachePurgeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { RatPasswdCachePurge(interp, 1); return TCL_OK; } tkrat_2.2cvs20100105-dfsg.orig/lib/ratSender.c000066400000000000000000001021121137544547100206420ustar00rootroot00000000000000/* * ratSender.c -- * * Handles sendig of messages * * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratStdFolder.h" #include #include #include #include /* * Stuff used to handle sender comm */ static int sender_created = 0; static int to_sender[2]; static FILE *to_sender_fh; static int from_sender[2]; typedef enum { EVENT_NONE, EVENT_LOG, EVENT_SEND_OK, EVENT_SEND_FAIL } send_event_t; /* * SMTP connection cache */ typedef struct smtp_conn_cache { char *host; time_t expires; SENDSTREAM *stream; struct smtp_conn_cache *next; } smtp_conn_cache_t; static smtp_conn_cache_t *conn_cache; /* * Data to pass to output function */ typedef struct { int fd; int errfd; char *errbuf; int errbufsize; int errbufused; } soutr_data_t; /* * SMTP authentication passwd */ char *smtp_passwd = NULL; /* * Current verboseness level */ static char cverboseness; /* * The sending process if any */ static pid_t sender_pid; static int sender_died = 0; static int sender_status; /* * Local functions */ static void RatSenderFileHandler(ClientData clientData, int mask); static void RatSenderHandler(Tcl_Interp *interp, int check_sender); static void RatReadString(int fd, Tcl_DString *ds); static void RatSender(void); static void RatAlarmHandler(int sig); static void RatSigChldHandler(int sig); static void RatFillinBody(BODY *b, char *bt); static char *RatSendSMTP(int in_fd, int out_fd, char *host, ENVELOPE *env, BODY *b); static void RatSendLog(int fd, const char *msg); static char *RatSendProg(int in_fd, int out_fd, char *host, ENVELOPE *env, BODY *b); static void RatAddAddresses(Tcl_DString *ds, ADDRESS *addr); static void RatSendProgChild(char *cmd, int to, int err); static long RatSendSoutr(void *stream, char *string); static void RatReadData(int fd, char **buf, int *size, int *used); static void RatWrapHeaderLines(ENVELOPE *env, BODY *b); static void RatWrapHeaderLine(char *type, char **s); /* *---------------------------------------------------------------------- * * RatNudgeSender -- * * Nudges the sender. This makes the sender rescan the outgoing * mailbox and send any messages found there. If we are online that is. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void RatNudgeSender(Tcl_Interp *interp) { pid_t pid; if (0 == sender_created) { if (pipe(to_sender)) return; if (pipe(from_sender)) { close(to_sender[0]); close(to_sender[1]); return; } if (0 == (pid = fork())) { RatSender(); /* Notreached */ exit(1); } close(to_sender[0]); close(from_sender[1]); to_sender_fh = fdopen(to_sender[1], "w"); Tcl_CreateFileHandler(from_sender[0], TCL_READABLE, RatSenderFileHandler, (ClientData)interp); sender_created = 1; } RatSenderHandler(interp, 0); } /* *---------------------------------------------------------------------- * * RatSenderFileHandler -- * * Handles file events from the sender. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void RatSenderFileHandler(ClientData clientData, int mask) { RatSenderHandler((Tcl_Interp*)clientData, mask & TCL_READABLE); } /* *---------------------------------------------------------------------- * * RatWriteString -- * * Writes the given string to the outgoing file * * Results: * non zero on failure * * Side effects: * None. * *---------------------------------------------------------------------- */ static int RatWriteString(const char *str, FILE *out) { unsigned int l = strlen(str); if (1 != fwrite(&l, sizeof(int), 1, out) || 1 != fwrite(str, l, 1, out)) { return -1; } else { return 0; } } /* *---------------------------------------------------------------------- * * RatSenderHandler -- * * The handler which handles the sending. The actual * sending is handled by RatSender. * The sender thread may generate the following events (written as * bytes on the pipe): * EVENT_LOG - Log a message * EVENT_SEND_OK - Message sent OK * EVENT_SEND_FAIL - Failed to send message * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void RatSenderHandler(Tcl_Interp *interp, int check_sender) { static Tcl_DString *log_ds = NULL; static RatFolderInfo *infoPtr = NULL; static char *name; static int verboseness = 0; static int is_sending = 0; static Tcl_DString *save_to_cmd = NULL; Tcl_CmdInfo cmdInfo; MessageInfo *msgPtr ; unsigned char c; char buf[2048], role[16], *header, *body; CONST84 char *s, *user; Tcl_Obj *oPtr, **objv, *eobjv[4]; int i, objc, n=0, validate; if (!log_ds) { log_ds = (Tcl_DString*)ckalloc(sizeof(Tcl_DString)); Tcl_DStringInit(log_ds); } if (!save_to_cmd) { save_to_cmd = (Tcl_DString*)ckalloc(sizeof(Tcl_DString)); Tcl_DStringInit(save_to_cmd); } /* * Handle possible file event */ if (check_sender) { if (1 != SafeRead(from_sender[0], &c, 1)) { RatLog(interp, RAT_ERROR, "Sender died!", RATLOG_NOWAIT); Tcl_DeleteFileHandler(from_sender[0]); sender_created = 0; is_sending = 0; c = EVENT_NONE; } switch ((send_event_t)c) { case EVENT_LOG: if (sizeof(i) != SafeRead(from_sender[0], &i, sizeof(i))) { return; } Tcl_DStringSetLength(log_ds, i); if (i != SafeRead(from_sender[0], Tcl_DStringValue(log_ds), i)) { return; } Tcl_GlobalEval(interp, Tcl_DStringValue(log_ds)); return; /* NOTREACHED */ case EVENT_SEND_OK: if (verboseness == 1) { strlcpy(buf, "RatLog 2 {} explicit",sizeof(buf)); Tcl_GlobalEval(interp, buf); } else if (verboseness > 1) { strlcpy(buf, "RatLog 2 $t(sent_ok)", sizeof(buf)); Tcl_GlobalEval(interp, buf); } is_sending = 0; /* Save copy of outgoing, if so requested */ if (Tcl_DStringLength(save_to_cmd)) { if (TCL_OK != Tcl_GlobalEval(interp, Tcl_DStringValue(save_to_cmd))) { RatLogF(interp, RAT_ERROR, "failed_save_sent", RATLOG_NOWAIT, Tcl_GetStringResult(interp)); } } /* Remove message from outfolder */ i = 0; RatFolderCmdSetFlag(interp, infoPtr, &i, 1, RAT_DELETED, 1); break; case EVENT_SEND_FAIL: if (verboseness == 1) { strlcpy(buf, "RatLog 2 {} explicit", sizeof(buf)); Tcl_GlobalEval(interp, buf); } if (sizeof(i) != SafeRead(from_sender[0], &i, sizeof(i))) { return; } Tcl_DStringSetLength(log_ds, i); if (i != SafeRead(from_sender[0], Tcl_DStringValue(log_ds), i)) { return; } Tcl_GlobalEval(interp, Tcl_DStringValue(log_ds)); eobjv[n++] = Tcl_NewStringObj("RatSendFailed", -1); eobjv[n++] = Tcl_NewStringObj(name, -1); eobjv[n++] = Tcl_NewStringObj(Tcl_DStringValue(log_ds), Tcl_DStringLength(log_ds)); for (i=0; inumber) { return; } is_sending = 1; if (verboseness == 1) { strlcpy(buf, "RatLog 2 $t(sending_message) explicit", sizeof(buf)); Tcl_GlobalEval(interp, buf); } name = RatFolderCmdGet(interp, infoPtr, 0); Tcl_GetCommandInfo(interp, name, &cmdInfo); msgPtr = (MessageInfo*)cmdInfo.objClientData; /* * Get role */ if ((s = Std_GetHeadersProc(interp, msgPtr)) && (s = strstr(s,"X-TkRat-Internal-Role"))) { for (s += 23; isspace(*s); s++); for (i=0; !isspace(s[i]) && *s && iname); Tcl_DStringAppendElement(save_to_cmd, buf); } /* * Send data to sender, data passed is: * char type - 0 = SMTP, 1 = prog * char verboseness * uint host_length * char host * uint hdr_length * char hdr * uint body_length * char body * For smtp * uint nhosts * uint host_length * char host * char passwd * uint from_length * char from * int cache - -1 = no cache, 0 = cache indefinitely, >0 = cache-time * For prog * uint cmd_length * char cmd * char ok_8bit */ snprintf(buf, sizeof(buf), "%s,sendprot", role); s = Tcl_GetVar2(interp, "option", buf, TCL_GLOBAL_ONLY); buf[0] = strcmp("smtp", s) ? 0 : 1; oPtr = Tcl_GetVar2Ex(interp, "option", "smtp_verbose", TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &verboseness); buf[1] = verboseness; if (1 != fwrite(buf, 2, 1, to_sender_fh)) goto fail; s = RatGetCurrent(interp, RAT_HOST, role); if (RatWriteString(s, to_sender_fh)) goto fail; RatMessageGetContent(interp, msgPtr, &header, &body); if (RatWriteString(header, to_sender_fh)) goto fail; if (RatWriteString(body, to_sender_fh)) goto fail; if (buf[0]) { snprintf(buf, sizeof(buf), "%s,smtp_hosts", role); oPtr = Tcl_GetVar2Ex(interp, "option", buf, TCL_GLOBAL_ONLY); Tcl_ListObjGetElements(interp, oPtr, &objc, &objv); if (1 != fwrite(&objc, sizeof(int), 1, to_sender_fh)) goto fail; snprintf(buf, sizeof(buf), "%s,validate_cert", role); oPtr = Tcl_GetVar2Ex(interp, "option", buf, TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &validate); snprintf(buf, sizeof(buf), "%s,smtp_user", role); user = Tcl_GetVar2(interp, "option", buf, TCL_GLOBAL_ONLY); for (i = 0; iexpires <= now) { smtp_close((*cp)->stream); ckfree((*cp)->host); ncp = (*cp)->next; ckfree(*cp); *cp = ncp; } else if (0 == a || a > (*cp)->expires) { a = (*cp)->expires; cp = &(*cp)->next; } } if (a) { alarm(a - time(NULL)); } if (1 != SafeRead(to_sender[0], &smtp, 1)) { exit(0); /* Master has died */ } alarm(0); if (1 != SafeRead(to_sender[0], &cverboseness, 1)) { exit(0); /* Master has died */ } RatReadString(to_sender[0], &host); RatReadString(to_sender[0], &header); RatReadString(to_sender[0], &body); /* * Parse message */ INIT(&string, mail_string, (void*)Tcl_DStringValue(&body), Tcl_DStringLength(&body)); rfc822_parse_msg(&env, &b, Tcl_DStringValue(&header), Tcl_DStringLength(&header), &string, Tcl_DStringValue(&host), 0); RatFillinBody(b, Tcl_DStringValue(&body)); if (NULL != (s = strstr(Tcl_DStringValue(&header), "X-TkRat-Internal-Bcc")) || NULL != (s = strstr(Tcl_DStringValue(&header), "X-TkRat-Original-bcc"))) { for (s += 22; isspace(*s); s++); for (i=0; '\r' != s[i] && *s && ibcc, buf, Tcl_DStringValue(&host)); } RatWrapHeaderLines(env, b); /* * Call the appropriate sender routine */ if (smtp) { errmsg = RatSendSMTP(to_sender[0], from_sender[1], Tcl_DStringValue(&host), env, b); } else { errmsg = RatSendProg(to_sender[0], from_sender[1], Tcl_DStringValue(&host), env, b); } /* * Report to master */ if (errmsg) { c = EVENT_SEND_FAIL; len = strlen(errmsg); if (0 > safe_write(from_sender[1], &c, 1) || 0 > safe_write(from_sender[1], &len, 4) || 0 > safe_write(from_sender[1], errmsg, len)) { exit(1); } } else { c = EVENT_SEND_OK; if (0 > safe_write(from_sender[1], &c, 1)) { exit(1); } } /* * Housekeeping */ mail_free_envelope(&env); mail_free_body(&b); } } /* *---------------------------------------------------------------------- * * RatAlarmHandler -- * * Signal hander which handles the ALRM signal. This function does * nothing in itself but the existence makes system-calls be * interrupted. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void RatAlarmHandler(int sig) { /* Do nothing */ } /* *---------------------------------------------------------------------- * * RatSigChldHandler -- * * Signal hander which handles the CHLD signal. Thsi sets a flag * that the sending child has dies (if that pid died). * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void RatSigChldHandler(int sig) { pid_t pid; int status; while (0 < (pid = waitpid(-1, &status, WNOHANG))) { if (pid == sender_pid) { sender_died = 1; sender_status = status; } } } /* *---------------------------------------------------------------------- * * RatFillinBody -- * * Fills in the body->contents.text.data pointers * * Results: * None. * * Side effects: * Modifies the body-structure. * *---------------------------------------------------------------------- */ static void RatFillinBody(BODY *b, char *bt) { PART *part; if (TYPEMULTIPART == b->type) { for (part = b->nested.part; part; part = part->next) { RatFillinBody(&part->body, bt); } } else { b->contents.text.data = (unsigned char*)ckalloc(b->contents.text.size+1); b->contents.text.data[b->contents.text.size] = '\0'; memcpy(b->contents.text.data, bt+b->contents.offset, b->contents.text.size); } } /* *---------------------------------------------------------------------- * * RatSendSMTP -- * * Sends a message via SMTP * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char* RatSendSMTP(int in_fd, int out_fd, char *host, ENVELOPE *env, BODY *b) { static char *err = NULL; smtp_conn_cache_t **cp, *ncp; SENDSTREAM *stream = NULL; long debug; char buf[1024], **smtp_hosts; unsigned int nhosts, len, i; Tcl_DString from, passwd; int cache; if (err) { ckfree(err); err = NULL; } /* Read data from master */ if (sizeof(nhosts) != SafeRead(in_fd, &nhosts, sizeof(nhosts))) { exit(0); /* Master has died */ } smtp_hosts = (char**)ckalloc((nhosts+1)*sizeof(char*)); for (i=0; ireturn_path, Tcl_DStringValue(&from), host); if (sizeof(int) != SafeRead(in_fd, &cache, sizeof(int))) { exit(0); /* Master has died */ } /* Get cached */ for (cp = &conn_cache; *cp; cp = &(*cp)->next) { if (!strcmp((*cp)->host, smtp_hosts[0])) { stream = (*cp)->stream; ckfree((*cp)->host); ncp = (*cp)->next; ckfree(*cp); *cp = ncp; if (250 != smtp_send(stream,"RSET",NIL)) { smtp_close(stream); stream = NULL; } break; } } /* Open new stream if needed */ if (!stream) { if (cverboseness > 1) { strlcpy(buf,"RatLog 2 $t(opening_smtp_conn) explicit",sizeof(buf)); RatSendLog(out_fd, buf); } if (cverboseness >= 3) { debug = 1; } else { debug = 0; } stream = smtp_open(smtp_hosts, debug); if (NULL == stream) { return ""; } } /* Send message */ if (cverboseness > 1) { strlcpy(buf, "RatLog 2 $t(sending_message) explicit", sizeof(buf)); RatSendLog(out_fd, buf); } if (T != smtp_mail(stream, "MAIL", env, b)) { err = cpystr(stream->reply); /* Do not cache connection if we got an error */ cache = -1; } /* Add to cache or close */ if (cache >= 0) { ncp = (smtp_conn_cache_t*)ckalloc(sizeof(*ncp)); ncp->host = cpystr(smtp_hosts[0]); if (cache) { ncp->expires = time(NULL)+cache; } else { /* We define infinity as a year :-) */ ncp->expires = time(NULL)+256*24*60*60; } ncp->stream = stream; ncp->next = conn_cache; conn_cache = ncp; } else { smtp_close(stream); } if (cverboseness > 0) { strlcpy(buf, "RatLog 2 {} explicit", sizeof(buf)); RatSendLog(out_fd, buf); } /* Free stuff */ for (i=0; smtp_hosts[i]; i++) { ckfree(smtp_hosts[i]); } ckfree(smtp_hosts); Tcl_DStringFree(&from); memset(smtp_passwd, '\0', strlen(smtp_passwd)); Tcl_DStringFree(&passwd); return err; } /* *---------------------------------------------------------------------- * * RatSendLog -- * * Sends a log message from the sender thread * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void RatSendLog(int fd, const char *msg) { char c = EVENT_LOG; unsigned int len = strlen(msg); int ignored; ignored = safe_write(fd, &c, 1); ignored = safe_write(fd, &len, sizeof(int)); ignored = safe_write(fd, msg, len); } /* *---------------------------------------------------------------------- * * RatSendProg -- * * Sends a message via an external program * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char* RatSendProg(int in_fd, int out_fd, char *host, ENVELOPE *env, BODY *b) { static Tcl_DString *cmd = NULL; static char *buf = NULL; static int bufsize = 0; static soutr_data_t sd = {0, 0, NULL, 0, 0}; struct sigaction nact; int to[2], err[2], len, r; pid_t child; char ok_8bit; long arg; /* * First create the command */ if (!cmd) { cmd = (Tcl_DString*)ckalloc(sizeof(*cmd)); Tcl_DStringInit(cmd); } if (bufsize < 1024) { bufsize = 1024; buf = (char*)ckalloc(bufsize); } if (cverboseness > 1) { strlcpy(buf, "RatLog 2 $t(launching_send_cmd) explicit", bufsize); RatSendLog(out_fd, buf); } RatReadString(in_fd, cmd); if (1 != SafeRead(in_fd, &ok_8bit, 1)) { exit(0); /* Master has died */ } RatAddAddresses(cmd, env->to); RatAddAddresses(cmd, env->cc); RatAddAddresses(cmd, env->bcc); /* * Execute send command */ if (pipe(to) || pipe(err)) { snprintf(buf, bufsize, "$t(prog_send_failed): %s",strerror(errno)); return buf; } if (0 == (child = fork())) { /* Child */ RatSendProgChild(Tcl_DStringValue(cmd), to[0], err[1]); /* Notreached */ exit(1); } if (-1 == child) { snprintf(buf, bufsize, "$t(prog_send_failed): %s",strerror(errno)); return buf; } close(to[0]); close(err[1]); sender_pid = child; sender_died = 0; memset(&nact,0,sizeof (struct sigaction)); sigemptyset(&nact.sa_mask); nact.sa_handler = RatSigChldHandler; sigaction(SIGCHLD, &nact, NULL); arg = O_NONBLOCK; fcntl(err[0], F_SETFL, arg); if (cverboseness > 1) { strlcpy(buf, "RatLog 2 $t(writing_message) explicit", bufsize); RatSendLog(out_fd, buf); } /* * Write message */ len = RatHeaderSize(env, b); if (len > bufsize) { bufsize = len+1024; buf = (char*)ckrealloc(buf, bufsize); } sd.fd = to[1]; sd.errfd = err[0]; sd.errbufused = 0; rfc822_output(buf, env, b, RatSendSoutr, (void*)&sd, ok_8bit); close(sd.fd); if (cverboseness > 1) { strlcpy(buf, "RatLog 2 $t(waiting_on_send_cmd) explicit", bufsize); RatSendLog(out_fd, buf); } /* * Wait for command to complete */ nact.sa_handler = SIG_DFL; sigaction(SIGCHLD, &nact, NULL); while (!sender_died && 0 == (r = waitpid(child, &sender_status,WNOHANG))) { RatReadData(sd.errfd, &sd.errbuf, &sd.errbufsize, &sd.errbufused); usleep(100000); /* 0.1 second */ } RatReadData(sd.errfd, &sd.errbuf, &sd.errbufsize, &sd.errbufused); /* * Check status */ if (child == r) { if (!WIFEXITED(sender_status) || WEXITSTATUS(sender_status)) { if (!sd.errbufused) { snprintf(sd.errbuf, sd.errbufsize, "Child terminated with code %d\n", WEXITSTATUS(sender_status)); sd.errbufused = strlen(sd.errbuf); } } else { sd.errbufused = 0; } } close(to[1]); close(err[0]); /* * Report */ if (sd.errbufused) { sd.errbuf[sd.errbufused] = '\0'; return sd.errbuf; } else { return NULL; } } /* *---------------------------------------------------------------------- * * RatAddAddresses -- * * Adds addresses to a buffer. This function reallocates the buffer * if needed. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void RatAddAddresses(Tcl_DString *ds, ADDRESS *addr) { char buf[1024]; ADDRESS *a; int r; for (a = addr; a; a = a->next) { if (!a->mailbox) { continue; } r = strlen(a->mailbox)*2+1; if (a->host) { r += strlen(a->host) + 1; } if (r >= sizeof(buf)) { /* Ridiculosuly long address */ continue; } buf[0] = '\0'; rfc822_address(buf, a); Tcl_DStringAppend(ds, " ", 1); Tcl_DStringAppend(ds, buf, -1); } } /* *---------------------------------------------------------------------- * * RatSendProgChild -- * * Child process responsible for executing the sender prog * if needed. * * Results: * None. * * Side effects: * Will modify 'cmd'. * *---------------------------------------------------------------------- */ static void RatSendProgChild(char *cmd, int to, int err) { struct rlimit rlim; int i, oi, fd, n; char **argv, buf[1024]; /* * Fix file descriptors */ getrlimit(RLIMIT_NOFILE, &rlim); for (i=0; ifd, data, len))) { if (0 > r || sender_died) { /* If failed the child must be dead */ return NIL; } /* * Read error output */ RatReadData(sd->errfd, &sd->errbuf, &sd->errbufsize, &sd->errbufused); usleep(100000); /* 0.1 second */ data += r; len -= r; } if (sender_died) { return NIL; } return T; } /* *---------------------------------------------------------------------- * * RatReadData -- * * Reads all available data from the given descriptor and stores * it in the given buffer (which may be reallocated). * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void RatReadData(int fd, char **buf, int *size, int *used) { int len; do { if (*used+1024 >= *size) { *size += 1024; *buf = (char*)ckrealloc(*buf, *size); (*buf)[*used] = '\0'; } len = SafeRead(fd, *buf + *used, *size - *used); if (len <= 0) { return; } *used += len; } while(len > 0); } /* *---------------------------------------------------------------------- * * RatSenderLog -- * * Used by RatLog to send a log message when running in the sender * thread. * * Results: * None. * * Side effects: * Sends the log message to the master * *---------------------------------------------------------------------- */ void RatSenderLog(const char *logcmd) { RatSendLog(from_sender[1], logcmd); } /* *---------------------------------------------------------------------- * * RatWrapHeaderLines -- * * Wraps all header-liens which are too long to fit on a line. * * Results: * None. * * Side effects: * Modifies to provided envelope and body * *---------------------------------------------------------------------- */ static void RatWrapHeaderLines(ENVELOPE *env, BODY *body) { RatWrapHeaderLine("Newsgroups", &env->newsgroups); RatWrapHeaderLine("Subject", &env->subject); RatWrapHeaderLine("In-Reply-To", &env->in_reply_to); RatWrapHeaderLine("Followup-to", &env->followup_to); RatWrapHeaderLine("References", &env->references); RatWrapHeaderLine("Content-Description: %s", &body->description); } /* *---------------------------------------------------------------------- * * RatWrapHeaderLine -- * * Wraps a header-line if needed. Assumes that the input is a * single line without newlines etc. * * Results: * None. * * Side effects: * Modifies to provided pointer * *---------------------------------------------------------------------- */ static void RatWrapHeaderLine(char *type, char **s) { int l, extra = 0, offset, ls; /* * Do we need to wrap this entry? */ l = strlen(type) + 2; if (!*s || l + strlen(*s) <= 78) { return; } /* * Wrap line */ ls = 0; while (strlen(*s+ls)+l > 78) { offset = 78-l; while (offset > 0 && !isspace((*s)[ls+offset])) offset--; if (0 == offset) { offset = 78-l; while ((*s)[ls+offset] && !isspace((*s)[ls+offset])) offset++; if (!(*s)[ls+offset]) { /* * We have reached the end of the line */ return; } } l = 1; /* Header length of continuation-lines is 1 */ /* * Make more room if needed */ if (extra < 2) { extra += 10; *s = ckrealloc(*s, strlen(*s)+extra+1); } /* * Move remaining text and insert line-break */ memmove(*s+ls+offset+2, *s+ls+offset, strlen(*s+ls+offset)+1); memcpy(*s+ls+offset, "\015\012", 2); extra -= 2; ls += offset+2; } } void mm_smtptrace(long smtpstate, char *string) { char buf[1024]; const char *key, *local; if (cverboseness < 2) { return; } switch (smtpstate) { case SMTPSTATE_MAIL_FROM: key = "sending_mail_from"; break; case SMTPSTATE_RCPT_TO: key = "sending_rcpt"; break; case SMTPSTATE_SENDING_DATA: key = "sending_data"; break; default: key = NULL; /* To keep the compiler happy */ break; } local = Tcl_GetVar2(timerInterp, "t", (CONST84 char*)key, TCL_GLOBAL_ONLY); snprintf(buf, sizeof(buf), local, string); RatLog(timerInterp, RAT_INFO, buf, RATLOG_EXPLICIT); } tkrat_2.2cvs20100105-dfsg.orig/lib/ratSequence.c000066400000000000000000000075421137544547100212050ustar00rootroot00000000000000/* * ratSequence.c -- * * This file contains code to build seqences of message numbers * * TkRat software and its included text is Copyright 1996-2005 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "rat.h" #define CHUNKSIZE 256 typedef struct { int used; int allocated; unsigned long *elems; Tcl_DString string; } rat_int_sequence_t; rat_sequence_t RatSequenceInit(void) { rat_int_sequence_t *rs = (rat_int_sequence_t*)ckalloc(sizeof(*rs)); rs->used = 0; rs->allocated = 0; rs->elems = NULL; Tcl_DStringInit(&rs->string); return (rat_sequence_t)rs; } void RatSequenceAdd(rat_sequence_t seq, unsigned long elem) { rat_int_sequence_t *rs = (rat_int_sequence_t*)seq; int i; if (rs->used == rs->allocated) { rs->allocated += CHUNKSIZE; rs->elems = (unsigned long*)ckrealloc( rs->elems, rs->allocated * sizeof(unsigned long)); } i=0; while (rs->elems[i] < elem && i < rs->used) { i++; } if (i == rs->used) { rs->elems[rs->used] = elem; } else { if (rs->elems[i] == elem) { return; } memmove(&rs->elems[i+1], &rs->elems[i], sizeof(unsigned long)*(rs->used-i)); rs->elems[i] = elem; } rs->used++; } int RatSequenceNotempty(rat_sequence_t seq) { rat_int_sequence_t *rs = (rat_int_sequence_t*)seq; return rs->used; } char* RatSequenceGet(rat_sequence_t seq) { rat_int_sequence_t *rs = (rat_int_sequence_t*)seq; char buf[32]; int i, j; if (Tcl_DStringLength(&rs->string)) { Tcl_DStringSetLength(&rs->string, 0); } for (i=0; iused; i++) { if (Tcl_DStringLength(&rs->string)) { Tcl_DStringAppend(&rs->string, ",", 1); } snprintf(buf, sizeof(buf), "%lu", rs->elems[i]); Tcl_DStringAppend(&rs->string, buf, -1); j = i; while(j < rs->used && rs->elems[j]+1 == rs->elems[j+1]) { j++; } if (j > i+1) { snprintf(buf, sizeof(buf), ":%lu", rs->elems[j]); Tcl_DStringAppend(&rs->string, buf, -1); i = j; } } return Tcl_DStringValue(&rs->string); } void RatSequenceFree(rat_sequence_t seq) { rat_int_sequence_t *rs = (rat_int_sequence_t*)seq; Tcl_DStringFree(&rs->string); ckfree(rs->elems); ckfree(rs); } /* * These functions are used for unit-testing */ static Tcl_ObjCmdProc RatSequenceCmd; static Tcl_CmdDeleteProc RatSequenceDelProc; int RatCreateSequenceCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { static int unique = 0; char name[32]; snprintf(name, sizeof(name), "seq%d", unique++); Tcl_CreateObjCommand(interp, name, RatSequenceCmd, RatSequenceInit(), RatSequenceDelProc); Tcl_SetResult(interp, name, TCL_VOLATILE); return TCL_OK; } static int RatSequenceCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { rat_int_sequence_t *rs = (rat_int_sequence_t*)clientData; long no; if (objc == 3 && !strcmp("add", Tcl_GetString(objv[1])) && TCL_OK == Tcl_GetLongFromObj(interp, objv[2], &no)) { RatSequenceAdd(rs, no); } else if (objc == 2 && !strcmp("get", Tcl_GetString(objv[1]))) { Tcl_SetObjResult(interp, Tcl_NewStringObj(RatSequenceGet(rs), -1)); } else if (objc == 2 && !strcmp("notempty", Tcl_GetString(objv[1]))) { Tcl_SetObjResult(interp, Tcl_NewBooleanObj(RatSequenceNotempty(rs))); } else { return TCL_ERROR; } return TCL_OK; } static void RatSequenceDelProc(ClientData clientData) { rat_int_sequence_t *rs = (rat_int_sequence_t*)clientData; RatSequenceFree(rs); } tkrat_2.2cvs20100105-dfsg.orig/lib/ratStdFolder.c000066400000000000000000001463741137544547100213320ustar00rootroot00000000000000/* * ratStdFolder.c -- * * This file contains code which implements standard c-client folders. * This means ONLY filefolders at this moment. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratStdFolder.h" #include /* * We use this structure to keep a list of open connections */ typedef struct Connection { MAILSTREAM *stream; /* Handler to c-client entity */ char *spec; /* Mailbox spec */ int *errorFlagPtr; /* Address of flag to set on hard errors */ int refcount; /* references count */ int closing; /* True if this connection is unused and waiting to be closed */ int isnet; /* Nonnull if this is a network conn */ Tcl_TimerToken token; /* Timer token for closing timer */ struct Connection *next; /* Struct linkage */ FolderHandlers *handlers; /* Event handlers */ } Connection; FolderHandlers **globHD; /* * Remember if we must initialize the package */ static int initialize = 1; /* * List of open connections */ static Connection *connListPtr = NULL; /* * The values below are used to catch calls to mm_log. That is when you * want to handle the message internally. */ static RatLogLevel logLevel; static char *logMessage = NULL; int logIgnore = 0; /* * These variables are used by mm_login */ static char loginPassword[MAILTMPLEN]; static char loginSpec[MAILTMPLEN]; static int loginStore; /* * This is used to build a list of found mailboxes when listing */ typedef struct Mailbox { char *name; /* The en component of name the mailbox name */ char *folder; /* The mailbox name */ long attributes; /* The attributes from c-client */ int delimiter; /* The delimiter in the folder names */ struct Mailbox *next; /* Pointer to the next mailbox on this level */ struct Mailbox *child; /* Pointer to subfolders */ } Mailbox; static Mailbox *mailboxListPtr = NULL; static char *mailboxSearchBase = NULL; static char lastDelimiter[2] = {'\0', '\0'}; static char *importCallback; static Tcl_Obj *testListPtr = NULL; static struct { long mask; char *name; Tcl_Obj *oPtr; } attrTable[] = { {LATT_NOINFERIORS, "noinferiors", NULL}, {LATT_NOSELECT, "noselect", NULL}, {LATT_MARKED, "marked", NULL}, {LATT_UNMARKED, "unmarked", NULL}, {LATT_REFERRAL, "referral", NULL}, {0, NULL, NULL} }; /* * Used to store search results */ long *searchResultPtr = NULL; int searchResultSize = 0; int searchResultNum = 0; /* * Used to store status results */ MAILSTATUS stdStatus; /* * File handler of debugging file */ static FILE *debugFile = NULL; /* * Procedures private to this module. */ static RatInitProc Std_InitProc; static RatCloseProc Std_CloseProc; static RatUpdateProc Std_UpdateProc; static RatInsertProc Std_InsertProc; static RatSetFlagProc Std_SetFlagProc; static RatGetFlagProc Std_GetFlagProc; static Tcl_TimerProc CloseConnection; static Tcl_ObjCmdProc RatImportCmd; static Tcl_ObjCmdProc RatTestImportCmd; static Connection *FindConn(MAILSTREAM *stream); static void RatImportBuildResult(Tcl_Interp *interp, Mailbox *mPtr, int *lastId, int id, int templatec, Tcl_Obj **templatev); static HandleExists Std_HandleExists; static HandleExpunged Std_HandleExpunged; static RatStdFolderType Std_GetType(const char *spec); static void RatDeleteVFolderStruct(Tcl_Interp *interp, int id); /* *---------------------------------------------------------------------- * * RatStdFolderInit -- * * Initializes the file folder command. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The C-client library is initialized and the apropriate mail drivers * are linked. * * *---------------------------------------------------------------------- */ int RatStdFolderInit(Tcl_Interp *interp) { int i; for (i=0; attrTable[i].mask; i++) { attrTable[i].oPtr = Tcl_NewStringObj(attrTable[i].name, -1); Tcl_IncrRefCount(attrTable[i].oPtr); } /* Link imap code */ #include "../imap/c-client/linkage.c" Tcl_CreateObjCommand(interp, "RatImport", RatImportCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatTestImport", RatTestImportCmd, NULL,NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * Std_StreamOpen -- * * Opens a standard c-client mailstream. This function handles * caching of passwords and connections. * * Results: * The mail stream. * * Side effects: * The caches may be modified. * *---------------------------------------------------------------------- */ MAILSTREAM* Std_StreamOpen(Tcl_Interp *interp, char *spec, long options, int *errorFlagPtr, FolderHandlers *handlers) { MAILSTREAM *stream = NULL; Connection *connPtr = NULL; char *cPtr; int len; if (errorFlagPtr) { *errorFlagPtr = 0; } if ('{' == spec[0]) { strlcpy(loginSpec, spec, sizeof(loginSpec)); cPtr = strchr(loginSpec, '}'); cPtr[1] = '\0'; len = strchr(spec, '}') - spec; if (NULL != (cPtr = strstr(spec, "/debug}"))) { len = cPtr-spec; } for (connPtr = connListPtr; connPtr; connPtr = connPtr->next) { if ((connPtr->closing || options & OP_HALFOPEN) && !strncmp(spec, connPtr->spec, len) && (!connPtr->stream->halfopen || options & OP_HALFOPEN)) { break; } } if (connPtr && T == mail_ping(connPtr->stream)) { stream = connPtr->stream; connPtr->refcount++; Tcl_DeleteTimerHandler(connPtr->token); if (connPtr->closing) { connPtr->handlers = handlers; connPtr->errorFlagPtr = errorFlagPtr; } connPtr->closing = 0; } } else if (options & OP_HALFOPEN) { /* Halfopen is only applicable to IMAP-streams */ return NULL; } if (stream && options & OP_HALFOPEN) { return stream; } loginPassword[0] = '\0'; stream = mail_open(stream, spec, options); if (stream && !connPtr) { connPtr = (Connection*)ckalloc(sizeof(Connection)); connPtr->stream = stream; connPtr->spec = cpystr(spec); connPtr->errorFlagPtr = errorFlagPtr; connPtr->refcount = 1; connPtr->closing = 0; connPtr->handlers = handlers; connPtr->next = connListPtr; connPtr->token = NULL; connPtr->isnet = (('{' == spec[0]) ? 1 : 0); connListPtr = connPtr; if (loginPassword[0] != '\0') { RatCachePassword(interp, spec, loginPassword, loginStore); memset(loginPassword, 0, strlen(loginPassword)); } } if (!stream && '{' == spec[0]) { Tcl_Obj *oPtr; int n; oPtr = Tcl_GetVar2Ex(interp, "ratNetOpenFailures", NULL, TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &n); Tcl_SetVar2Ex(interp, "ratNetOpenFailures", NULL, Tcl_NewIntObj(++n), TCL_GLOBAL_ONLY); } if (stream && stream->halfopen && 0 == (options & OP_HALFOPEN)) { Std_StreamClose(interp, stream); stream = NIL; } return stream; } /* *---------------------------------------------------------------------- * * Std_StreamClose -- * * Closes a standard c-client mailstream. This function handles * caching of passwords and connections. * * Results: * None. * * Side effects: * The caches may be modified. * *---------------------------------------------------------------------- */ void Std_StreamClose(Tcl_Interp *interp, MAILSTREAM *stream) { Connection *connPtr; Tcl_Obj *oPtr; for (connPtr = connListPtr; connPtr && stream != connPtr->stream; connPtr = connPtr->next); if (connPtr) { int timeout, doCache; if (--connPtr->refcount) { return; } oPtr = Tcl_GetVar2Ex(interp, "option", "cache_conn", TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &doCache); if (doCache && RAT_IMAP == Std_GetType(connPtr->stream->mailbox) && (!connPtr->errorFlagPtr || 0 == *connPtr->errorFlagPtr)) { oPtr = Tcl_GetVar2Ex(interp, "option", "cache_conn_timeout", TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &timeout); connPtr->closing = 1; if (connPtr->errorFlagPtr) { connPtr->errorFlagPtr = NULL; } if (timeout) { connPtr->token = Tcl_CreateTimerHandler(timeout*1000, CloseConnection, (ClientData)connPtr); } else { connPtr->token = NULL; } connPtr->handlers = NULL; } else { CloseConnection((ClientData)connPtr); } } else { logIgnore++; mail_close_full(stream, NIL); logIgnore--; } } /* *---------------------------------------------------------------------- * * Std_StreamCloseAllCached -- * * Forces a close of all cached connections * * Results: * None. * * Side effects: * The caches may be modified. * *---------------------------------------------------------------------- */ void Std_StreamCloseAllCached(Tcl_Interp *interp) { Connection *connPtr, *nextPtr; for (connPtr = connListPtr; connPtr; connPtr = nextPtr) { nextPtr = connPtr->next; if (connPtr->closing) { Tcl_DeleteTimerHandler(connPtr->token); CloseConnection((ClientData)connPtr); } } } /* *---------------------------------------------------------------------- * * OpenStdFolder -- * * Opens a standard c-client folder and if it is a filefolder and * is of an incompatible format (unfortunately generated by an older * version of this program) we convert it. * * Results: * The mail stream. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int OpenStdFolder(Tcl_Interp *interp, char *spec, void *voidPtr, int append_only, MAILSTREAM **stream_out) { MAILSTREAM *stream = NULL; RatStdFolderType type; StdFolderInfo *stdPtr = (StdFolderInfo*)voidPtr; struct stat sbuf; char *to_free = NULL; long options; type = Std_GetType(spec); if (RAT_UNIX == type) { spec = to_free = cpystr(RatTranslateFileName(interp, spec)); } if ('/' == spec[0] && stat(spec, &sbuf) && ENOENT == errno) { int fd; fd = open(spec, O_CREAT | O_WRONLY, 0600); close(fd); } logLevel = RAT_BABBLE; if (append_only) { options = OP_HALFOPEN; } else { options = 0; } stream = Std_StreamOpen(interp, spec, options, (stdPtr ? &stdPtr->error : NULL), (stdPtr ? &stdPtr->handlers : NULL)); if (logLevel > RAT_WARN) { Tcl_SetResult(interp, logMessage, TCL_VOLATILE); return TCL_ERROR; } if (NIL == stream && (!append_only || '{' == spec[0])) { Tcl_AppendResult(interp, "Failed to open std mailbox \"", spec, "\"", (char *) NULL); return TCL_ERROR; } if (stream && !strcmp(stream->dtb->name, "mbx")) { type = RAT_MBX; } if (stdPtr) { stdPtr->stream = stream; stdPtr->referenceCount = 1; stdPtr->exists = (stream ? stream->nmsgs : 0); stdPtr->type = type; stdPtr->mailbox = cpystr(spec); } if (to_free) { ckfree(to_free); } *stream_out = stream; return TCL_OK; } /* *---------------------------------------------------------------------- * * RatStdFolderCreate -- * * Creates a std folder entity. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * A std folder is created. * * *---------------------------------------------------------------------- */ RatFolderInfo* RatStdFolderCreate(Tcl_Interp *interp, int append_only, Tcl_Obj *defPtr) { RatFolderInfo *infoPtr; StdFolderInfo *stdPtr; MAILSTREAM *stream = NULL; char buf[32]; Tcl_Obj *oPtr; char *spec; int i; /* * Now it is time to initialize things */ if (initialize) { env_parameters(SET_LOCALHOST, (void*)Tcl_GetHostName()); initialize = 0; } stdPtr = (StdFolderInfo *) ckalloc(sizeof(*stdPtr)); stdPtr->handlers.state = (void*)stdPtr; stdPtr->handlers.exists = Std_HandleExists; stdPtr->handlers.expunged = Std_HandleExpunged; stdPtr->mailbox = NULL; if (NULL == (spec = RatGetFolderSpec(interp, defPtr)) || TCL_OK != OpenStdFolder(interp, spec, stdPtr, append_only,&stream)){ ckfree(stdPtr); return NULL; } infoPtr = (RatFolderInfo*)ckalloc(sizeof(*infoPtr)); infoPtr->type = "std"; Tcl_ListObjIndex(interp, defPtr, 0, &oPtr); infoPtr->name = cpystr(Tcl_GetString(oPtr)); infoPtr->size = -1; infoPtr->number = (stream ? stream->nmsgs : 0); infoPtr->recent = (stream ? stream->recent : 0); infoPtr->unseen = 0; if (infoPtr->number) { sprintf(buf, "1:%ld", stream->nmsgs); mail_fetchfast_full(stream, buf, NIL); for (i = 1; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) infoPtr->unseen++; } infoPtr->initProc = Std_InitProc; infoPtr->finalProc = NULL; infoPtr->closeProc = Std_CloseProc; infoPtr->updateProc = Std_UpdateProc; infoPtr->insertProc = Std_InsertProc; infoPtr->setFlagProc = Std_SetFlagProc; infoPtr->getFlagProc = Std_GetFlagProc; infoPtr->infoProc = Std_InfoProc; infoPtr->setInfoProc = Std_SetInfoProc; infoPtr->createProc = Std_CreateProc; infoPtr->syncProc = NULL; infoPtr->dbinfoGetProc = NULL; infoPtr->dbinfoSetProc = NULL; infoPtr->private = (ClientData) stdPtr; return infoPtr; } /* *---------------------------------------------------------------------- * * Std_InitProc -- * * See the documentation for initProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for initProc in ratFolder.h * * *---------------------------------------------------------------------- */ static void Std_InitProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MessageInfo *msgPtr; int i, j, start, end; if (-1 == index) { start = 0; end = infoPtr->number; } else { start = index; end = start+1; } for (i=start; ifolderInfoPtr = infoPtr; msgPtr->name[0] = '\0'; msgPtr->type = RAT_CCLIENT_MESSAGE; msgPtr->bodyInfoPtr = NULL; msgPtr->msgNo = i; msgPtr->fromMe = RAT_ISME_UNKOWN; msgPtr->toMe = RAT_ISME_UNKOWN; msgPtr->clientData = NULL; for (j=0; jinfo[j] = NULL; } infoPtr->privatePtr[i] = (ClientData)msgPtr; } RatStdMsgStructInit(infoPtr, interp, index, stdPtr->stream, stdPtr->type); } /* *---------------------------------------------------------------------- * * CloseStdFolder -- * * See the documentation for closeProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for closeProc in ratFolder.h * * *---------------------------------------------------------------------- */ void CloseStdFolder(Tcl_Interp *interp, MAILSTREAM *stream) { Std_StreamClose(interp, stream); } /* *---------------------------------------------------------------------- * * Std_CloseProc -- * * See the documentation for closeProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for closeProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_CloseProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int expunge) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MessageInfo *msgPtr; int i, j; if (stdPtr->stream) { if (expunge && !infoPtr->append_only) { logIgnore++; mail_expunge(stdPtr->stream); logIgnore--; } Std_StreamClose(interp, stdPtr->stream); } if (stdPtr->mailbox) { ckfree(stdPtr->mailbox); } if (0 == --stdPtr->referenceCount) { for (i=0; inumber; i++) { if (NULL == infoPtr->msgCmdPtr[i]) { msgPtr = (MessageInfo*)infoPtr->privatePtr[i]; if (msgPtr) { for (j=0; jinfo[j]) { Tcl_DecrRefCount(msgPtr->info[j]); msgPtr->info[j] = NULL; } } ckfree(msgPtr->clientData); ckfree(infoPtr->privatePtr[i]); } } } ckfree(stdPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * Std_UpdateProc -- * * See the documentation for updateProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for updateProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_UpdateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp,RatUpdateType mode) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; int numNew = 0, oldExists, i, nmsgs; char sequence[16]; if (infoPtr->append_only) { return 0; } if (RAT_SYNC == mode) { MESSAGECACHE *cachePtr; MessageInfo *msgPtr; int i, offset = 0; if (infoPtr->number) { for (i=0; inumber; i++) { cachePtr = mail_elt(stdPtr->stream, i+1); if (cachePtr->deleted) { if (-1 != infoPtr->size) { infoPtr->size -= cachePtr->rfc822_size; } if (infoPtr->msgCmdPtr[i]) { RatMessageDelete(interp, infoPtr->msgCmdPtr[i]); } offset++; } else if (offset) { infoPtr->msgCmdPtr[i-offset] = infoPtr->msgCmdPtr[i]; infoPtr->privatePtr[i-offset] = infoPtr->privatePtr[i]; if (infoPtr->privatePtr[i]) { msgPtr = (MessageInfo*)infoPtr->privatePtr[i]; msgPtr->msgNo = i - offset; } } } for (i=infoPtr->number-offset; inumber; i++) { infoPtr->msgCmdPtr[i] = NULL; infoPtr->privatePtr[i] = NULL; } } mail_expunge(stdPtr->stream); numNew = stdPtr->exists - (infoPtr->number - offset); } else if (RAT_CHECKPOINT == mode) { oldExists = infoPtr->number; mail_check(stdPtr->stream); numNew = stdPtr->exists-oldExists; } else { oldExists = infoPtr->number; if (T != mail_ping(stdPtr->stream)) { char buf[1024]; stdPtr->stream = NIL; snprintf(buf, sizeof(buf), "%s close 1", infoPtr->cmdName); Tcl_GlobalEval(interp, buf); Tcl_SetResult(interp, "Lost contact with mailbox", TCL_STATIC); Tcl_SetErrorCode(interp, "C_CLIENT", "streamdied", NULL); return -1; } numNew = stdPtr->exists-oldExists; } if (numNew) { sprintf(sequence, "%d:%d", stdPtr->exists-numNew+1, stdPtr->exists); mail_fetchfast_full(stdPtr->stream, sequence, NIL); } infoPtr->number = stdPtr->exists; infoPtr->recent = (stdPtr->stream ? stdPtr->stream->recent : 0); nmsgs = (stdPtr->stream ? stdPtr->stream->nmsgs : 0); for (i = 1,infoPtr->unseen=0; i <= nmsgs; i++) { if (!mail_elt(stdPtr->stream,i)->seen) infoPtr->unseen++; } return numNew; } /* *---------------------------------------------------------------------- * * Std_InsertProc -- * * See the documentation for insertProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for insertProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_InsertProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int argc, char *argv[]) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; char flags[128], date[128]; Tcl_CmdInfo cmdInfo; Tcl_DString ds; STRING string; int i; if (NIL == stdPtr->stream && '{' == stdPtr->mailbox[0]) { Tcl_AppendResult(interp, "Failed to open std mailbox \"", argv[2], "\" for insert", (char *) NULL); return TCL_ERROR; } Tcl_DStringInit(&ds); for (i=0; istream, stdPtr->mailbox, flags, date, &string)){ Tcl_SetResult(interp, "mail_append failed", TCL_STATIC); return TCL_ERROR; } Tcl_DStringSetLength(&ds, 0); if (!stdPtr->exists && stdPtr->stream) { if (T != mail_ping(stdPtr->stream)) { char buf[1024]; Tcl_DStringFree(&ds); snprintf(buf, sizeof(buf), "%s close", infoPtr->cmdName); Tcl_GlobalEval(interp, buf); Tcl_SetResult(interp, "Mailbox stream died", TCL_STATIC); Tcl_SetErrorCode(interp, "C_CLIENT", "streamdied", NULL); return TCL_ERROR; } } } Tcl_DStringFree(&ds); return TCL_OK; } static int intcompare(const void *v1, const void *v2) { return *(int*)v1 - *(int*)v2; } /* *---------------------------------------------------------------------- * * Std_SetFlagProc -- * * See the documentation for setFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for setFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_SetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int *ilist, int count, RatFlag flag, int value) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MessageInfo *msgPtr; MESSAGECACHE *cachePtr; char s[128]; int i; if (!stdPtr->stream || stdPtr->stream->rdonly) { return TCL_OK; } qsort(ilist, count, sizeof(int), intcompare); if (RAT_SEEN == flag) { for (i=0; istream, ilist[i]+1); if (cachePtr->seen != value) { if (cachePtr->seen) { infoPtr->unseen++; } else { infoPtr->unseen--; } } } } for (i=0, s[0] = '\0'; i= sizeof(s)-32) { if (value) { mail_setflag_full(stdPtr->stream, s, flag_name[flag].imap_name, NIL); } else { mail_clearflag_full(stdPtr->stream, s, flag_name[flag].imap_name, NIL); } s[0] = '\0'; } else { strlcat(s, ",", sizeof(s)); } } for (i=0; iprivatePtr[ilist[i]]; if (msgPtr->info[RAT_FOLDER_STATUS]) { Tcl_DecrRefCount(msgPtr->info[RAT_FOLDER_STATUS]); msgPtr->info[RAT_FOLDER_STATUS] = NULL; } (void)mail_fetchenvelope(stdPtr->stream, ilist[i]+1); cachePtr = mail_elt(stdPtr->stream, ilist[i]+1); switch (flag) { case RAT_SEEN: cachePtr->seen = value; break; break; case RAT_DELETED: cachePtr->deleted = value; break; case RAT_FLAGGED: cachePtr->flagged = value; break; case RAT_ANSWERED: cachePtr->answered = value; break; case RAT_DRAFT: cachePtr->draft = value; break; case RAT_RECENT: cachePtr->recent = value; break; case RAT_FLAG_END: break; } infoPtr->recent = (stdPtr->stream ? stdPtr->stream->recent : 0); if (logLevel > RAT_WARN) { Tcl_SetResult(interp, logMessage, TCL_VOLATILE); return TCL_ERROR; } } return TCL_OK; } /* *---------------------------------------------------------------------- * * Std_GetFlagProc -- * * See the documentation for getFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for getFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_GetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, RatFlag flag) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MESSAGECACHE *cachePtr; char sequence[8]; int value = 0; if (!stdPtr->stream) return 0; sprintf(sequence, "%d", index+1); logLevel = RAT_BABBLE; (void)mail_fetchstructure_full(stdPtr->stream, index+1, NIL, NIL); cachePtr = mail_elt(stdPtr->stream, index+1); switch (flag) { case RAT_SEEN: value = cachePtr->seen; break; case RAT_DELETED: value = cachePtr->deleted; break; case RAT_FLAGGED: value = cachePtr->flagged; break; case RAT_ANSWERED: value = cachePtr->answered; break; case RAT_DRAFT: value = cachePtr->draft; break; case RAT_RECENT: value = cachePtr->recent; break; case RAT_FLAG_END: break; } return value; } /* *---------------------------------------------------------------------- * * Std_InfoProc -- * * See the documentation for infoProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for infoProc in ratFolder.h * * *---------------------------------------------------------------------- */ Tcl_Obj* Std_InfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index) { RatFolderInfo *infoPtr = (RatFolderInfo*)clientData; return Std_GetInfoProc(interp, (ClientData)infoPtr->privatePtr[index], type, 0); } /* *---------------------------------------------------------------------- * * Std_SetInfoProc -- * * See the documentation for setInfoProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for setInfoProc in ratFolder.h * * *---------------------------------------------------------------------- */ void Std_SetInfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index, Tcl_Obj *oPtr) { RatFolderInfo *infoPtr = (RatFolderInfo*)clientData; MessageInfo *msgPtr = (MessageInfo*)infoPtr->privatePtr[index]; if (msgPtr->info[type]) { Tcl_DecrRefCount(msgPtr->info[type]); } msgPtr->info[type] = oPtr; if (oPtr) { Tcl_IncrRefCount(oPtr); } } /* *---------------------------------------------------------------------- * * Std_CreateProc -- * * See the documentation for createProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for createProc in ratFolder.h * * *---------------------------------------------------------------------- */ char* Std_CreateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; return RatStdMessageCreate(interp, infoPtr, stdPtr->stream, index); } /* *---------------------------------------------------------------------- * * RatImportCmd -- * * Import folders (via mm_list) * * Results: * The folders found are stored in the vFolderDef array * * Side effects: * RatLogin may be called. * * *---------------------------------------------------------------------- */ static int RatImportCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *oPtr, **iobjv, **bobjv, *origPtr; int iobjc, bobjc, subscribed, i, lastId, id; char *spec, path[1024], buf[1024]; MAILSTREAM *stream = NULL; /* * Check arguments * check that we got one * check that it is is a folder definition id and that it * points to a correct import-folder. */ if (objc != 2 && objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " id ?callback?\"", (char *) NULL); return TCL_ERROR; } origPtr = Tcl_GetVar2Ex(interp, "vFolderDef", Tcl_GetString(objv[1]), TCL_GLOBAL_ONLY); if (TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &id) || NULL == origPtr || TCL_OK != Tcl_ListObjGetElements(interp, origPtr, &iobjc, &iobjv) || 6 != iobjc || strcmp("import", Tcl_GetString(iobjv[1])) || TCL_OK != Tcl_ListObjGetElements(interp, iobjv[3], &bobjc, &bobjv)){ Tcl_AppendResult(interp, "Bad folder id specified \"", Tcl_GetString(objv[1]), "\"", (char *) NULL); return TCL_ERROR; } spec = RatGetFolderSpec(interp, iobjv[3]); logIgnore++; stream = Std_StreamOpen(interp, spec, OP_HALFOPEN, NULL, NULL); logIgnore--; /* * See if we only want subscribed folders */ Tcl_ListObjLength(interp, iobjv[2], &i); for (subscribed = 0, i -= 2; i>=0; i -= 2) { Tcl_ListObjIndex(interp, iobjv[2], i, &oPtr); if (!strcmp("subscribed", Tcl_GetString(oPtr))) { Tcl_ListObjIndex(interp, iobjv[2], i+1, &oPtr); Tcl_GetIntFromObj(interp, oPtr, &subscribed); break; } } /* * Store callback, if specified */ if (3 == objc) { importCallback = Tcl_GetString(objv[2]); } else { importCallback = NULL; } /* * Run search * This builds a list of all found folders in mailboxListPtr * First we run a dummy-search to get the hierarchy delimiter */ if ((mailboxSearchBase = strchr(spec, '}'))) { mailboxSearchBase++; } else { mailboxSearchBase = spec; } mail_list(stream, "", spec); strlcpy(buf, spec, sizeof(buf)); if (*mailboxSearchBase && lastDelimiter[0] != mailboxSearchBase[strlen(mailboxSearchBase)-1]){ strlcat(buf, lastDelimiter, sizeof(buf)); } strlcat(buf, Tcl_GetString(iobjv[4]), sizeof(buf)); if (subscribed) { mail_lsub(stream, "", buf); } else { mail_list(stream, "", buf); } if (stream) { Std_StreamClose(interp, stream); } /* * Compare list in mailboxListPtr with already existing list */ if ('{' == spec[0]) { strlcpy(path, strchr(spec, '}')+1, sizeof(path)); } else { strlcpy(path, spec, sizeof(path)); } if ('*' == path[strlen(path)-1] || '%' == path[strlen(path)-1]) { path[strlen(path)-1] = '\0'; } if (mailboxListPtr && path[strlen(path)-1] == mailboxListPtr->delimiter) { path[strlen(path)-1] = '\0'; } snprintf(buf, sizeof(buf), "lindex [lsort -integer [array names vFolderDef]] end"); Tcl_GlobalEval(interp, buf); lastId = atoi(Tcl_GetStringResult(interp)); RatImportBuildResult(interp, mailboxListPtr, &lastId, id, bobjc, bobjv); mailboxListPtr = NULL; return TCL_OK; } /* *---------------------------------------------------------------------- * * RatImportBuildResult -- * * Recursive function which parses the import result into folders * * Results: * None * * Side effects: * The vFolderDef array may be modified * * *---------------------------------------------------------------------- */ typedef struct { int id; int objc; Tcl_Obj **objv; } Mbox; static void RatImportBuildResult(Tcl_Interp *interp, Mailbox *mPtr, int *lastId, int id, int templatec, Tcl_Obj **templatev) { Tcl_Obj **objv, *oPtr, *lPtr, *iPtr, *idList; Mailbox *nPtr; Mbox *mbox_in, *mbox_out = NULL; int i, num_mbox_in, mbox_out_alloc = 0, count, changed = 0, disconnected = 0, listpos; char buf[32]; if (!strcmp("dis", Tcl_GetString(templatev[1]))) { disconnected = 1; } /* * Split list of ids and create list of definitions */ snprintf(buf, sizeof(buf), "%d", id); oPtr = Tcl_GetVar2Ex(interp, "vFolderDef", buf, TCL_GLOBAL_ONLY); Tcl_ListObjIndex(interp, oPtr, 1, &iPtr); if (!strcmp("struct", Tcl_GetString(iPtr))) { listpos = 3; } else { listpos = 5; } Tcl_ListObjIndex(interp, oPtr, listpos, &idList); Tcl_ListObjGetElements(interp, idList, &num_mbox_in, &objv); mbox_in = (Mbox*)ckalloc(sizeof(Mbox)*num_mbox_in); for (i=0; i= mbox_out_alloc) { mbox_out_alloc += 100; mbox_out = (Mbox*)ckrealloc(mbox_out, mbox_out_alloc*sizeof(Mbox)); } for (i=0; iname, Tcl_GetString(mbox_in[i].objv[0])) && ((!strcmp("struct", Tcl_GetString(mbox_in[i].objv[1])) && 0 == (LATT_NOINFERIORS & mPtr->attributes)) || ((strcmp("struct", Tcl_GetString(mbox_in[i].objv[1])) && 0 != (LATT_NOINFERIORS & mPtr->attributes))))) { break; } } if (i == num_mbox_in) { /* Not found => new */ changed = 1; if (0 == (mPtr->attributes & LATT_NOSELECT)) { /* Create ordinary folder */ mbox_out[count].id = ++(*lastId); lPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj(mPtr->name, -1)); Tcl_ListObjAppendElement(interp, lPtr, templatev[1]); Tcl_ListObjAppendElement(interp, lPtr, templatev[2]); if (5 == templatec) { Tcl_ListObjAppendElement(interp, lPtr, templatev[3]); } Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj(mPtr->folder, -1)); snprintf(buf, sizeof(buf), "%d", mbox_out[count].id); Tcl_SetVar2Ex(interp, "vFolderDef", buf, lPtr,TCL_GLOBAL_ONLY); Tcl_ListObjGetElements(interp, lPtr, &mbox_out[count].objc, &mbox_out[count].objv); if (disconnected) { RatDisManageFolder(interp, RAT_MGMT_CREATE, lPtr); } count++; } if (mPtr->child) { /* Create struct */ mbox_out[count].id = ++(*lastId); lPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj(mPtr->name, -1)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj("struct", 6)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewObj()); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewObj()); snprintf(buf, sizeof(buf), "%d", mbox_out[count].id); Tcl_SetVar2Ex(interp, "vFolderDef", buf, lPtr,TCL_GLOBAL_ONLY); Tcl_ListObjGetElements(interp, lPtr, &mbox_out[count].objc, &mbox_out[count].objv); count++; } } else { /* Found => old */ mbox_out[count].id = mbox_in[i].id; mbox_out[count].objc = mbox_in[i].objc; mbox_out[count].objv = mbox_in[i].objv; mbox_in[i].id = 0; count++; } if (mPtr->child) { RatImportBuildResult(interp, mPtr->child, lastId, mbox_out[count-1].id, templatec, templatev); } nPtr = mPtr->next; ckfree(mPtr); } for (i=0; i 3 && !strcmp("struct", Tcl_GetString(objv[1])) && TCL_OK == Tcl_ListObjGetElements(interp, objv[3], &objc, &objv)) { /* Recursively delete children */ for (i=0; itoken); logIgnore++; mail_close_full(connPtr->stream, NIL); logIgnore--; for (connPtrPtr = &connListPtr; *connPtrPtr != connPtr; connPtrPtr = &(*connPtrPtr)->next); *connPtrPtr = connPtr->next; ckfree(connPtr->spec); ckfree(connPtr); } /* *---------------------------------------------------------------------- * * FindConn -- * * Find the connection pointer for the stream * * Results: * A connection pointer (or NULL) * * Side effects: * None * * *---------------------------------------------------------------------- */ static Connection* FindConn(MAILSTREAM *stream) { Connection *connPtr; for (connPtr=connListPtr; connPtr && connPtr->stream != stream; connPtr=connPtr->next); return connPtr; } /* *---------------------------------------------------------------------- * * Handle* -- * * Handle events from mailbox * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void Std_HandleExists(void *state, unsigned long nmsg) { StdFolderInfo *stdPtr = (StdFolderInfo *) state; stdPtr->exists = nmsg; } static void Std_HandleExpunged(void *state, unsigned long index) { StdFolderInfo *stdPtr = (StdFolderInfo *) state; stdPtr->exists--; } /* *---------------------------------------------------------------------- * * Std_GetType -- * * Determines the type of folder from a mailbox stream name * * Results: * The type * * Side effects: * None * * *---------------------------------------------------------------------- */ static RatStdFolderType Std_GetType(const char *spec) { const char *c; RatStdFolderType type; if ('{' == spec[0]) { type = RAT_IMAP; for (c=spec+1; *c != '}'; c++) { if ('/' == c[0] && 'p' == c[1] && 'o' == c[2] && 'p' == c[3] && '3' == c[4]) { type = RAT_POP; break; } } } else if ('#' == spec[0] && 'm' == spec[1] && 'h' == spec[2]) { type = RAT_MH; } else { type = RAT_UNIX; } return type; } /* *---------------------------------------------------------------------- * * RatStdManageFolder -- * * Create or delete folders on disk or remote server * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ int RatStdManageFolder(Tcl_Interp *interp, RatManagementAction op, int mbx, Tcl_Obj *fPtr) { MAILSTREAM *stream; struct stat sbuf; char *spec; Tcl_Obj *oPtr; int result, error, exists; spec = RatGetFolderSpec(interp, fPtr); if (TCL_OK == Tcl_ListObjIndex(interp, fPtr, 1, &oPtr) && oPtr && !strcmp("imap", Tcl_GetString(oPtr))) { stream = Std_StreamOpen(interp, spec, OP_HALFOPEN, &error, NULL); if (!stream) { Tcl_SetResult(interp,"Failed to open stream to server",TCL_STATIC); return TCL_ERROR; } } else { stream = NULL; } switch (op) { case RAT_MGMT_CREATE: if ('/' == spec[0]) { /* * Since this is a file folder we check if the file already * exists, and do nothing if that is the case. This is to * avoid getting an error message */ if (0 == stat(spec, &sbuf)) { return TCL_OK; } } if (mbx) { result = mbx_create(stream, spec); } else { result = mail_create(stream, spec); if (T == result) { mail_subscribe(stream, spec); } } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); break; case RAT_MGMT_CHECK: exists = mail_status(stream, spec, SA_UIDVALIDITY); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(exists)); result = 1; break; case RAT_MGMT_DELETE: logIgnore++; (void)mail_delete(stream, spec); logIgnore--; result = 1; break; case RAT_MGMT_SUBSCRIBE: result = mail_subscribe(stream, spec); break; case RAT_MGMT_UNSUBSCRIBE: result = mail_unsubscribe(stream, spec); break; default: result = 0; break; } if (stream) { Std_StreamClose(interp, stream); } Tcl_ListObjIndex(interp, fPtr, 1, &oPtr); if (result && !strcmp("dis", Tcl_GetString(oPtr))) { RatDisManageFolder(interp, op, fPtr); } if (result) { return TCL_OK; } else { return TCL_ERROR; } } /* *---------------------------------------------------------------------- * * RatStdCheckNet -- * * Check if we have any network connections which are active * and if not go offline. * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ void RatStdCheckNet(Tcl_Interp *interp) { Connection *connPtr; char buf[64]; int existsnetok = 0; for (connPtr = connListPtr; connPtr; connPtr = connPtr->next) { if (connPtr->isnet && (!connPtr->errorFlagPtr || 0 == *connPtr->errorFlagPtr)) { existsnetok = 1; } } if (0 == existsnetok) { strlcpy(buf, "SetOnlineStatus 0", sizeof(buf)); Tcl_Eval(interp, buf); } } /* *---------------------------------------------------------------------- * * mm_* * * The functions below are called from the C-client library. They * are docuemnted in Internal.DOC. * *---------------------------------------------------------------------- */ void mm_searched (MAILSTREAM *stream,unsigned long number) { if (searchResultSize == searchResultNum) { searchResultSize += 1024; searchResultPtr = (long*)ckrealloc(searchResultPtr, searchResultSize*sizeof(long)); } searchResultPtr[searchResultNum++] = number; } void mm_exists (MAILSTREAM *stream, unsigned long nmsgs) { Connection *connPtr = FindConn(stream); if (connPtr && connPtr->handlers && connPtr->handlers->exists) { (*connPtr->handlers->exists)(connPtr->handlers->state, nmsgs); } } void mm_expunged (MAILSTREAM *stream, unsigned long index) { Connection *connPtr = FindConn(stream); if (connPtr && connPtr->handlers && connPtr->handlers->expunged) { (*connPtr->handlers->expunged)(connPtr->handlers->state, index); } } void mm_mailbox (char *string) { } void mm_bboard (char *string) { } void mm_notify (MAILSTREAM *stream,char *string,long errflg) { if (errflg == BYE) { Connection *connPtr = FindConn(stream); if (connPtr && connPtr->errorFlagPtr) { *connPtr->errorFlagPtr = 1; } } } void mm_log (char *string,long errflg) { switch(errflg) { case NIL: logLevel = RAT_BABBLE; break; case PARSE: logLevel = RAT_PARSE; break; case WARN: logLevel = RAT_WARN; break; case BYE: logLevel = RAT_FATAL; break; case ERROR: /* fallthrough */ default: logLevel = RAT_ERROR; break; } ckfree(logMessage); logMessage = cpystr(string); if (logIgnore) { return; } RatLog(timerInterp, logLevel, string, RATLOG_NOWAIT); } void mm_dlog (char *string) { CONST84 char *filename; if (!debugFile && NULL != (filename = RatGetPathOption(timerInterp, "debug_file"))) { debugFile = fopen(filename, "a"); if (debugFile) { fchmod(fileno(debugFile), 0600); } } if (debugFile) { fprintf(debugFile, "%s\n", string); fflush(debugFile); } RatLog(timerInterp, RAT_BABBLE, string, RATLOG_TIME); } void mm_login (NETMBX *mbPtr, char *user, char *pwd, long trial) { char *pw; int objc; Tcl_Obj *oPtr, **objv; /* * Work differently in the sender child process */ if (is_sender_child) { if (mbPtr->authuser[0]) { strlcpy(user, mbPtr->authuser, MAILTMPLEN); } else { strlcpy(user, mbPtr->user, MAILTMPLEN); } strlcpy(pwd, smtp_passwd, MAILTMPLEN); return; } /* * Check for cached entry */ if ((pw = RatGetCachedPassword(timerInterp, loginSpec))) { strlcpy(user, mbPtr->user, MAILTMPLEN); strlcpy(pwd, pw, MAILTMPLEN); return; } oPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj("RatLogin", -1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj(mbPtr->host, -1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewLongObj(trial)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj(mbPtr->user, -1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj(mbPtr->service,-1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewLongObj(mbPtr->port)); if (TCL_OK != Tcl_EvalObj(timerInterp, oPtr) || NULL == (oPtr = Tcl_GetObjResult(timerInterp)) || TCL_OK != Tcl_ListObjGetElements(timerInterp, oPtr, &objc, &objv) || 3 != objc) { pwd[0] = '\0'; return; } strlcpy(user, Tcl_GetString(objv[0]), MAILTMPLEN); strlcpy(pwd, Tcl_GetString(objv[1]), MAILTMPLEN); if ('\0' != user[0]) { strlcpy(loginPassword, Tcl_GetString(objv[1]), MAILTMPLEN); Tcl_GetBooleanFromObj(timerInterp, objv[2], &loginStore); } else { /* User pressed cancel */ loginStore = 0; logIgnore++; } } void mm_critical (MAILSTREAM *stream) { } void mm_nocritical (MAILSTREAM *stream) { } long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { char buf[64]; sprintf(buf, "Disk error: %ld", errcode); RatLog(timerInterp, RAT_FATAL, buf, RATLOG_TIME); return 1; } void mm_fatal (char *string) { RatLog(timerInterp, RAT_FATAL, string, RATLOG_TIME); } void mm_flags (MAILSTREAM *stream,unsigned long number) { } void mm_list(MAILSTREAM *stream, int delimiter, char *spec, long attributes) { Mailbox **mPtrPtr = &mailboxListPtr, *nPtr; char *name, *folder, *s, *e, d; int do_decode = 0; Tcl_DString *encoded; lastDelimiter[0] = delimiter; if ('{' == spec[0]) { for (s=spec; *s && 0 == (*s & 0x80); s++); if (!*s) { do_decode = 1; } } /* * Prepare new Mailbox structure */ if ((folder = strchr(spec, '}'))) { folder++; } else { folder = spec; } if (delimiter && (NULL != (name = strrchr(folder, delimiter)))) { name++; } else { name = folder; } if (!strncmp(mailboxSearchBase, folder, strlen(mailboxSearchBase))) { s = folder+strlen(mailboxSearchBase); } else { s = folder; } /* * Call callback if specified */ if (importCallback) { Tcl_DString cmd; int i; Tcl_DStringInit(&cmd); Tcl_DStringAppend(&cmd, importCallback, -1); Tcl_DStringAppendElement(&cmd, s); Tcl_DStringStartSublist(&cmd); for (i=0; attrTable[i].mask; i++) { if (attributes & attrTable[i].mask) { Tcl_DStringAppendElement(&cmd, Tcl_GetString(attrTable[i].oPtr)); } } Tcl_DStringEndSublist(&cmd); Tcl_Eval(timerInterp, Tcl_DStringValue(&cmd)); Tcl_DStringFree(&cmd); } /* * Are we just doing a test import? */ if (testListPtr) { Tcl_Obj *objv[3]; int i; objv[0] = Tcl_NewObj(); for (i=0; attrTable[i].mask; i++) { if (attributes & attrTable[i].mask) { Tcl_ListObjAppendElement(NULL, objv[0], attrTable[i].oPtr); } } d = (char)delimiter; objv[1] = Tcl_NewStringObj(&d, 1); objv[2] = Tcl_NewStringObj(s, -1); Tcl_ListObjAppendElement(NULL, testListPtr, Tcl_NewListObj(3, objv)); return; } /* * First find the right level */ for (; delimiter && (e = strchr(s, delimiter)); *e = delimiter, s = e+1) { *e = '\0'; if (!strlen(s)) { continue; } while (*mPtrPtr && 0 > strcmp((*mPtrPtr)->name, s)) { mPtrPtr = &(*mPtrPtr)->next; } if (!*mPtrPtr || strcmp((*mPtrPtr)->name, s)) { nPtr = (Mailbox*)ckalloc(sizeof(Mailbox)+strlen(s)*3+1); nPtr->name = (char*)nPtr+sizeof(Mailbox); strcpy(nPtr->name, (do_decode ? RatMutf7toUtf8(s) : s)); nPtr->folder = NULL; nPtr->attributes = LATT_NOSELECT; nPtr->next = *mPtrPtr; nPtr->child = NULL; *mPtrPtr = nPtr; mPtrPtr = &nPtr->child; } else { mPtrPtr = &(*mPtrPtr)->child; } } if (attributes & LATT_NOSELECT) { return; } /* * Find location and link it */ while (*mPtrPtr && 0 > strcmp((*mPtrPtr)->name, name)) { mPtrPtr = &(*mPtrPtr)->next; } /* * Ignore duplicates */ encoded = RatEncodeQP((unsigned char*)folder); if (*mPtrPtr && (*mPtrPtr)->folder && !strcmp((*mPtrPtr)->folder, Tcl_DStringValue(encoded)) && (*mPtrPtr)->attributes == attributes) { Tcl_DStringFree(encoded); ckfree(encoded); return; } /* * Create actual folder entry */ nPtr = (Mailbox*)ckalloc( sizeof(Mailbox) + strlen(name)*3 + Tcl_DStringLength(encoded) + 2); nPtr->name = (char*)nPtr+sizeof(Mailbox); strcpy(nPtr->name, (do_decode ? RatMutf7toUtf8(name) : name)); nPtr->folder = nPtr->name+strlen(nPtr->name)+1; strcpy(nPtr->folder, Tcl_DStringValue(encoded)); nPtr->attributes = attributes; nPtr->delimiter = delimiter; nPtr->next = *mPtrPtr; nPtr->child = NULL; *mPtrPtr = nPtr; Tcl_DStringFree(encoded); ckfree(encoded); } void mm_lsub (MAILSTREAM *stream, int delimiter, char *name, long attributes) { mm_list(stream, delimiter, name, attributes | LATT_NOINFERIORS); } void mm_status (MAILSTREAM *stream, char *mailbox, MAILSTATUS *status) { memcpy(&stdStatus, status, sizeof(MAILSTATUS)); } #ifdef MEM_DEBUG void ratStdFolderCleanup() { ckfree(logMessage); } #endif /* MEM_DEBUG */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratStdFolder.h000066400000000000000000000057341137544547100213310ustar00rootroot00000000000000/* * ratStdFolder.h -- * * Declarations of functions used in the Std folder and messages * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #ifndef _RATSTDFOLDER #define _RATSTDFOLDER #include "ratFolder.h" /* * This list correlates to the ratStdTypeNames array in ratStdFolder.c */ typedef enum { RAT_UNIX, RAT_IMAP, RAT_POP, RAT_MH, RAT_MBX, RAT_DIS } RatStdFolderType; /* * Here we handle the events which may come from the mail-server * via parts of the mm interface. */ typedef void (HandleExists)(void *state, unsigned long nmsgs); typedef void (HandleExpunged)(void *state, unsigned long index); typedef struct { void *state; HandleExists *exists; HandleExpunged *expunged; } FolderHandlers; MAILSTREAM *Std_StreamOpen(Tcl_Interp *interp, char *name, long options, int *errorFlagPtr, FolderHandlers *handlers); void Std_StreamClose(Tcl_Interp *interp, MAILSTREAM *stream); void Std_StreamCloseAllCached(Tcl_Interp *interp); RatCreateProc Std_CreateProc; RatGetHeadersProc Std_GetHeadersProc; RatGetEnvelopeProc Std_GetEnvelopeProc; RatCreateBodyProc Std_CreateBodyProc; RatFetchTextProc Std_FetchTextProc; RatEnvelopeProc Std_EnvelopeProc; RatMsgDeleteProc Std_MsgDeleteProc; RatMakeChildrenProc Std_MakeChildrenProc; RatFetchBodyProc Std_FetchBodyProc; RatBodyDeleteProc Std_BodyDeleteProc; RatInfoProc Std_GetInfoProc; RatGetInternalDateProc Std_GetInternalDateProc; RatInfoProc Std_InfoProc; RatSetInfoProc Std_SetInfoProc; /* * used to store search results */ extern long *searchResultPtr; extern int searchResultSize; extern int searchResultNum; /* * Used to store status results */ extern MAILSTATUS stdStatus; /* * Controls if we should ignore loging calls or not */ extern int logIgnore; /* * This is the private part of a std folder info structure. */ typedef struct StdFolderInfo { MAILSTREAM *stream; /* Handler to c-client entity */ int referenceCount; /* Number of entities referencing this entry */ int exists; /* Number of messages which actually exists in this folder */ int error; /* Error status */ RatStdFolderType type; /* The exact type of this folder */ FolderHandlers handlers; /* The event handlers */ char *mailbox; /* Mailbox specifier */ } StdFolderInfo; /* * The ClientData for each message entity */ typedef struct StdMessageInfo { MAILSTREAM *stream; MESSAGECACHE *eltPtr; ENVELOPE *envPtr; BODY *bodyPtr; RatStdFolderType type; char *spec; } StdMessageInfo; /* ratStdMessage.c */ extern void RatStdMsgStructInit(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, MAILSTREAM *stream, RatStdFolderType type); extern char *RatStdMessageCreate (Tcl_Interp *interp, RatFolderInfoPtr infoPtr, MAILSTREAM *stream, int msgNo); #endif /* _RATSTDFOLDER */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratStdMessage.c000066400000000000000000000442761137544547100215010ustar00rootroot00000000000000/* * ratStdMessage.c -- * * This file contains code which implements standard c-client messages. * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratStdFolder.h" /* * The ClientData for each bodypart entity */ typedef struct StdBodyInfo { char *section; } StdBodyInfo; /* * The number of message entities created. This is used to create new * unique command names. */ static int numStdMessages = 0; #ifdef MEM_DEBUG static char *mem_header = NULL; #endif /* MEM_DEBUG */ /* *---------------------------------------------------------------------- * * RatStdMessagesInit -- * * Initializes the given MessageProcInfo entry for a c-client message * * Results: * None. * * Side effects: * The given MessageProcInfo is initialized. * * *---------------------------------------------------------------------- */ void RatStdMessagesInit(MessageProcInfo *messageProcInfoPtr) { messageProcInfoPtr->getHeadersProc = Std_GetHeadersProc; messageProcInfoPtr->getEnvelopeProc = Std_GetEnvelopeProc; messageProcInfoPtr->getInfoProc = Std_GetInfoProc; messageProcInfoPtr->createBodyProc = Std_CreateBodyProc; messageProcInfoPtr->fetchTextProc = Std_FetchTextProc; messageProcInfoPtr->envelopeProc = Std_EnvelopeProc; messageProcInfoPtr->msgDeleteProc = Std_MsgDeleteProc; messageProcInfoPtr->makeChildrenProc = Std_MakeChildrenProc; messageProcInfoPtr->fetchBodyProc = Std_FetchBodyProc; messageProcInfoPtr->bodyDeleteProc = Std_BodyDeleteProc; messageProcInfoPtr->getInternalDateProc = Std_GetInternalDateProc; messageProcInfoPtr->dbinfoGetProc = NULL; } /* *---------------------------------------------------------------------- * * RatStdMessageCreate -- * * Creates a std message entity * * Results: * The name of the new message entity. * * Side effects: * The message's long cache entry is locked until the message is * deleted. * * *---------------------------------------------------------------------- */ char* RatStdMessageCreate(Tcl_Interp *interp, RatFolderInfoPtr folderInfoPtr, MAILSTREAM *stream, int msgNo) { MessageInfo *msgPtr = (MessageInfo*)folderInfoPtr->privatePtr[msgNo]; StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; stdMsgPtr->envPtr = mail_fetchstructure_full(stream, msgNo+1, &stdMsgPtr->bodyPtr, NIL); stdMsgPtr->eltPtr = mail_elt(stream, msgNo+1); stdMsgPtr->eltPtr->lockcount++; stdMsgPtr->spec = cpystr(stream->mailbox); sprintf(msgPtr->name, "RatStdMsg%d", numStdMessages++); Tcl_CreateObjCommand(interp, msgPtr->name, RatMessageCmd, (ClientData)msgPtr, NULL); return msgPtr->name; } /* *---------------------------------------------------------------------- * * RatStdEasyCopyingOK -- * * Check if we can lets c-client handle the copying of this message * * Results: * A boolean which says if it is OK or not. * * Side effects: * None. * *---------------------------------------------------------------------- */ int RatStdEasyCopyingOK(Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_Obj *defPtr) { StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; Tcl_Obj **objv; int objc; Tcl_ListObjGetElements(interp, defPtr, &objc, &objv); switch (stdMsgPtr->type) { case RAT_DIS: return 0; case RAT_MBX: return 0; case RAT_UNIX: return 0; case RAT_MH: return !strcasecmp(Tcl_GetString(objv[1]), "mh"); case RAT_POP: return 0; case RAT_IMAP: if (strcasecmp(Tcl_GetString(objv[1]), "imap")) { return 0; } return !strcmp(stdMsgPtr->spec, RatGetFolderSpec(interp, defPtr)); } return 0; } /* *---------------------------------------------------------------------- * * RatStdMessageCopy -- * * Copy a message to another c-client folder. * * Results: * A boolean which says if it went OK or not. * * Side effects: * The destination folder is modified. * * *---------------------------------------------------------------------- */ int RatStdMessageCopy (Tcl_Interp *interp, MessageInfo *msgPtr, char *destination) { StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; int flagged = stdMsgPtr->eltPtr->flagged; int deleted = stdMsgPtr->eltPtr->deleted; char *cPtr, seq[16]; int r = TCL_ERROR; sprintf(seq, "%d", msgPtr->msgNo+1); if (flagged) { mail_clearflag(stdMsgPtr->stream, seq, flag_name[RAT_FLAGGED].imap_name); } if (deleted) { mail_clearflag(stdMsgPtr->stream, seq, flag_name[RAT_DELETED].imap_name); } switch (stdMsgPtr->type) { case RAT_UNIX: /* fallthrough */ case RAT_MBX: /* fallthrough */ case RAT_DIS: /* fallthrough */ case RAT_MH: /* fallthrough */ case RAT_POP: /* fallthrough */ if (T == mail_copy_full(stdMsgPtr->stream, seq, destination, 0)) { r = TCL_OK; } break; case RAT_IMAP: cPtr = strchr(destination, '}'); if (cPtr && mail_copy_full(stdMsgPtr->stream, seq, &cPtr[1], 0)) { r = TCL_OK; } break; } if (flagged) { mail_setflag(stdMsgPtr->stream, seq, flag_name[RAT_FLAGGED].imap_name); } if (deleted) { mail_setflag(stdMsgPtr->stream, seq, flag_name[RAT_DELETED].imap_name); } return r; } /* *---------------------------------------------------------------------- * * Std_GetHeadersProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ char* Std_GetHeadersProc(Tcl_Interp *interp, MessageInfo *msgPtr) { StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; static char *header = NULL; static int headerSize = 0; unsigned long length; char *fetchedHeader = mail_fetchheader_full(stdMsgPtr->stream, msgPtr->msgNo+1, NIL, &length, NIL); if (length > 2 && fetchedHeader[length-3] == '\n') { length -= 2; } if (length+64 > headerSize) { headerSize = length+64; header = (char*)ckrealloc(header, headerSize); } memmove(header, fetchedHeader, length); header[length] = '\0'; if (stdMsgPtr->eltPtr->seen) { strcpy(&header[length], "Status: RO\r\n"); length += strlen(&header[length]); } if (stdMsgPtr->eltPtr->answered) { strcpy(&header[length], "X-Status: A\r\n"); length += strlen(&header[length]); } #ifdef MEM_DEBUG mem_header = header; #endif /* MEM_DEBUG */ return header; } /* *---------------------------------------------------------------------- * * Std_GetEnvelopeProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ char* Std_GetEnvelopeProc(Tcl_Interp *interp, MessageInfo *msgPtr) { static char buf[1024]; ADDRESS *adrPtr; time_t date; struct tm tm, *tmPtr; StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; if (stdMsgPtr->envPtr->return_path) { adrPtr = stdMsgPtr->envPtr->sender; } else if (stdMsgPtr->envPtr->sender) { adrPtr = stdMsgPtr->envPtr->sender; } else { adrPtr = stdMsgPtr->envPtr->from; } if (adrPtr && RatAddressSize(adrPtr, 0) < sizeof(buf)-6) { strlcpy(buf, "From ", sizeof(buf)); rfc822_address(buf+5, adrPtr); } else { strlcpy(buf, "From unkown", sizeof(buf)); } tm.tm_sec = stdMsgPtr->eltPtr->seconds; tm.tm_min = stdMsgPtr->eltPtr->minutes; tm.tm_hour = stdMsgPtr->eltPtr->hours; tm.tm_mday = stdMsgPtr->eltPtr->day; tm.tm_mon = stdMsgPtr->eltPtr->month - 1; tm.tm_year = stdMsgPtr->eltPtr->year+69; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; date = (int)mktime(&tm); tmPtr = gmtime(&date); sprintf(buf + strlen(buf), " %s %s %2d %02d:%02d GMT %04d\n", dayName[tmPtr->tm_wday], monthName[tmPtr->tm_mon], tmPtr->tm_mday, tmPtr->tm_hour, tmPtr->tm_min,tmPtr->tm_year+1900); return buf; } /* *---------------------------------------------------------------------- * * Std_CreateBodyProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ BodyInfo* Std_CreateBodyProc(Tcl_Interp *interp, MessageInfo *msgPtr) { StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; StdBodyInfo *stdBodyInfoPtr = (StdBodyInfo*)ckalloc(sizeof(StdBodyInfo)); msgPtr->bodyInfoPtr = CreateBodyInfo(interp, msgPtr, stdMsgPtr->bodyPtr); msgPtr->bodyInfoPtr->clientData = (ClientData)stdBodyInfoPtr; if (TYPEMULTIPART == msgPtr->bodyInfoPtr->bodyPtr->type) { stdBodyInfoPtr->section = NULL; } else { stdBodyInfoPtr->section = cpystr("1"); } return msgPtr->bodyInfoPtr; } /* *---------------------------------------------------------------------- * * Std_FetchTextProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ char* Std_FetchTextProc(Tcl_Interp *interp, MessageInfo *msgPtr) { StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; return mail_fetchtext_full(stdMsgPtr->stream, msgPtr->msgNo+1, NIL, NIL); } /* *---------------------------------------------------------------------- * * Std_EnvelopeProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ ENVELOPE* Std_EnvelopeProc(MessageInfo *msgPtr) { return ((StdMessageInfo*)msgPtr->clientData)->envPtr; } /* *---------------------------------------------------------------------- * * Std_MsgDeleteProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ void Std_MsgDeleteProc(MessageInfo *msgPtr) { RatFolderInfo *infoPtr = msgPtr->folderInfoPtr; StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; infoPtr->privatePtr[msgPtr->msgNo] = NULL; stdMsgPtr->eltPtr->lockcount--; ckfree(stdMsgPtr->spec); ckfree(stdMsgPtr); } /* *---------------------------------------------------------------------- * * Std_MakeChildrenProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ void Std_MakeChildrenProc(Tcl_Interp *interp, BodyInfo *bodyInfoPtr) { StdBodyInfo *stdBodyInfoPtr = (StdBodyInfo*)bodyInfoPtr->clientData; BODY *bodyPtr = bodyInfoPtr->bodyPtr; BodyInfo *partInfoPtr, **partInfoPtrPtr; StdBodyInfo *partStdInfoPtr; int index = 1; PART *partPtr; int size; if (!bodyInfoPtr->firstbornPtr) { partInfoPtrPtr = &bodyInfoPtr->firstbornPtr; for (partPtr = bodyPtr->nested.part; partPtr; partPtr = partPtr->next) { partInfoPtr = CreateBodyInfo(interp, bodyInfoPtr->msgPtr, &partPtr->body); partStdInfoPtr = (StdBodyInfo*)ckalloc(sizeof(StdBodyInfo)); *partInfoPtrPtr = partInfoPtr; partInfoPtrPtr = &partInfoPtr->nextPtr; partInfoPtr->msgPtr = bodyInfoPtr->msgPtr; partInfoPtr->clientData = (ClientData)partStdInfoPtr; if (stdBodyInfoPtr->section) { size = strlen(stdBodyInfoPtr->section) + 8; partStdInfoPtr->section = (char*)ckalloc(size); snprintf(partStdInfoPtr->section, size, "%s.%d", stdBodyInfoPtr->section, index++); } else { partStdInfoPtr->section = (char*)ckalloc(8); sprintf(partStdInfoPtr->section, "%d", index++); } } } } /* *---------------------------------------------------------------------- * * Std_FetchBodyProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ char* Std_FetchBodyProc(BodyInfo *bodyInfoPtr, unsigned long *lengthPtr) { StdMessageInfo *stdMsgPtr=(StdMessageInfo*)bodyInfoPtr->msgPtr->clientData; if (bodyInfoPtr->decodedTextPtr) { *lengthPtr = Tcl_DStringLength(bodyInfoPtr->decodedTextPtr); return Tcl_DStringValue(bodyInfoPtr->decodedTextPtr); } return mail_fetchbody_full(stdMsgPtr->stream, bodyInfoPtr->msgPtr->msgNo+1, ((StdBodyInfo*)(bodyInfoPtr->clientData))->section, lengthPtr,NIL); } /* *---------------------------------------------------------------------- * * Std_BodyDeleteProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ void Std_BodyDeleteProc(BodyInfo *bodyInfoPtr) { StdBodyInfo *partStdInfoPtr = (StdBodyInfo*)bodyInfoPtr->clientData; ckfree(partStdInfoPtr->section); ckfree(bodyInfoPtr->clientData); } /* *---------------------------------------------------------------------- * * Std_GetInternalDateProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ MESSAGECACHE* Std_GetInternalDateProc(Tcl_Interp *interp, MessageInfo *msgPtr) { StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; return stdMsgPtr->eltPtr; } /* *---------------------------------------------------------------------- * * Std_GetInfoProc -- * * See ratFolder.h * *---------------------------------------------------------------------- */ Tcl_Obj* Std_GetInfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int notused) { Tcl_Obj *oPtr = NULL; MessageInfo *msgPtr = (MessageInfo*)clientData; StdMessageInfo *stdMsgPtr = (StdMessageInfo*)msgPtr->clientData; ADDRESS *addressPtr; int i, presIndex; if (msgPtr->info[type]) { if (type == RAT_FOLDER_INDEX && msgPtr->folderInfoPtr) { Tcl_GetIntFromObj(interp, msgPtr->info[type], &i); if (i < msgPtr->folderInfoPtr->number && msgPtr->folderInfoPtr->privatePtr[ msgPtr->folderInfoPtr->presentationOrder[i-1]] == (ClientData)msgPtr) { return msgPtr->info[type]; } } else { return msgPtr->info[type]; } } switch (type) { case RAT_FOLDER_SUBJECT: /* fallthrough */ case RAT_FOLDER_CANONSUBJECT: /* fallthrough */ case RAT_FOLDER_ANAME: /* fallthrough */ case RAT_FOLDER_NAME: /* fallthrough */ case RAT_FOLDER_MAIL_REAL: /* fallthrough */ case RAT_FOLDER_MAIL: /* fallthrough */ case RAT_FOLDER_NAME_RECIPIENT: /* fallthrough */ case RAT_FOLDER_MAIL_RECIPIENT: /* fallthrough */ case RAT_FOLDER_SIZE: /* fallthrough */ case RAT_FOLDER_SIZE_F: /* fallthrough */ case RAT_FOLDER_DATE_F: /* fallthrough */ case RAT_FOLDER_DATE_N: /* fallthrough */ case RAT_FOLDER_DATE_IMAP4: /* fallthrough */ case RAT_FOLDER_TO: /* fallthrough */ case RAT_FOLDER_FROM: /* fallthrough */ case RAT_FOLDER_SENDER: /* fallthrough */ case RAT_FOLDER_CC: /* fallthrough */ case RAT_FOLDER_FLAGS: /* fallthrough */ case RAT_FOLDER_UNIXFLAGS: /* fallthrough */ case RAT_FOLDER_MSGID: /* fallthrough */ case RAT_FOLDER_REF: /* fallthrough */ case RAT_FOLDER_THREADING: /* fallthrough */ case RAT_FOLDER_REPLY_TO: return RatGetMsgInfo(interp, type, msgPtr, stdMsgPtr->envPtr, NULL, stdMsgPtr->eltPtr, stdMsgPtr->eltPtr->rfc822_size); case RAT_FOLDER_PARAMETERS: if (!stdMsgPtr->bodyPtr) { stdMsgPtr->envPtr = mail_fetchstructure_full( stdMsgPtr->stream, msgPtr->msgNo+1, &stdMsgPtr->bodyPtr, NIL); } return RatGetMsgInfo(interp, type, msgPtr, stdMsgPtr->envPtr, stdMsgPtr->bodyPtr, stdMsgPtr->eltPtr, stdMsgPtr->eltPtr->rfc822_size); case RAT_FOLDER_TYPE: if (stdMsgPtr->envPtr->optional.subtype) { oPtr = Tcl_NewStringObj( body_types[stdMsgPtr->envPtr->optional.type], -1); Tcl_AppendStringsToObj(oPtr, "/", stdMsgPtr->envPtr->optional.subtype, NULL); } else { if (!stdMsgPtr->bodyPtr) { stdMsgPtr->envPtr = mail_fetchstructure_full( stdMsgPtr->stream, msgPtr->msgNo+1, &stdMsgPtr->bodyPtr, NIL); } oPtr=Tcl_NewStringObj(body_types[stdMsgPtr->bodyPtr->type],-1); Tcl_AppendStringsToObj(oPtr, "/", stdMsgPtr->bodyPtr->subtype, NULL); } break; case RAT_FOLDER_STATUS: if (RAT_ISME_UNKOWN == msgPtr->toMe) { msgPtr->toMe = RAT_ISME_NO; for (addressPtr = stdMsgPtr->envPtr->to; addressPtr; addressPtr = addressPtr->next) { if (RatAddressIsMe(interp, addressPtr, 1)) { msgPtr->toMe = RAT_ISME_YES; break; } } } oPtr = Tcl_NewStringObj(NULL, 0); if (!stdMsgPtr->eltPtr->seen) { Tcl_AppendToObj(oPtr, "N", 1); } if (stdMsgPtr->eltPtr->deleted) { Tcl_AppendToObj(oPtr, "D", 1); } if (stdMsgPtr->eltPtr->flagged) { Tcl_AppendToObj(oPtr, "F", 1); } if (stdMsgPtr->eltPtr->answered) { Tcl_AppendToObj(oPtr, "A", 1); } if (RAT_ISME_YES == msgPtr->toMe) { Tcl_AppendToObj(oPtr, "+", 1); } else { Tcl_AppendToObj(oPtr, " ", 1); } break; case RAT_FOLDER_INDEX: if (msgPtr->folderInfoPtr) { for (i=0; i< msgPtr->folderInfoPtr->number; i++) { presIndex = msgPtr->folderInfoPtr->presentationOrder[i]; if (msgPtr->folderInfoPtr->privatePtr[presIndex] == (ClientData)msgPtr){ oPtr = Tcl_NewIntObj(i+1); break; } } } break; case RAT_FOLDER_UID: oPtr = Tcl_NewIntObj( mail_uid(stdMsgPtr->stream, msgPtr->msgNo+1)); case RAT_FOLDER_END: break; } if (!oPtr) { oPtr = Tcl_NewObj(); } msgPtr->info[type] = oPtr; Tcl_IncrRefCount(oPtr); return oPtr; } /* *---------------------------------------------------------------------- * * RatStdMsgStructInit -- * * Initializes the client data part of the message info structures * * Results: * None. * * Side effects: * More data is allocated * * *---------------------------------------------------------------------- */ void RatStdMsgStructInit(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, MAILSTREAM *stream, RatStdFolderType type) { StdMessageInfo *stdMsgPtr; int i, start, end; char seq[32]; if (-1 == index) { start = 0; end = infoPtr->number; sprintf(seq, "%d:%d", 1, end); } else { start = index; end = start+1; sprintf(seq, "%d", end); } for (i=start; istream = stream; stdMsgPtr->eltPtr = mail_elt(stream, i+1); stdMsgPtr->envPtr = mail_fetch_structure(stream, i+1, NIL, NIL); stdMsgPtr->bodyPtr = NULL; stdMsgPtr->type = type; stdMsgPtr->spec = NULL; ((MessageInfo*)infoPtr->privatePtr[i])->clientData = (ClientData)stdMsgPtr; } } #ifdef MEM_DEBUG void ratStdMessageCleanup() { ckfree(mem_header); } #endif /* MEM_DEBUG */ tkrat_2.2cvs20100105-dfsg.orig/lib/ratWatchdog.c000066400000000000000000000044461137544547100211750ustar00rootroot00000000000000/* * ratWatchdog.c -- * * Provides a small forked copy of tkrat which cleans up * when the parent dies. * * * TkRat software and its included text is Copyright 1996-2004 by * Martin Forssn * * The full text of the legal notice is contained in the file called * COPYRIGHT, included with this distribution. */ #include "rat.h" #include static void RatWatchdogCleanup(const char *tmp); /* *---------------------------------------------------------------------- * * RatReleaseWatchdog -- * * Release the watchdog which eventually will cleanup the tmp- * directory. * * Results: * None. * * Side effects: * forks. * * *---------------------------------------------------------------------- */ void RatReleaseWatchdog(const char *tmpdir) { struct rlimit rlim; int i, leash[2]; char c; /* * The leash is used to release the watchdog (child) when the parent * dies. */ if (pipe(leash)) return; if (0 == fork()) { /* * Install signal handlers */ signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGABRT, SIG_IGN); signal(SIGPIPE, SIG_IGN); /* * The watchdog starts by closing all decriptors except our * end of the leash. */ getrlimit(RLIMIT_NOFILE, &rlim); for (i=0; id_name) || !strcmp("..", d->d_name)) { continue; } snprintf(buf, sizeof(buf), "%s/%s", tmpdir, d->d_name); unlink(buf); } closedir(dir); rmdir(tmpdir); } tkrat_2.2cvs20100105-dfsg.orig/misc/000077500000000000000000000000001137544547100167375ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/misc/.cvsignore000066400000000000000000000000111137544547100207270ustar00rootroot00000000000000Makefile tkrat_2.2cvs20100105-dfsg.orig/misc/Makefile.in000066400000000000000000000025031137544547100210040ustar00rootroot00000000000000############################################################################# # TkRat software and its included text is Copyright 1996-2004 by # # Martin Forssen. # # # # The full text of the legal notices is contained in the file called # # COPYRIGHT, included with this distribution. # ############################################################################# # Installation directories datarootdir = @datarootdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ MAN_DIR = @mandir@ BIN_DIR = @bindir@ DATA_DIR = @datadir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` LIB_DIR = @libdir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` SHELL = /bin/sh VERSION = @VERSION@ INSTALL = @INSTALL@ TRANSFORM = @program_transform_name@ INSTALL_PREFIX = @INSTALL_PREFIX@ #-------- No changes should be done below -------- BITMAPS = *.xbm *.xpm all: install.shared: if test ! -d ${INSTALL_PREFIX}${DATA_DIR} ; then\ ${INSTALL} -m 0755 -d ${INSTALL_PREFIX}${DATA_DIR} ;\ fi for i in ${BITMAPS} ; \ do \ ${INSTALL} -m 0644 $$i ${INSTALL_PREFIX}${DATA_DIR} ;\ done install: install.data tkrat_2.2cvs20100105-dfsg.orig/misc/install-sh000077500000000000000000000112441137544547100207450ustar00rootroot00000000000000#! /bin/sh # # install - install a program, script, or datafile # This comes from X11R5. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. # # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" tranformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 tkrat_2.2cvs20100105-dfsg.orig/misc/tkrat.xbm000066400000000000000000000063321137544547100206000ustar00rootroot00000000000000#define tkrat2_width 64 #define tkrat2_height 64 static unsigned char tkrat2_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x55, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xaa, 0xaa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x55, 0x55, 0x31, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0xaa, 0x79, 0x08, 0x00, 0x00, 0x00, 0x60, 0x55, 0x55, 0xd5, 0x14, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0xaa, 0xad, 0x2d, 0x00, 0x00, 0x00, 0xe0, 0x55, 0x55, 0x55, 0x3d, 0x00, 0x00, 0x00, 0x60, 0xae, 0xaa, 0xed, 0x2b, 0x00, 0x00, 0x00, 0x20, 0x58, 0x55, 0xf9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0xac, 0xaa, 0x59, 0x55, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xad, 0xaa, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xaa, 0x54, 0x55, 0x01, 0x00, 0x00, 0x00, 0x54, 0xd5, 0xee, 0xab, 0x01, 0x00, 0x00, 0x00, 0xae, 0xaa, 0xd6, 0x55, 0x01, 0x00, 0x00, 0x00, 0x57, 0xd5, 0xea, 0xab, 0x01, 0x00, 0x00, 0xc0, 0xaa, 0xaa, 0x55, 0x55, 0x01, 0x00, 0x00, 0x40, 0x55, 0xd5, 0xab, 0xaa, 0x01, 0x00, 0x00, 0xc0, 0xaa, 0xfa, 0x55, 0xf5, 0x01, 0x00, 0x00, 0x40, 0x55, 0x5d, 0xab, 0x0e, 0x02, 0x00, 0x00, 0xe0, 0xaa, 0xae, 0x56, 0xc5, 0x05, 0x00, 0x00, 0x68, 0x55, 0x57, 0xad, 0x86, 0x04, 0x00, 0x00, 0xb8, 0xaa, 0xab, 0x7f, 0x85, 0x02, 0x00, 0x00, 0x58, 0xd5, 0xd5, 0xeb, 0x6b, 0x01, 0x00, 0x00, 0xa8, 0xea, 0x6a, 0xd5, 0xfc, 0x00, 0x00, 0x00, 0x58, 0x75, 0xb5, 0xaa, 0x11, 0x00, 0x00, 0x00, 0xae, 0xba, 0x6a, 0x55, 0x17, 0x00, 0x00, 0x00, 0x57, 0x5d, 0xb5, 0xaa, 0xfe, 0x07, 0x00, 0x00, 0xab, 0xae, 0x6a, 0x55, 0x55, 0x0d, 0x00, 0x00, 0x55, 0x57, 0xf5, 0xaa, 0xaa, 0x3a, 0x00, 0x00, 0xab, 0xab, 0xaa, 0x55, 0xd5, 0x3f, 0x00, 0x00, 0xd5, 0x55, 0xd5, 0xab, 0x3a, 0x28, 0x00, 0x00, 0xab, 0xab, 0xaa, 0xfe, 0x0f, 0x44, 0x00, 0x80, 0xd5, 0x55, 0xff, 0x03, 0x01, 0x44, 0x00, 0x80, 0xea, 0xaa, 0xab, 0x03, 0x81, 0xff, 0x03, 0x80, 0xd5, 0xd5, 0x55, 0x0d, 0xc2, 0x80, 0x06, 0x80, 0xea, 0xea, 0xaa, 0x1a, 0x42, 0x80, 0x04, 0x80, 0xd5, 0x75, 0x55, 0x35, 0x4c, 0xe4, 0x04, 0x80, 0xea, 0xba, 0xaa, 0x2a, 0x4c, 0xdc, 0x04, 0x80, 0xd5, 0x75, 0x55, 0x55, 0x54, 0x7c, 0x04, 0x80, 0xea, 0xba, 0xaa, 0x6a, 0x5c, 0x04, 0x04, 0x80, 0xd5, 0x5d, 0x55, 0x55, 0xd4, 0x04, 0x06, 0x80, 0xab, 0xba, 0xaa, 0x6a, 0x94, 0xff, 0x03, 0x00, 0xd5, 0x5d, 0x55, 0x55, 0x0e, 0x00, 0x00, 0x00, 0xab, 0xbb, 0xaa, 0x6a, 0x0a, 0x00, 0x00, 0x00, 0x55, 0x75, 0x55, 0x35, 0x05, 0x00, 0x00, 0x00, 0xae, 0xab, 0xaa, 0x1a, 0x07, 0x00, 0x00, 0x00, 0x5c, 0x77, 0x55, 0x0d, 0x05, 0x00, 0x00, 0x00, 0xb0, 0xee, 0xaa, 0x86, 0x05, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x55, 0x53, 0x02, 0x00, 0x00, 0x00, 0x60, 0x80, 0xff, 0xef, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfb, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; tkrat_2.2cvs20100105-dfsg.orig/misc/tkrat.xpm000066400000000000000000000114241137544547100206140ustar00rootroot00000000000000/* XPM */ static char * tkrat_xpm[] = { "64 64 32 1", " c None", ". c #000000", "+ c #FFFF91", "@ c #FFFFB2", "# c #FFFFA2", "$ c #FFFF7B", "% c #FFF74D", "& c #FFFF68", "* c #FFFF84", "= c #D4882A", "- c #CF8529", "; c #CA8128", "> c #FFFFBB", ", c #AD6F22", "' c #FFFF59", ") c #540480", "! c #750680", "~ c #4B047A", "{ c #4D047D", "] c #7B0680", "^ c #800980", "/ c #56058C", "( c #800A80", "_ c #3F0366", ": c #800880", "< c #FFFFFF", "[ c #CFBF91", "} c #FDEAB1", "| c #F1DFA9", "1 c #ECDAA5", "2 c #FFFF82", "3 c #E0CF9D", " ", " ", " ", " ", " ", " . . ", " .....+... . ", " ..@+++#$%+&.. ", " .@#*=======-%;. ", " ..>*===========,. ", " .>*============-,. ", " .>*==============,. .. ", " .@*===============,. .@&. . ", " .+=,,-============,. .@*-;. .#. ", " .%,,.;-===========,. .+==-,. .#,. ", " .,.. .%===========,. .+==,,. .$,. ", " .. ..&-==========,. .;,,,...#.,. ", " . .'==========,. ....$+#$-.. ", " .@*==========,. .@+#*===-%&. ", " .&-=========,,. .@*========-;. ", " .'=========,. .#=,,,,-====-,. ", " .>*=========,. .@*=,...;-====,. ", " .@*==========,. .+==,....'====,. ", " ..#===========,. .#==-;..>*====,. ", " ..@#*==========,,..@*===-%#*=====,. ", " .@*===========,,...+=========,,,,,. ", " .+===========,,.$;.+========,,..... ", " .#==========,,.**,.$-======,,.)!~{]. ", " .@*=========,,.**=--.--=====,.)^/.../. ", " . .#=========,,.**==,,,.,,,-==,.({~_.:_. ", " ..>*========,,.**==,,......,,,,.^___./. ", " .@*========,,.**==,,.$++++$....,._.._. ", " .+=======,,,.**==,,.**====-;.<<...... ", " .#======,,..**===,.$*======-=.[}|. ", " ..>*=====,,.$#*====,.+========-=..[. ", " ..>*=====,,.**======,.+=========-;......... ", " .@*=====,,.**=======,.$-=========-%+++++++&. ", " .+=====,,.**========--.--============,,,,,,,.. ", " .+=====,.$*==========--.--=========,,,........ ", " .+====,,.#============--.,,,,,,,,,,,.. . . ", " .+====,.$*=======,,,,,,,,........... . . ", " ..#====,.+======,,,......,.<<<<<1. . . ", " .@*===,,.#====,,,..$++++$..|[|}}[. ........... ", " .+====,.$*===,,..$#*====-%2..||}|[. ..@+++++%$$.. ", " .+====,.+====,.*#*========-%2.||}[. .@*===,,,.',. ", " .+====,.+===,,.#============-,.<}|[.. .+=,,,,...+,. ", " .+====,.+===,.$*=============,.<|}[.. .+=,.,.,'.#,. ", " .+====,.+===,.+==============-,.<}[.'. .+=,.....**,. ", " .+====,.$-==,.+===============,.<}[.=. .&-,.@++#*,,. ", " .+====-,.%==,.+===============,.<}[.=. ..,,,,,,,,,.. ", " .&-====,.+==,.+===============,.<[[.,. ........... ", " .%====,.$-=,.+===============,.<[.,. ", " .+====-,.%=,.+==============,,.<[.,. ", " .&-====,.$-,.$-============,,.<[.,. ", " .,,-==-,.%-,.%===========,,.<<[.=. ", " ..;-==,.+=,.$-=========,,.<<[[.-. ", " .;,,,.;,,,.%========,,.<<[[.,,. ", " .,........&-=,,,,,,,.3[.[.,,. ", " .. .,,,.........;......... ", " ...,=====*'=,,==,..'=,. ", " ........,...... ... ", " .. ", " ", " "}; tkrat_2.2cvs20100105-dfsg.orig/misc/tkrat_small.xbm000066400000000000000000000043251137544547100217700ustar00rootroot00000000000000#define rat_width 50 #define rat_height 50 static char rat_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xaa, 0x11, 0x00, 0x00, 0x00, 0x00, 0x56, 0x55, 0x29, 0x02, 0x00, 0x00, 0x00, 0xab, 0xaa, 0x55, 0x07, 0x00, 0x00, 0x00, 0x55, 0x55, 0x6d, 0x0d, 0x00, 0x00, 0x00, 0xbf, 0xaa, 0x55, 0x0b, 0x00, 0x00, 0x00, 0x61, 0x55, 0xf9, 0x0f, 0x00, 0x00, 0x00, 0xb0, 0xaa, 0xa9, 0x1a, 0x00, 0x00, 0x00, 0x50, 0x55, 0x55, 0x15, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0xac, 0x2a, 0x00, 0x00, 0x00, 0x50, 0xd5, 0xf6, 0x35, 0x00, 0x00, 0x00, 0xa8, 0xaa, 0xea, 0x2a, 0x00, 0x00, 0x00, 0x56, 0xd5, 0x55, 0x35, 0x00, 0x00, 0x00, 0xaa, 0xea, 0xab, 0x2a, 0x00, 0x00, 0x00, 0x56, 0xb5, 0x55, 0x3d, 0x00, 0x00, 0x00, 0xaa, 0x5a, 0xab, 0x42, 0x00, 0x00, 0x40, 0x55, 0xad, 0x56, 0xb9, 0x00, 0x00, 0xc0, 0xaa, 0x56, 0xbf, 0x51, 0x00, 0x00, 0x40, 0x55, 0xab, 0xf5, 0x2a, 0x00, 0x00, 0xc0, 0xaa, 0xd5, 0x6a, 0x1f, 0x00, 0x00, 0x40, 0xd5, 0x6a, 0xd5, 0x04, 0x00, 0x00, 0xb0, 0x6a, 0xb5, 0xaa, 0xff, 0x00, 0x00, 0x50, 0xb5, 0x6a, 0x55, 0x55, 0x01, 0x00, 0xb0, 0x5a, 0xd5, 0xaa, 0xaa, 0x02, 0x00, 0x50, 0xad, 0xaa, 0x55, 0xfd, 0x03, 0x00, 0xb0, 0x5a, 0x55, 0xff, 0x87, 0x04, 0x00, 0x58, 0xad, 0xfa, 0x42, 0x80, 0x04, 0x00, 0xa8, 0x56, 0xaf, 0x43, 0xe0, 0x1f, 0x00, 0x58, 0xab, 0x55, 0x85, 0x18, 0x60, 0x00, 0xa8, 0xd6, 0xaa, 0x8a, 0x08, 0x48, 0x00, 0x58, 0xab, 0x55, 0x15, 0x09, 0x4e, 0x00, 0xa8, 0xd6, 0xaa, 0x1a, 0x49, 0x49, 0x00, 0x58, 0x6b, 0x55, 0x35, 0xc9, 0x47, 0x00, 0xa8, 0xd6, 0xaa, 0x2a, 0x59, 0x60, 0x00, 0x58, 0x6d, 0x55, 0x35, 0xe1, 0x1f, 0x00, 0xb0, 0xd6, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x50, 0x6d, 0x55, 0x95, 0x00, 0x00, 0x00, 0xa0, 0xda, 0xaa, 0x4a, 0x00, 0x00, 0x00, 0xc0, 0xb5, 0x55, 0x45, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xab, 0x22, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf5, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0xae, 0xea, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; tkrat_2.2cvs20100105-dfsg.orig/misc/tkrat_smallmask.xbm000066400000000000000000000043711137544547100226450ustar00rootroot00000000000000#define tkrat_smallmask_width 50 #define tkrat_smallmask_height 50 static char tkrat_smallmask_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x11, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x39, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7d, 0x07, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7d, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7d, 0x0f, 0x00, 0x00, 0x00, 0xe1, 0xff, 0xf9, 0x0f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xf9, 0x1f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xfd, 0x1f, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x87, 0x04, 0x00, 0xf8, 0xff, 0xff, 0x7f, 0x80, 0x04, 0x00, 0xf8, 0xff, 0xff, 0x7f, 0xe0, 0x1f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xf9, 0x7f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xf9, 0x7f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xf9, 0x7f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xf9, 0x7f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xe1, 0x1f, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; tkrat_2.2cvs20100105-dfsg.orig/misc/tkratmask.xbm000066400000000000000000000064311137544547100214540ustar00rootroot00000000000000#define tkratmask_width 64 #define tkratmask_height 64 #define tkratmask_x_hot 53 #define tkratmask_y_hot 37 static unsigned char tkratmask_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x31, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x79, 0x08, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xfd, 0x1c, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xfd, 0x3d, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xfd, 0x3d, 0x00, 0x00, 0x00, 0x60, 0xfe, 0xff, 0xfd, 0x3b, 0x00, 0x00, 0x00, 0x20, 0xf8, 0xff, 0xf9, 0x3b, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xf9, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0xe8, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x28, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x44, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x44, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0x03, 0x80, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0x03, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x60, 0x80, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; tkrat_2.2cvs20100105-dfsg.orig/test/000077500000000000000000000000001137544547100167635ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/test/.cvsignore000066400000000000000000000000141137544547100207560ustar00rootroot00000000000000run TESTDIR tkrat_2.2cvs20100105-dfsg.orig/test/data.tcl000066400000000000000000000437221137544547100204100ustar00rootroot00000000000000# This file contains test messages etc. set hdr {From MAILER-DAEMON Fri Dec 1 07:34:39 2000 Date: 01 Dec 2000 07:34:39 +0100 From: Mail System Internal Data Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA Message-ID: <975652479@kilauea.firedoor.se> X-IMAP: 0975652479 0000000010 Status: RO This text is part of the internal format of your mail folder, and is not a real message. It is created automatically by the mail system software. If deleted, important folder data will be lost, and it will be re-created with the data reset to initial values. } set timestamp [clock format [clock seconds] -format "%Y%m%d%H%M%S"] for {set i 1} {$i < 21} {incr i} { upvar #0 msg$i m set i2 [format "%02d" $i] set date "Sun, 26 Nov 2000 12:36:$i2 +0100 (MET)" set data [string repeat "123456789\n" $i] set m "From maf@tkrat.org Tue Sep 5 18:02:22 2000 +0100 Date: $date Message-Id: <$timestamp-$i@tkrat.org> From: Martin Forssen Subject: test $i2 To: Martin Forssen $i MIME-Version: 1.0 Content-Type: TEXT/plain; charset=us-ascii test $i $data " } # List of messages to generate # - Name of test message # - Argument to RatCreateMesssage call # - Expected generated message set smsgs { { "Basic no frills message" { { {to to_user} {subject subject_string} {from maf@tkrat.org} } {TEXT PLAIN {{charset us-ascii}} 7bit "" {} {{X-TkRat-Internal {{Cited {}} {noWrap {}} {no_spell {}}}}} {utfblob {This is the body}}} } { {From: maf@tkrat.org} {Subject: subject_string} {To: to_user@test.domain} {MIME-Version: 1.0} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {X-TkRat-Internal: {Cited {}} {noWrap {}} {no_spell {}}} {} {This is the body} {} } { { {From maf@tkrat.org} {Subject subject_string} {To to_user@test.domain} {MIME-Version 1.0} {Content-Type {TEXT/PLAIN; CHARSET=us-ascii}} {X-TkRat-Internal {{Cited {}} {noWrap {}} {no_spell {}}}} } { {TEXT PLAIN} { {CHARSET us-ascii} } {} {} } } } { "Minimalistic message" { { {to to_user} {subject {No subject}} {from maf@tkrat.org} {X-TkRat-Internal {{Cited {}} {noWrap {}} {no_spell {}}}} } {TEXT PLAIN {{charset us-ascii}} 7bit INLINE {} {} {utfblob ""}} } { {From: maf@tkrat.org} {Subject: No subject} {To: to_user@test.domain} {MIME-Version: 1.0} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {Content-Disposition: INLINE} {X-TkRat-Internal: {Cited {}} {noWrap {}} {no_spell {}}} {} {} } { { {From maf@tkrat.org} {Subject {No subject}} {To to_user@test.domain} {MIME-Version 1.0} {Content-Type {TEXT/PLAIN; CHARSET=us-ascii}} {Content-Disposition INLINE} {X-TkRat-Internal {{Cited {}} {noWrap {}} {no_spell {}}}} } { {TEXT PLAIN} { {CHARSET us-ascii} } {INLINE} {} } } } { "Message with full headers" { { {date date_string} {from maf@tkrat.org} {sender s_user} {reply_to rt_user} {subject subject_string} {to to_user} {cc cc_user} {in_reply_to } {message_id } {newsgroups news.group} {followup_to follow.to} {references " "} {X-TkRat-Test test_string} } { TEXT PLAIN {{charset us-ascii} {foo bar}} 7bit INLINE {{dfoo dbar}} { {content_id } {content_description desc_string} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } {utfblob {This is the body}} } } { {Newsgroups: news.group} {Date: date_string} {From: maf@tkrat.org} {Sender: s_user@test.domain} {Reply-To: rt_user@test.domain} {Subject: subject_string} {To: to_user@test.domain} {cc: cc_user@test.domain} {In-Reply-To: } {Message-ID: } {Followup-to: follow.to} {References: } {MIME-Version: 1.0} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii; FOO=bar} {Content-ID: } {Content-Description: desc_string} {Content-Disposition: INLINE; DFOO=dbar} {X-TkRat-Test: test_string} {X-TkRat-Internal: {Cited {}} {noWrap {}}} {} {This is the body} {} } { { {Newsgroups news.group} {Date date_string} {From maf@tkrat.org} {Sender s_user@test.domain} {Reply-To rt_user@test.domain} {Subject subject_string} {To to_user@test.domain} {cc cc_user@test.domain} {In-Reply-To } {Message-ID } {Followup-to follow.to} {References { }} {MIME-Version 1.0} {Content-Type {TEXT/PLAIN; CHARSET=us-ascii; FOO=bar}} {Content-ID } {Content-Description desc_string} {Content-Disposition {INLINE; DFOO=dbar}} {X-TkRat-Test test_string} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { {TEXT PLAIN} { {CHARSET us-ascii} {FOO bar} } {INLINE} { {DFOO dbar} } } } } { "Message with long and local parameters" { { {to to_user} {subject subject_string} {from maf@tkrat.org} } {TEXT PLAIN {{charset us-ascii} {Lunch Rksmrgs.txt} {Long1 "This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces"} {Long2 "Detta r en vldigt lng header-rad som behver brytas i exakt tv delar"}} 7bit "" {} {{X-TkRat-Internal {{Cited {}} {noWrap {}} {no_spell {}}}}} {utfblob {This is the body}}} } { {From: maf@tkrat.org} {Subject: subject_string} {To: to_user@test.domain} {MIME-Version: 1.0} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii;} { LUNCH="=?iso-8859-1?Q?R=E4ksm=F6rg=E5s=2Etxt?=";} { LUNCH*=iso-8859-1''R%E4ksm%F6rg%E5s%2Etxt;} { LONG1=This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces;} { LONG1*0=This_is_a_very_long_header_value_which_needs_to_be_broken_into_exac;} { LONG1*1=tly_two_pieces;} { LONG2="=?iso-8859-1?Q?Detta_=E4r_en_v=E4ldigt_l=E5ng_header-rad_som_beh=F6ver_brytas_i_exakt_tv=E5_delar?=";} { LONG2*0*=iso-8859-1''Detta%20%E4r%20en%20v%E4ldigt%20l%E5ng%20header-rad%20s;} { LONG2*1*=om%20beh%F6ver%20brytas%20i%20exakt%20tv%E5%20delar} {X-TkRat-Internal: {Cited {}} {noWrap {}} {no_spell {}}} {} {This is the body} {} } { { {From maf@tkrat.org} {Subject subject_string} {To to_user@test.domain} {MIME-Version 1.0} {Content-Type {TEXT/PLAIN; CHARSET=us-ascii; LUNCH="Rksmrgs.txt"; LUNCH*=iso-8859-1''R%E4ksm%F6rg%E5s%2Etxt; LONG1=This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces; LONG1*0=This_is_a_very_long_header_value_which_needs_to_be_broken_into_exac; LONG1*1=tly_two_pieces; LONG2="Detta r en vldigt lng header-rad som behver brytas i exakt tv delar"; LONG2*0*=iso-8859-1''Detta%20%E4r%20en%20v%E4ldigt%20l%E5ng%20header-rad%20s; LONG2*1*=om%20beh%F6ver%20brytas%20i%20exakt%20tv%E5%20delar}} {X-TkRat-Internal {{Cited {}} {noWrap {}} {no_spell {}}}} } { {TEXT PLAIN} { {CHARSET us-ascii} {LUNCH Rksmrgs.txt} {LUNCH Rksmrgs.txt} {LONG1 This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces} {LONG1 This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces} {LONG2 {Detta r en vldigt lng header-rad som behver brytas i exakt tv delar}} {LONG2 {Detta r en vldigt lng header-rad som behver brytas i exakt tv delar}} } {} {} } } } { "Message with plain attachment" { { {to maf} {subject test} {from maf@tkrat.org} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { MULTIPART MIXED {{boundary BD}} 7bit {} {} {} { { TEXT PLAIN {{charset us-ascii}} 7bit INLINE {} {} {utfblob {Body text}} } { TEXT PLAIN {{name attachment.txt}} 7bit ATTACHMENT {{filename attachment.txt}} {} {file $tmp/test_attachment.txt}} } } } { {From: maf@tkrat.org} {Subject: test} {To: maf@test.domain} {MIME-Version: 1.0} {Content-Type: MULTIPART/MIXED; BOUNDARY=BD} {X-TkRat-Internal: {Cited {}} {noWrap {}}} {} {--BD} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {Content-Disposition: INLINE} {} {Body text} {--BD} {Content-Type: TEXT/PLAIN; NAME=attachment.txt} {Content-Disposition: ATTACHMENT; FILENAME=attachment.txt} {} {Line 1 of attachment} {Line 2 of attachment} {} {--BD--} {} } { { {From maf@tkrat.org} {Subject test} {To maf@test.domain} {MIME-Version 1.0} {Content-Type {MULTIPART/MIXED; BOUNDARY=BD}} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { {MULTIPART MIXED} { {BOUNDARY BD} } {} {} { {TEXT PLAIN} { {CHARSET us-ascii} } {INLINE} {} } { {TEXT PLAIN} { {NAME attachment.txt} } {ATTACHMENT} { {FILENAME attachment.txt} } } } } } { "Multipart message with long and local parameters" { { {to maf} {subject test} {from maf@tkrat.org} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { MULTIPART MIXED {{boundary BD}} 7bit {} {} {} { { TEXT PLAIN {{charset us-ascii}} 7bit INLINE {} {} {utfblob {Body text}} } { TEXT PLAIN {{name "This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces"}} 7bit ATTACHMENT {{filename "Detta r en vldigt lng header-rad som behver brytas i exakt tv delar"}} {} {file $tmp/test_attachment.txt}} } } } { {From: maf@tkrat.org} {Subject: test} {To: maf@test.domain} {MIME-Version: 1.0} {Content-Type: MULTIPART/MIXED; BOUNDARY=BD} {X-TkRat-Internal: {Cited {}} {noWrap {}}} {} {--BD} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {Content-Disposition: INLINE} {} {Body text} {--BD} {Content-Type: TEXT/PLAIN;} { NAME=This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces;} { NAME*0=This_is_a_very_long_header_value_which_needs_to_be_broken_into_exact;} { NAME*1=ly_two_pieces} {Content-Disposition: ATTACHMENT;} { FILENAME="=?iso-8859-1?Q?Detta_=E4r_en_v=E4ldigt_l=E5ng_header-rad_som_beh=F6ver_brytas_i_exakt_tv=E5_delar?=";} { FILENAME*0*=iso-8859-1''Detta%20%E4r%20en%20v%E4ldigt%20l%E5ng%20header-rad%;} { FILENAME*1*=20som%20beh%F6ver%20brytas%20i%20exakt%20tv%E5%20delar} {} {Line 1 of attachment} {Line 2 of attachment} {} {--BD--} {} } { { {From maf@tkrat.org} {Subject test} {To maf@test.domain} {MIME-Version 1.0} {Content-Type {MULTIPART/MIXED; BOUNDARY=BD}} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { {MULTIPART MIXED} { {BOUNDARY BD} } {} {} { {TEXT PLAIN} { {CHARSET us-ascii} } {INLINE} {} } { {TEXT PLAIN} { {NAME This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces} {NAME This_is_a_very_long_header_value_which_needs_to_be_broken_into_exactly_two_pieces} } {ATTACHMENT} { {FILENAME {Detta r en vldigt lng header-rad som behver brytas i exakt tv delar}} {FILENAME {Detta r en vldigt lng header-rad som behver brytas i exakt tv delar}} } } } } } { "Message with binary attachment" { { {to maf} {subject test_bin} {from maf@tkrat.org} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { MULTIPART MIXED {{boundary BD}} 7bit {} {} {} { { TEXT PLAIN {{charset us-ascii}} 7bit INLINE {} {} {utfblob {Body text II}} } { APPLICATION OCTET-STREAM {{name attachment.bin}} binary ATTACHMENT {{filename attachment.bin}} {} {file $tmp/test_attachment.bin}} } } } { {From: maf@tkrat.org} {Subject: test_bin} {To: maf@test.domain} {MIME-Version: 1.0} {Content-Type: MULTIPART/MIXED; BOUNDARY=BD} {X-TkRat-Internal: {Cited {}} {noWrap {}}} {} {--BD} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {Content-Disposition: INLINE} {} {Body text II} {--BD} {Content-Type: APPLICATION/OCTET-STREAM; NAME=attachment.bin} {Content-Transfer-Encoding: BASE64} {Content-Disposition: ATTACHMENT; FILENAME=attachment.bin} {} {AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss} {LS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZ} {WltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWG} {h4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz} {tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g} {4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==} {} {--BD--} {} } { { {From maf@tkrat.org} {Subject test_bin} {To maf@test.domain} {MIME-Version 1.0} {Content-Type {MULTIPART/MIXED; BOUNDARY=BD}} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { {MULTIPART MIXED} { {BOUNDARY BD} } {} {} { {TEXT PLAIN} { {CHARSET us-ascii} } {INLINE} {} } { {APPLICATION OCTET-STREAM} { {NAME attachment.bin} } {ATTACHMENT} { {FILENAME attachment.bin} } } } } } { "Message with attached message" { { {to maf} {subject test_msg} {from maf@tkrat.org} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { MULTIPART MIXED {{boundary BD}} 7bit {} {} {} { { TEXT PLAIN {{charset us-ascii}} 7bit INLINE {} {} {utfblob {Body text III}} } { MESSAGE RFC822 {} 7bit ATTACHMENT {} {} { { {to to_user} {subject subject_string} {from maf@tkrat.org} } {TEXT PLAIN {{charset us-ascii}} 7bit "" {} {} {utfblob {This is the body}}} {file $tmp/test_attachment.txt} } } } } } { {From: maf@tkrat.org} {Subject: test_msg} {To: maf@test.domain} {MIME-Version: 1.0} {Content-Type: MULTIPART/MIXED; BOUNDARY=BD} {X-TkRat-Internal: {Cited {}} {noWrap {}}} {} {--BD} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {Content-Disposition: INLINE} {} {Body text III} {--BD} {Content-Type: MESSAGE/RFC822} {Content-Disposition: ATTACHMENT} {} {From: maf@tkrat.org} {Subject: subject_string} {To: to_user@test.domain} {MIME-Version: 1.0} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {} {This is the body} {} {--BD--} {} } { { {From maf@tkrat.org} {Subject test_msg} {To maf@test.domain} {MIME-Version 1.0} {Content-Type {MULTIPART/MIXED; BOUNDARY=BD}} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { {MULTIPART MIXED} { {BOUNDARY BD} } {} {} { {TEXT PLAIN} { {CHARSET us-ascii} } {INLINE} {} } { {MESSAGE RFC822} {} {ATTACHMENT} {} { { {From maf@tkrat.org} {Subject subject_string} {To to_user@test.domain} {MIME-Version 1.0} {Content-Type {TEXT/PLAIN; CHARSET=us-ascii}} } { {TEXT PLAIN} { {CHARSET us-ascii} } {} {} } } } } } } { "Message with attached message with 8-bit attachment" { { {to maf} {subject test_msg} {from maf@tkrat.org} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { MULTIPART MIXED {{boundary BD}} 7bit {} {} {} { { TEXT PLAIN {{charset us-ascii}} 7bit INLINE {} {} {utfblob {Body text III}} } { MESSAGE RFC822 {} 7bit ATTACHMENT {} {} { { {to maf} {subject test_8bit} {from maf@tkrat.org} } { MULTIPART MIXED {{boundary BD2}} 7bit {} {} {} { { TEXT PLAIN {{charset us-ascii}} 7bit INLINE {} {} {utfblob {Body text}} } { TEXT PLAIN {{name foo}} 8bit ATTACHMENT {} {} {file $tmp/test_attachment.8bit}} } } } } } } } { {From: maf@tkrat.org} {Subject: test_msg} {To: maf@test.domain} {MIME-Version: 1.0} {Content-Type: MULTIPART/MIXED; BOUNDARY=BD} {X-TkRat-Internal: {Cited {}} {noWrap {}}} {} {--BD} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {Content-Disposition: INLINE} {} {Body text III} {--BD} {Content-Type: MESSAGE/RFC822} {Content-Disposition: ATTACHMENT} {} {From: maf@tkrat.org} {Subject: test_8bit} {To: maf@test.domain} {MIME-Version: 1.0} {Content-Type: MULTIPART/MIXED; BOUNDARY=BD2} {} {--BD2} {Content-Type: TEXT/PLAIN; CHARSET=us-ascii} {Content-Disposition: INLINE} {} {Body text} {--BD2} {Content-Type: TEXT/PLAIN; NAME=foo} {Content-Transfer-Encoding: QUOTED-PRINTABLE} {Content-Disposition: ATTACHMENT} {} {R=E4ckmackan} {} {--BD2--} {} {--BD--} {} } { { {From maf@tkrat.org} {Subject test_msg} {To maf@test.domain} {MIME-Version 1.0} {Content-Type {MULTIPART/MIXED; BOUNDARY=BD}} {X-TkRat-Internal {{Cited {}} {noWrap {}}}} } { {MULTIPART MIXED} { {BOUNDARY BD} } {} {} { {TEXT PLAIN} { {CHARSET us-ascii} } {INLINE} {} } { {MESSAGE RFC822} {} {ATTACHMENT} {} { { {From maf@tkrat.org} {Subject test_8bit} {To maf@test.domain} {MIME-Version 1.0} {Content-Type {MULTIPART/MIXED; BOUNDARY=BD2}} } { {MULTIPART MIXED} { {BOUNDARY BD2} } {} {} { {TEXT PLAIN} { {CHARSET us-ascii} } {INLINE} {} } { {TEXT PLAIN} { {NAME foo} } {ATTACHMENT} {} } } } } } } } } # Create data files set f [open $tmp/test_attachment.txt w] puts $f "Line 1 of attachment" puts $f "Line 2 of attachment" close $f set f [open $tmp/test_attachment.8bit w] puts $f "Rckmackan" close $f set f [open $tmp/test_attachment.bin w] fconfigure $f -encoding binary for {set i 0} {$i < 256} {incr i} { puts -nonewline $f [format "%c" $i] } close $f tkrat_2.2cvs20100105-dfsg.orig/test/imap-cyrus.tcl000066400000000000000000000020211137544547100215530ustar00rootroot00000000000000# IMAP-definieition and functions for dealing with an # Cyrus imap-server running on localhost. set imap_n user.$env(USER).test set cyrus_dir /var/spool/imap set mailServer(localhost) [list localhost 143 {debug} $env(USER)] set imap_def [list Test imap {} localhost ${imap_n}] set imap_def1 [list Test imap {} localhost ${imap_n}1] set imap_def2 [list Test imap {} localhost ${imap_n}2] set imap_fn1 ${imap_n}1 set imap_fn2 ${imap_n}2 set dis_def [list Test dis {} localhost $imap_n] set imap_map $dir/disconnected/localhost:143/$imap_n+maf+imap/mappings set start_uid 1 proc init_imap_folder {def} { global LEAD RatDeleteFolder $def if [catch {RatCreateFolder $def} result] { puts "$LEAD Failed to create folder $result [list $def]" exit 1 } } proc cleanup_imap_folder {def} { RatDeleteFolder $def } proc insert_imap {def args} { global dir hdr env foreach m $args { set f [open "|/usr/pd/cyrus/bin/deliver -m [lindex $def 4]" w] puts $f [join [lrange [split $m "\n"] 1 end] "\n"] close $f } } tkrat_2.2cvs20100105-dfsg.orig/test/imap-uwash.tcl000066400000000000000000000016351137544547100215470ustar00rootroot00000000000000# IMAP-definitions and functions for dealing with an # UWashington imap-server running on localhost. set imap_fn $dir/imap-folder.[pid] set mailServer(localhost) \ [list localhost {} {debug {ssh-cmd {%s %s -l %s exec /usr/sbin/%sd}}} \ $env(USER)] set imap_def [list Test imap {} localhost $imap_fn] set imap_def1 [list Test imap {} localhost ${imap_fn}-1] set imap_def2 [list Test imap {} localhost ${imap_fn}-2] set imap_fn1 ${imap_fn}-1 set imap_fn2 ${imap_fn}-2 set dis_def [list Test dis {} localhost $imap_fn] set imap_map $dir/disconnected/localhost:143$imap_fn+$env(USER)+imap/mappings set start_uid 11 proc init_imap_folder {def} { global hdr set fh [open [lindex $def 4] w] puts $fh $hdr close $fh } proc cleanup_imap_folder {def} { file delete [lindex $def 4] } proc insert_imap {def args} { set fh [open [lindex $def 4] a] foreach m $args { puts $fh $m } close $fh } tkrat_2.2cvs20100105-dfsg.orig/test/pgp_pub000066400000000000000000000015371137544547100203500ustar00rootroot00000000000000=3I098[8GY{|iJ %$[\% ly5I 3k;YbJZjE Tv iE`?'fbJ ͢gCyZ\L!7#ɒp"—Ϙ4GmXco=/G#C}}tK}[Yuk%G򒛛!qL-<"Ѩy31s)>up}gL{ 5# gʞUe 7 Osٌˈ]XlPH?X9&U>ĖNc| W2˺u & )])~>U+ |-?ʢ2hՁ2/S& pxB'J_?TkRat Test Key (Do not trust this key!!!) Y=3I  `1NJ<}.Cʺڦ?ޝ]dup}gL{ 5# gʞUe 7 Osٌˈ]XlPH?X9&U>ĖNc| W2˺u & )])~>U+ |-?ʢ2hՁ2/S& pxB'J_?TkRat Test Key (Do not trust this key!!!) Y=3I  `1NJQ%PI:X^+Pm`VV=3J˾"] 1] set resp "250 rcpt ok" } elseif {"get_rcpt" == $state && "DATA" == $cmd} { set resp "354 Enter mail" set state data } elseif {"data" == $state && "." != $line} { lappend message $line return } elseif {"data" == $state && "." == $line} { set resp "250 Message accepted" set state command } elseif {"RSET" == $line} { set resp "250 reset" set state command } else { set resp "500 Command unrecognized" } if {$debug} { foreach o [split $resp "\n"] { puts "OUT: $o" } } puts $c $resp } tkrat_2.2cvs20100105-dfsg.orig/test/test_addrlist.tcl000066400000000000000000000054161137544547100223420ustar00rootroot00000000000000puts "$HEAD Test addrlist" namespace eval test_addrlist { } # Test one explicit mapping request proc test_addrlist::test_match {match max expected} { global verbose variable addresses StartTest "Matching '$match'" set ret [GetMatchingAddrs $match $max] set mismatch 0 if {[llength $ret] != [llength $expected]} { set mismatch 1 } for {set i 0} {0 == $mismatch && $i < [llength $ret]} {incr i} { if {[lindex $ret $i] != [lindex $addresses [lindex $expected $i]]} { set mismatch 1 } } if {$mismatch} { ReportError "Bad result" if {$verbose} { puts "Got [llength $ret] element(s)" foreach e $ret { puts " $e" } puts "Expected [llength $expected] element(s)" foreach ei $expected { puts " [lindex $addresses $ei]" } } } } # Test the address list proc test_addrlist::test_addrlist {} { global option variable addresses AddrListAdd "Apple Core " AddrListAdd "apa@tkrat.org, adam@tkrat.org (Adam Somebody)" AddrListAdd "Martin Forssen " set addresses [list \ "Martin Forssen " \ "apa@tkrat.org" \ "Adam Somebody " \ "Apple Core "] test_match m 10 {0} test_match ma 10 {0} test_match a 10 {1 2 3} test_match ap 10 {1 3} test_match a 1 {1} AddrListAdd "Apple Core " test_match a 10 {3 1 2} } # Test parsing of addresses proc test_addrlist::test_addrparse {} { global option # Setup roles set option(tr0,from) test@foo.com set option(tr1,from) test@bar.com # Setup tests set tests { {"rcpt" "rcpt@foo.com" "rcpt@bar.com" "rcpt"} {"rcpt@foo.com" "rcpt@foo.com" "rcpt@foo.com" "rcpt@foo.com"} {"rcpt@bar.com" "rcpt@bar.com" "rcpt@bar.com" "rcpt@bar.com"} {"rcpt@apa.com" "rcpt@apa.com" "rcpt@apa.com" "rcpt@apa.com"} } foreach t $tests { StartTest "Parsing [lindex $t 0]" set a [RatCreateAddress [lindex $t 0] tr0] if {[$a get mail] != [lindex $t 1]} { ReportError "tr0: got '[$a get mail]' expected '[lindex $t 1]'" } set a [RatCreateAddress [lindex $t 0] tr1] if {[$a get mail] != [lindex $t 2]} { ReportError "tr1: got '[$a get mail]' expected '[lindex $t 2]'" } set a [RatCreateAddress -nodomain [lindex $t 0]] if {[$a get mail] != [lindex $t 3]} { ReportError "nodomain: got '[$a get mail]' expected '[lindex $t 3]'" } } } test_addrlist::test_addrlist test_addrlist::test_addrparse tkrat_2.2cvs20100105-dfsg.orig/test/test_alias.tcl000066400000000000000000000164251137544547100216270ustar00rootroot00000000000000puts "$HEAD Test alias" namespace eval test_alias { } proc test_alias::do_test {case} { global option set r [lindex $case 0] set a [lindex $case 1] StartTest "Expanding $a" set e [RatAlias expand display $a $r] set o [lindex $case 2] if {$e != $o} { ReportError "expand display '$a' gave '$e' expected '$o'" } set display $e set e [RatAlias expand sending $a $r] set o [lindex $case 3] if {$e != $o} { ReportError "expand sending '$a' gave '$e' expected '$o'" } set e [RatAlias expand sending $display $r] set o [lindex $case 3] if {$e != $o} { ReportError "expand sending (via display) '$a' gave '$e' expected '$o'" } set e [RatAlias expand pgp $a $r] set o [lindex $case 4] if {$e != $o} { ReportError "expand pgp '$a' gave '$e' expected '$o'" } set e [RatAlias expand pgp $display $r] set o [lindex $case 4] if {$e != $o} { ReportError "expand pgp (via display) '$a' gave '$e' expected '$o'" } set e [RatAlias expand pgpactions $a $r] set o [lindex $case 5] if {$e != $o} { ReportError "expand pgpactions '$a' gave '$e' expected '$o'" } set e [RatAlias expand pgpactions $display $r] set o [lindex $case 5] if {$e != $o} { ReportError "expand pgpactions (via display) '$a' gave '$e' expected '$o'" } } proc test_alias::test_alias {} { global option env # Setup role and aliases to test with set option(tr42,from) role@from.adr set option(tr43,from) "Full Name <$env(USER)@domain.org>" RatAlias add Personal key_simple {Key Name} al_simple@exp.org {} {} RatAlias add Personal key_nogecos {} al_nogecos@exp.org {} {} RatAlias add Personal key_nofull \ {Full Name} {al_nofull@exp.org} {} {} {nofullname} RatAlias add Personal key_list {List} {e1, e2} {} {} RatAlias add Personal key_req {Recurs} {al_req@exp.org, key_nogecos} {} {} RatAlias add Personal key_pgp1 \ {PGP1} al_pgp1@exp.org {} {p1 pgp1} {pgp_sign} RatAlias add Personal key_pgp2 \ {PGP2} {al_pgp1@exp.org, al_pgp2} {} {p2 pg2} {pgp_encrypt} RatAlias add Personal key_loop {Key Loop} key_loop@from.adr {} {} RatAlias add Personal primary {First Level} second@from.adr {} {} RatAlias add Personal second {Second Level} third@from.adr {} {} # List of tests. Each test consists of: # Role # Input string # Result of expand level 1 # Result of expand level 2 # Result of pgp expansion # Expected pgp actions set tests \ [list \ [list tr43 \ "" \ "" \ "" \ "" \ "0 0" ] \ [list tr43 \ "foo (foo,,,)" \ "\"foo,,,\" " \ "\"foo,,,\" " \ "foo@domain.org" \ "0 0" ] \ [list tr43 \ "Full Name <$env(USER)@domain.org>" \ "Full Name <$env(USER)@domain.org>" \ "Full Name <$env(USER)@domain.org>" \ "$env(USER)@domain.org" \ "0 0" ] \ [list tr42 \ "should, not, change" \ "should, not, change" \ "should@from.adr, not@from.adr, change@from.adr" \ "should@from.adr not@from.adr change@from.adr" \ "0 0" ] \ [list tr42 \ "key_simple" \ "Key Name " \ "Key Name " \ "al_simple@exp.org" \ "0 0" ] \ [list tr42 \ "key_loop" \ "Key Loop " \ "Key Loop " \ "key_loop@from.adr" \ "0 0" ] \ [list tr42 \ "$env(USER)" \ "$env(GECOS) <$env(USER)>" \ "$env(GECOS) <$env(USER)@from.adr>" \ "$env(USER)@from.adr" \ "0 0" ] \ [list tr42 \ "foo, key_simple ,bar" \ "foo, Key Name , bar" \ "foo@from.adr, Key Name , bar@from.adr" \ "foo@from.adr al_simple@exp.org bar@from.adr" \ "0 0" ] \ [list tr42 \ "(foo)(foo2) (foo3) key_simple (bar)(bar2) (bar3)" \ "Key Name " \ "Key Name " \ "al_simple@exp.org" \ "0 0" ] \ [list tr42 \ "key_nogecos" \ "key_nogecos" \ "al_nogecos@exp.org" \ "al_nogecos@exp.org" \ "0 0" ] \ [list tr42 \ "key_nofull" \ "Full Name " \ "al_nofull@exp.org" \ "al_nofull@exp.org" \ "0 0" ] \ [list tr42 \ "key_list" \ "List " \ "e1, e2" \ "e1@from.adr e2@from.adr" \ "0 0" ] \ [list tr42 \ "key_req" \ "Recurs " \ "al_req@exp.org, al_nogecos@exp.org" \ "al_req@exp.org al_nogecos@exp.org" \ "0 0" ] \ [list tr42 \ "m" \ "m" \ "m@from.adr" \ "m@from.adr" \ "0 0" ] \ [list tr42 \ "a, b, c" \ "a, b, c" \ "a@from.adr, b@from.adr, c@from.adr" \ "a@from.adr b@from.adr c@from.adr" \ "0 0" ] \ [list tr42 \ "key_pgp1" \ "PGP1 " \ "PGP1 " \ "{p1 pgp1}" \ "1 0" ] \ [list tr42 \ "al_pgp1@exp.org" \ "al_pgp1@exp.org" \ "al_pgp1@exp.org" \ "{p1 pgp1}" \ "1 0" ] \ [list tr42 \ "key_pgp2" \ "PGP2 " \ "al_pgp1@exp.org, al_pgp2" \ "{p2 pg2}" \ "1 1" ] \ [list tr42 \ "al_pgp2@exp.org" \ "al_pgp2@exp.org" \ "al_pgp2@exp.org" \ "al_pgp2@exp.org" \ "0 0" ] \ [list tr42 \ "primary" \ "First Level " \ "First Level " \ "second@from.adr" \ "0 0" ] \ ] # Do tests foreach case $tests { do_test $case } # Test alias which collides with a local user RatAlias add Personal $env(USER) {Collision} {coll@coll.org} {} {} # List of tests. Each test consists of: # Role # Input string # Result of expand level 1 # Result of expand level 2 # Result of expand level 3 set tests [list \ [list tr42 \ "$env(USER)" \ "Collision <$env(USER)>" \ "Collision " \ "coll@coll.org" \ "0 0" ]] # Do tests foreach case $tests { do_test $case } } # Test the RatSplitAdr function proc test_alias::test_split {} { global verbose option env # List of tests # Alist to split # Expected output set tests { { "foo" "foo"} { "foo,bar" "foo bar"} { " foo , bar " "foo bar"} { "foo,,bar" "foo {} bar"} { " foo , , bar " "foo {} bar"} } foreach case $tests { set i [lindex $case 0] set e [lindex $case 1] StartTest "Splitting '$i'" set r [RatSplitAdr $i] if {"$r" != "$e"} { ReportError "Gave '$r' expected '$e'" } } } test_alias::test_alias test_alias::test_split tkrat_2.2cvs20100105-dfsg.orig/test/test_current.tcl000066400000000000000000000037201137544547100222120ustar00rootroot00000000000000puts "$HEAD Test current" namespace eval test_current { } proc test_current::test_current {} { global verbose option env # Setup option(domain) and find out data set old_domain $option(domain) set option(domain) default.domain set defaultHost [info hostname] set defaultMailbox $env(USER) set defaultPersonal $env(GECOS) if {1 == [llength [split $defaultHost .]]} { set expHost $defaultHost.$option(domain) } else { set expHost $defaultHost } # Test definition # role_name from uqa_domain smtp_helo # host mailbox personal ehlo set tests [list \ [list tr0 "from@my.addr (fr om)" "" "" \ my.addr from "fr om" my.addr] \ [list tr1 "" "" ""\ $expHost $defaultMailbox $defaultPersonal $expHost] \ [list tr2 "from@my.addr" "" "" \ my.addr from $defaultPersonal my.addr] \ [list tr3 "from@my.addr (Rkan)" "" ""\ my.addr from "=?iso-8859-1?Q?R=E4kan?=" my.addr] \ [list tr4 "from@my.addr (fr om)" "uqa.domain" "helo.host" \ uqa.domain from "fr om" helo.host] \ ] # Init roles foreach case $tests { set r [lindex $case 0] set option($r,from) [lindex $case 1] set option($r,uqa_domain) [lindex $case 2] set option($r,smtp_helo) [lindex $case 3] } # Do tests foreach case $tests { set r [lindex $case 0] StartTest "Current with role $r" set v [RatGetCurrent host $r] if {"[lindex $case 4]" != $v} { ReportError "Host was '$v' expected '[lindex $case 4]'" } set v [RatGetCurrent mailbox $r] if {"[lindex $case 5]" != $v} { ReportError "Mailbox was '$v' expected '[lindex $case 5]'" } set v [RatGetCurrent personal $r] if {"[lindex $case 6]" != $v} { ReportError "Personal was '$v' expected '[lindex $case 6]'" } set v [RatGetCurrent smtp_helo $r] if {"[lindex $case 7]" != $v} { ReportError "Smtp_helo was '$v' expected '[lindex $case 7]'" } } set option(domain) $old_domain } test_current::test_current tkrat_2.2cvs20100105-dfsg.orig/test/test_dbase.tcl000066400000000000000000000103031137544547100216010ustar00rootroot00000000000000puts "$HEAD Test dbase operations" namespace eval test_dbase { variable subjects } proc test_dbase::verify_search {search_exp expected} { variable subjects set fh [RatOpenFolder [list Dbase dbase {} remove +1 $search_exp]] set real [$fh list %s] set exp {} foreach s $expected { lappend exp [lindex $subjects $s] } if {$exp != $real} { puts [format "%-20s %-20s" "Expected result" "Actual result"] for {set i 0} {$i < [llength $exp] || $i < [llength $real]} {incr i} { puts [format "%-20s %-20s" [lindex $exp $i] [lindex $real $i]] } ReportError "Dbase search result mismatch" } $fh close } proc test_dbase::verify_dbinfo {dbinfo exp src} { global verbose if {$exp != $dbinfo} { if {$verbose} { puts " Got: $dbinfo" puts "Expected: $exp" } ReportError "$src dbinfo mismatch" } } proc test_dbase::test_dbase {} { global option dir hdr variable subjects set option(dbase_dir) $dir/db file delete -force $option(dbase_dir) StartTest "Creating empty database" verify_search {or} {} StartTest "Inserting messages" # Prepare test messages set fn $dir/folder.[pid] set def [list Test file {} $fn] set fh [open $fn w] puts $fh $hdr for {set i 1} {$i < 21} {incr i} { upvar \#0 msg$i m puts $fh $m } close $fh set f1 [RatOpenFolder $def] set file_subjects [$f1 list %s] set dates [$f1 list %D] set exp {} set start_time [clock seconds] for {set i 0} {$i < 20} {incr i} { set msg [$f1 get $i] lappend subjects [lindex $file_subjects $i] set key [format "key%02d lock%02d" $i $i] RatInsert $msg $key +1 remove lappend exp $i verify_search {or} $exp verify_search {and} {} } $f1 close StartTest "Single keyword search" for {set i 0} {$i < 20} {incr i} { set key [format "key%02d" $i] verify_search [list or keywords $key] $i verify_search [list and keywords $key] $i } StartTest "Multi keyword search (or)" verify_search [list or keywords [list key01 key02]] {1 2} verify_search [list or keywords [list key03 key02]] {2 3} StartTest "Multi keyword search (and)" verify_search [list and keywords [list key03 lock03]] {3} StartTest "Time interval search" # We have to add an hour here because the $dates is in GMT set d2 [expr [lindex $dates 2]+3600] set d3 [expr [lindex $dates 3]+3600] verify_search [list int $d2 $d2 or] {2} verify_search [list int $d2 $d3 or] {2 3} verify_search [list int $d2 $d2 and] {} verify_search [list int $d2 $d3 and] {} StartTest "Time interval and keyword search" verify_search [list int $d2 $d3 and keywords key02] {2} verify_search [list int $d2 $d3 or keywords [list key02 key01]] {2} StartTest "Folder dbinfo" set search_exp [list or keywords [list key01 key02]] set fh [RatOpenFolder [list Dbase dbase {} remove +1 $search_exp]] verify_dbinfo [$fh dbinfo_get] "{key01 key02} 1 remove" "Folder" StartTest "Message dbinfo" set msg [$fh get 0] set expire [expr $start_time+24*60*60] verify_dbinfo [$msg dbinfo_get] "{key01 lock01} $expire remove" "Message" StartTest "Update expiration" set new_expire [expr $start_time+2*24*60*60] $fh dbinfo_set 0 "new_key" $new_expire backup StartTest "Folder dbinfo again" set search_exp [list or keywords [list key01 key02]] set fh [RatOpenFolder [list Dbase dbase {} remove +1 $search_exp]] verify_dbinfo [$fh dbinfo_get] "{key01 key02} 1 remove" "Folder" StartTest "Message dbinfo again" set msg [$fh get 0] verify_dbinfo [$msg dbinfo_get] "new_key $new_expire backup" "Message" StartTest "Update expiration for list" set new_expire [expr $start_time+2*24*60*60] $fh dbinfo_set {0 1} "newer_key" $new_expire backup StartTest "Message 0 dbinfo" set msg [$fh get 0] verify_dbinfo [$msg dbinfo_get] "newer_key $new_expire backup" "Message" StartTest "Message 1 dbinfo" set msg [$fh get 1] verify_dbinfo [$msg dbinfo_get] "newer_key $new_expire backup" "Message" } test_dbase::test_dbase tkrat_2.2cvs20100105-dfsg.orig/test/test_delattachment.tcl000066400000000000000000000066201137544547100233470ustar00rootroot00000000000000puts "$HEAD Test delete attachment" namespace eval test_delattachment { } # Create messages proc test_delattachment::init {} { global t dir variable base_msg "Date: Sat, 16 Jul 2005 19:14:00 +0100 (MET) From: Martin Forssen Subject: test with attachments To: Martin Forssen Message-ID: <42@tkrat.org> MIME-Version: 1.0 Content-Type: MULTIPART/MIXED; BOUNDARY=BD --BD @ATTACHMENT_0@ --BD @ATTACHMENT_1@ --BD @ATTACHMENT_2@ --BD-- " set deleted "Content-Type: TEXT/PLAIN; CHARSET=us-ascii $t(deleted_attachment)" set a0 "Content-Type: TEXT/PLAIN; CHARSET=us-ascii Attachment 0, to complicate matters BD" set a1_full "Content-Type: MULTIPART/MIXED; BOUNDARY=2BD --2BD Content-Type: TEXT/PLAIN; CHARSET=us-ascii Attachment 1_0 --2BD Content-Type: TEXT/PLAIN; CHARSET=us-ascii Attachment 1_1 --2BD-- " set a1_top "Content-Type: MULTIPART/MIXED; BOUNDARY=2BD --2BD Content-Type: TEXT/PLAIN; CHARSET=us-ascii Attachment 1_0 --2BD $deleted --2BD-- " set a1_bottom "Content-Type: MULTIPART/MIXED; BOUNDARY=2BD --2BD $deleted --2BD Content-Type: TEXT/PLAIN; CHARSET=us-ascii Attachment 1_1 --2BD-- " set a2 "Content-Type: TEXT/PLAIN; CHARSET=us-ascii Attachment 2" variable orig_msg $base_msg regsub @ATTACHMENT_0@ $orig_msg $a0 orig_msg regsub @ATTACHMENT_1@ $orig_msg $a1_full orig_msg regsub @ATTACHMENT_2@ $orig_msg $a2 orig_msg variable tests [list \ [list "No deletions" {} $a0 $a1_full $a2] \ [list "Last simple" {2} $a0 $a1_full $deleted] \ [list "two simple" {0 2} $deleted $a1_full $deleted] \ [list "two simple2" {2 0} $deleted $a1_full $deleted] \ [list "Entire multipart" {1} $a0 $deleted $a2] \ [list "First subpart" {{1 0}} $a0 $a1_bottom $a2] \ [list "Second subpart" {{1 1}} $a0 $a1_top $a2] \ [list "Second sub and last" {{1 1} 2} $a0 $a1_top $deleted] \ [list "Second sub and last2" {2 {1 1}} $a0 $a1_top $deleted]] variable fn1 $dir/folder.[pid]-1 variable def1 [list Test1 file {} $fn1] } # Run a test proc test_delattachment::run_test {msg test} { global verbose hdr variable base_msg StartTest [lindex $test 0] # Initialize expected message regsub @ATTACHMENT_0@ $base_msg [lindex $test 2] expected regsub @ATTACHMENT_1@ $expected [lindex $test 3] expected regsub @ATTACHMENT_2@ $expected [lindex $test 4] expected regsub -all "\n" $expected "\r\n" expected # Run test if {[catch {$msg delete_attachments [lindex $test 1]} nmsg]} { ReportError "Command failed: $nmsg" return } # Verify result set result [$nmsg rawText] if {[string compare $expected $result]} { if {$verbose} { puts "Expected:" puts $expected puts "Result:" puts $result } ReportError "New messages differs from expected" } } # Run all tests proc test_delattachment::test_delattachment {} { global hdr variable tests variable orig_msg variable fn1 variable def1 init # Create base message set fh [open $fn1 w] puts $fh $hdr puts $fh "From maf@tkrat.org Tue Sep 5 18:02:22 2000 +0100" puts $fh $orig_msg close $fh set f [RatOpenFolder $def1] set msg [$f get 0] foreach test $tests { run_test $msg $test } $f close file delete $fn1 } test_delattachment::test_delattachment tkrat_2.2cvs20100105-dfsg.orig/test/test_disoffline.tcl000066400000000000000000000132351137544547100226540ustar00rootroot00000000000000puts "$HEAD Test disconnected folders in offline mode" namespace eval test_disoffline { global start_uid variable uidmap {} variable uid_lastlocal 0 variable uid_lastremote [expr $start_uid-1] } proc test_disoffline::verify_map {mf map} { global tmp foreach e $map { set expected($e) 1 } if {![catch "open $mf r" f]} { set f [open $mf r] file copy -force $mf $tmp/map while {-1 != [gets $f line]} { if {[catch {unset expected($line)}]} { close $f return "Did not expect [list $line]" } } close $f } if {0 != [array size expected]} { return "Did not find [list [array names expected]]" } return "" } proc test_disoffline::dis_verify {f map name {diff 0}} { variable uidmap set expected_map $uidmap set num [expr {[llength $uidmap]+$diff}] set i [lindex [$f info] 1] if {$num != $i} { ReportError "$name: found $i messages expected $num" return } set err [verify_map $map $expected_map] if {"" != $err} { ReportError "$name: map verify failed\n$uidmap\n$err" } } proc test_disoffline::add_to_uidmap {} { variable uidmap variable uid_lastlocal variable uid_lastremote lappend uidmap [list [incr uid_lastremote] [incr uid_lastlocal]] } proc test_disoffline::add_mixed_to_uidmap {} { variable uidmap variable uid_lastlocal variable uid_lastremote lappend uidmap [list [expr {$uid_lastremote+2}] [expr {$uid_lastlocal+1}]] lappend uidmap [list [expr {$uid_lastremote+1}] [expr {$uid_lastlocal+2}]] incr uid_lastlocal +2 incr uid_lastremote +2 } proc test_disoffline::remove_from_uidmap {index} { variable uidmap set uidmap [lreplace $uidmap $index $index] } proc test_disoffline::test_disoffline {} { global option dir hdr mailServer imap_def dis_def imap_map \ msg1 msg2 msg3 msg4 msg5 msg6 msg7 msg8 msg9 msg10 \ msg11 msg12 msg13 msg14 msg15 msg16 msg17 msg18 msg19 # Setup InitTestmsgs RatLibSetOnlineMode 0 set tmpfn [pwd]/folder.[pid]-tmp set tmpdef [list Test file {} $tmpfn] init_imap_folder $imap_def insert_imap $imap_def $msg1 StartTest "opening" set f [RatOpenFolder $dis_def] dis_verify $f $imap_map "Initial" StartTest "update after open" $f update update dis_verify $f $imap_map "After first update" StartTest "update after netsync" $f netsync $f update update add_to_uidmap dis_verify $f $imap_map "After netsync" StartTest "new mail arrival" insert_imap $imap_def $msg2 $f update update dis_verify $f $imap_map "Before netsync" add_to_uidmap $f netsync $f update sync dis_verify $f $imap_map "After 1 new message" StartTest "multiple new messages" insert_imap $imap_def $msg3 $msg4 add_to_uidmap add_to_uidmap $f netsync $f update update dis_verify $f $imap_map "After 2 new messages" StartTest "deleting message" $f setFlag 1 deleted 1 $f update sync $f netsync remove_from_uidmap 1 dis_verify $f $imap_map "After deleting" StartTest "new message and one deleted" $f setFlag 1 deleted 1 remove_from_uidmap 1 insert_imap $imap_def $msg5 $f netsync $f update sync add_to_uidmap dis_verify $f $imap_map "After new & deleted" StartTest "inserting one message" set fh [open $tmpfn w] puts $fh $hdr puts $fh $msg6 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" set m [$f2 get 0] $f insert $m $f2 close file delete $tmpfn $f netsync $f update sync add_to_uidmap dis_verify $f $imap_map "After inserting" StartTest "inserting one message two times (different)" set fh [open $tmpfn w] puts $fh $msg7 puts $fh $msg8 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] $f insert [$f2 get 1] $f2 close add_to_uidmap add_to_uidmap file delete $tmpfn $f netsync $f update sync dis_verify $f $imap_map "After inserting one two times" StartTest "inserting two messages" set fh [open $tmpfn w] puts $fh $msg9 puts $fh $msg10 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] [$f2 get 1] $f2 close add_to_uidmap add_to_uidmap file delete $tmpfn $f netsync $f update sync dis_verify $f $imap_map "After inserting two" StartTest "deleting inserted directly" set n [lindex [$f info] 1] set fh [open $tmpfn w] puts $fh $msg11 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] $f2 close file delete $tmpfn $f setFlag $n deleted 1 $f netsync $f update sync add_to_uidmap remove_from_uidmap $n dis_verify $f $imap_map "After immediately deleted inserted" StartTest "flagging" set option(cache_conn) 0 $f netsync set f2 [RatOpenFolder $imap_def] $f2 setFlag 0 flagged 1 if {0 != [$f getFlag 0 flagged]} { $f2 close ReportError "Flag set before it was expected to be" } else { $f2 close $f netsync dis_verify $f $imap_map "After setting flag" if {1 != [$f getFlag 0 flagged]} { ReportError "Flag not set after sync" } } set option(cache_conn) 1 StartTest "resetting folder" set num [lindex [$f info] 1] for {set i 0} {$i < $num} {incr i} { $f setFlag $i deleted 1 remove_from_uidmap 0 } insert_imap $imap_def $msg12 add_to_uidmap $f netsync $f update sync dis_verify $f $imap_map "After reset" # Cleanup $f close cleanup_imap_folder $imap_def file delete -force $dir/disconnected # Restore environment RatLibSetOnlineMode 0 } test_disoffline::test_disoffline tkrat_2.2cvs20100105-dfsg.orig/test/test_disonline.tcl000066400000000000000000000215761137544547100225250ustar00rootroot00000000000000puts "$HEAD Test disconnected folders in online mode" namespace eval test_disonline { global start_uid variable uidmap {} variable uid_lastlocal 0 variable uid_lastremote [expr $start_uid-1] } proc test_disonline::verify_map {mf map} { global tmp foreach e $map { set expected($e) 1 } if {[catch {open $mf r} f]} { return 0 } file copy -force $mf $tmp/map while {-1 != [gets $f line]} { if {[catch {unset expected($line)}]} { close $f return 0 } } close $f if {0 != [array size expected]} { return 0 } return 1 } proc test_disonline::dis_verify {f map name {diff 0}} { variable uidmap set expected_map $uidmap set num [expr {[llength $uidmap]+$diff}] set i [lindex [$f info] 1] if {$num != $i} { ReportError "$name: Got $i expected $num" } if {![verify_map $map $expected_map]} { ReportError "$name: map verify failed" } } proc test_disonline::add_to_uidmap {} { variable uidmap variable uid_lastlocal variable uid_lastremote lappend uidmap [list [incr uid_lastremote] [incr uid_lastlocal]] } proc test_disonline::add_mixed_to_uidmap {} { variable uidmap variable uid_lastlocal variable uid_lastremote lappend uidmap [list [expr {$uid_lastremote+2}] [expr {$uid_lastlocal+1}]] lappend uidmap [list [expr {$uid_lastremote+1}] [expr {$uid_lastlocal+2}]] incr uid_lastlocal +2 incr uid_lastremote +2 } proc test_disonline::remove_from_uidmap {index} { variable uidmap set uidmap [lreplace $uidmap $index $index] } proc test_disonline::verify_flag {f index flag expected desc} { set real [$f getFlag $index $flag] if {$expected != $real} { ReportError "$desc: flag $flag was $real (expected $expected)" } } proc test_disonline::test_disonline {} { global dir errors mailServer dis_def imap_map imap_def \ msg1 msg2 msg3 msg4 msg5 msg6 msg7 msg8 msg9 msg10 \ msg11 msg12 msg13 msg14 msg15 msg16 msg17 msg18 msg19 msg20 # Setup InitTestmsgs RatLibSetOnlineMode 1 set tmpfn [pwd]/folder.[pid]-tmp set tmpdef [list Test file {} $tmpfn] init_imap_folder $imap_def insert_imap $imap_def $msg1 add_to_uidmap StartTest "opening" set f [RatOpenFolder $dis_def] dis_verify $f $imap_map "Initial" StartTest "update after open" $f update update dis_verify $f $imap_map "After first update" StartTest "new mail arrival" insert_imap $imap_def $msg2 add_to_uidmap $f update sync dis_verify $f $imap_map "After 1 new message" $f close StartTest "opening again" set f [RatOpenFolder $dis_def] dis_verify $f $imap_map "Second open" StartTest "new mail arrival again" insert_imap $imap_def $msg18 add_to_uidmap $f update sync dis_verify $f $imap_map "After another new message" StartTest "multiple new messages" insert_imap $imap_def $msg3 $msg4 add_to_uidmap add_to_uidmap after 300 $f update update dis_verify $f $imap_map "After 2 new messages" StartTest "deleting message" $f setFlag 1 deleted 1 $f update sync remove_from_uidmap 1 dis_verify $f $imap_map "After deleting" StartTest "new message and one deleted" $f setFlag 1 deleted 1 remove_from_uidmap 1 insert_imap $imap_def $msg5 $f update sync add_to_uidmap dis_verify $f $imap_map "After new & deleted" StartTest "inserting one message" set fh [open $tmpfn w] puts $fh $msg6 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" set m [$f2 get 0] $f insert $m $f2 close file delete $tmpfn after 300 $f update sync add_to_uidmap dis_verify $f $imap_map "After inserting" StartTest "inserting one message two times (different)" set fh [open $tmpfn w] puts $fh $msg7 puts $fh $msg8 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] $f insert [$f2 get 1] $f2 close add_to_uidmap add_to_uidmap file delete $tmpfn $f update sync dis_verify $f $imap_map "After inserting one two times" StartTest "inserting two messages" set fh [open $tmpfn w] puts $fh $msg9 puts $fh $msg10 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] [$f2 get 1] $f2 close add_to_uidmap add_to_uidmap file delete $tmpfn $f update sync dis_verify $f $imap_map "After inserting two" StartTest "inserting two messages sequentially" set fh [open $tmpfn w] puts $fh $msg9 puts $fh $msg10 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] $f insert [$f2 get 1] $f2 close add_to_uidmap add_to_uidmap file delete $tmpfn $f close set f [RatOpenFolder $dis_def] $f update sync dis_verify $f $imap_map "After inserting two sequentially" StartTest "deleting inserted directly" set n [lindex [$f info] 1] set fh [open $tmpfn w] puts $fh $msg11 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] $f2 close file delete $tmpfn $f setFlag $n deleted 1 $f update sync add_to_uidmap remove_from_uidmap $n dis_verify $f $imap_map "After immediately deleted inserted" StartTest "insert, delete and new messages" set fh [open $tmpfn w] puts $fh $msg12 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] add_to_uidmap $f2 close file delete $tmpfn $f setFlag 1 deleted 1 remove_from_uidmap 1 insert_imap $imap_def $msg13 add_to_uidmap $f update sync dis_verify $f $imap_map "After insert, deleted and new" StartTest "resetting folder" for {set i 0} {$i <9} {incr i} { $f setFlag $i deleted 1 remove_from_uidmap 0 } insert_imap $imap_def $msg14 add_to_uidmap $f update sync dis_verify $f $imap_map "After reset" StartTest "offline-->online transition" # Go offline RatLibSetOnlineMode 0 $f update sync dis_verify $f $imap_map "When offline" RatLibSetOnlineMode 1 dis_verify $f $imap_map "When online again" $f update sync dis_verify $f $imap_map "After sync" StartTest "offline-->online transition (with flag changes)" RatLibSetOnlineMode 0 $f setFlag 1 seen 0 $f setFlag 1 seen 1 $f setFlag 1 flagged 1 $f setFlag 1 flagged 0 dis_verify $f $imap_map "When offline" verify_flag $f 1 seen 1 "When offline" verify_flag $f 1 flagged 0 "When offline" # Go online RatLibSetOnlineMode 1 dis_verify $f $imap_map "When online" verify_flag $f 1 seen 1 "When online" verify_flag $f 1 flagged 0 "When online" $f update sync dis_verify $f $imap_map "After sync" verify_flag $f 1 seen 1 "After sync" verify_flag $f 1 flagged 0 "After sync" StartTest "offline-->online transition (with new remote messages)" RatLibSetOnlineMode 0 # Insert two new in remote insert_imap $imap_def $msg15 $msg16 $f update sync dis_verify $f $imap_map "When offline" # Go online add_to_uidmap add_to_uidmap RatLibSetOnlineMode 1 dis_verify $f $imap_map "When online" StartTest "offline-->online transition (with new local messages)" RatLibSetOnlineMode 0 # Insert one new in local set fh [open $tmpfn w] puts $fh $msg17 puts $fh $msg18 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] [$f2 get 1] $f2 close file delete $tmpfn dis_verify $f $imap_map "When offline" 2 add_to_uidmap add_to_uidmap # Go online RatLibSetOnlineMode 1 dis_verify $f $imap_map "When online" StartTest "offline-->online transition (with new messages in both)" RatLibSetOnlineMode 0 # Insert one new in local set fh [open $tmpfn w] puts $fh $msg19 close $fh set f2 [RatOpenFolder $tmpdef] $f2 list "%s" $f insert [$f2 get 0] $f2 close file delete $tmpfn # Insert one new in remote insert_imap $imap_def $msg20 $f update sync dis_verify $f $imap_map "When offline" 1 add_mixed_to_uidmap # Go online RatLibSetOnlineMode 1 dis_verify $f $imap_map "When online" StartTest "offline-->online transition (with local delete)" RatLibSetOnlineMode 0 # Set delete flag in lcoal # The seen flag manipulation here is to simulate what happens when one # reads a folder. And it did trigger a bug once. $f setFlag 1 seen 0 $f setFlag 1 deleted 1 $f setFlag 1 seen 1 dis_verify $f $imap_map "When offline" # Go online RatLibSetOnlineMode 1 dis_verify $f $imap_map "When online" $f update sync remove_from_uidmap 1 dis_verify $f $imap_map "After sync" # Cleanup $f close cleanup_imap_folder $imap_def file delete -force $dir/disconnected # Restore environment RatLibSetOnlineMode 0 } test_disonline::test_disonline tkrat_2.2cvs20100105-dfsg.orig/test/test_encoding.tcl000066400000000000000000000115521137544547100223200ustar00rootroot00000000000000puts "$HEAD Test encoding" namespace eval test_encoding { } proc test_encoding::test_encoding {} { test_encoding::test_qp_encoding test_encoding::test_header_encoding } proc test_encoding::test_qp_encoding {} { # # List of test cases # {text_to_encode charset expected_encoded_text} # set tests { {foo "" foo} {f=oo "" f=3Doo} {f=3Doo "" f=3D3Doo} {Rkan iso8859-1 R=E4kan} {Rkan utf-8 R=C3=A4kan} } foreach te $tests { set in [lindex $te 0] set charset [lindex $te 1] set out [lindex $te 2] StartTest "Encoding '$in' in '$charset'" if {[catch {RatEncodeQP $charset $in} result]} { ReportError "RatEncodeQP failed: $result" continue } if {$result != $out} { ReportError "RatEncodeQP encoded '$in' in '$charset' to '$result' expected '$out'" continue } if {[catch {RatDecodeQP $charset $out} result]} { ReportError "RatDecodeQP failed: $result" continue } if {$result != $in} { ReportError "RatDecodeQP encoded '$out' in '$charset' to '$result' expected '$in'" continue } } } proc test_encoding::test_header_encoding {} { global tcl_version set encodings {us-ascii iso-8859-1 iso-2022-jp} # # Check suggested encodings # set tests { {"us-ascii" "Ok in us-ascii"} {"iso-8859-1" "Ok in iso-8859-1 \u00a9"} {"iso-2022-jp" "in iso-2022-jp \u306f"} {"" "Not ok in any \u01a9"} } lappend tests [list "us-ascii" "Long string [string repeat { } 16385]"] set index 0 foreach te $tests { set data [lindex $te 1] StartTest "Encoding body\[[incr index]\] in [lindex $te 0]" set logdata [string range $data 0 20] if {[catch {RatCheckEncodings data $encodings} enc]} { ReportError "Failed to check encodings for {$logdata}: $enc" continue } if {[lindex $te 0] != $enc} { ReportError [concat "Suggested frong encoding for {$logdata}." \ "Expected [lindex $te 0] but got $enc"] } } # # Check header encodings # set tests { {0 "" ""} {0 "Test" "Test"} {0 "Te?st" "Te?st"} {65 "Break line" "Break line"} {66 "Break line" "Break\n line"} {0 "R\ue4kan lever?" "=?iso-8859-1?Q?R=E4kan_lever=3F?="} {0 "R\ue4ksm\uf6rg\ue5s" "=?iso-8859-1?Q?R=E4ksm=F6rg=E5s?="} {0 "\ue5\ue4\uf6" "=?iso-8859-1?Q?=E5=E4=F6?="} {8 "Fwd: From our Home and Family to your Home and Family . . . 0001a" "Fwd: From our Home and Family to your Home and Family . . . \n \n 0001a"} } # The way iso2022-jp is encoded changes between versions if {8.4 > $tcl_version} { set tests [concat $tests { {0 "\u306f" "=?iso-2022-jp?Q?=1B=28B=1B=24=40=24O?="} {33 "\u306f\u306f" "=?iso-2022-jp?Q?=1B=28B=1B=24=40=24O=24O?="} {34 "\u306f\u306f" "=?iso-2022-jp?Q?=1B=28B=1B=24=40=24O?=\n =?iso-2022-jp?Q?=1B=28B=1B=24=40=24O?="} {9 "(linuxppc-jp:11528) Performa5410 \u3067\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af" "(linuxppc-jp:11528) Performa5410\n =?iso-2022-jp?Q?=1B=28B=1B=24=40=24G=24N=25M=25C=25H=25o!=3C=25/?="} }] } elseif {8.4 == $tcl_version} { set tests [concat $tests { {0 "\u306f" "=?iso-2022-jp?Q?=1B$B$O=1B(B?="} {43 "\u306f\u306f" "=?iso-2022-jp?Q?=1B$B$O$O=1B(B?="} {44 "\u306f\u306f" "=?iso-2022-jp?Q?=1B$B$O=1B(B?=\n =?iso-2022-jp?Q?=1B$B$O=1B(B?="} {9 "(linuxppc-jp:11528) Performa5410 \u3067\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af" "(linuxppc-jp:11528) Performa5410\n =?iso-2022-jp?Q?=1B$B$G$N%M%C%H%o!<%/=1B(B?="} }] } else { set tests [concat $tests { {0 "\u306f" "=?iso-2022-jp?Q?=1B=24B=24O=1B=28B?="} {35 "\u306f\u306f" "=?iso-2022-jp?Q?=1B=24B=24O=24O=1B=28B?="} {36 "\u306f\u306f" "=?iso-2022-jp?Q?=1B=24B=24O=1B=28B?=\n =?iso-2022-jp?Q?=1B=24B=24O=1B=28B?="} {9 "(linuxppc-jp:11528) Performa5410 \u3067\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af" "(linuxppc-jp:11528) Performa5410\n =?iso-2022-jp?Q?=1B=24B=24G=24N=25M=25C=25H=25o!=3C=25/=1B=28B?="} }] } set index 0 foreach te $tests { StartTest "Encoding header\[[incr index]\] [lindex $te 1]" # Encode header if {[catch {RatTest encode_header $encodings \ [lindex $te 0] [lindex $te 1]} e]} { ReportError "Failed to encode header line: $e" continue } # Check encoded version if {$e != [lindex $te 2]} { ReportError [join [list "Incorrectly encoded header-line" \ " Got [list $e]" \ "Expected [list [lindex $te 2]]"] "\n"] } # Decode the encoded if {[catch {RatTest decode_header $e} d]} { ReportError "Failed to decode header line: $e" continue } # Check decoded version if {$d != [lindex $te 1]} { ReportError [join [list "Incorrectly decoded header-line" \ " Decoded [list $e]" \ " Got [list $d]" \ "Expected [list [lindex $te 1]]"] "\n"] } } } test_encoding::test_encoding tkrat_2.2cvs20100105-dfsg.orig/test/test_folderbasics.tcl000066400000000000000000000145211137544547100231710ustar00rootroot00000000000000puts "$HEAD Test basic folder operations" namespace eval test_folderbasics { } proc test_folderbasics::check_folder {type fn fh msglist} { global hdr imap_serv cyrus_dir set num [llength $msglist] foreach m $msglist { foreach l [split $m "\n"] { if {[regexp -nocase {subject:[ ]*(.+)$} $l unused s]} { set s1($s) "" set s2($s) "" break } } } # Apply checks to folder handle if {$num != [lindex [$fh info] 1]} { ReportError "Number of messages in tkrat folder is wrong [lindex [$fh info] 1] != $num" } foreach s [$fh list %s] { if {![info exists s1($s)]} { ReportError "Subject '$s' not found in tkrat folder" } else { unset s1($s) } } if {[array size s1]} { ReportError "Subjects [array names s1] not found in tkrat folder" } # Apply checks to underlying file if {"imap" == $type && "cyrus" == $imap_serv} { set f [open "|cyrcat $cyrus_dir $fn"] } else { set f [open $fn r] seek $f [string length $hdr] } set found 0 while {-1 != [gets $f line]} { if {[regexp -nocase {subject:[ ]*(.+)$} $line unused s]} { incr found if {![info exists s2($s)]} { ReportError "Subject '$s' not found in underlying file" } else { unset s2($s) } } } close $f if {$num != $found} { ReportError "Number of messages in underlying folder is wrong $found != $num" } if {[array size s2]} { ReportError "Subjects [array names s2] not found in underlying file" } } proc test_folderbasics::type_tests {type fn1 def1 fn2 def2} { global option dir hdr imap_serv \ msg1 msg2 msg3 msg4 msg5 msg6 msg7 msg8 msg9 msg10 \ msg11 msg12 msg13 msg14 msg15 msg16 msg17 msg18 msg19 InitTestmsgs StartTest "$type: Opening folder with two messages" if {"imap" == [lindex $def1 1]} { init_imap_folder $def1 insert_imap $def1 $msg1 $msg2 } else { set fh [open $fn1 w] puts $fh $hdr puts $fh $msg1 puts $fh $msg2 close $fh } set f1 [RatOpenFolder $def1] check_folder [lindex $def1 1] $fn1 $f1 [list $msg1 $msg2] StartTest "$type: New message arrival" if {"imap" == [lindex $def1 1]} { insert_imap $def1 $msg3 } else { set fh [open $fn1 a] puts $fh $msg3 close $fh } $f1 update sync check_folder [lindex $def1 1] $fn1 $f1 [list $msg1 $msg2 $msg3] StartTest "$type: Message deletion" $f1 setFlag 1 deleted 1 $f1 update sync check_folder [lindex $def1 1] $fn1 $f1 [list $msg1 $msg3] StartTest "$type: Deleting multiple messages" $f1 setFlag 0 deleted 1 $f1 setFlag 1 deleted 1 $f1 update sync check_folder [lindex $def1 1] $fn1 $f1 {} StartTest "$type: Multiple new message arrival" if {"imap" == [lindex $def1 1]} { insert_imap $def1 $msg4 $msg5 } else { set fh [open $fn1 a] puts $fh $msg4 puts $fh $msg5 close $fh } $f1 update sync check_folder [lindex $def1 1] $fn1 $f1 [list $msg4 $msg5] StartTest "$type: New message deletion and simultaneously new message" if {"imap" == [lindex $def1 1]} { insert_imap $def1 $msg6 } else { set fh [open $fn1 a] puts $fh $msg6 close $fh } $f1 setFlag 1 deleted 1 $f1 update sync check_folder [lindex $def1 1] $fn1 $f1 [list $msg4 $msg6] StartTest "$type: Inserting message" if {"imap" == [lindex $def2 1]} { init_imap_folder $def2 insert_imap $def2 $msg7 } else { set fh [open $fn2 w] puts $fh $hdr puts $fh $msg7 close $fh } set f2 [RatOpenFolder $def2] $f2 list "%s" set m [$f2 get 0] $f1 insert $m $f2 close check_folder [lindex $def1 1] $fn1 $f1 [list $msg4 $msg6 $msg7] StartTest "$type: Inserting multiple messages" if {"imap" == [lindex $def2 1]} { insert_imap $def2 $msg8 insert_imap $def2 $msg9 } else { set fh [open $fn2 a] puts $fh $msg8 puts $fh $msg9 close $fh } set f2 [RatOpenFolder $def2] $f2 list "%s" set m1 [$f2 get 1] set m2 [$f2 get 2] $f1 insert $m1 $m2 $f2 close check_folder [lindex $def1 1] $fn1 $f1 [list $msg4 $msg6 $msg7 $msg8 $msg9] StartTest "$type: Copying to another (not open) folder" $f1 list "%s" set m [$f1 get 0] $m copy $def2 set f2 [RatOpenFolder $def2] check_folder [lindex $def2 1] $fn2 $f2 [list $msg7 $msg8 $msg9 $msg4] StartTest "$type: Copying to another open folder" set m [$f1 get 1] $m copy $def2 $f2 update update check_folder [lindex $def2 1] $fn2 $f2 [list $msg7 $msg8 $msg9 $msg4 $msg6] # Cleanup set old_cache $option(cache_conn) set option(cache_conn) 0 $f1 close $f2 close set option(cache_conn) $old_cache if {"imap" == [lindex $def1 1]} { cleanup_imap_folder $def1 cleanup_imap_folder $def2 } } proc test_folderbasics::test_folderbasics {} { global option dir hdr mailServer imap_def1 imap_def2 \ imap_fn1 imap_fn2 \ msg1 msg2 msg3 msg4 msg5 msg6 msg7 msg8 msg9 msg10 \ msg11 msg12 msg13 msg14 msg15 msg16 msg17 msg18 msg19 set fn1 $dir/folder.[pid]-1 set fn2 $dir/folder.[pid]-2 # Test file-folders set def1 [list Test1 file {} $fn1] set def2 [list Test2 file {} $fn2] type_tests File $fn1 $def1 $fn2 $def2 # Test imap-folders with connection caching set old_cache $option(cache_conn) set otion(cache_conn) 1 type_tests IMAP-cache $imap_fn1 $imap_def1 $imap_fn2 $imap_def2 # Test imap-folders without connection caching set otion(cache_conn) 0 type_tests IMAP-nocache $imap_fn1 $imap_def1 $imap_fn2 $imap_def2 set option(cache_conn) $old_cache # Prepare coming test set def1 [list Test1 file {} $fn1] set fh [open $fn1 w] puts $fh $hdr puts $fh $msg1 puts $fh $msg2 close $fh init_imap_folder $imap_def2 insert_imap $imap_def2 $msg3 StartTest "Copying between a file and an (not open) imap folder" set f1 [RatOpenFolder $def1] set m [$f1 get 0] $m copy $imap_def2 set f2 [RatOpenFolder $imap_def2] check_folder imap $imap_fn2 $f2 [list $msg3 $msg1] StartTest "Copying between a file and an open imap folder" set m [$f1 get 1] $m copy $imap_def2 check_folder imap $imap_fn2 $f2 [list $msg3 $msg1 $msg2] # Cleanup set old_cache $option(cache_conn) set option(cache_conn) 0 $f1 close $f2 close set option(cache_conn) $old_cache file delete $fn1 $fn2 cleanup_imap_folder $imap_def2 } test_folderbasics::test_folderbasics tkrat_2.2cvs20100105-dfsg.orig/test/test_import.tcl000066400000000000000000000153561137544547100220520ustar00rootroot00000000000000puts "$HEAD Test import" namespace eval test_import { # List of directories to remove when we are done variable cleanup {} # Array contaning desired result variable result } proc test_import::check_result {path idr idv} { global vFolderDef variable result if {"" == $path} { set type "import" set contents 5 } else { set type "struct" set contents 3 } if {$type != [lindex $vFolderDef($idv) 1]} { ReportError [join [list "vFolderDef($idv) is not an $type entry" \ "[list $vFolderDef($idv)]"] "\n"] return 1 } foreach id [lindex $result($idr) $contents] { set r([lindex $result($id) 1],[lindex $result($id) 0]) $result($id) set rid([lindex $result($id) 1],[lindex $result($id) 0]) $id } foreach id [lindex $vFolderDef($idv) $contents] { set v([lindex $vFolderDef($id) 1],[lindex $vFolderDef($id) 0]) \ $vFolderDef($id) set vid([lindex $vFolderDef($id) 1],[lindex $vFolderDef($id) 0]) $id } if {[array size r] != [array size v]} { ReportError "Length of contents in struct differs ($path)" return 1 } foreach n [array names r] { if {![info exists v($n)]} { ReportError "Did not find element $path/$n in vFolderDef" return 1 } if {"[lindex $r($n) 2]" != "[lindex $v($n) 2]"} { ReportError [joid [list "Flags of $path/$n differs" \ "[lindex $r($n) 2] != [lindex $v($n) 2]"] \ "\n"] return 1 } switch [lindex $r($n) 1] { file { if {"[lindex $r($n) 3]" != "[lindex $v($n) 3]"} { ReportError \ [join [list "Filename of $path/$n differs" \ "[lindex $r($n) 3] != [lindex $v($n) 3]"] \ "\n"] return 1 } } struct { if {1 == [check_result $path/$n $rid($n) $vid($n)]} { return 1 } } } } return 0 } proc test_import::test_import {} { global option vFolderDef mailServer env imap_serv variable result # Copy old values set vfd_backup [array get vFolderDef] # Create hierarchy # a # b # c+ # a # b+ # d # e+ # f # g set base [pwd]/import file mkdir $base exec touch $base/a file mkdir $base/c exec touch $base/c/a file mkdir $base/c/b exec touch $base/c/b/d file mkdir $base/e exec touch $base/e/f exec touch $base/g exec touch $base/b # Setup result original set result(0) {{} struct {} {1}} set result(1) [list test import {subscribed 0} \ [list NAME1 file {} $base/import] * {2 3 4 5 6}] set result(2) [list a file {} $base/a] set result(3) [list b file {} $base/b] set result(4) [list c struct {} {7 8}] set result(5) [list e struct {} {9}] set result(6) [list g file {} $base/g] set result(7) [list a file {} $base/c/a] set result(8) [list b struct {} {10}] set result(9) [list f file {} $base/e/f] set result(10) [list d file {} $base/c/b/d] StartTest "Simple file import" catch {unset vFolderDef} set vFolderDef(0) {{} struct {} {1}} set vFolderDef(1) [list testroot import {} \ [list NAME1 file {} $base] * {}] RatImport 1 check_result "" 1 1 StartTest "Re-import" RatImport 1 check_result "" 1 1 StartTest "Re-import with addition at top level" exec touch $base/h set result(1) [list test import {subscribed 0} \ [list NAME1 file {} $base/import] * {2 3 4 5 6 11}] set result(11) [list h file {} $base/h] RatImport 1 check_result "" 1 1 StartTest "Re-import with deletion at top level" file delete $base/h set result(1) [list test import {subscribed 0} \ [list NAME1 file {} $base/import] * {2 3 4 5 6}] unset result(11) RatImport 1 check_result "" 1 1 StartTest "Re-import with addition down below" exec touch $base/c/b/h set result(8) [list b struct {} {10 11}] set result(11) [list h file {} $base/c/b/h] RatImport 1 check_result "" 1 1 StartTest "Re-import with deletion down below" file delete $base/c/b/h set result(8) [list b struct {} {10}] unset result(11) RatImport 1 check_result "" 1 1 StartTest "With a flag-change" set result(3) [list b file {flag 1} $base/b] foreach id [array names vFolderDef] { if {"b" == [lindex $vFolderDef($id) 0]} { set vFolderDef($id) [lreplace $vFolderDef($id) 2 2 {flag 1}] break } } RatImport 1 check_result "" 1 1 StartTest "import via IMAP" if {$imap_serv == "cyrus"} { set result(1) [lreplace $result(1) 3 3 \ [list NAME! imap {} localhost user.$env(USER).import]] # Setup result original set ib user.$env(USER).import set result(0) {{} struct {} {1}} set result(1) [list test import {subscribed 0} \ [list NAME1 imap {} localhost ${ib}] * \ {2 102 3 103 4 5 6 106}] set result(2) [list a imap {} localhost ${ib}.a] set result(3) [list b imap {} localhost ${ib}.b] set result(4) [list c struct {} {7 107 8}] set result(5) [list e struct {} {9 109}] set result(6) [list g imap {} localhost ${ib}.g] set result(7) [list a imap {} localhost ${ib}.c.a] set result(8) [list b struct {} {10 110}] set result(9) [list f imap {} localhost ${ib}.e.f] set result(10) [list d imap {} localhost ${ib}.c.b.d] set result(102) [list a struct {} {}] set result(103) [list b struct {} {}] set result(106) [list g struct {} {}] set result(107) [list a struct {} {}] set result(109) [list f struct {} {}] set result(110) [list d struct {} {}] foreach id [array names result] { if {"imap" == [lindex $result($id) 1]} { init_imap_folder $result($id) } } unset vFolderDef set vFolderDef(0) {{} struct {} {1}} set vFolderDef(1) [list testroot import {} \ [list NAME1 imap {} localhost $ib] * {}] RatImport 1 check_result "" 1 1 # Cleanup foreach id [array names result] { cleanup_imap_folder $result($id) } } else { set result(1) [lreplace $result(1) 3 3 \ [list NAME! imap {} localhost $base/import]] foreach id [array names result] { if {"file" == [lindex $result($id) 1]} { set result($id) [lreplace $result($id) 1 2 imap {} localhost] } } unset vFolderDef set vFolderDef(0) {{} struct {} {1}} set vFolderDef(1) [list testroot import {} \ [list NAME1 imap {} localhost $base] * {}] RatImport 1 check_result "" 1 1 } StartTest "Import after clear" unset result unset vFolderDef set result(0) {{} struct {} {1}} set result(1) [list test import {subscribed 0} \ [list NAME1 file {} $base.noexist] * {}] set vFolderDef(0) {{} struct {} {1}} set vFolderDef(1) [list testroot import {} \ [list NAME1 file {} $base.noexist] * {2}] set vFolderDef(2) [list g file {} $base/NOEXIST] RatImport 1 check_result "" 1 1 # Cleanup unset vFolderDef array set vFolderDef $vfd_backup file delete -force $base } test_import::test_import tkrat_2.2cvs20100105-dfsg.orig/test/test_msgcreat.tcl000066400000000000000000000040021137544547100223270ustar00rootroot00000000000000puts "$HEAD Message creation" namespace eval test_msgcreat { variable ignore_headers {X-IMAPbase Status X-Status X-Keywords X-UID X-Original-Status} } proc test_msgcreat::compare {filename data} { variable ignore_headers set line 0 set f [open $filename r] # Read and ignore from-line gets $f rl foreach el $data { incr line if {-1 == [gets $f rl]} { ReportError "Generated file too short" close $f return 1 } while {-1 != [lsearch $ignore_headers \ [string trimright [lindex $rl 0] ":"]]} { gets $f rl } if {$rl != $el} { ReportError [join [list "Generated message differs in line $line" \ "Got: $rl" \ "Exp: $el"] "\n"] close $f return 1 } } if {-1 != [gets $f rl]} { ReportError "Generated file too long\nGot: $rl" close $f return 1 } return 0 } proc test_msgcreat::test_msgcreat {} { global dir option smsgs # Test folders to store messages in set fn $dir/folder.[pid] set def [list Test1 file {} $fn] set role $option(default_role) set option($role,from) "test@test.domain" # Do tests foreach case $smsgs { StartTest "[lindex $case 0]" # Create message #1 if [catch {RatCreateMessage $role [lindex $case 1]} msg] { ReportError "Failed to create message(1): $msg" continue } # Insert into folder file delete -force $fn set fh [RatOpenFolder $def] if [catch {$fh insert $msg} error] { ReportError "Failed to insert: $error" continue } $fh close if {0 != [compare $fn [lindex $case 2]]} { continue } rename $msg "" # Create message #2 if [catch {RatCreateMessage $role [lindex $case 1]} msg] { ReportError "Failed to create message(2): $msg" continue } # Copy to folder file delete -force $fn if [catch {$msg copy $def} error] { ReportError "Failed to copy: $error" continue } if {0 != [compare $fn [lindex $case 2]]} { ReportError "Copy differed" continue } rename $msg "" } } test_msgcreat::test_msgcreat tkrat_2.2cvs20100105-dfsg.orig/test/test_msgid.tcl000066400000000000000000000023411137544547100216310ustar00rootroot00000000000000puts "$HEAD Test Messsage-ID parsing" namespace eval test_msgid { } proc test_msgid::test_msgid {} { global option dir hdr # List of message-ids to test # Each element in teh list is a tuple # Header - Header to look in # MsgId - Expected message id set tests { {"" "msg@id"} {" " "msg2@id"} {" (foo 's message at)" "msg@id"} {"<\\>\">\"[>]msg@id>" "\{>>[>]msg@id\}"} } # Folder to use for testing set fn $dir/folder.[pid] set def [list Test file {} $fn] foreach te $tests { StartTest "Parsing '[lindex $te 0]'" # Generate folder set fh [open $fn w 0644] puts $fh $hdr puts $fh "From maf@kilauea Thu Sep 6 14:25:09 2001 -0400" puts $fh "Date: Thu, 06 Sep 2001 14:25:00" puts $fh "Message-ID: [lindex $te 0]" puts $fh "" puts $fh "Body" close $fh # Read message and get msgid set f [RatOpenFolder $def] set actual [$f list %M] $f close # Verify if {[string compare $actual [lindex $te 1]]} { ReportError [join [list "Failed to extract correct msgid" \ " Header: [lindex $te 0]" \ "Expected: [lindex $te 1]" \ " Actual: $actual"] "\n"] } } file delete -force $fn } test_msgid::test_msgid tkrat_2.2cvs20100105-dfsg.orig/test/test_msgparse.tcl000066400000000000000000000037331137544547100223550ustar00rootroot00000000000000puts "$HEAD Message parsing" namespace eval test_msgparse { } proc test_msgparse::compare_msg {msg data} { set err [CompareLists [lindex $data 0] [$msg headers]] if {"" != $err} { ReportError "Headers differed:\n$err" return false } return [compare_body [$msg body] [lindex $data 1]] } proc test_msgparse::compare_body {body data} { if {[$body type] != [lindex $data 0]} { ReportError \ "Body type differed, got [$body type] expected [lindex $data 0]" return false } set err [CompareLists [lindex $data 1] [$body params]] if {"" != $err} { ReportError "Body parameters differed:\n$err" return false } if {[$body disp_type] != [lindex $data 2]} { ReportError "Body disposition differed, got [$body disp_type] expected [lindex $data 2]" return false } set err [CompareLists [lindex $data 3] [$body disp_parm]] if {"" != $err} { ReportError "Body disposition parameters differed:\n$err" return false } if {"MESSAGE RFC822" == [$body type]} { return [compare_msg [$body message] [lindex $data 4]] } elseif {"MULTIPART" == [lindex [$body type] 0]} { set index 4 foreach c [$body children] { if {"true" != [compare_body $c [lindex $data $index]]} { return false } incr index } } else { return true } } proc test_msgparse::test_msgparse {} { global dir option smsgs # Test folders to store messages in set fn $dir/folder.[pid] set def [list Test1 file {} $fn] set role $option(default_role) set option($role,from) "test@test.domain" # Do tests foreach case $smsgs { StartTest "[lindex $case 0]" # Store message into folder file delete -force $fn set f [open $fn w] puts $f "From maf@kilauea Thu Dec 26 23:11:56 2002 +0100" foreach l [lindex $case 2] { puts $f $l } close $f # Open folder and get message set fh [RatOpenFolder $def] $fh list "%s" set msg [$fh get 0] compare_msg $msg [lindex $case 3] $fh close } } test_msgparse::test_msgparse tkrat_2.2cvs20100105-dfsg.orig/test/test_parmencode.tcl000066400000000000000000000055201137544547100226450ustar00rootroot00000000000000puts "$HEAD Parameter encoding" namespace eval test_parmencode { } proc test_parmencode::compare_lists {exp got n} { global option set diff [expr [llength $exp]-[llength $got]] for {set i 0} {0 == $diff && $i < [llength $exp]} {incr i} { if {[lindex $exp $i] != [lindex $got $i]} { set diff 1 } } if {$diff} { puts "Expected:" foreach p $exp { puts " [list $p]" } puts "Got:" foreach p $got { puts " [list $p]" } ReportError "Encode parameter '$n' with '$option(parm_enc)' failed" } } proc test_parmencode::test_parmencode {} { global option set old_enc $option(parm_enc) # Each test consists of # - Name of test case # - Parameters to encode # - Expected result with rfc2047 # - Expected result with rfc2231 # - Expected result with both set tests { { "one simple parameter" {{p1 simple.txt}} {{P1 simple.txt}} {{P1 simple.txt}} {{P1 simple.txt}} } { "two simple parameters" {{p1 simple.txt} {p2 stupido}} {{P1 simple.txt} {P2 stupido}} {{P1 simple.txt} {P2 stupido}} {{P1 simple.txt} {P2 stupido}} {{P1 simple.txt} {P2 stupido}} } { "one localized parameter" {{p1 Rckmackan.txt}} {{P1 =?iso-8859-1?Q?R=E4ckmackan=2Etxt?=}} {{P1* iso-8859-1''R%E4ckmackan%2Etxt}} { {P1 =?iso-8859-1?Q?R=E4ckmackan=2Etxt?=} {P1* iso-8859-1''R%E4ckmackan%2Etxt} } } { "one localized parameter and one simple" {{p1 Rckmackan.txt} {p2 simple.txt}} {{P1 =?iso-8859-1?Q?R=E4ckmackan=2Etxt?=} {P2 simple.txt}} {{P1* iso-8859-1''R%E4ckmackan%2Etxt} {P2 simple.txt}} { {P1 =?iso-8859-1?Q?R=E4ckmackan=2Etxt?=} {P1* iso-8859-1''R%E4ckmackan%2Etxt} {P2 simple.txt} } } { "A long parameter" {{parameter1 {This parameter has a really long value, actually it is so long that it should wrap}}} {{PARAMETER1 {This parameter has a really long value, actually it is so long that it should wrap}}} { {PARAMETER1*0 {This parameter has a really long value, actually it is so long}} {PARAMETER1*1 { that it should wrap}} } { {PARAMETER1 {This parameter has a really long value, actually it is so long that it should wrap}} {PARAMETER1*0 {This parameter has a really long value, actually it is so long}} {PARAMETER1*1 { that it should wrap}} } } } foreach te $tests { set n [lindex $te 0] StartTest "Encoding $n" set option(parm_enc) rfc2047 set r [RatTest encode_parameters [lindex $te 1]] compare_lists [lindex $te 2] $r $n set option(parm_enc) rfc2231 set r [RatTest encode_parameters [lindex $te 1]] compare_lists [lindex $te 3] $r $n set option(parm_enc) both set r [RatTest encode_parameters [lindex $te 1]] compare_lists [lindex $te 4] $r $n } set option(parm_enc) $old_enc } test_parmencode::test_parmencode tkrat_2.2cvs20100105-dfsg.orig/test/test_pgp.tcl000066400000000000000000000215001137544547100213120ustar00rootroot00000000000000puts "$HEAD Test pgp" namespace eval test_pgp { variable tmpfn [pwd]/folder.[pid]-tmp variable tmpdef [list Test file {} $tmpfn] set fh [open $tmpfn w] puts $fh $hdr close $fh variable error_msg } proc test_pgp::send_msg {role msg} { global vFolderDef vFolderOutgoing folderExists tickle option variable error_msg proc RatSendFailed {msg errmsg} { global test_pgp::error_msg set test_pgp::error_msg $errmsg } smtp_server::start set error_msg NONE set option(cache_conn) 0 set option($role,from) maf@test.domain set option($role,sendprot) smtp set option($role,smtp_hosts) "localhost:[smtp_server::get_port]" set option($role,smtp_user) "" set option($role,smtp_passwd) "" set fh [RatOpenFolder $vFolderDef($vFolderOutgoing)] $fh insert $msg RatNudgeSender # Wait for send to complete for {set i 0} {$i < 600} {incr i} { # Force event loop after 100 "set tickle 1" vwait tickle if {0 == $folderExists($fh)} { break } } if {"NONE" != $error_msg} { puts "Err: $error_msg" } set sent [lindex [smtp_server::get_received] 1] smtp_server::stop return $sent } proc test_pgp::test_signing {} { global option hdr smsgs variable tmpfn variable tmpdef set role $option(default_role) # Loop over test messages. #foreach mt [list [lindex $smsgs 1]] foreach mt $smsgs { StartTest "Signing [lindex $mt 0]" # Create message set msg [RatCreateMessage $role [lindex $mt 1]] # Sign it if {[catch {$msg pgp true false $role test_key@tkrat.org {}} err]} { ReportError "Failed to sign: $err" continue } # Send it set sent_list [send_msg $role $msg] set sent_msg [join $sent_list "\n"] # Check signature with gpg set boundary [string range [lindex $sent_list end] 2 end-2] set mfile "msg.[pid]" set sfile "sig.[pid]" for {set i 0} {"[lindex $sent_list $i]" != "--$boundary"} {incr i} { } set fd [open $mfile w] fconfigure $fd -translation crlf set first 1 for {incr i} {"[lindex $sent_list $i]" != "--$boundary"} {incr i} { if {$first} { set first 0 } else { puts -nonewline $fd "\n" } puts -nonewline $fd [lindex $sent_list $i] } close $fd set fd [open $sfile w] for {incr i} {"[lindex $sent_list $i]" != "--$boundary--"} {incr i} { puts $fd [lindex $sent_list $i] } close $fd if {[catch "exec gpg $option(pgp_args) --verify $sfile $mfile 2>errout" err]} { set fh [open errout r] while {-1 != [gets $fh line]} { puts $line } close $fh puts "File: [pwd]/$mfile" puts "Sig: [pwd]/$sfile" ReportError "External signature verification failed" continue } # Check signature with tkrat (ok expected) set fh [open $tmpfn w] puts $fh $hdr puts $fh "From maf@tkrat.org Tue Sep 5 18:02:22 2000 +0100" puts $fh $sent_msg close $fh set fh [RatOpenFolder $tmpdef] set msg [$fh get 0] set body [$msg body] $body checksig if {"pgp_good" != [$body sigstatus]} { ReportError "Signature check in TkRat failed [$body sigstatus]" continue } $fh close 1 # Check signature with tkrat (failure expected) set fh [open $tmpfn w] puts $fh $hdr puts $fh "From maf@tkrat.org Tue Sep 5 18:02:22 2000 +0100" # Add a trailing blank to each bodypart regsub -all -- "--$boundary" $sent_msg "\n--$boundary" broken puts $fh $broken close $fh set fh [RatOpenFolder $tmpdef] set msg [$fh get 0] set body [$msg body] $body checksig if {"pgp_bad" != [$body sigstatus]} { ReportError "Signature check of bad message in TkRat failed [$body sigstatus]" continue } $fh close 1 } } proc test_pgp::test_encrypting {} { global option hdr smsgs variable tmpfn variable tmpdef set role $option(default_role) # Loop over test messages. foreach mt $smsgs { StartTest "Encrypting [lindex $mt 0]" # Create message set msg [RatCreateMessage $role [lindex $mt 1]] # Encrypt & sign it if {[catch {$msg pgp true true $role test_key@tkrat.org test_key@tkrat.org} err]} { ReportError "Failed to encrypt: $err" continue } # Send it set sent_list [send_msg $role $msg] set sent_msg [join $sent_list "\n"] # Check encryption & signature with gpg set mfile "msg.[pid]" set fd [open $mfile w] fconfigure $fd -translation crlf regexp -- {-----BEGIN PGP MESSAGE.*END PGP MESSAGE-----} $sent_msg enc puts $fd $enc close $fd set ea "--status-fd 2 --decrypt $mfile 2>status.[pid]" if {[catch "exec gpg $option(pgp_args) $ea" output]} { set err 1 } else { set err 0 } catch {unset gpgout} set status "" set fh [open "status.[pid]" r] while {-1 != [gets $fh line]} { set status "$status$line\n" if {{[GNUPG:]} == "[lindex $line 0]"} { set gpgout([lindex $line 1]) [lrange $line 2 end] } } if {1 == $err || ![info exists gpgout(GOODSIG)] \ || ![info exists gpgout(DECRYPTION_OKAY)]} { ReportError "External verification failed\n$status" continue } set expected_list {} set expected_body_list {} set in_header 1 set in_content 0 foreach l [lindex $mt 2] { if {$in_header} { if {[regexp "^Content-" $l]} { lappend expected_list $l set in_content 1 } elseif {$in_content && (" " == [string index $l 0] || "\t" == [string index $l 0])} { lappend expected_list $l } elseif {"" == $l} { lappend expected_list $l set in_header 0 set in_content 0 } else { set in_content 0 } } else { lappend expected_list $l lappend expected_body_list $l } } set expected [join [lrange $expected_list 0 end-1] "\n"] if {"$output" != "$expected"} { puts "******** Expected" puts $expected puts "******** Output" puts $output ReportError "Externally decrypted text does not match expected text" continue } # Check decryption with tkrat set fh [open $tmpfn w] puts $fh $hdr puts $fh "From maf@tkrat.org Tue Sep 5 18:02:22 2000 +0100" puts $fh $sent_msg close $fh set fh [RatOpenFolder $tmpdef] set msg [$fh get 0] set body [$msg body] $body checksig if {"pgp_good" != [$body sigstatus]} { ReportError "Decryption check in TkRat failed signature part" continue } set expected_body [join [lrange $expected_body_list 0 end-1] "\n"] if {"[$body data true]" != "$expected_body"} { ReportError "Internally decrypted text does not match expected text" puts "******** Expected" puts $expected_body_list puts "******** Output" puts [$body data true] continue } $fh close 1 } } proc test_pgp::compare_key {e o} { foreach i {0 2 4 5} { if {[lindex $e $i] != [lindex $o $i]} { return "fail" } } foreach i {1 3} { set ei [lindex $e $i] set oi [lindex $o $i] if {[llength $ei] != [llength $oi]} { return "fail" } for {set j 0} {$j < [llength $ei]} {incr j} { if {[lindex $ei $j] != [lindex $oi $j]} { return "fail" } } } return "ok" } proc test_pgp::compare_keylist {expected output} { if {[lindex $expected 0] != [lindex $output 0]} { return "fail" } set e [lindex $expected 1] set o [lindex $output 1] if {[llength $e] != [llength $o]} { return "fail" } for {set i 0} {$i < [llength $e]} {incr i} { if {"fail" == [compare_key [lindex $e $i] [lindex $o $i]]} { return "fail" } } return "ok" } proc test_pgp::test_keylist {} { StartTest "Listing keys" set publist { {Public keyring} { {ED6087318702C78A test_key@tkrat.org {pub 1024 DSA (sign only)} {{TkRat Test Key (Do not trust this key!!!) }} 1 0 } {36D3FDF09AC2D77E test_key@tkrat.org {sub 768 ElGamal (encrypt only)} {{TkRat Test Key (Do not trust this key!!!) }} 0 1}} } set result [RatPGP listkeys PubRing] if {"fail" == [compare_keylist $publist $result]} { puts "******** Expected" puts $publist puts "******** Output" puts $result ReportError "Public keylist differes from expected" } set seclist { {Secret keyring} { {ED6087318702C78A test_key@tkrat.org {sec 1024 DSA (sign only)} {{TkRat Test Key (Do not trust this key!!!) }} 1 0} {36D3FDF09AC2D77E test_key@tkrat.org {ssb 768 ElGamal (encrypt only)} {{TkRat Test Key (Do not trust this key!!!) }} 0 1} } } set result [RatPGP listkeys SecRing] if {"fail" == [compare_keylist $seclist $result]} { puts "******** Expected" puts $seclist puts "******** Output" puts $result ReportError "Secret keylist differes from expected" } } proc test_pgp::test_pgp {} { global option tmp RatLibSetOnlineMode 1 set option(pgp_version) gpg-1 set option(pgp_args) "--no-default-keyring --keyring [pwd]/../pgp_pub --secret-keyring [pwd]/../pgp_sec --always-trust --no-options --homedir $tmp" #test_signing #test_encrypting test_keylist } proc RatGetPGPPassPhrase {} { return [list "ok" ""] } test_pgp::test_pgp tkrat_2.2cvs20100105-dfsg.orig/test/test_sending.tcl000066400000000000000000000274361137544547100221710ustar00rootroot00000000000000puts "$HEAD Test sending" namespace eval test_sending { # Messages to send variable messages # Location of files variable tmpfn [pwd]/folder.[pid]-tmp # Error message when send failed variable error_msg } proc test_sending::generate_send_command {} { global tclsh_binary set cmd [pwd]/testsend.[pid] set fh [open $cmd w 0755] puts $fh "#!$tclsh_binary" puts $fh { # Usage: sendcmd STATUS FD AMOUNT WHEN RECIPIENTS set out [open "$argv0-out" w 0600] puts $out [lrange $argv 4 end] set mfd [lindex $argv 1] if {"big" == [lindex $argv 2]} { set msg "Start of message\n" for {set i 0} {$i < [expr 9000/64]} {incr i} { set msg "$msg[string repeat M 63]\n" } set msg "${msg}End of message" } else { set msg "Message" } set when [lindex $argv 3] while {-1 != [gets stdin line]} { if {"during" == $when} { puts $mfd $msg } puts $out $line } if {"after" == $when} { puts $mfd $msg } exit [lindex $argv 0] } close $fh return $cmd } proc test_sending::generate_messages {num} { variable tmpfn variable messages global hdr set fh [open $tmpfn w 0600] puts $fh $hdr for {set i 0} {$i < $num} {incr i} { set i2 [format "%02d" $i] set id "<$i@no.such.domain>" set date "Sat, 5 Jul 2002 10:14:$i2 +0100 (MET)" set m "From maf@math.chalmers.se Tue Sep 5 18:02:22 2000 +0100 Date: $date From: Martin Forssen Sender: Martin Forssen Reply-To: Martin Forssen Subject: test $i2 To: Martin Forssen $i cc: cc@tkrat.org Bcc: bcc@tkrat.org Message-ID: $id MIME-Version: 1.0 Content-Type: TEXT/PLAIN; CHARSET=us-ascii X-TkRat-Internal-role: r0 test $i " puts $fh $m lappend messages $m } set m "From maf@math.chalmers.se Tue Sep 5 18:02:22 2000 +0100 Date: Sat, 5 Jul 2002 10:14:42 +0100 (MET) From: Martin Forssen Sender: Martin Forssen Reply-To: Martin Forssen Subject: Test with a really long subject line which definitely should wrap Y into at least three lines. Specially since it invokes the magic formula Y Shrimp-sandwich To: Martin Forssen cc: cc@tkrat.org Bcc: bcc@tkrat.org Message-ID: <1@no,such.domain> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; CHARSET=us-ascii X-TkRat-Internal-role: r0 test with long header line " puts $fh $m set r [regsub -all "Y" $m "Y\n" m] lappend messages $m close $fh set tmpdef [list Test file {} $tmpfn] return [RatOpenFolder $tmpdef] } proc test_sending::generate_complicated_message {num} { variable tmpfn variable messages global hdr set fh [open $tmpfn a 0600] puts $fh $hdr for {set i 0} {$i < $num} {incr i} { set i2 [format "%02d" $i] set id "<$i@no.such.domain>" set date "Sat, 5 Jul 2002 10:14:$i2 +0100 (MET)" set m "From maf@math.chalmers.se Tue Sep 5 18:02:22 2000 +0100 Date: Sat, 5 Jul 2002 10:14:42 +0100 (MET) From: Martin Forssen Sender: Martin Forssen Reply-To: Martin Forssen Subject: Test with a really long subject line which definitely should wrap into at least three lines. Specially since it invokes the magic formula 'Rckmackan' To: Martin Forssen cc: cc@tkrat.org Bcc: bcc@tkrat.org Message-ID: <1@no,such.domain> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; CHARSET=us-ascii X-TkRat-Internal-role: r0 test $i " puts $fh $m lappend messages $m } close $fh set tmpdef [list Test file {} $tmpfn] return [RatOpenFolder $tmpdef] } proc test_sending::count {needle haystack} { set n 0 set pos 0 while {-1 != [set i [string first $needle $haystack $pos]]} { incr n set pos [expr $i+1] } return $n } proc RatSendFailed {msg errmsg} { global vFolderDef vFolderOutgoing global test_sending::error_msg set test_sending::error_msg $errmsg } proc test_sending::do_send {message_index mf} { global vFolderDef vFolderOutgoing folderExists tickle variable error_msg set error_msg "NONE" set fh [RatOpenFolder $vFolderDef($vFolderOutgoing)] set msg [$mf get $message_index] $fh insert $msg $fh close RatNudgeSender # Wait for send to complete for {set i 0} {$i < 600} {incr i} { # Force event loop after 100 "set tickle 1" vwait tickle if {"NONE" != $error_msg} { set result fail break } if {0 == $folderExists($fh)} { set result ok break } } if {"NONE" != $error_msg} { set result fail } return $result } proc test_sending::test_prog {message_index mf cmd status fd amount when} { global option verbose variable error_msg variable messages if {0 != $status} { set expected fail } else { set expected ok } StartTest "Prog $message_index $expected $fd $amount $when" # Setup for sending set option(r0,sendprot) prog set option(r0,sendprog) "$cmd $status $fd $amount $when" set option(smtp_verbose) 0 file delete -force $cmd-out set result [do_send $message_index $mf] # Prepare for testing set m [lrange [split [lindex $messages $message_index] "\n"] 1 end] # Handle expected failures if {0 != $status} { if {"fail" != $result} { ReportError "Succeeded when a failure was expected" return } # Check error_msg if {"stderr" != $fd} { if {"" != $error_msg} { ReportError "Got error message when no stderr output" if {$verbose} { puts $error_msg } return } return } if {"silent" == $when} { if {"" != $error_msg} { ReportError "Got error message in silent mode" if {$verbose} { puts $error_msg } return } return } set cm [count "Message" $error_msg] set csm [count "Start of message" $error_msg] set cem [count "End of message" $error_msg] set lines [llength $m] if {"during" == $when} { # The '2' is for the bcc and X-TkRat lines set expected [expr $lines-2] } else { set expected 1 } if {"big" == $amount} { if {$cm != 0 || $csm != $expected || $cem != $csm} { ReportError "Unexpected message" puts "$cm != 0 || $csm != $expected || $cem != $csm" return } } else { if {$cm != $expected || $csm != 0 || $cem != $csm} { ReportError "Unexpected message" puts "$cm != $expected || $csm != 0 || $cem != $csm" return } } return } # We only get here if the send was expected to work if {"ok" != $result} { ReportError "Send failed unexpectedly\n$error_msg" return } # Check generated message set fh [open "[lindex $cmd 0]-out" r] gets $fh line if {"maf@tkrat.org cc@tkrat.org bcc@tkrat.org" != $line} { ReportError "Unexpected recipients\n$line" return } set i 0 while {-1 != [gets $fh line]} { while {[string match -nocase "bcc:*" [lindex $m $i]] || [string match -nocase "x-tkrat-internal*" [lindex $m $i]]} { incr i } if {$i > [llength $m]} { ReportError "Sent message too long\n[list $line]" close $fh return } if {$line != [lindex $m $i]} { ReportError [join [list "Sent message differs from expected" \ "Got: <$line>" \ "Exp: <[lindex $m $i]>"] "\n"] close $fh return } incr i } close $fh if {$i < [llength $m]} { ReportError "Sent message too short\nGot $i lines expected [llength $m]" return } return } proc test_sending::test_smtp {message_index mf status desc} { global option variable messages variable smtp_recipients StartTest "SMTP $desc" # Setup for sending set option(r0,from) "maf@tkrat.org" set option(r0,sendprot) smtp set option(r0,smtp_hosts) "localhost:[smtp_server::get_port]" set option(r0,smtp_user) "" set option(r0,smtp_passwd) "" set option(smtp_verbose) 4 smtp_server::prepare_incoming $status set result [do_send $message_index $mf] if {"$result" != "$status"} { ReportError "Sending got state '$result' when '$status' was expected" } # Get result set r [smtp_server::get_received] # Check that nothing got delivered in the failure case if {"fail" == $status} { if {"" != [lindex $r 1]} { ReportError "Got message even though sending failed" } return } # Check recipients if {"[lindex $r 0]" != "maf@tkrat.org cc@tkrat.org bcc@tkrat.org"} { ReportError "Wrong recipients of SMTP message\nGot: '[lindex $r 0]'" } # Check the message which got delivered set exp [lrange [split [lindex $messages $message_index] "\n"] 1 end] set i 0 foreach line [lindex $r 1] { while {[string match -nocase "bcc:*" [lindex $exp $i]] || [string match -nocase "x-tkrat-internal*" [lindex $exp $i]]} { incr i } if {$i > [llength $exp]} { ReportError "Sent message too long\n[list $line]" return } if {$line != [lindex $exp $i]} { ReportError [join [list "Sent message differs from expected" \ "Got: <$line>" \ "Exp: <[lindex $exp $i]>"] "\n"] return } incr i } if {$i < [llength $exp]} { ReportError "Sent message too short\nMissing [expr [llength $exp]-$i] lines" return } } proc test_sending::test_sending {} { global option tickle # Initialize stuff set cmd [generate_send_command] set mf [generate_messages 11] RatLibSetOnlineMode 1 # Test program sending test_prog 0 $mf $cmd 0 stderr silent after test_prog 1 $mf $cmd 1 stderr silent after test_prog 2 $mf $cmd 0 stderr normal after test_prog 3 $mf $cmd 1 stderr normal after test_prog 4 $mf $cmd 0 stderr big after test_prog 5 $mf $cmd 1 stderr big after test_prog 6 $mf $cmd 0 stderr normal during test_prog 7 $mf $cmd 1 stderr normal during test_prog 8 $mf $cmd 0 stderr big during test_prog 9 $mf $cmd 1 stderr big during test_prog 10 $mf $cmd 0 stdout big during test_prog 11 $mf $cmd 0 stderr normal after # Test SMTP sending set option(cache_conn) 0 smtp_server::start test_smtp 0 $mf ok "Simple message ok expected" test_smtp 1 $mf fail "Simple message fail expected" # Test connectio caching set option(cache_conn) 0 set option(cache_conn_timeout) 10 test_smtp 0 $mf ok "Simple message should not keep conn open" if {[smtp_server::get_state] != "disconnected"} { ReportError "Cached connection when option(cache_conn) = 0" } set option(cache_conn) 1 set option(cache_conn_timeout) 1 test_smtp 1 $mf ok "Simple message should keep conn open (cache check)" set o [smtp_server::get_opens] if {[smtp_server::get_state] != "command"} { ReportError "SMTP stream not in command mode when expected" } test_smtp 2 $mf ok "Simple message should reuse conn" if {[smtp_server::get_opens] != $o} { ReportError "SMTP stream not reused as expected" } after 2000 "set tickle 1" vwait tickle if {[smtp_server::get_state] != "disconnected"} { ReportError "Connection still open after expected timeout" } set option(cache_conn_timeout) 3 test_smtp 3 $mf ok "Simple message should keep conn open (extend check)" after 2000 "set tickle 1" vwait tickle if {[smtp_server::get_state] != "command"} { ReportError "SMTP stream not in command mode when expected" } test_smtp 4 $mf ok "Simple message which should extend the timer" after 2000 "set tickle 1" vwait tickle if {[smtp_server::get_state] != "command"} { ReportError "SMTP stream not in command mode when expected" } after 2000 "set tickle 1" vwait tickle if {[smtp_server::get_state] != "disconnected"} { ReportError "Connection still open after expected timeout" } set option(cache_conn_timeout) 10 test_smtp 5 $mf ok "Simple message should keep conn open (server closes)" smtp_server::close_session test_smtp 6 $mf ok "Simple message which should use closed cached conn" test_smtp 11 $mf ok "Complicated message ok expected" $mf close smtp_server::stop } test_sending::test_sending tkrat_2.2cvs20100105-dfsg.orig/test/test_sequence.tcl000066400000000000000000000026611137544547100223430ustar00rootroot00000000000000puts "$HEAD Test sequence" namespace eval test_sequence { } # Test sequence creation proc test_sequence::test_notempty {} { set s [RatCreateSequence] if {[$s notempty]} { ReportError "Reported not empty when in fact empty" } $s add 1 if {![$s notempty]} { ReportError "Reported !notempty when one number added" } rename $s "" } proc test_sequence::run_test {test} { global verbose set s [RatCreateSequence] foreach e [lindex $test 0] { $s add $e } set expected [lindex $test 1] set result [$s get] if {$result != $expected} { if {$verbose} { puts " Got: $result" puts "Expected: $expected" } ReportError "Bad result for $test" } } proc test_sequence::test_sequence {} { StartTest "Test sequences" test_notempty foreach t { {{1} "1"} {{1 2} "1,2"} {{1 2 3} "1:3"} {{1 2 3 5} "1:3,5"} {{5 1 2 3} "1:3,5"} {{1 3 5} "1,3,5"} {{1 5 3} "1,3,5"} {{5 3 1} "1,3,5"} {{1 3 5 4} "1,3:5"} {{1 5 4 3} "1,3:5"} {{5 4 3 1} "1,3:5"} {{4 5 3 1} "1,3:5"} {{5 5 6} "5,6"} {{1 4 2 3 7 9 6 8} "1:4,6:9"} {{2 123456 3 1234567 4} "2:4,123456,1234567"} {{123456789} "123456789"} {{123456789 123456787} "123456787,123456789"} } { run_test $t } } test_sequence::test_sequence tkrat_2.2cvs20100105-dfsg.orig/test/test_sorting.tcl000066400000000000000000000214631137544547100222210ustar00rootroot00000000000000puts "$HEAD Test message sorting" namespace eval test_sorting { } proc test_sorting::test_sorting {} { global option dir hdr mailServer \ msg1 msg2 msg3 msg4 msg5 msg6 msg7 msg8 msg9 msg10 \ msg11 msg12 msg13 msg14 msg15 msg16 msg17 msg18 msg19 variable tsmsg # Prepare test folder set fn $dir/folder.[pid] set def [list Test file {} $fn] set fh [open $fn w] puts $fh $hdr foreach m [list $msg1 $msg2 $msg3 $msg4 $msg5 \ $msg6 $msg7 $msg8 $msg9 $msg10] { puts $fh $m } close $fh # Loop through basic tests set f1 [RatOpenFolder $def] foreach tc { {folder {1 2 3 4 5 6 7 8 9 10}} {reverseFolder {10 9 8 7 6 5 4 3 2 1}} {date {1 2 3 4 5 6 7 8 9 10}} {reverseDate {10 9 8 7 6 5 4 3 2 1}} {size {1 2 3 4 5 6 7 8 9 10}} {reverseSize {10 9 8 7 6 5 4 3 2 1}} {subject {1 2 3 4 5 6 7 8 9 10}} {subjectonly {1 2 3 4 5 6 7 8 9 10}} } { StartTest "Sort order '[lindex $tc 0]'..." $f1 setSortOrder [lindex $tc 0] $f1 update update set expected {} foreach m [lindex $tc 1] { lappend expected [format "test %02d" $m] } set current [$f1 list %s] if {$expected != $current} { ReportError "Sort failed" puts "Expected:" foreach m $expected {puts $m} puts "Got:" foreach m $current {puts $m} } } $f1 close file delete $fn foreach func {get_simple_thread get_back_thread get_real_thread get_strange_msgid get_mlist_subject} { set gts [eval $func] StartTest "Sort order 'threaded' [lindex $gts 0] ..." set fn $dir/folder.[pid] set def [list Test file {} $fn] set fh [open $fn w+] puts $fh $hdr foreach msg [lindex $gts 1] { puts $fh $msg } close $fh set f1 [RatOpenFolder $def] $f1 setSortOrder threaded $f1 update update set expected [lindex $gts 2] set real [$f1 list "%t%M"] if {[llength $expected] != [llength $real]} { set fail "Fail lengthdiff" } else { set fail "" for {set i 0} {$fail == "" && $i < [llength $expected]} {incr i} { # puts "Exp: <[lindex $expected $i]> Real <[lindex $real $i]>" if {[lindex $expected $i] != [lindex $real $i]} { set fail "Fail <[lindex $expected $i]> != <[lindex $real $i]>" } } } $f1 close file delete $fn if {"" != $fail} { ReportError "Threads sorting failed: $fail" for {set i 0} {$i < [llength $expected] || $i < [llength $real]} \ {incr i} { puts [format "Exp: %-20s Real: %-20s" \ [lindex $expected $i] [lindex $real $i]] } } } } proc test_sorting::get_simple_thread {} { lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:00 Subject: other THIS: msg1 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:01 Subject: foo THIS: msg2 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:02 Subject: foo In-Reply-To: THIS: msg3 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:03 Subject: foo In-Reply-To: THIS: msg4 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:04 Subject: foo In-Reply-To: THIS: msg5 } set expected { {m1@foo.bar} {m2@foo.bar} {+m3@foo.bar} {|+m4@foo.bar} {+m5@foo.bar} } return [list "simple" $ml $expected] } proc test_sorting::get_back_thread {} { lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:00 Subject: other THIS: msg1 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:01 Subject: foo In-Reply-To: THIS: msg2 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:02 Subject: foo In-Reply-To: THIS: msg3 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:03 Subject: foo THIS: msg4 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:04 Subject: bar THIS: msg5 } set expected { {m1@foo.bar} {m4@foo.bar} {+m2@foo.bar} {+m3@foo.bar} {m5@foo.bar} } return [list "backref" $ml $expected] } proc test_sorting::get_real_thread {} { lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:09 -0400 Subject: foo In-Reply-To: References: THIS: msg1 } lappend ml { From maf@kilauea Thu Sep 6 16:00:23 2001 -0400 Message-Id: In-Reply-To: References: Date: Thu, 6 Sep 2001 16:00:23 -0400 Subject: foo THIS: msg2 } lappend ml { From maf@kilauea Thu Sep 6 18:03:18 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 18:03:18 -0400 Subject: foo In-Reply-To: References: THIS: msg3 } lappend ml { From maf@kilauea Thu Sep 6 23:01:30 2001 +0200 Date: Thu, 6 Sep 2001 23:01:30 +0200 (CEST) Subject: foo In-Reply-To: Message-ID: THIS: msg4 } lappend ml { From maf@kilauea Thu Sep 6 23:12:57 2001 +0200 Date: Thu, 6 Sep 2001 23:12:57 +0200 (MEST) Subject: foo In-Reply-To: Message-ID: THIS: msg5 } lappend ml { From maf@kilauea Fri Sep 7 00:01:11 2001 +0200 Date: Fri, 7 Sep 2001 00:01:11 +0200 (CEST) Subject: foo In-Reply-To: Message-ID: THIS: msg6 } lappend ml { From maf@kilauea Fri Sep 7 00:06:46 2001 +0200 Date: Fri, 7 Sep 2001 00:06:46 +0200 (MEST) Subject: foo In-Reply-To: Message-ID: THIS: msg7 } lappend ml { From maf@kilauea Thu Sep 6 21:15:51 2001 -0400 Subject: foo References: Date: 06 Sep 2001 21:15:51 -0400 In-Reply-To: Bar Foo's message of "Thu, 06 Sep 2001 17:44:02 -0400" Message-ID: THIS: msg8 } lappend ml { From maf@kilauea Thu Sep 6 14:35:50 2001 -0700 Date: Thu, 06 Sep 2001 14:25:09 -0400 Message-Id: Subject: foo In-Reply-To: from "Foo Bar" at Sep 06, 2001 11:01:30 PM THIS: msg9 } lappend ml { From maf@kilauea Thu Sep 6 17:44:02 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 17:44:02 -0400 Subject: foo In-Reply-To: References: THIS: msg10 } set expected { {m2@foo.bar} {+m12@foo.bar} {|+m4@foo.bar} {+m8@foo.bar} { +m1@foo.bar} { +m6@foo.bar} { |+m9@foo.bar} { | +m7@foo.bar} { +m3@foo.bar} { +m11@foo.bar} } return [list "Real-sample" $ml $expected] } proc test_sorting::get_strange_msgid {} { lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:28:00 Subject: other THIS: msg1 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:27:00 In-Reply-To: <"Joe_Doe"@[127.0.0.1]> Subject: foo THIS: msg2 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:26:00 In-Reply-To: <"Joe\_Doe"@[127\.0\.0\.1]> Subject: bar THIS: msg3 } set expected { {Joe_Doe@[127.0.0.1]} {+m3@foo.bar} {+m2@foo.bar} } return [list "Contorted msgid" $ml $expected] } proc test_sorting::get_mlist_subject {} { lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:00 Subject: [listname] foo THIS: msg1 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:01 Subject: Re: [listname] foo References: In-ReplyTo: THIS: msg2 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:02 Subject: something different THIS: msg3 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:03 Subject: [listname] Re: foo THIS: msg4 } lappend ml { From maf@kilauea Thu Sep 6 14:25:09 2001 -0400 Message-Id: Date: Thu, 06 Sep 2001 14:25:04 Subject: [listname] Re: foo References: In-ReplyTo: THIS: msg5 } set expected { {m1@foo.bar} {+m2@foo.bar} {+m4@foo.bar} {+m5@foo.bar} {m3@foo.bar} } return [list "mlist_subject" $ml $expected] } test_sorting::test_sorting tkrat_2.2cvs20100105-dfsg.orig/test/test_wrap.tcl000066400000000000000000000275661137544547100215170ustar00rootroot00000000000000puts "$HEAD Test wrapping" namespace eval test_wrap { } proc test_wrap::test_cited_wrap {} { lappend texts [list { > 1.1) If you use a "reply" function, its not possible, you must choose one > and only one of the two messages. } { > 1.1) If you use a "reply" function, its not possible, you must choose > one and only one of the two messages. }] lappend texts [list { > - If you use a "reply" function, its not possible, you must choose one > and only one of the two messages. > and only one of the two messages. } { > - If you use a "reply" function, its not possible, you must choose > one and only one of the two messages. > and only one of the two messages. }] lappend texts [list { > Martin, > I discussed the issues surrounding separate versus combined daemons > for firewatch and monitor with Nathan. We came up with a slightly > different proposal that I'd like to see what you think about it. } { > Martin, > I discussed the issues surrounding separate versus combined > daemons for firewatch and monitor with Nathan. We came up with a > slightly different proposal that I'd like to see what you think about > it. }] lappend texts [list { > Ok. Thanks for updating that. I agree with everything you have. So, going > with your list we are down to fixing: > 696 > 789 } { > Ok. Thanks for updating that. I agree with everything you have. So, > going with your list we are down to fixing: > 696 > 789 }] lappend texts [list { > Hur r det tnkt att man skall kunna stoppa individuella services? Idag > stoppar man ju portforwards i portforwardtabben. Skall man infra ett > 'stop service' val i menyn man fr upp om man hgerklickar p ikonen fr > servicen (i Client). I Connect skulle man kunna byta texten 'run' p > kanppen intill service ikonen till 'stop' nr man vl startat en service. } { > Hur r det tnkt att man skall kunna stoppa individuella services? > Idag stoppar man ju portforwards i portforwardtabben. Skall man infra > ett 'stop service' val i menyn man fr upp om man hgerklickar p > ikonen fr servicen (i Client). I Connect skulle man kunna byta texten > 'run' p kanppen intill service ikonen till 'stop' nr man vl startat > en service. }] lappend texts [list { >> > 1. r allt nedan som str under 3.3 med i 3.3 och det under 4.0 med i 4.0 >> > (och inte redan implementerat i 3.3 eller tvrt om)? } { >> > 1. r allt nedan som str under 3.3 med i 3.3 och det under 4.0 med >> > i 4.0 (och inte redan implementerat i 3.3 eller tvrt om)? }] lappend texts [list { > - when using the Color-Config "Steel Blue" the Balloon Help is unreadable > because of white text on yellow background. (I hunted in the src for > the balloon-help color-config, because I wanted to send you a diff, but > sorry, seems that I am to unfamiliar with Tk) } { > - when using the Color-Config "Steel Blue" the Balloon Help is > unreadable because of white text on yellow background. (I hunted in > the src for the balloon-help color-config, because I wanted to send > you a diff, but sorry, seems that I am to unfamiliar with Tk) }] lappend texts [list { > 1.) Only doing the changes on Mercury > > results: Venus was inaccessible because of the differences in the appgate.conf } { > 1.) Only doing the changes on Mercury > > results: Venus was inaccessible because of the > differences in the appgate.conf }] lappend texts [list { > 2.) Doing changes on Mercury and Venus and restarting the daemons on both. } { > 2.) Doing changes on Mercury and Venus and restarting the daemons on > both. }] lappend texts [list { > 2.) Doing changes on Mercury and Venus and restarting the daemons on both. > Also a hypotetical second line which should force yet another wrap. } { > 2.) Doing changes on Mercury and Venus and restarting the daemons on > both. Also a hypotetical second line which should force yet > another wrap. }] lappend texts [list { > 2.) Doing changes on Mercury and Venus and restarting the daemons on both. > Result: Everything works until we add or delete something that requires a Commit. At that time we get the error back in the Appgate.conf. } { > 2.) Doing changes on Mercury and Venus and restarting the daemons on > both. > Result: Everything works until we add or delete > something that requires a Commit. At that time we get > the error back in the Appgate.conf. }] lappend texts [list { > should not wrap} { > should not wrap}] lappend texts [list { > detta r det frsta meddelande jag skickar med nokian...en s lnge knns det bra...det enda som inte verkar fungera r att instllningarna inte sparas...} { > detta r det frsta meddelande jag skickar med nokian...en s lnge > knns det bra...det enda som inte verkar fungera r att > instllningarna inte sparas... }] set index 0 foreach te $texts { StartTest "Wrapping cited text [incr index]" if {[lindex $te 1] != [RatWrapCited [lindex $te 0]]} { puts "---- Original ----" puts [lindex $te 0] puts "---- Expected ----" puts [lindex $te 1] puts "---- Actual ----" puts [RatWrapCited [lindex $te 0]] ReportError "Wrapping failed" } } } proc test_wrap::test_edit_wrap {} { global option # The test cases # Wrap is set to --| # 1 lappend texts [list { Hej hopp. } { Hej hopp. }] # 2 lappend texts [list { This text should wrap } { This text should wrap }] # 3 lappend texts [list { This text should wrap } { This text should wrap }] # 4 lappend texts [list { This text should wrap over multimple lines } { This text should wrap over multimple lines }] # 5 lappend texts [list { This text should also wrap over multimple lines } { This text should also wrap over multimple lines }] # 6 lappend texts [list {
* This text is part of
* a list
* where each line should wrap
} { * This text is part of * a list * where each line should wrap }] # 7 lappend texts [list {
1 This text is part of
2 a list
3 where each line should wrap
} { 1 This text is part of 2 a list 3 where each line should wrap }] # 8 lappend texts [list {
1. This text is part of
2. a list
3. where each line should wrap
} { 1. This text is part of 2. a list 3. where each line should wrap }] # 9 lappend texts [list {
1) This text is part of
2) a list
3) where each line should wrap
} { 1) This text is part of 2) a list 3) where each line should wrap }] # 10 lappend texts [list {
- This text is part of
- a list
- where each line should wrap
} { - This text is part of - a list - where each line should wrap }] # 11 lappend texts [list {
*grin* This is a simple test } { *grin* This is a simple test }] # 12 lappend texts [list {
1.This text is part of
2.a list
3.where each line should wrap
} { 1.This text is part of 2.a list 3.where each line should wrap }] # 13 lappend texts [list { This_is_a_wery_long_word } { This_is_a_wery_long_word }] # 14 lappend texts [list { This_is_a_wery_long_word two } { This_is_a_wery_long_word two }] # 15 lappend texts [list { one This_is_a_wery_long_word two } { one This_is_a_wery_long_word two }] text .t rat_edit::create .t set old_wrap_length $option(wrap_length) set option(wrap_length) 20 set index 0 foreach te $texts { StartTest "Wrapping edit [incr index]" # Insert the text .t delete 1.0 end .t insert 1.0 [lindex $te 0] # Convert any
\n to newlines with noWrap set pos 1.0 while {"" != [set pos [.t search "
\n" $pos]]} { .t replace $pos $pos+5c "\n" noWrap } # Wrap between all instances of noWrap set pos "2.0 lineend" rat_edit::wrap .t $pos while {"" != [set range [.t tag nextrange noWrap $pos]]} { set pos [lindex $range 1] rat_edit::wrap .t $pos } if {[.t get 1.0 end-1c] != [lindex $te 1]} { puts "---- Original ----" puts [lindex $te 0] puts "---- Expected ----" puts [lindex $te 1] puts "---- Actual ----" puts [.t get 1.0 end-1c] ReportError "Wrapping failed" } } set option(wrap_length) $old_wrap_length destroy .t } proc test_wrap::test_edit_insert {} { global option # The test cases (_ is cursor location) # Wrap is set to --| # 1 lappend texts [list { Hej hopp._ } {space} { Hej hopp. _ }] # 2 lappend texts [list { There is no such thing as a free lunch_ } {space} { There is no such thing as a free lunch _ }] # 3 lappend texts [list { These lines will_ foo wrapped } {space} { These lines will _ foo wrapped }] # 4 lappend texts [list { These lines will_ foo wrapped } {space space} { These lines will _ foo wrapped }] # 5 lappend texts [list { These lines will_ foo wrapped } {space b e} { These lines will be_ foo wrapped }] # 6 lappend texts [list { These foo _ wrapped } {BackSpace} { These foo_ wrapped }] # 7 lappend texts [list { These foo _ wrapped } {BackSpace} { These foo _wrapped }] # 8 lappend texts [list { These lines will_ foo wrapped } {BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace} { These_ foo wrapped }] # 9 lappend texts [list { These foo _
* wrapped } {BackSpace} { These foo_ * wrapped }] # 10 lappend texts [list { These foo bar fuu _
* wrapped } {h e j} { These foo bar fuu hej_ * wrapped }] # 11 lappend texts [list { These foo
* wrapped foo bar _ } {h e j} { These foo * wrapped foo bar hej_ }] # 12 lappend texts [list { These foo _ "wrapped" } {BackSpace} { These foo_ "wrapped" }] # 13 lappend texts [list { These foo _
* wrapped } {BackSpace} { These foo_ * wrapped }] # 14 lappend texts [list { These foo bar fuu _
* wrapped } {h e j} { These foo bar fuu hej_ * wrapped }] # 15 lappend texts [list { These foo bar fuu
* wrapped foo bar _
* wrap2 } {h e j} { These foo bar fuu * wrapped foo bar hej_ * wrap2 }] # 16 lappend texts [list { These foo
* wrapped foo bar _ } {h e j} { These foo * wrapped foo bar hej_ }] # 17 lappend texts [list { These foo _ "wrapped" } {BackSpace} { These foo_ "wrapped" }] # 18 lappend texts [list { one two three_ four five six seven } {space} { one two three _ four five six seven }] # 19 lappend texts [list { one two three_ four five six seven } {space space space} { one two three _ four five six seven }] # 20, this one breaks but I will ignore this #lappend texts [list { #one two three_ four #five six seven #} {space space space space space space space space} { #one two three #_four five six seven #}] # Wrap is set to --| text .t rat_edit::create .t pack .t wm deiconify . set old_wrap_length $option(wrap_length) set option(wrap_length) 20 set index 0 foreach te $texts { StartTest "Wrapping edit insert [incr index]" # Insert the text .t delete 1.0 end .t insert 1.0 [lindex $te 0] # Convert any
\n to newlines with noWrap set pos 1.0 while {"" != [set pos [.t search "
\n" $pos]]} { .t replace $pos $pos+5c "\n" noWrap } # Set cursor location to '_' .t mark set insert [.t search "_" 1.0] .t delete insert # Insert new text focus -force .t update foreach k [lindex $te 1] { event generate .t <$k> } # Get result .t insert insert "_" set real [.t get 1.0 end-1c] if {[.t get 1.0 end-1c] != [lindex $te 2]} { puts "---- Original ----" puts [lindex $te 0] puts "---- Expected ----" puts [lindex $te 2] puts "---- Actual ----" puts [.t get 1.0 end-1c] ReportError "Action failed" } } set option(wrap_length) $old_wrap_length destroy .t wm withdraw . } package require rat_edit 1.0 test_wrap::test_cited_wrap test_wrap::test_edit_wrap test_wrap::test_edit_insert tkrat_2.2cvs20100105-dfsg.orig/tkrat/000077500000000000000000000000001137544547100171315ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/tkrat/.cvsignore000066400000000000000000000001261137544547100211300ustar00rootroot00000000000000Makefile languages.tcl text_*.tcl tclIndex .gdb_history tkrat.install tkrat .messages tkrat_2.2cvs20100105-dfsg.orig/tkrat/Makefile.in000066400000000000000000000075701137544547100212070ustar00rootroot00000000000000############################################################################# # TkRat software and its included text is Copyright 1996-2004 by # # Martin Forssn. # # # # The full text of the legal notice is contained in the file called # # COPYRIGHT, included with this distribution. # ############################################################################# SHELL = /bin/sh # Installation directories datarootdir = @datarootdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ MAN_DIR = @mandir@ BIN_DIR = @bindir@ DATA_DIR = @datadir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` LIB_DIR = @libdir@/`echo tkrat${VERSION} | sed '${TRANSFORM}'` sysconfdir = @sysconfdir@ INSTALL = @INSTALL@ VERSION = @VERSION@ TCLSH = @tclsh@ WISH = @wish@ COMPRESS = @compress@ CSUFFIX = @csuffix@ SSH = @ssh@ TRANSFORM = @program_transform_name@ RATLIBDIR = ${libdir}/`echo ratatosk | sed '${TRANSFORM}'` INSTALL_PREFIX = @INSTALL_PREFIX@ # Used for development environment only ETAGS = @etags@ #-------- No changes should be done below -------- TCLTMP = tcl.tmp INDEXCMD = echo "auto_mkindex . ${INDEXSRC}" | ${TCLSH} INDEX = tclIndex INDEXSRC = alias.tcl client.tcl compose.tcl convert.tcl dbase.tcl \ dialog.tcl exp.tcl folder.tcl font.tcl help.tcl \ info.tcl keydef.tcl options.tcl pgp.tcl fontedit.tcl \ preferences.tcl print.tcl show.tcl source.tcl start.tcl \ vfolder.tcl vfolderdef.tcl watcher.tcl winctl.tcl html.tcl \ mime.tcl vfolderwizard.tcl firstwizard.tcl addrlist.tcl \ delattachments.tcl group.tcl .messages/languages.tcl html3.tcl \ @TEXTFILES@ LANGUAGES = .messages/languages.tcl LANGSRC = Text/balloon.text Text/changes.text Text/help.text \ Text/messages.text Text/features.text all: ${LANGUAGES} ${INDEX} tkrat.install install.bin: tkrat.install if test ! -d ${INSTALL_PREFIX}${BIN_DIR} ; then \ ${INSTALL} -m 0755 -d ${INSTALL_PREFIX}${BIN_DIR} ;\ fi ${INSTALL} -m 0755 tkrat.install ${INSTALL_PREFIX}${BIN_DIR}/tkrat install.shared: ${INDEX} ${INDEXSRC} if test ! -d ${INSTALL_PREFIX}${DATA_DIR} ; then \ ${INSTALL} -m 0755 -d ${INSTALL_PREFIX}${DATA_DIR} ;\ fi for i in `ls ${INDEXSRC}` ; \ do \ ${INSTALL} -m 0644 $$i ${INSTALL_PREFIX}${DATA_DIR} ;\ done cd ${INSTALL_PREFIX}${DATA_DIR}; \ echo "auto_mkindex . *.tcl" | ${TCLSH} cd ${INSTALL_PREFIX}${DATA_DIR}; \ echo "if [catch {pkg_mkIndex . *.tcl} \ error] \ {puts \$$error; exit 1}" | ${TCLSH} install: install.shared install.bin ${LANGUAGES}: ${LANGSRC} [ -d .messages ] || mkdir .messages cd Text; ${TCLSH} dotext.tcl -nowarn ${INDEX}: ${INDEXSRC} ${LANGUAGES} Makefile ${INDEXCMD} Makefile: Makefile.in ../config.status cd ..; ./config.status tkrat/Makefile ${MAKE} || exit 1 exit 0 tkrat.install: Makefile rm -f $@ echo "#!${WISH}" >$@ echo "# This file automatically generated `date`" >>$@ echo "set env(LIBDIR) ${DATA_DIR}" >>$@ echo "set env(CONFIG_DIR) ${sysconfdir}/ratatosk" >>$@ echo "set env(COMPRESS) ${COMPRESS}" >>$@ echo "set env(CSUFFIX) ${CSUFFIX}" >>$@ echo "set env(SSH) \"${SSH}\"" >>$@ echo "foreach dir {${DATA_DIR}/util ${LIB_DIR}} {" >>$@ echo ' source $$dir/pkgIndex.tcl' >>$@ echo "}" >>$@ echo "set auto_path [concat ${DATA_DIR} \$$auto_path]" >>$@ echo "TkRatClientStart" >>$@ backup: tar zcf ~/backup/tkrat.`date +%Y%m%d`.tar.gz \ `ls ${INDEXSRC}` Makefile Text clean: rm -rf .messages rm -f core tkrat.install tcl.tmp ${INDEX} tags-internal: ${ETAGS} --append --output=../TAGS --lang=none \ --regex='/proc[ \t]+\([^ \t]+\)/\1/' ${INDEXSRC} tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/000077500000000000000000000000001137544547100200555ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/.cvsignore000066400000000000000000000000161137544547100220520ustar00rootroot00000000000000languages.tcl tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/CONTRIBUTORS000066400000000000000000000001241137544547100217320ustar00rootroot00000000000000fr: cmartin@univ-brest.fr de: Thomas.Fetcke@t-online.de pt: rlpires@telin.rug.ac.be tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/README000077500000000000000000000073511137544547100207460ustar00rootroot00000000000000 README file for the text system TkRat software and its included text is Copyright 1996-2004 by Martin Forssn. The full text of the legal notice is contained in the file called COPYRIGHT, included with this distribution. The dotext.tcl program builds the text files used by TkRat from the source files (suffixes .text and .help) located in this directory. When run without arguments ('tclsh dotext.tcl') it will print warnings about missing messages. Thus one can for example do tclsh dotext.tcl | grep Svenska to see if there are any messages in Swedish missing. During the normal build process, this output is suppressed. TO ADD A NEW LANGUAGE The only thing that has to be done to add a new language is to modify the "languages" list in defs.tcl. This will work since all messages that are not provided in the new language will fall back to the English version. :-) It is, however, appreciated if the person adding a new language also produces texts for that language. The syntax of the source files should be obvious (but is documented below anyway). You can use dotext.tcl ('tclsh dotext.tcl') to check if you have provided all needed texts for a new language. dotext.tcl will warn about any missing definitions. FORMAT OF THE SOURCE FILES The texts of TkRat are divided among four variables: t, changes, balloon and help. There is a one-to-one correlation between source files in this directory and variables. Messages for each language must be written in the character set specified in dotext.tcl for that language. That means that multiple character sets are used simultaneously in the source files. Each source file starts with a variable statement: variable Where is the name of the variable that the content of this file should go into. After that, the .text files consist of a lot of labels and their definitions. Each looks like: label Where is the label name (must be lower case and unique) and and are shorthand names for languages as defined in the "languages" list in dotext.tcl. The files are read via the tcl 'source' command. That means that they will follow the usual tcl quotation rules, etc. That is: * A message that contains embedded spaces bust be enclosed in quotes ("") or braces ({}). Example: "Tkrat 0.64" * A message which contains newlines must be enclosed by braces ({}). It is always a good idea to look how the files are written before adding anything new. FORMAT OF THE GENERATED FILES The output is generated in the directory specified by the outdir variable in dotext.tcl. First, a file 'languages.tcl' is created and in this file a procedure GetLanguages is defined. This procedure returns a list of the available languages. This file also defines the InitMessages proc, which should be called to initialize a set of messages (arguments are variable and language). The actual messages are stored in files named text__.tcl where is the variable and is the language of the messages in the file. Each such file contains one proc which is named "init_VAR_LANGUAGE" (VAR and LANGUAGE are substituted with the appropriate values), this is used by the auto-loading mechanism. This command builds the text files from the data files found in this directory. As input, it uses all files in the directory which end in ".text". These files should be in the following format: variable label [etc...] label [etc...] Where is the name of the variable which is to hold these messages, is the label of a message, and are language shorthand names as specified below. tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/balloon.text000066400000000000000000007323261137544547100224260ustar00rootroot00000000000000################################################################# # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssen # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # variable balText label current_folder_name sv {Namnet p den aktiva mappen} en {Name of the current folder} de {Der Name des angezeigten Ordners.} it {Nome della cartella corrente} fr {Nom de la bote lettres courante} sr {Ime tekueg skupa} pl {Nazwa aktywnego folderu} pt {Nome da pasta actual} label current_folder_size sv {Totalstorlek i bytes p den aktiva mappen} en {Total size in bytes of the current folder} de {Gre des angezeigten Ordners in Byte.} it {Dimensione totale in byte della cartella corrente} fr {Taille totale de la bote lettres courante en octets} sr {Ukupna veliina tekueg skupa u bajtovima} pl {Cakowity rozmiar folderu w bajtach} pt {Tamanho total em bytes da pasta actual} label current_folder_nummsg sv {Antal bre i den aktiva mappen} en {Number of messages in the current folder} de {Anzahl Nachrichten im angezeigten Ordner.} it {Numero di messaggi nella cartella corrente} fr {Nombre de messages dans la bote lettres courante} sr {Broj pisama u tekuem skupu} pl {Liczba wiadomoci w aktywnym folderze} pt {Nmero de mensagens na pasta actual} label status_text sv {Meddelanden frn TkRat} en {Messages from TkRat} de {Meldungen ber den Programmablauf.} it {Messaggi da TkRat} fr {Messages de TkRat} sr {Poruke TkRat-a} pl {Wiadomoci z TkRat} pt {Mensagens do TkRat} label move_msg sv {Denna menyn sparar en kopia av det aktiva brevet i den angivna mappen och markera brevet som raderat i denna mappen} en {This menu will put a copy of the current message into the indicated folder and mark the message for deletion in this folder} de {Aus diesem Men knnen Sie einen Ordner auswhlen, in den die angezeigte Nachricht verschoben wird. Im aktuellen Ordner wird die Nachricht zum Lschen vorgemerkt.} it {Questo menu salva una copia del messaggio corrente nella cartella specificata e contrassegnera' il messaggio per la rimozione} fr {Ce menu sauve une copie du message courant dans la bote lettres dsigne et le marque comme tant dtruire} sr {Ovaj meni e sauvati kopiju tekueg pisma u naznaenom skupu i oznaiti pismo za brisanje iz tekueg foldera} pl {Menu zapisze kopi wiadomoci w wybranym folderze i oznaczy j jako usunit w obecnym} pt {Este menu grava uma cpia da mensagem na pasta indicada e marca a mensagem para a sua apagamento nesta pasta} label delete_msg sv {Markera det aktiva brevet fr radering} en {Mark the current message for deletion} de {Die angezeigte Nachricht zum Lschen vormerken.} it {Contrassegna il messaggio corrente per la rimozione} fr {Marque le message courant pour destruction} sr {Oznauje tekuu poruku za brisanje} pl {Przeznacz t wiadomo do usunicia} pt {Marca a mensagem para apagamento} label compose_msg sv {Skriv ett nytt brev} en {Compose a new message} de {Eine neue Nachricht schreiben.} it {Componi un nuovo messaggio} fr {Rdige un nouveau message} sr {Sastavljanje novog pisma} pl {Utwrz now wiadomo} pt {Inicia a redaco de uma nova mensagem} label pgp_none sv {Detta betyder att det aktiva brevet inte r signerat} en {This means that the current message is not signed} de {Die angezeigte Nachricht trgt keine Unterschrift (digitale Signatur).} it {Significa che il messaggio corrente non firmato} fr {Ceci signifie que le message n'est pas sign} sr {Ovo znai da tekue pismo nije potpisano} pl {To oznacza, e wiadomo nie jest oznaczona sygnatur} pt {Significa que a mensagem no est assinada} label pgp_unchecked sv {Detta betyder att brevet r signerat men att signaturen har inte har kontrollerats nnu. Tryck p den vnstra knappen fr att kontrollera den} en {This means that this message is signed but the signature has not yet been checked. Press left button to check it.} de {Die angezeigte Nachricht trgt eine Unterschrift (digitale Signatur), die aber noch nicht geprft wurde. Zum Prfen gengt ein Mausklick auf diese Schaltflche.} it {Significa che il messaggio firmato ma la firma non ancora stata verificata. Premere il tasto sinistro per verificarla} fr {Ceci signifie que le message est sign mais que la vrification n'a pas eu lieu. Cliquez le bouton 1 pour le vrifier} sr {Ovo znai da je tekue pismo potpisano ali potpis nije jo proveren. Kliknite levim dugmetom da se potpis proveri.} pl {To oznacza, e wiadomo jest oznaczona sygnatur, ale sygnatura nie zostaa jeszcze sprawdzona. Nacinij lewy przycisk by j sprawdzi.} pt {Significa que a mensagem est assinada mas a assinatura ainda no foi verificada. Pressione o boto esquerdo do rato para a verificao} label pgp_good sv {Detta brev r korrekt signerat} en {The signature on this message is good} de {Die Unterschrift (digitale Signatur) unter der angezeigten Nachricht ist gltig.} it {La firma del messaggio accettata} fr {La signature de ce message est correcte} sr {Potpis na ovom pismu je ispravan} pl {Sygnatura tej wiadomoci jest poprawna} pt {A assinatura nesta mensagem correcta} label pgp_bad sv {Signaturen passar INTE till brevets innehll} en {The signature does NOT match the content of this message} de {Die Unterschrift (digitale Signatur) unter der angezeigten Nachricht ist UNGLTIG.} it {La firma NON verifica il contenuto del messaggio} fr {La signature ne correspond pas au contenu du message} sr {Potpis na ovom pismu NE ODGOVARA njegovom sadraju!} pl {Sygnatura nie zgadza si z treci wiadomoci} pt {A assinatura no corresponde ao contedo da mensagem} label part_sig sv {En del av brevet var signerat, meninte allt} en {A portion of the message was signed, but not all of it} de {Nur ein Teil der Nachricht ist unterschrieben (digital signiert).} it {Solo una parte del messaggio firmata} fr {Seule une partie du message est signe} sr {Deo pisma je potpisan, ali ne i celo pismo} pl {Cz listu zostaa oznaczona sygnatur, ale nie cay} pt {A mensagem est apenas parcialmente assinada} label watcher_enable sv {Lter dig stta p eller stnga av vktarfnstret} en {Lets you enable or disable the Watcher window} de {Der Wchter benachrichtigt Sie, wenn neue Nachrichten eintreffen. Mit dieser Option knnen Sie den Wchter ein- oder ausschalten.} it {Abilita o disabilita la finestra di controllo} fr {Active ou dsactive la fentre de contrle} sr {Dozvoljava / onemoguuje nadzorni prozor} pl {Pozwala odblokowa lub zablokowa okno kontroli} pt {Permite (des)activar o controlo (watcher)} label reread_userproc sv {Lser om din userproc-fil} en {Rereads your userproc file} de {Benutzerprozeduren neu einlesen.} it {Rilegge il file userproc} fr {Relit le fichier userproc} sr {Ponovo ita vau userproc-datoteku} pl {Ponownie czyta plik userproc} pt {Rel o ficheiro userproc} label reread_mailcap sv {Lser om alla mailcap-filer} en {Rereads all mailcap files} de {mailcap-Dateien neu einlesen.} it {Rilegge i file mailcap} fr {Relit tous les fichiers mailcap} sr {Ponovo ita sve mailcap-datoteke} pl {Ponownie czyta pliki mailcap} pt {Rel os ficheiros mailcap} label check_dbase sv {Kontrollerar om det finns fel i TkRat's databas} en {Checks the TkRat database for any errors} de {Die TkRat Datenbank auf Fehler prfen.} it {Controlla che il database TkRat non contenga errori} fr {Vrifie la base de donnes de TkRat} sr {Proverava da li ima greaka u TkRat bazi} pl {Sprawdza, czy w bazie danych TkRat s bdy} pt {Verifica se a base de dados est livre de erros} label check_fix_dbase sv {Kontrollerar om det finns fel i TkRat's databas, gr ocks sitt bsta att reparera alla fel som hittas} en {Checks the TkRat database for any errors, also does its best to correct any errors found} de {Die TkRat Datenbank auf Fehler prfen und ggf. eine Korrektur versuchen.} it {Controlla il database TkRat e, se trova errori, fa del suo meglio per ripararli} fr {Vrifie la base de donnes de TkRat et tente de la corriger} sr {Proverava da li ima greaka u TkRat bazi, i ujedno pokuava da ih ispravi} pl {Sprawdza, czy w bazie danych TkRat s bdy, oraz prubje jak moe je naprawi} pt {Verifica se a base de dados est livre de erros, tenta corrigir quaisquer erros encontrados} label see_log sv {Visar en lista ver alla interna meddelanden som genererats av TkRat} en {Shows a log of all internal messages generated by TkRat} de {Zeige eine Liste der Meldungen, die TkRat angezeigt hat.} it {Mostra la lista dei messaggi generati internamente da TkRat} fr {Donne le journal des messages internes de TkRat} sr {Prikazuje listu svih internih poruka TkRat-a} pl {Pokazuje list wszystkich wewntrznych wiadomoci wygenerowanych przez TkRat} pt {Mostra log das mensagens internas do TkRat} label quit sv {Avslutar TkRat} en {Quits TkRat} de {TkRat beenden.} it {Termina TkRat} fr {Termine TkRat} sr {Kraj rada} pl {Zamyka TkRat} pt {Termina o TkRat} label folder_menu sv {Denna meny listar dina mappar. Du kan vlja en mapp i menyn fr att titta p den mappen istllet} en {This menu shows a list of your folders. You can select any listed item to examine it.} de {Die Liste der Ordner anzeigen. Bei Auswahl eines Ordners wird dieser anstelle des aktuellen Ordners angezeigt.} it {Questo menu mostra una lista delle cartelle. E' possibile selezionare qualsiasi cartella per vederne il contenuto} fr {Ce menu liste toutes vos botes lettres. Slectionnez un article pour examiner la bote correspondante} sr {Ovaj meni sadri spisak vaih skupova. Moete odabrati bilo koju stavku da biste je pregledali.} pl {Menu pokazuje list twoich folderw. Moesz wybra dowolny element by sprawdzi wybrany folder} pt {Mostra uma lista das suas pastas. Pode seleccionar qualquer item e verificar essa pasta} label message_menu sv {Denna meny innehller operationer p enstaka brev} en {This menu contains functions which operate on messages} de {Dieses Men enthlt einige Befehle, mit denen Sie die angezeigte Nachricht bearbeiten knnen.} it {Questo menu contiene operazioni che si possono effettuare sui messaggi} fr {Ce menu contient des oprations qui s'appliquent un message} sr {Ovaj meni sadri naredbe za rad sa tekuim pismom} pl {Menu zawiera polecenia modyfikowania wiadomoci} pt {Este menu contem aces que so operadas numa mensagem} label find sv {Skapar ett fnster dr du kan ska i brevlistan eller i det aktiva brevet} en {Pops up a window which lets you search in the list of messages or the current message} de {Mit dieser Funktion knnen Sie innerhalb der Nachrichtenliste oder der angezeigten Nachricht nach einem Textfragment suchen lassen.} it {Mostra una finestra che permette di cercare nella lista dei messaggi o nel messaggio corrente} fr {Ouvre la fentre de recherche dans la liste des messages ou dans le message courant} sr {Otvara prozor u kojem moete zadati pretragu unutar tekueg pisma ili grupe pisama} pl {Okno umoliwiajce wyszukiwanie w zbiorze wiadomoci lub w aktywnej wiadomoci} pt {Abre uma janela que lhe permite realizar uma busca na lista de mensagens ou na mensagem actual} label compose sv {Skriv ett nytt brev} en {Compose a new message} de {Eine neue Nachricht schreiben.} it {Componi un nuovo messaggio} fr {Rdiger un nouveau message} sr {Sastavljanje novog pisma} pl {Utwrz now wiadomo} pt {Inicia a redaco de uma nova mensagem} label reply_sender sv {Skriv ett svar till avsndaren av det aktiva brevet} en {Send a reply to the originator of the current message} de {Eine Antwort an den Absender der angezeigten Nachricht schreiben.} it {Rispondi al mittente del messaggio corrente} fr {Rpondre uniquement l'expditeur du message courant} sr {alje odgovor poiljaocu tekueg pisma} pl {Odpowiedz nadawcy wiadomoci} pt {Escreve uma resposta ao remetente da mensagem} label reply_all sv {Skriv ett svar som gr tillsvl avsndaren som alla mottagare av det aktiva brevet} en {Send a reply to the originator and all recipients of the current message} de {Eine Antwort an den Absender und an alle Empfnger der angezeigten Nachricht schreiben.} it {Rispondi al mittente e a tutti i destinatari del messaggio corrente} fr {Rpondre l'expditeur du message courant ainsi qu' tous ses destinataires} sr {alje odgovor poiljaocu i svim primaocima tekueg pisma} pl {Odpowiedz nadawcy i wszystkim adresatom wiadomoci} pt {Escreve uma resposta ao remetente e a outros destinatrios da mensagem} label forward_inline sv {Skicka vidare det aktiva brevet} en {Forward the current message as inline text} de {Die angezeigte Nachricht innerhalb einer neuen Nachricht weiterleiten.} it {Rispedisci il messaggio corrente} fr {Faire suivre le message courant} sr {Prosleuje tekue pismo} pl {Forwarduj t wiadomo} pt {Reenvia a mensagem} label forward_attached sv {Skicka vidare det aktiva brevet som en bilaga} en {Forward the current message as an attachment} de {Die angezeigte Nachricht als Anhang einer neuen Nachricht weiterleiten.} it {Rispedisci il messaggio corrente come allegato} fr {Faire suivre le message courant en pice jointe} sr {Prosleuje tekue pismo dalje kao dodatak} pl {Forwarduj t wiadomo jako zacznik} pt {Reenvia a mensagem como ficheiro anexo} label extract_adr sv {Skapa alias frn adresserna i det aktiva brevet} en {Create aliases from the addresses in the current message} de {Adressen aus der angezeigten Nachricht in ein Adressbuch bernehmen.} it {Crea alias dalla lista di indirizzi del messaggio corrente} fr {Crer un alias partir des adresses contenu dans le message courant.} sr {Sastavlja aliase iz adresa u tekuem pismu} pl {Utwrz aliasy z adresw w tej wiadomoci} pt {Cria alias a partir de endereos presentes na mensagem} label move sv {Flytta det aktiva brevet till en annan mapp} en {Move the current message to another folder} de {Die angezeigte Nachricht in einen anderen Ordner verschieben.} it {Muove il messaggio corrente in un altro folder} fr {Dplacer le message vers une autre bote lettres} sr {Premeta tekuu poruku u drugi skup} pl {Przenie wiadomo do innego foldera} pt {Move a mensagem para outra pasta} label delete sv {Markera det aktiva brevet fr radering. Det kommer inte att raderas frrn du synkroniserar mappen, byter till en annan mapp eller avslutar TkRat} en {Mark the current message for deletion. It will not actually be deleted until you synchronize the folder, change to another folder, or quit TkRat.} de {Die angezeigte Nachricht zum Lschen vormerken. Tatschlich gelscht werden die vorgemerkten Nachrichten beim Abgleich des Ordners, beim Wechsel zu einem anderen Ordner oder beim Beenden von TkRat.} it {Contrassegna il messaggio corrente per la rimozione. Il messaggio non verra' cancellato fino a quando non verra' sincronizzata la cartella, selezionata un'altra cartella o terminato il programma} fr {Marque le message pour destruction. Il ne sera pas dtruit avant de changer de bote lettres, de synchroniser les botes lettres ou de quitter TkRat} sr {Oznaava tekue pismo za brisanje. Pismo e biti zaista obrisano tek onda kada sinhronizujete skup, preete na drugi skup, ili prekinete rad u TkRat-u.} pl {Przeznacz wiadomo do usunicia. W rzeczywistoci nie bdzie ona usunita do czasu synchronizacji folderu, przejd do innego folderu albo opu TkRat} pt {Marca a mensagem para apagamento. A mensagem ser apagada apenas depois de sincronizar a pasta, mudar para outra pasta ou terminar o programa} label undelete sv {Ta bort raderingsmarkeringen p det aktiva brevet} en {Remove the deletion marking on the current message} de {Vormerkung zum Lschen rckgngig machen.} it {Rimuove il contrassegno di cancellazione del messaggio corrente} fr {Enlve la marque de destruction} sr {Uklanja oznaku za brisanje sa tekueg pisma} pl {Anuluj usunicie wiadomoci} pt {Remove a marca de apagamento da mensagem} label print sv {Skriv ut det aktiva brevet} en {Print the current message} de {Die angezeigte Nachricht ausdrucken.} it {Stampa il messaggio corrente} fr {Imprime le message courant} sr {tampa tekue pismo} pl {Drukuj wiadomo} pt {Imprime a mensagem} label structure sv {Denna undermeny visar strukturen p brevet och dess bilagor} en {This submenu shows the structure of the message and its attachments} de {Den Aufbau der Nachricht (Text, Anhnge etc.) anzeigen.} it {Questo sottomenu mostra la struttura del messaggio e degli allegati} fr {Ce sous-menu montre la structure du message et de ses pices jointes} sr {Ovaj podmeni prikazuje strukturu pisama i njegovih dodataka} pl {To menu pokazuje struktur listu i jego zacznikw} pt {Este submenu mostra a estrutura da mensagem e dos ficheiros anexos} label show_all_headers sv {Visa alla huvudrader i meddelanden} en {Show all header lines of messages} de {Alle Kopfzeilen der Nachricht anzeigen.} it {Mostra tutte le intestazion i dei messaggi} fr {Montre tous les en-ttes du message} sr {Pokazuje kompletno zaglavlje pisama} pl {Poka wszystkie nagwki wiadomoci} pt {Mostra todas as linhas de cabealhos} label show_selected_headers sv {Visa endast de valda huvudraderna} en {Show only the selected header lines} de {Nur die in den Einstellungen festgelegten Kopfzeilen anzeigen.} it {Mostra solo le intestazioni selezionate} fr {Ne montre que les en-ttes slectionns} sr {Pokazuje samo odabrane delove zaglavlja} pl {Poka tylko wybrane nagwki} pt {Mostra apenas os cabealhos seleccionados} label show_no_headers sv {Visa inga huvudrader} en {Do not show any header lines} de {Keine Kopfzeilen anzeigen.} it {Non mostra alcuna intestazione} fr {Ne montre aucun en-tte} sr {Ne prikazuje zaglavlje pisama} pl {Nie pokazuj adnych nagwkw} pt {No mostra qualquer linha de cabealho} label group_menu sv {Denna meny innehller kommandon som arbetar med en grupp av brev. Alla brev som ingr i gruppen r mrkta med ett 'F'. Dessa kommandon appliceras endast p markerade brev i den aktiva mappen} en {This menu works on a group of messages. All messages which are part of the group are marked with a 'F'. These commands only apply to flagged messages in the active folder.} de {Men mit Kommandos zur Bearbeitung einer Gruppe von Nachrichten. Die Gruppe besteht aus allen Nachrichten im aktuellen Ordner, die mit 'F' markiert sind.} it {Questo menu opera sun un gruppo di messaggi. Tutti i messaggi che fanno parte del gruppo sono contrassegnati da una 'F'. Questi comandi valgono solo per i messaggi contrassegnati nella cartella attiva} fr {Ce menu travaille sur un groupe de messages signals par un 'F'. Les commandes ne s'appliquent qu'aux messages ainsi marqus dans la bote lettres courante.} sr {Naredbe ovog menija obrauju grupu pisama, tj. sva pisma koja su oznaena sa 'F' i nalaze se u tekuem skupu} pl {To menu operuje na grupie wiadomoci. Wszystkie wiadomoci nalece do grupy s oznaczone przez 'F'. Te polecenia dziaaj tylko na oflagowanych wiadomociach w aktywnym folderze} pt {Este menu processa um grupo de mensagens em simultneo. Todas as mensagens parte de um grupo esto marcadas com um 'F' ('flag'). Os comandos do menu aplicam-se apenas a mensagens marcadas nesta pasta} label create_in_win sv {Skapa en ny lista |ver meddelanden och gr gruppmarkeringen i det fnstret} en {Pop up a new list of messages and mark them for group inclusion} de {ffnet ein neues Fenster mit der Nachrichtenliste zum Markieren einer Gruppe von Nachrichten.} it {Mostra una nuova lista di messaggi e permette di effettuare selezioni di gruppi al suo interno} fr {Ouvre une nouvelle liste de messages pour grouper les messages de la bote lettres courante} sr {Otvara novi spisak pisama za oznaavanje grupe} pl {Otwrz now list wiadomoci i oznacz grup w tym oknie} pt {Abre uma nova lista de mensagens que permite agrupar mensagens} label create_by_expr sv {Skapa grupp mha uttryck} en {Create a group by expression} de {Nachrichten anhand von Suchkriterien auswhlen.} it {Crea un gruppo utilizzando un'espressione} fr {Cre un groupe avec une formule} sr {Stvara grupu prema izrazu} pl {Utwrz grup z wyraenia} pt {Cria um grupo atravs de uma expresso} label use_saved_expr sv {Skapa en grupp mha ett sparat uttryck} en {Build a group by using an old, saved expression} de {Nachrichten anhand gespeicherter Suchkriterien auswhlen.} it {Crea un gruppo usando un'espressione salvata in precedenza} fr {Cre un groupe avec une formule sauvegarde} sr {Stvara grupu koristei ranije sauvani izraz} pl {Zbuduj grup uywajc starego zapisanego wyraenia} pt {Cria um grupo atravs de uma expresso gravada} label clear_group sv {Ta bort gruppmarkeringen frn alla brev} en {Remove group marking on all messages} de {Gruppe auflsen (Markierung von allen Nachrichten entfernen).} it {Rimuove il contrassegno di raggruppamento da tutti i messaggi} fr {te les marques de groupe tous les messages} sr {Uklanja grupne oznake sa svih pisama} pl {Usu oznaczenia grupy dla wszystkich wiadomoci} pt {Remove marcaes de grupo das mensagens} label delete_group sv {Markera alla brev i gruppen fr radering} en {Mark all messages in the group for deletion} de {Alle markierten Nachrichten zum Lschen vormerken.} it {Contrassegna tutti i messaggi del gruppo per la cancellazione} fr {Marque pour la destruction tous les messages du groupe} sr {Oznaava sva pisma u grupi za brisanje} pl {Przeznacz wszystkie wiadomoci w grupie do usunicia} pt {Marca todas as mensagens do grupo para remoo} label undelete_group sv {Ta bort raderingsmrkningen fr alla brev i gruppen} en {Remove the deletion marking from all messages in the group} de {Bei allen markierten Nachrichten die Vormerkung zum Lschen rckgngig machen.} fr {Annule la marque pour destruction de tous les messages du groupe} sr {Ponitava oznake za brisanje sa svih pisama u grupi} pl {Anuluj usunicie wszystkich wiadomoci z grupy} pt {Remove marcao para apagamento das mensagens do grupo} label print_group sv {Skriv ut alla brev i gruppen} en {Print all messages in the group} de {Alle markierten Nachrichten ausdrucken.} it {Stampa tutti i messaggi nel gruppo} fr {Imprime tous les messages du groupe} sr {tampa sva pisma iz grupe} pl {Drukuj wszystkie wiadomoci z grupy} pt {Imprime todas as mensagens do grupo} label move_group sv {Flytta samtliga brev i gruppen till en annan mapp} en {Move all messages in the group to another folder} de {Alle markierten Nachrichten in einen anderen Ordner verschieben.} it {Muove tutti i messaggi nel gruppo in un'altra cartella} fr {Dplace tous les messages du groupe vers une autre bote lettres} sr {Premeta sva pisma iz grupe u drugi skup} pl {Przenie wszystkie wiadomoci z grupy do innego katalogu} pt {Move todas as mensagens do grupo para outra pasta} label admin_menu sv {Denna meny innehller diverse administrativa kommandon} en {This menu contains misc. administrative commands} de {Diverse Einstellungen und Verwaltungsfunktionen.} it {Questo menu contiene vari comandi d'amministrazione} fr {Ce menu contient diverses commandes administratives} sr {Ovaj meni sadri pomone i administrativne naredbe} pl {To menu zawiera rne opcje konfiguracyjne} pt {Este menu contm diversos comandos de configurao} label browse_mode sv {Tiitta-mod ser till att brdtexten i breven inte laddas automatiskt. Fr att se brdtexten av ett brev s mste man trycka p knappen "Visa texten"} en {Browse mode keeps the bodies of messages from being loaded automatically. The actual message bodies are loaded when you press the "Show body" button.} de {Im Browsemodus wird der Inhalt der Nachrichten nicht automatisch angezeigt, sondern erst bei Bettigen der Schaltflche "Zeige Inhalt".} # it {Modo titoli disabilita il caricamento automatico del corpo dei # messaggi. Il corpo viene caricato quando si preme "Mostra corpo" o si # fa doppio click sul titolo del messaggio nella lista dei messaggi} # fr {En mode papillon, le corps des messages n'est affich que # lorsque vous appuyez le bouton "Montrer Corps" ou lorsque vous # double-cliquez sur un message de la liste.} # sr {U reimu pregledanja se sadraj pisama ne uitava automatski, # ve samo onda kada kliknete na dugme "Pokai sadraj" ili dvaput # kliknete na pismo u spisku skupa.} # pl {Tryb przegldania uniemoliwia automatyczne otwieranie treci # wiadomoci. Tre jest otwierana dopiero po naciniciu przycisku # "poka tre" lub podwjnym klikniciu na wiadomoci w licie # wiadomoci} # pt {Este modo evita que o corpo das mensagens seja carregado # imediatamente. Este apenas carregado quando pressionar o # boto "Mostrar corpo" ou duplo clique na mensagem} label update sv {Tittar efter nya brev i den aktiva mappen, denna funktion raderar inte brev markerade fr radering} en {Checks the current folder for new messages, does not delete messages marked for deletion} de {Neu im Ordner eingetroffene Nachrichten hinzuladen (lscht nicht die zum Lschen vorgemerkten Nachrichten).} it {Controlla la cartella corrente per nuovi messaggi. Non cancella i messaggi contrassegnati} fr {Vrifie l'arrive de nouveaux messages dans la bote lettres courante. Les messages marqus ne sont pas dtruits.} sr {Proverava da li u tekuem skupu ima novih pisama; ne uklanja pisma oznaena za brisanje} pl {Sprawd czy w folderze s nowe wiadomoci, nie usuwa wiadomoci przeznaczonych do usunicia} pt {Verificar a chegada de novas mensagens na pasta, no apaga mensagens marcadas para apagamento} label sync sv {Synkroniserar den visade mappen med mappen p disk, dvs skriver ut eventuella ndrade flaggor raderar brev som markerats fr radering och tittar efter nya brev} en {Synchronizes the displayed folder with the actual mailbox; that is, writes any flag-changes, deletes messages marked for deletion, and checks for new messages.} de {Ordner mit Speicherkopie abgleichen. Ldt neu eingetroffene Nachrichten und lscht zum Lschen vorgemerkte Nachrichten.} it {Sincronizza la cartella visualizzata con la cartella fisica, cio salva contrassegni, cancella i messaggi e controlla la presenza di messaggi nuovi} fr {Synchronise la bote lettres visualise avec la bote lettres relle. Les messages dtruire sont dtruits. La prsence de nouveaux messages est teste.} sr {Sinhronizuje prikazani skup sa stvarnim stanjem - upisuje promenjene oznake, uklanja pisma oznaena za brisanje i proverava ima li novih pisama.} pl {Synchronizuj wywietlany folder ze skrzynk, czyli zapisz wszystkie zmiany flag, usu wybrane wiadomoci i sprawd, czy s nowe wiadomoci} pt {Sincroniza a pasta com a caixa de correio real, isto , actualiza mudanas nas marcaes, apaga mensagens marcadas para remoo e verifica a chegada de novas mensagens} label newedit_folder sv {ppnar fnstret dr man kan skapa nya mappar och ndra befintliga} en {Opens the window where you may add and edit folders} de {ffnet ein Fenster, in dem Ordner angelegt und bearbeitet werden knnen.} it {Apre la finestra che permette di aggiungere e modificare le cartelle} fr {Ouvre un dialogue pour crer et diter les botes lettres.} sr {Otvara prozor u kome moete dodati i menjati skupove} pl {Okno, gdzie moesz dodawa i edytowa foldery} pt {Abre a janela onde poder (re)definir pastas} label preferences sv {ppnar instllningsfnstret} en {Opens the Preferences window} de {ffnet ein Fenster zum Bearbeiten diverser Programm-Einstellungen.} it {Apre la finestra delle preferenze} fr {Ouvre la fentre des prfrences.} sr {Otvara prozor za podeavanje opcija} pl {Okno preferencji} pt {Abre a janela de preferncias/configurao} label define_keys sv {Lter dig inspektera och ndra tangentbordsdefinitionerna fr detta fnster} en {Lets you view/change the key definitions for this window} de {Tastaturzuordnungen fr dieses Fenster bearbeiten.} it {Permette la visualizzazione/modifica delle definizioni dei tasti per questa finestra} fr {Voir et modifier les raccourcis clavier de cette fentre.} sr {Pregled i promena naredaba dodeljenih tasterima za tekui prozor} pl {Pozwala przglda/zmienia definicje klawiatury dla tego okna} pt {Permite visualizar e actualizar as teclas de atalho para esta janela} label saved_expr sv {Lter dig redigera de sparade uttrycken som anvnds fr att skapa grupper} en {Lets you edit the saved expressions used to create groups} de {Gespeicherte Suchkriterien (zur Auswahl von Gruppen) anzeigen und bearbeiten.} it {Permette la modifica delle espressioni salvate usate per creare gruppi} fr {Permet d'diter les expressions sauves pour crer les groupes.} sr {Omoguava izmene sauvanih izraza za stvaranje grupa} pl {Pozwala edytowa zapisane wyraenia uywane do tworzenia grup} pt {Permite a edio de expresses usadas para criar grupos de mensagens} label sort_threaded sv {Sortera svar efter breven de svarar p} en {Sort replies after the messages they are in response to} de {Die Nachrichten nach dem Diskussionsverlauf sortieren. Antworten erscheinen unterhalb der Nachricht, auf die sie sich beziehen.} fr {Affiche la rponse aprs le message auquel la rponse s'applique..} sr {Rea odgovore prema pismima na koja su upueni} pl {Sortuj odpowiedzi po odpowiadajcych im wiadomociach} pt {Ordena mensagens de resposta a seguir s mensagens s quais do resposta} label sort_subject sv {Sortera breven efter datum, men gruppera ihop brev med samma mne} en {Sort messages according to date, but group messages with the same subject together} de {Die Nachrichten alphabetisch nach dem Betreff sortieren, Nachrichten mit demselben Betreff aufsteigend nach dem Absende-Datum.} it {Riordina i messaggi a seconda della data, raggruppando messaggi con lo stesso soggetto} fr {Trie les messages par dates tout en les groupant par objet. Les plus rcents en bas.} sr {Rea pisma po datumu, ali grupie pisma sa istim naslovom zajedno} pl {Sortuj wiadomoci zalenie od daty, ale grupuj wiadomoci z tym samym tematem razem} pt {Ordena mensagens de acordo com a data, mas agrupa mensagens com o mesmo assunto} label sort_subjectonly sv {Sortera breven alfabetsikt efter mne} en {Sort messages alphabetically by subject} de {Die Nachrichten alphabetisch nach dem Betreff sortieren.} it {Riordina i messaggi alfabeticamente secondo il soggetto} fr {Trie les messages par ordre alphabtique d'objet.} sr {Rea pisma po azbunom redu naslova} pl {Sortuj wiadomoci alfabetycznie wg. tematu} pt {Ordena mensagens pelo assunto, alfabeticamente} label sort_sender sv {Sortera breven alfabetsikt efter avsndare och efter datum} en {Sort messages alpabetically by sender and by date} de {Die Nachrichten alphabetisch nach dem Absender sortieren, Nachrichten desselben Absenders aufsteigend nach dem Absende-Datum.} fr {Trie les messages par ordre alphabtique d'expditeur et par date. Les plus rcents en bas} sr {Rea pisma po azbunom redu poiljalaca i datumu} pl {Sortuj wiadomoci alfabetycznie wg. nadawcy i daty} pt {Ordena mensagens alfabeticamente por remetente e data} label sort_senderonly sv {Sortera breven alfabetsikt efter avsndare} en {Sort messages alphabetically by sender} de {Die Nachrichten alphabetisch nach dem Absender sortieren.} it {Riordina i messaggi alfabeticamente secondo il mittente} fr {Trie les messages par ordre alphabtique de l'expditeur.} sr {Rea pisma po azbunom redu poiljalaca} pl {Sortuj wiadomoci alfabetycznie wg. nadawcy} pt {Ordena mensagens alfabeticamente por remetente} label sort_folder sv {Sortera inte breven} en {Do not sort the messages} de {Die Nachrichten nicht sortieren, sondern die physische Reihenfolge der Nachrichten im Ordner verwenden. (In der Regel ist das die Reihenfolge, in der die Nachrichten im Ordner abgelegt wurden.)} it {Non riordina i messaggi} fr {Ne trie pas les messages.} sr {Iskljuuje reanje pisama} pl {Nie sortuj wiadomoci} pt {No ordena as mensagens (ordem de entrada na pasta)} label sort_reverseFolder sv {Sortera inte breven men vnd ordningen} en {Do not sort the messages but reverse the order} de {Die Nachrichten nicht sortieren, sondern die umgekehrte physische Reihenfolge der Nachrichten im Ordner verwenden. (In der Regel ist das die umgekehrte Reihenfolge, in der die Nachrichten im Ordner abgelegt wurden.)} it {Non riordina i messaggi ma li mostra invertiti} fr {Ne trie pas les messages, renverse l'ordre d'apparition.} sr {Ne rea pisma, ali obre redosled} pl {Nie sortuj wiadomoci, ale odwr ich kolejno} pt {Inverte a ordem das mensagens} label sort_date sv {Sortera breven efter avsndningstid (tidigare hgre upp)} en {Sort the messages according to date sent (earlier at the top)} de {Die Nachrichtenliste aufsteigend nach dem Absende-Datum der Nachrichten sortieren. Die lteren Nachrichten erscheinen oben in der Liste.} it {Riordina i messaggi a seconda della data di spedizione (i primi in alto)} fr {Trie les messages par date d'expdition (les plus rcents en bas).} sr {Rea pisma prema datumu slanja (ranije na vrhu)} pl {Sortuj wiadomoci zalenie od daty wysania (wczeniejsze na gr)} pt {Ordena mensagens de acordo com a data de envio (topo mais antiga)} label sort_reverseDate sv {Sortera breven efter avsndningstid (tidigare lngre ner)} en {Sort the messages according to date sent (earlier at the bottom)} de {Die Nachrichtenliste absteigend nach dem Absende-Datum der Nachrichten sortieren. Die neuesten Nachrichten erscheinen oben in der Liste.} it {Riordina i messaggi a seconda della data di spedizione (i primi in basso)} fr {Trie les messages par date d'expdition (les plus rcents en haut).} sr {Rea pisma prema datumu slanja (ranije na dnu)} pl {Sortuj wiadomoci zalenie od daty wysanie (wczeniejsze na d)} pt {Ordena as mensagens de acordo com a data de envio (mais recente no topo)} label sort_size sv {Sortera breven efter storlek (srre lngst ner)} en {Sort the messages according to message size (larger at the bottom)} de {Die Nachrichtenliste aufsteigend nach der Gre der Nachrichten sortieren. Die kleinsten Nachrichten erscheinen oben in der Liste.} fr {Trie les messages par leur taille (les plus gros en bas).} sr {Rea pisma prema veliini (najvea na dnu)} pl {Sortuj wiadomoci zalenie od razmiaru (wiksze na d)} pt {Ordena mensagens de acordo com o tamanho (topo mais pequena)} label sort_reverseSize sv {Sortera breven efter storlek (srre lngst upp)} en {Sort the messages according to message size (larger at the top)} de {Die Nachrichtenliste absteigend nach der Gre der Nachrichten sortieren. Die grten Nachrichten erscheinen oben in der Liste.} fr {Trie les messages par leur taille (les plus gros en haut).} sr {Rea pisma prema veliini (najvea na vrhu)} pl {Sortuj wiadomoci zalenie od rozmiaru (wiksze na gr)} pt {Ordena mensagens de acordo com o tamanho (topo maior)} label help_menu sv {Ger diverse olika former av hjlp} en {Gives various forms of help} de {Diverse Hilfen anzeigen.} it {Fornisce vari tipi di aiuto} fr {Donne des formes d'aide varies.} sr {Daje razne vidove pomoi} pl {Daje rne formy pomocy} pt {Fornece diversas formas de ajuda} label balloon_help sv {Stter p/stnger av ballonghjlpen} en {Enables/disables the balloon help} de {Die Kontexthilfen ein-/ausschalten.} it {Abilita/disabilita l'aiuto-balloon} fr {Active/dsactive les bulles d'aide.} sr {Ukljuuje/iskljuuje balon-poruke} pl {Wcza/Wycza bombelki z pomoc} pt {(Des)Activa a ajuda em balo} label show_version sv {Visa vilken version av TkRat du anvnder} en {Show which version of TkRat you are using} de {Die Version des TkRat-Programms anzeigen.} it {Mostra la versione corrente di TkRat} fr {Indique la version actuelle de TkRat} sr {Pokazuje koju verziju TkRat-a koristite} pl {Pokazuje wersj TkRat, ktrej uywasz} pt {Mostra a verso do TkRat que est a usar} label explain_ratatosk sv {Frklarar varifrn namnet Ratatosk kommer} en {Explains where the name Ratatosk comes from} de {Die Herkunft des Namens "Ratatosk" erklren.} it {Spiega l'origine del nome Ratatosk} fr {Explique l'origine du nom Ratatosk.} sr {Objanjava odakle ime Ratatosk} pl {Informuje, skd si wzia nazwa Ratatosk} pt {Explica a origem do nome "Ratatosk"} label help_window sv {Visar hjlpfnstret} en {Shows the Help window} de {Das Haupt-Hilfefenster anzeigen.} it {Mostra la finestra d'aiuto} fr {Ouvre la fentre d'aide.} sr {Otvara prozor za pomo} pl {Okno pomocy} pt {Exibe a janela de ajuda} label send_bug sv {Lter dig skicka en felrapport till frfattaren av TkRat} en {Lets you send a bug report to the author of TkRat} de {Dem Autor von TkRat eine Fehlermeldung zusenden.} it {Permette di spedire un rapporto malfunzione all'autore di TkRat} fr {Expdie un rapport de bogue l'auteur de TkRat} sr {alje poruku o greci (bagu) autoru TkRat-a} pl {Pozwala wysa raport o bdach do autora TkRat} pt {Permite-lhe enviar um relatrio de problema ao autor do programa} label group_list_editor sv {Markerade brev kommer att ing i gruppen. Anvnd vnster musknapp fr att markera/demarkera brev} en {Marked messages will be members of the group. Use the left mousebutton to toggle marking of the message.} de {Die in dieser Liste ausgewhlten Nachrichten bilden die Gruppe der markierten Nachrichten. Die Auswahl einer Nachricht lsst sich mit der linken Maustaste umkehren.} # fr {Les messages marqus seront membres du groupe. # Utilisez le bouton 3 pour inverser la marque.} # pt {Mensagens marcadas sero membros do grupo. # Use o boto direito do rato para inverter a marcao} label group_window_ok sv {Stng detta fnster och flytta gruppmarkeringarna till mappfnstret} en {Close this window and transfer the group markings to the Folder window} de {Dieses Fenster schlieen und die Markierung ins Ordner-Fenster bernehmen.} it {Chiude la finestra e trasferisce i contrassegni di gruppo alla finestra cartella} fr {Ferme cette fentre et appliquer le marquage de groupe la fentre de la bote lettres.} sr {Zatvara ovaj prozor i prenosi grupne oznake u spisak tekueg skupa} pl {Zamknij to okno i przenie oznaczenia grupy do okna folderu} pt {Fecha esta janela e transfere as marcaes de grupo para a janela da pasta} label group_window_selall sv {Markera alla brev} en {Mark all messages} de {Alle Nachrichten zum Markieren auswhlen.} it {Contrassegna tutti i messaggi} fr {Marque tous les messages.} sr {Oznaava sva pisma} pl {Oznacza wszystkie wiadomoci} pt {Marca todas as mensagens} label group_window_unselall sv {Ta bort markeringen frn alla brev} en {Unmark all messages} de {Die Markierung von allen Nachrichten entfernen.} it {Rimuove il contrassegno di tutti i messaggi} fr {te la marque de tous les messages.} sr {Uklanja oznake sa svih pisama} pl {Odznacza wszystkie wiadomoci} pt {Remove todas as marcaes} label cancel sv {Avbryt operationen och stng fnstret} en {Cancel operation and close window} de {Den Vorgang abbrechen und dieses Fenster schlieen.} it {Annulla l'operazione e chiude la finestra} fr {Annule l'opration et ferme la fentre.} sr {Odustajanje od operacije i zatvaranje prozora} pl {Anuluj operacj i zamknij okno} pt {Cancela esta operao e fecha a janela} label toggle_ignore_case sv {Ignorera stora/sm bokstver vid skning p/av} en {Toggle "Ignore case" when matching} de {Aktivieren Sie diese Option, falls bei der Suche die Gro-/Kleinschreibung beachtet werden soll.} it {Ignora/considera maiuscole/minuscole nella ricerca} fr {Ignorer/prendre en compte les diffrences minuscule/majuscule lors de la recherche.} sr {Da li se zanemaruje razlika izmeu malih i velikih slova} pl {Ignoruj wielko liter w wyszukiwaniu} pt {Considera ou no maisculas nas comparaes} label find_exact sv {Hitta exakt samma strng} en {Find exact string} de {Exakt den Text in der Suchmaske suchen.} it {Ricerca la stringa esatta} fr {Recherche exacte de chane.} sr {Trai se taan izraz} pl {Szukaj dokadnie odpowiadajcego} pt {Encontrar a sequncia exacta de caracteres} label find_regexp sv {Sk med reguljruttryck} en {Find with regular expression} de {Suchmaske als "regulren Ausdruck" deuten.} it {Ricerca con espressione regolare} fr {Recherche une expression rgulire.} sr {Pretraga po regularnim izrazima} pl {Szukaj z regularnym wyraeniem} pt {Procurar com expresso regular} label find_in_mlist sv {Sk i brevlistan (inte i sjlva breven)} en {Search in the list of messages (not in the body of the message)} de {In der Liste der Nachrichten des angezeigten Ordners suchen.} it {Ricerca nella lista dei messaggi (non nel corpo dei messaggi)} fr {Recherche dans la liste des messages (pas dans le corps).} sr {Pretraga u listi pisama (a ne u samim pismima)} pl {Szukaj w licie wiadomoci (nie w aktywnych wiadomociach)} pt {Procurar na lista de mensagens (e no na prpria mensagem)} label find_in_body sv {Sk i det aktiva brevet} en {Search the body of the current message} de {Im Inhalt der angezeigten Nachricht suchen.} it {Ricerca nel corpo del messaggio corrente} fr {Recherche dans le corps du message courant.} sr {Pretraga unutar tekueg pisma} pl {Przeszukaj tre tej wiadomoci} pt {Procurar no corpo da mensagem} label find_first sv {Sk frn toppen} en {Start a search from the top} de {Suche am Anfang beginnen.} it {Inizia la ricerca dall'alto} fr {Commence une recherche au dbut.} sr {Pretraga poinje od vrha} pl {Rozpocznij szukanie od gry} pt {Iniciar uma procurar a partir do topo} label find_next sv {Hitta nsta} en {Find next} de {Vorwrts suchen.} it {Trova il prossimo} fr {Occurrence suivante.} sr {Trai dalje} pl {Znajd tekst} pt {Prxima ocurrncia} label dismiss sv {Stng fnstret} en {Close window} de {Dieses Fenster schlieen.} it {Chiudi la finestra} fr {Ferme la fentre.} sr {Zatvori prozor} pl {Zamknij okno} pt {Fecha a janela} label show_adrbook_menu sv {Denna meny lter dig vlja vilka adressbscker som skall visas i listan nedan} en {This menu lets you determine which address books should be shown in the list below} de {Men zur Auswahl der unten angezeigten Adressbcher.} it {Questo menu permette di modificare quali agende devono essere mostrate nella lista in basso} fr {Ce menu permet de choisir quel carnet d'adresse afficher dans la liste ci-dessous.} sr {Ovaj meni omoguava da odredite koji e adresar biti prikazan ispod} pl {To menu pozwala ustali, ktre ksiki adresowe powinny by pokazywane na licie na dole} pt {Este menu permite-lhe determinar qual o livro de endereos a mostrar na lista seguinte} label use_system_aliases sv {Inkludera adresser frn systemts globala adressbok} en {Include aliases from the system-wide address book} de {Die Eintrge des systemweiten Adressbuchs einbeziehen.} it {Includi gli alias dall'agenda di sistema} fr {Inclut les alias du carnet d'adresses systme.} sr {Ubaci aliase iz sistemskog adresara} pl {Uyj aliasy z systemu} pt {Inclui alias do livro de endereos de sistema} label add_addrbook sv {Lgg till en ny adressbok} en {Add a new address book} de {Ein neues Adressbuch hinzufgen.} it {Aggiungi una nuova agenda} fr {Ajoute un nouveau carnet d'adresses.} sr {Dodaj novi adresar} pl {Dodaj now ksik adresow} pt {Adiciona um novo livro de endereos} label delete_addrbook sv {Radera en adressbok, tar bara bort boken ur TkRats listor. Dvs boken raderas inte frn disken} en {Delete an address book, this only removes the book from TkRat's internal tables. The actual address book file is NOT deleted.} de {Ein Adressbuch aus der Liste lschen. Die Adressbuch-Datei wird nicht gelscht.} it {Cancella un'agenda. Rimuove il nome dell'agenda dalla lista di TkRat, ma il file contenente gli indirizzi non viene cancellato} fr {Efface un carnet d'adresses. Le fichier du carnet d'adresses n'est pas dtruit ; TkRat ne l'utilisera plus.} sr {Obrii adresar. Ovo samo uklanja adresar iz TkRat-ovih internih tabela, a datoteka sa adresama se NE brie.} pl {Usu ksik adresow, to usuwa ksik tylko z TkRat'a. Prawdziwy plik danych ksizki adresowej nie jest usuwany} pt {Apaga um livro de endereos - remove apenas o livro das tabelas internas do TkRat, o ficheiro real no apagado} label set_default_addrbook sv {Vlj standardbok, dvs vilken adressbok som nya adresser skall lggas in i om du inte explicit anger ngot annat} en {Set default address book, i.e. to which book new aliases are added if you do not explicitly choose otherwise} de {Ein Adressbuch als Standard auswhlen. Neue Aliasse werden in diesem Adressbuch eingetragen, falls sie nicht ausdrcklich einem anderen Adressbuch zugewiesen werden.} it {Seleziona l'agenda di default, cio quella in cui vengono aggiunti i nuovi alias quando non si specifica dove appartengono} fr {Slectionne le carnet d'adresses par dfaut. C..d. celui auquel seront ajouts les alias si aucun autre n'est prcis.} sr {Postavlja osnovni adresar, tj. onaj iz koga se koriste aliasi kada nita nije eksplicitno zadato} pl {Ustaw domyln ksizk adresow, np. do ktrej bd dodawane nowe aliasy jeli sam nie zadecydujesz oczywicie} pt {Selecciona o livro de endereos por defeito, ao qual novos endereos sero adicionados salvo explicitamente configurado de outra forma} label alias_list sv {Detta r en lista ver dina adresser} en {This is a list of the aliases} de {Liste der Aliasse.} it {Questa la lista degli alias} fr {Ceci est la liste des alias.} sr {Ovo je spisak aliasa} pl {Lista aliasw} pt {Uma lista de endereos, alias} label alias_adr_book sv {Adressbok som denna address skall ing i} en {Address book this alias should belong to} de {Adressbuch, zu dem dieser Alias gehrt.} it {Agenda a cui questo alias deve appartenere} fr {Carnet d'adresses auquel cet alias doit appartenir.} sr {Adresar u kom se nalazi ovaj alias} pl {Ksika adresowa, do ktrej powinien nalee ten alias} pt {O livro de endereos ao qual o alias pertence} label alias_alias sv {Adressens korta namn, detta r det namn du skriver in i ett adressflt} en {Short name of alias, this is the name you write in an address field} de {Der hier vergebene Kurzname (Alias) kann beim Erstellen neuer Nachrichten in die Adressfelder eingetragen werden.} it {Nome breve per l'alias. Questo il nome che bisogna scrivere nel campo dell'indirizzo} fr {Nom court de l'alias. C'est le nom que vous tapez en tant qu'adresse.} sr {Kratko ime za alias, tj. ime koje upisujete u polje za adresu} pl {Krtka nazwa aliasu, to jest nazwa, ktr wpiszesz w polu adresu} pt {Nome curto do alias, este nome que deve escrever num campo de endereo} label alias_fullname sv {Addressens lnga namn, detta r namnet p den person som adressen pekar p} en {Full name of alias, this is the full name of the person the alias points to} de {Der vollstndige Name des Empfngers.} it {Nome completo dell'alias. Questo il nome della persona indicata dall'alias} fr {Nom complet de l'alias. C'est le nom complet de la personne que l'alias dsigne.} sr {Puno ime aliasa, tj. puno ime osobe na koju se alias odnosi} pl {Pena nazwa aliasu, to jest pene nazwisko osoby, na ktr wskazuje alias} pt {Nome completo do alias, este o nome completo da pessoa qual o alias pertence} label alias_content sv {Adressens innehll, detta r var adressen expanderas till nr ett brev skickas. Detta kan ven vara en lista av adresses, separerade med komma. Man kan ocks anvnda andra adresser som man har definierat.} en {The content of the alias, this is what the alias is expanded to when the message is sent. This may actually be a list of addresses seperated by commas. You may also use other aliases you have defined.} de {Die E-Mail Adresse des Empfngers, durch die der Alias beim Versand ersetzt wird. Es ist auch mglich, hier eine durch Kommata getrennte Liste von Empfngern einzutragen. Anderweitig definierte Aliasse knnen ebenfalls als Empfnger angegeben werden.} it {Il contenuto dell'alias. Questo ci che viene espanso quando si spedisce il messaggio} fr {Contenu de l'alias. C'est l'adresse qui sera employe lors de l'expdition du message.} sr {Sadraj aliasa - ono u ta se alias razvija kad se pismo poalje} pl {Tre aliasu, zostanie ona umieszczona w wiadomoci do wysania} pt {O contedo do alias, este endereo que ficar visvel aquando do envio da mensagem} label alias_comment sv {Addresskommentar, detta r bara fr din egen anvndning} en {Alias comment, this is just for your own use} de {Notizen zu diesem Alias, zu Ihrem freien Gebrauch.} it {Commento alias. Questo solo per convenienza} fr {Commentaire de l'alias. Pour votre utilisation personnelle.} sr {Komentar uz alias. Ovo je za vau linu upotrebu.} pl {Komentarz aliasu, dla twojego wasnego uytku} pt {Comentrio para seu uso pessoal exclusivamente} label name_of_adrbook sv {Namnet som TkRat akll anvnda internt fr att referea till denna bok} en {The name TkRat should use internally to reference this address book} de {TkRat fhrt das Adressbuch unter diesem Namen.} it {Il nome che TkRat deve usare internamente per gestire questa agenda} fr {Le nom utilis par TkRat pour dsigner ce carnet d'adresses.} sr {Ime adresara koje e TkRat interno koristiti} pl {Nazwa, ktr TkRat bdzie si posugiwa wewntrznie dla tej ksiki} pt {O nome que o TkRat usa internamente para se referir a este livro de endereos} label name_of_adrbook_file sv {Namnet p den fil dr adressboken sparas} en {The file in which to store the address book} de {Die Datei, in der das Adressbuch gespeichert wird.} fr {Fichier dans lequel le carnet d'adresses est sauvegard} sr {Datoteka u koju streba snimiti adresar} pl {Plik w ktrym bdzie przechowywana ksika adresowa} pt {O ficheiro no qual o livro de endereos est armazenado} label list_of_books_to_delete sv {Vlj de adressbcker som du vill radera i denna listan, tryck sen p "radera"} en {Select those address books you want to delete in this list and then press "Delete"} de {Whlen Sie die Adressbcher, welche Sie lschen mchten, aus der Liste aus, und bettigen Sie die Schaltflche unten.} it {Selezionare le agende che si vogliono rimuovere da questa lista e premere "cancella"} fr {Slectionnez les carnets d'adresses dtruire puis cliquez dtruire.} sr {Odaberite one adresare koje treba obrisati i zatim pritisnite "Brii"} pl {Wybierz te ksiki adresowe, ktre chcesz usun i nacinij "Usu"} pt {Seleccione os livros de endereos que pretende remover e depois pressione "Apagar"} label aliases_adr_book sv {Addressbok som dessa adresser skall ing i} en {Address book these aliases should belong to} de {Das Adressbuch, dem diese Aliasse hinzugefgt werden sollen.} it {Agenda a cui devono appartenere questi alias} fr {Carnet d'adresses auquel ces alias doivent appartenir.} sr {Adresar u koji spadaju ovi aliasi} pl {Ksika adresowa, do ktrej te aliasy powinny nalee} pt {Livro de endereos ao qual os alias pertencem} label aliasadd_use sv {Om denna knappen r omarkerad s kommer addressen p denna rad att ignoreras} en {If this box is unchecked, then the alias on this line is ignored} de {Dieser Alias wird nur angelegt, wenn die Option ausgewhlt ist.} it {Se questo bottone non premuto allora l'alias su questa linea viene ignorato} fr {L'alias de cette ligne est actif seulement si la case est coche.} sr {Ako ova stavka nije odabrana, onda se alias na ovoj liniji ignorie} pl {Jeli ten przycisk jest odznaczony alias bdzie ignorowany} pt {Se este boto no estiver pressionado ento o alias nesta linha ser ignorado} label alias_chooser sv {Denna lista lter dig vlja en adress att lgga till i adressfltet. Det finns flera olika stt avv vlja en adress: * Man kan vlja en mha den vnstra musknappen * Man kan anvnda pil-upp och pil-ner tangenterna * Man kan brja skriva in adressen Nr du r klar s trycker du bara p return eller tab. Man kan stnga listan utan att vlja en adress genom att trycka p escape eller ctrl-c} en {This list lets you choose an alias to add to your address field. There are several ways to select an address: * You can select one with the left mousebutton * You can use the up and down arrow keys * You can start to type the address When you are done, just press Tab or Enter. You can dismiss the window without selecting an alias with the Escape key or Ctrl-c.} de {Whlen Sie einen Alias aus der Liste, der zu dem Adressfeld hinzugefgt werden soll. Die Auswahl knnen Sie auf verschiedenen Wegen vornehmen: - durch Auswahl eines Eintrags mit der linken Maustaste, - durch Bewegen der Auswahl mit den Pfeiltasten, oder - durch Eingabe der ersten Buchstaben des Alias auf der Tastatur. Der ausgewhlte Alias wird mit der Tabulator- oder der Enter-Taste dem Adressfeld hinzugefgt. Zum Abbrechen drcken Sie die Escape-Taste oder Strg-C.} it {Questa lista permette di selezionare un alias da aggiungere al campo dell'indirizzo. Ci sono vari modi per selezionare un indirizzo: * Selezionandolo con il pulsante destro del mouse * Utilizzando le frecce * Digitando le prime lettere dell'indirizzo Per terminare la selezione si pu premere tab o ok. E' possibile chiudere la finestra senza selezionare un alias con il tasto escape o con ctrl-c} fr {Cette liste vous permet de choisir un alias ajouter votre champ d'adresse. Pour choisir une adresse, vous pouvez : o en slectionner une avec le bouton 1 de la souris, o utiliser les touches haut et bas, o taper l'adresse. Lorsque vous avez fini, vous pouvez taper la touche tab ou le bouton OK, Vous pouvez annuler avec la touche chap ou C-c.} sr {Sa ove liste moete odabrati alias koji ete dodati u polje za adresu. Ima vie naina da se odabere adresa: * moete kliknuti na adresu levim dugmetom mia, * moete koristiti strelice za gore / dole, * moete poeti da kucate adresu. Kada zavrite pritisnite Tab ili Ok. Prozor moete ukloniti bez biranja aliasa ako pritisnete taster Escape ili Ctrl+C.} pl {Ta lista pozwala doda alias do pola adresu. Jest kilka sposobw wybrania adresu: * moesz wybra ktry klikajc lewym przyciskiem myszy * moesz uywa klawiszy strzaek gra i d * moesz po prostu zacz pisa adres Kiedy skoczysz nacinij tab lub ok. Moesz zamkn okno bez zaznaczania aliasu naciskajc escape lub ctr-c} pt {Esta lista permite-lhe escolher um alias para adicionar ao campo de endereo. H vrias maneiras de seleccionar um endereo: * com o boto esquerdo do rato * com as teclas de cursor 'cima' e 'baixo' * simplesmente escrever um endereo Depois de inserido, pressione TAB ou no boto Ok. Pode fechar a janela sem escolher um alias atravs da tecla ESCAPE ou CTRL-C.} label compose_insert_file sv {Ls in innehllet i en fil vid markrens position i texten} en {Insert the content of a file at the cursor current position} de {Den Inhalt einer Datei an der Cursorposition einfgen.} it {Inserisce il contenuto di un file alla posizione del cursore} fr {Insre le contenu d'un fichier la position du curseur.} sr {Ubacuje sadraj datoteke na tekuu poziciju kurzora} pl {Wpisz tre pliku w danej pozycji kursora} pt {Inserir o contedo de um ficheiro na posio actual do cursor} label automatic_wrap sv {Kontrollerar den automatiska radbrytningen} en {Controls the automatic linewrapping function} de {Mit dieser Option aktivieren Sie den automatischen Zeilenumbruch fr den Text ihrer Nachricht.} it {Controlla la funzione di a-capo automatico} fr {Contrle le retour la ligne automatique.} sr {Kontrolie automatski prelom redova} pl {Kontroluje funkcje automatycznego zawijania} pt {Controla a funo automtica de dobragem de linhas} label abort_compose sv {Avbryt komponerandet} en {Abort the composition} de {Die Bearbeitung abbrechen und die Nachricht verwerfen.} it {Annulla la composizione} fr {Annule la rdaction.} sr {Odustajanje od pisanja} pl {Anuluj pisanie} pt {Cancela a redaco da mensagem} label undo sv {Ogr den senaste ndringen} en {Undo the last change} de {Die letzte nderung zurcknehmen.} it {Annulla l'ultima modifica} fr {Annule la dernire modification.} sr {Ponitava poslednju izmenu} pl {Cofa ostatni zmian} pt {Anula a ltima aco} label cut sv {Klipp ut den markerade texten} en {Cut the marked text} de {Den markierten Text ausschneiden.} it {Taglia il testo selezionato} fr {Coupe le texte slectionn.} sr {Iseca oznaeni tekst} pl {Wycina zaznaczony tekst} pt {Remove o texto seleccionado} label copy sv {Kopiera den markerade texten} en {Copy the marked text} de {Den markierten Text kopieren.} it {Copia il testo selezionato} fr {Copie le texte slectionn.} sr {Kopira oznaeni tekst} pl {Kopiuje zaznaczony tekst} pt {Copia o texto seleccionado} label paste sv {Klipp in text} en {Paste text} de {Den Text aus der Zwischenablage einfgen.} it {Incolla testo} fr {Colle du texte.} sr {Umee tekst} pl {Wkleja tekst} pt {Insere o texto copiado na posio actual do cursor} label cut_all sv {Klipp ut all text ur brevet} en {Cut all text from the message} de {Den gesamten Text ausschneiden.} it {Taglia tutto il testo del messaggio} fr {Coupe tout le texte du message.} sr {Iseca sav tekst pisma} pl {Wycina cay tekst z wiadomoci} pt {Remove todo o texto da mensagem} label copy_all sv {Kopiera all text i brevet} en {Copy all text of the message} de {Den gesamten Text kopieren.} it {Copia tutto il testo del messaggio} fr {Copie tout le texte du message.} sr {Kopira sav tekst pisma} pl {Kopiuje cay tekst wiadomoci} pt {Copia todo o texto da mensagem} label wrap_paragraph sv {Formatera stycket} en {Format the current paragraph} de {Den aktuellen Absatz neu umbrechen.} fr {Formate le paragraphe courant.} sr {Prelama oznaene redove} pl {Formatuj oznaczony akapit} pt {Formata o pargrafo (dobra as linhas longas)} label run_through_command sv {Kr den markerade texten (all text om ingen r markerad) genom ett externt kommando} en {Run the marked text (all text if nothing is marked) through an external command} de {Den markierten bzw. den gesamten Text durch ein Programm filtern lassen.} it {Filtra il testo selezionato (tutto il messaggio se non ne stata selezionata una parte) attraverso un comando esterno} fr {Passe le texte slectionn (ou sinon tout le texte) dans un filtre externe.} sr {Usmerava oznaeni tekst (sav tekst ako nita nije oznaeno) kroz spoljnu komandu} pl {Przeka zaznaczony tekst (cay jeli nic nie zaznaczono) do zewntrznego programu} pt {Passa o texto seleccionado (ou todo o texto caso no haja seleces) a um comando externo} label headers_menu sv {Denna meny lter dig kontrollera vilka huvudrader som visas i fnstret} en {This menu lets you control which header lines are shown in the Compose window} de {Das Men zur Auswahl der Kopfzeilen, die beim Erstellen einer Nachricht zum Bearbeiten angezeigt werden.} it {Questo menu permette di controllare quali intestazioni vengono mostrate nella finestra di composizione} fr {Ce menu permet de contrler quels en-ttes sont affichs dans la fentre de rdaction.} sr {Ovaj meni odreuje koji e delovi zaglavlja biti prikazani u prozoru za sastavljanje pisma} pl {To menu pozwala wybra, ktre nagwki bd wywietlane w oknie tworzenia wiadomoci} pt {Este menu permite-lhe escolher que cabealhos devem estar visveis na janela de redaco} label pgp_sign sv {Signera brevet med en PGP-signatur} en {Sign the message with a PGP signature} de {Diese Nachricht mit PGP unterschreiben.} it {Firma il messaggio utilizzando PGP} fr {Signe le message avec PGP.} sr {Potpisuje pismo PGP potpisom} pl {Oznacz wiadomo sygnatur PGP} pt {Assina a mensagem com a assinatura PGP} label pgp_encrypt sv {Kryptera brevet mha PGP} en {Encrypt the message with PGP} de {Diese Nachricht mit PGP verschlsseln.} it {Cripta i messaggi con PGP} fr {Chiffre le message avec PGP.} sr {ifruje pismo putem PGP-a} pl {Zakoduj wiadomo PGP} pt {Cifra a mensagem com PGP} label command_list sv {ndra listan ver sparade kommandon (som man kan skicka text igenom)} en {Edit the list of saved commands (which you can run text through)} de {Die gespeicherten Befehle, mit denen der Nachrichtentext gefiltert werden kann, bearbeiten.} it {Modifica la lista di comandi salvati (per filtrare testo)} fr {dite la liste des commandes sauves (pour les filtres).} sr {Menja listu sauvanih komandi (kroz koje moete propustiti tekst)} pl {Edytuj list programw (do ktrych moesz wysya tekst)} pt {Edita a lista de comandos gravados (aos quais se pode passar texto)} label attach_file sv {Lgg till en fil som bilaga till brevet} en {Attach a file to the message} de {Eine Datei als Anhang zur Nachricht hinzufgen.} it {Allega un file al messaggio} fr {Joint un fichier au message.} sr {Prikai datoteku uz pismo} pl {Docz plik do wiadomoci} pt {Anexar um ficheiro mensagem} label attach_special sv {Speciella type of bilagor} en {Special types of attachments} de {Einen speziellen Anhang aus der Liste zur Nachricht hinzufgen.} it {Allegati speciali} fr {Types spciaux de pices jointes.} sr {Posebne vrste dodataka} pl {Specjalne typy zacznikw} pt {Tipo especial de anexo} label attach_keys sv {Lgg till PGP nycklar} en {Attach PGP keys} de {Eigene ffentliche PGP-Schlssel als Anhang hinzufgen.} it {Allega chiavi PGP} fr {Ajoute une clef PGP au message.} sr {Prikai PGP klju} pl {Docz klucze PGP} pt {Anexar chaves PGP} label detach sv {Ta bort den markerade bilagan} en {Detach the marked attachment} de {Den markierten Anhang entfernen.} it {Rimuove l'allegato selezionato} fr {Dtache la pice jointe slectionne.} sr {Ukloni oznaeni dodatak} pl {Usu oznaczony zacznik} pt {Remover o anexo seleccionado} label attachments sv {Lista ver bilagor} en {List of attachments} de {Die Liste der Anhnge, die mit dieser Nachricht verschickt werden.} it {Lista di allegati} fr {Liste des pices jointes} sr {Spisak dodataka} pl {Lista zacznikw} pt {Lista de ficheiros anexos} label send sv {Skicka brevet och spara en kopia i den specificerade "spara utgende" mappen (om ngon, anvnd "Ny/ndra mapp"- fnstret fr att ange vilken mapp det skall vara)} en {Send the message and save a copy in the designated "Save outgoing" folder} de {Diese Nachricht abschicken. Eine Kopie wird automatisch gespeichert, sofern fr die gewhlte Rolle ein Ordner zum Sichern festgelegt wurde.} it {Spedisci il messaggio e salvane una copia nella cartella indicata per il salvataggio in uscita} fr {Envoie le message et sauve une copie dans la bote lettres "messages expdis" s'il en existe une.} sr {alje poruku i uva kopiju u naznaenom skupu. Koristite prozor "Rad sa skupovima" da oznaite skup u koji se podrazumevano upisuju poslata pisma.} pl {Wylij wiadomo i zapisz kopi w folderze oznaczonym "Zapisz wychodzce" (Jeli nie ustawione, uyj "Nowy/Edytuj folder" by ustawi folder "Zapisz wychodzce")} pt {Envia a mensagem e guarda uma cpia na pasta designada de "mensagens enviadas", caso exista} label sendsave sv {Skicka brevet och spara en kopia i en mapp (som specificeras via denna meny)} en {Send the message and save a copy in a folder (specified via this menu)} de {Diese Nachricht abschicken. Eine Kopie wird nur in dem ausgewhlten Ordner gespeichert.} it {Spedisci il messaggio e salvane una copia nella cartella specificata} fr {Expdie le message et en sauve une copie dans une bote lettres prcise dans ce menu.} sr {alje poruku i uva kopiju u skupu koji birate u ovom meniju} pl {Wylij wiadomo i zapisz kopi w folderze (Okrelonym w tym menu)} pt {Envia a mensagem e guarda uma cpia na pasta a seleccionar atravs deste menu} label eedit sv {Anvnd en extern textredigerare p brdtexten} en {Use an external editor on the message body} de {Den Text der Nachrichten mit einem externen Editor bearbeiten.} it {Utilizza un editor esterno per il corpo del messaggio} fr {Utilise un autre diteur pour rdiger le message.} sr {Koristi spoljni editor za sadraj pisma} pl {Uyj zewntrznego edytora dla treci wiadomoci} pt {Usa um editor de texto externo} label compose_to sv {De primra mottagarana av brevet. Detta r ett adressflt vilket betyder att TkRat kommer att frska ordna adresserna i kolumner, adresser som finns i adressboken kommer att expanderas (med addresatens fulla namn) och syntaktiskt ogiltiga adresser markeras med rtt. Du kan anvnda ^L fr att f en lista ver dina alias} en {The primary recipients of the message. This is an address field, which means that TkRat will try to keep the addresses in nice columns, it will expand aliases (to their full names) and it will mark illegal addresses in red. You can use ^l to get a list of aliases.} de {Primre Empfnger dieser Nachricht. TkRat ordnet die Adressen in Spalten an, bersetzt Aliasse und markiert fehlerhafte und unbekannte Adressen rot. Eine Liste der Aliasse kann mit Strg-L angezeigt werden.} it {I destinatari primari del messaggio. Questo un campo d'indirizzo. TkRat cercher di mantenere gli indirizzi ordinati per colonna, espandendo gli alias (ai loro nomi completi), segnando indirizzi invalidi in rosso. Premendo ^L si pu accedere alla lista di alias.} fr {Les destinataires principaux du message. Comme il s'agit d'un champ d'adresses, TkRat essayera de conserver les destinataires en colonnes, tendra les alias et signalera les adresses illgales en rouge. C-l (lettre l) permet d'accder la liste des alias.} sr {Osnovni primalac pisma. Ovo je polje za adrese, to znai da e TkRat pokuati da adrese dri u kolonama, razvie aliase (u puna imena) i oznaie nedozvoljene adrese crvenom bojom. Pritisnite Ctrl+L da dobijete spisak aliasa.} pl {Gwni adresaci wiadomoci. To jest pole adresowe, tzn. TkRat sprbuje utrzyma adresy w kolumnach, rozwinie aliasy (do penych nazw) i oznaczy niepoprawne adresy na czerwono. Moesz uy Ctrl+L by otworzy list aliasw} pt {O destinatrio principal desta mensagem. Tratando-se de um campo de endereos, o TkRat procurar ordenar os endereos em colunas bem visveis e identificar possveis erros a vermelho. Poder usar CRTL-L para ter acesso lista de alias} label compose_subject sv {Brevet mne} en {The subject of the message} de {Der Betreff der Nachricht.} it {Il soggetto del messaggio} fr {L'objet du message.} sr {Naslov pisma} pl {Temat wiadomoci} pt {O assunto da mensagem} label compose_cc sv {De som skall f kopior av brevet. Detta r ett adressflt vilket betyder att TkRat kommer att frska ordna adresserna i kolumner, adresser som finns i adressboken kommer att expanderas (med addresatens fulla namn) och syntaktiskt ogiltiga adresser markeras med rtt. Du kan anvnda ^l fr att f en lista ver dina alias} en {Those who should receive carbon copies of the message. This is an address field, which means that TkRat will try to keep the addresses in nice columns, it will expand aliases (to their full names) and it will mark illegal addresses in red. You can use ^l to get a list of aliases.} de {Sekundre Empfnger dieser Nachricht. TkRat ordnet die Adressen in Spalten an, bersetzt Aliasse und markiert fehlerhafte und unbekannte Adressen rot. Eine Liste der Aliasse kann mit Strg-L angezeigt werden.} it {Lista di destinatari che riceveranno una copia carbone del messaggio. Questo un campo d'indirizzo. TkRat cercher di mantenere gli indirizzi ordinati per colonna, espandendo gli alias (ai loro nomi completi), segnando indirizzi invalidi in rosso. Premendo ^l si pu accedere alla lista di alias.} fr {Destinataires recevant une copie carbone du message. Comme il s'agit d'un champ d'adresses, TkRat essayera de conserver les destinataires en colonnes, tendra les alias et signalera les adresses illgales en rouge. C-l (lettre l) permet d'accder la liste des alias.} sr {Kome treba uputiti kopije pisma. Ovo je polje za adrese, to znai da e TkRat pokuati da adrese dri u kolonama, razvie aliase (u puna imena) i oznaie nedozvoljene adrese crvenom bojom. Pritisnite Ctrl+L da dobijete spisak aliasa.} pl {Ci, ktrzy powinni otrzymywa kopie wiadomoci. To jest pole adresowe, tzn. TkRat sprbuje utrzyma adresy w kolumnach, rozwinie aliasy (do penych nazw) i oznaczy niepoprawne adresy na czerwono. Moesz uy Ctrl+L by otrzyma list aliasw} pt {Destinatrios que recebero uma cpia da mensagem. Tratando-se de um campo de endereos, o TkRat procurar ordenar os endereos em colunas bem visveis e identificar possveis erros a vermelho. Poder usar CRTL-L para ter acesso lista de alias} label compose_bcc sv {De som skall f blinda kopior, dvs de vriga mottagarna av brevet kommer inte att se att dessa personer ocks ftt kopior. Detta r ett adressflt vilket betyder att TkRat kommer att frska ordna adresserna i kolumner, adresser som finns i adressboken kommer att expanderas (med addresatens fulla namn) och syntaktiskt ogiltiga adresser markeras med rtt. Du kan anvnda ^l fr att f en lista ver dina alias} en {Those who should receive blind carbon copies, i.e. the other recipients will not be able to see that these recipients also got the message. This is an address field, which means that TkRat will try to keep the addresses in nice columns, it will expand aliases (to their full names) and it will mark illegal addresses in red. You can use ^l to get a list of aliases.} de {Empfnger, die in der Nachricht nicht genannt werden. TkRat ordnet die Adressen in Spalten an, bersetzt Aliasse und markiert fehlerhafte und unbekannte Adressen rot. Eine Liste der Aliasse kann mit Strg-L angezeigt werden.} it {Lista di destinatari che riceveranno copie carbone cieche del messaggio. Questo un campo d'indirizzo. TkRat cercher di mantenere gli indirizzi ordinati per colonna, espandendo gli alias (ai loro nomi completi), segnando indirizzi invalidi in rosso. Premendo ^l si pu accedere alla lista di alias.} fr {Destinataires masqus (BCC) recevant une copie sans que les autres destinataires en soient informs. Comme il s'agit d'un champ d'adresses, TkRat essayera de conserver les destinataires en colonnes, tendra les alias et signalera les adresses illgales en rouge. C-l (lettre l) permet d'accder la liste des alias.} sr {Kome treba poslati "slepe" kopije pisma, tj. drugi primaoci nee videti da su i ove osobe dobile pismo. Ovo je polje za adrese, to znai da e TkRat pokuati da adrese dri u kolonama, razvie aliase (u puna imena) i oznaie nedozvoljene adrese crvenom bojom. Pritisnite Ctrl+L da dobijete spisak aliasa.} pl {Ci ktrzy otrzymali "lepe" kopie listu, to znaczy, e inni nie bd mogli zobaczy, e e ci adresaci rwnie dostali list. To jest pole adresowe, tzn. TkRat sprbuje utrzyma adresy w kolumnach, rozwinie aliasy (do penych nazw) i oznaczy niepoprawne adresy na czerwono. Moesz uy Ctrl+L by otrzyma list aliasw} pt {Destinatrios que recebero uma cpia cega da mensagem (isto , outros no sabero que esta mensagem foi tambm entregue a este endereo). Tratando-se de um campo de endereos, o TkRat procurar ordenar os endereos em colunas bem visveis e identificar possveis erros a vermelho. Poder usar CRTL-L para ter acesso lista de alias} label compose_keywords sv {Ngra nyckelord som beskriver brevet} en {A set of keywords describing the message} de {Eine Liste von Stichwrtern zur Nachricht.} it {Una lista di parole chiave che descrivono il messaggio} fr {Un ensemble de mots-cls dcrivant le message.} sr {Niz kljunih rei koje opisuju pismo} pl {Zbir sw kluczowych okrelajcych wiadomo} pt {Um conjunto de palavras chave descriptivas da mensagem} label compose_content_description sv {En beskrivning av brevets innehll} en {A description of the content of the message} de {Eine Beschreibung des Inhalts der Nachricht.} it {Descrizione del contenuto del messaggio} fr {Une description du contenu du message.} sr {Opis sadraja pisma} pl {Opis treci wiadomoci} pt {Uma descrio do contedo da mensagem} label compose_reply_to sv {Vart svara p brevet skall skickas. Detta r ett adressflt vilket betyder att TkRat kommer att frska ordna adresserna i kolumner, adresser som finns i adressboken kommer att expanderas (med addresatens fulla namn) och syntaktiskt ogiltiga adresser markeras med rtt. Du kan anvnda ^l fr att f en lista ver dina alias} en {Where replies to the message should be sent. This is an address field, which means that TkRat will try to keep the addresses in nice columns, it will expand aliases (to their full names) and it will mark illegal addresses in red. You can use ^l to get a list of aliases.} de {Adresse(n), an die Antworten auf diese Nachricht geschickt werden sollen. TkRat ordnet die Adressen in Spalten an, bersetzt Aliasse und markiert fehlerhafte und unbekannte Adressen rot. Eine Liste der Aliasse kann mit Strg-L angezeigt werden.} it {Indirizzo a cui mandare le risposte. Questo un campo d'indirizzo. TkRat cercher di mantenere gli indirizzi ordinati per colonna, espandendo gli alias (ai loro nomi completi), segnando indirizzi invalidi in rosso. Premendo ^l si pu accedere alla lista di alias.} fr {A qui les rponses ce message devront tre expdies. Comme il s'agit d'un champ d'adresses, TkRat essayera de conserver les destinataires en colonnes, tendra les alias et signalera les adresses illgales en rouge. C-l (lettre l) permet d'accder la liste des alias.} sr {Gde treba slati odgovore na ovo pismo. Ovo je polje za adrese, to znai da e TkRat pokuati da adrese dri u kolonama, razvie aliase (u puna imena) i oznaie nedozvoljene adrese crvenom bojom. Pritisnite Ctrl+L da dobijete spisak aliasa.} pl {Gdzie powinny zosta wysane odpowiedzi na t wiadomo. To jest pole adresowe, tzn. TkRat sprbuje utrzyma adresy w kolumnach, rozwinie aliasy (do penych nazw) i oznaczy niepoprawne adresy na czerwono. Moesz uy Ctrl+L by otrzyma list aliasw} pt {Endereo para o qual eventuais respostas devero ser dirigidas. Tratando-se de um campo de endereos, o TkRat procurar ordenar os endereos em colunas bem visveis e identificar possveis erros a vermelho. Poder usar CRTL-L para ter acesso lista de alias} label compose_comments sv {Kommentarer p brevet} en {Comments on the message} de {Anmerkungen zur Nachricht.} it {Commenti sul messaggio} fr {Commentaires concernant le message.} sr {Komentari o pismu} pl {Komentarz wiadomoci} pt {Comentrios mensagem} label compose_from sv {Vem brevet kommer ifrn. Detta r ett adressflt vilket betyder att TkRat kommer att frska ordna adresserna i kolumner, adresser som finns i adressboken kommer att expanderas (med addresatens fulla namn) och syntaktiskt ogiltiga adresser markeras med rtt. Du kan anvnda ^l fr att f en lista ver dina alias} en {Who the message is from. This is an address field, which means that TkRat will try to keep the addresses in nice columns, it will expand aliases (to their full names) and it will mark illegal addresses in red. You can use ^l to get a list of aliases.} de {Der bzw. die Absender dieser Nachricht. TkRat ordnet die Adressen in Spalten an, bersetzt Aliasse und markiert fehlerhafte und unbekannte Adressen rot. Eine Liste der Aliasse kann mit Strg-L angezeigt werden.} it {Mittente del messaggio. Questo un campo d'indirizzo. TkRat cercher di mantenere gli indirizzi ordinati per colonna, espandendo gli alias (ai loro nomi completi), segnando indirizzi invalidi in rosso. Premendo ^l si pu accedere alla lista di alias.} fr {De qui provient le message. Comme il s'agit d'un champ d'adresses, TkRat essayera de conserver les destinataires en colonnes, tendra les alias et signalera les adresses illgales en rouge. C-l (lettre l) permet d'accder la liste des alias.} sr {Od koga dolazi pismo. Ovo je polje za adrese, to znai da e TkRat pokuati da adrese dri u kolonama, razvie aliase (u puna imena) i oznaie nedozvoljene adrese crvenom bojom. Pritisnite Ctrl+L da dobijete spisak aliasa.} pl {Od kogo jest ta wiadomo To jest pole adresowe, tzn. TkRat sprbuje utrzyma adresy w kolumnach, rozwinie aliasy (do penych nazw) i oznaczy niepoprawne adresy na czerwono. Moesz uy Ctrl+L by otrzyma list aliasw} pt {Endereo que origina a mensagem. Tratando-se de um campo de endereos, o TkRat procurar ordenar os endereos em colunas bem visveis e identificar possveis erros a vermelho. Poder usar CRTL-L para ter acesso lista de alias} label type_menu sv {Anvnd denna meny fr att specificera vilken typ bilagan har} en {Use this menu to specify which type of attachment this is} de {Mit diesem Men knnen Sie die Bezeichnung des Inhalts-Typs des Anhangs festlegen.} it {Menu per specificare il tipo di allegato} fr {Utilisez ce menu pour prciser le type d'une pice jointe.} sr {Ovim menijem oznaavate vrstu zakaene datoteke} pl {Uyj tego menu by wybra typ zacznika} pt {Use este menu para especificar de que tipo de anexo se trata} label encoding_menu sv {Vilken kodning bilagan har just NU. Detta har ingenting att gra med vilken kodning den kommer att f nr den skickas} en {The encoding this attachment has RIGHT NOW. This has nothing to do with which encoding it will be sent in.} de {Die aktuelle Kodierung der anzuhngenden Datei. Diese ist unabhngig von der Kodierung, die fr den Versand verwendet wird.} it {La codifica attuale dell'allegato. Non ha niente a che vedere con la codifica con cui verr spedito.} fr {Le codage que la pice jointe possde MAINTENANT. Ceci n'a rien voir avec le codage qui sera utilis pour l'expdition.} sr {Kodiranje koje ovaj dodatak SADA ima. Ovo nema nikakve veze s kodiranjem koje e biti upotrebljeno za slanje.} pl {Obecne kodowanie wiadomoci. Nie ma to nic wsplnego z kodowaniem z jakim zostanie wysana wiadomo} pt {O esquema de codificao deste anexo. No est relacionado com a codificao a utilizar aquando do envio da mensagem} label attach_description sv {En kort beskrivning av denna bilaga} en {A short description of this attachment} de {Eine kurze Beschreibung dieses Anhangs.} it {Una breve descrizione dell'allegato} fr {Une brve description de cette pice jointe.} sr {Kratak opis ovog dodatka} pl {Krtki opis zacznika} pt {Uma curta descrio do anexo} label attach_id sv {Under vissa frutsttningar s kan man behva skicka med en identifierare med bilagan} en {Under some circumstances, one may need to send an identifier with the attachment} de {In speziellen Fllen kann es erforderlich sein, einem Anhang hiermit einen eindeutigen Bezeichner (Content-ID) zu geben.} it {In certi casi necessario definire un indentificatore per l'allegato} fr {Identifiant de la pice jointe. Parfois utile.} sr {Pod izvesnim okolnostima moe biti potreban identifikator dodatka} pl {W pewnych okolicznociach moesz chcie wysa zacznik z identyfikatorem} pt {Em certas circunstncias poder ser necessrio enviar um identificador com o anexo} label choose_msg sv {Detta brevet innehll ett eller flera andra brev som bilagor. Du mste nu vlja vilket brev du vill svara p (eller skicka vidare.)} en {This message contained one or more embedded messages. You must now choose which message you wish to reply to (or forward).} de {Diese Nachricht enthlt eine oder mehrere Nachrichten als Anhnge. Bitte whlen Sie aus, ob die gesamte Nachricht oder ein bestimmter Anhang beantwortet bzw. weitergeleitet werden soll.} it {Questo messaggio contiene uno o pi messaggi. Scegli a quale rispondere o il messaggio da rispedire} fr {Ce message contient un ou plusieurs messages. Vous devez maintenant choisir auquel rpondre (ou lequel faire suivre).} sr {Ovo pismo sadri jednu ili vie ubaenih poruka. Odaberite na koju od njih elite da odgovorite (ili da je prosledite).} pl {Ta wiadomo zawiera jedn lub wiele osadzonych wiadomoci. Musisz teraz wybra, na ktr wiadomo odpowiedzie (lub przeforwardowa)} pt {Esta mensagem contm uma ou mais mensagens embebidas. Dever escolher a qual a mensagem deseja responder (ou reenviar)} label save_cmd_as sv {Om du vill att detta kommando skall sparas fr framtida anvndning s skriv in namnet det skall sparas under hr och se till att knappen r markerad} en {If you wish to save this command for future use, then you just write a label here and make sure the button is checked} de {Um diese Befehle in Zukunft direkt aus dem Men aufrufen zu knnen, geben Sie hier einen Namen fr den Men-Eintrag an und aktivieren die Option zum Sichern.} it {Se vuoi salvare questo comando per utilizzi futuri, inserisci un identificativo e assicurati che il pulsante sia premuto} fr {Si vous voulez sauver cette commande pour une utilisation future, donnez un nom ici et assurez vous que la case soit coche.} sr {Ako elite da ovu komandu sauvate za buduu upotrebu, upiite ovde naziv i proverite da li je opcija odabrana} pl {Jeli chcesz zapisa to polecenie na przyszo, wpisz tylko etykiet i upewnij si e przycisk jest wcinity} pt {Se deseja guardar este comando para uso futuro ento escreva aqui uma identificao do comando e assegure-se que o boto est actuado} label command_to_run_through sv {Skriv in en kommandorad hr. Den valda texten kommer att kras igenom kommandoraden} en {Write a command line here, the selected text will be piped through it} de {Geben Sie eine Befehlszeile ein. Der markierte Text wird als Eingabe an den Befehl ausgegeben und durch die Ausgabe des Befehls ersetzt werden.} it {Inserisci il comando. Il testo selezionato verr filtrato attraverso questo comando} fr {Entrez ici une commande. Elle traitera le texte slectionn.} sr {Ovde upiite komandnu liniju kroz koju e biti usmeren izabrani tekst} pl {Tutaj wpisz lini polece. Zaznaczony tekst bdzie przekazany przez to polecenie} pt {Introduza um comando. O texto seleccionado ser enviado atravs de uma 'pipe'} label saved_commands sv {Lista ver sparade kommandon} en {List of saved commands} de {Die Liste der gespeicherten Befehle.} it {Lista di comandi salvati} fr {Liste des commandes sauves.} sr {Spisak sauvanih komandi} pl {Lista zapisanych polece} pt {Lista de comandos gravados} label apply_changes_to_cmd sv {Applicera de gjorda ndringarna p kommandot} en {Apply the made changes to the command} de {Die nderungen an der Befehlszeile speichern.} it {Applica le modifiche fatte al comando} fr {Applique les changements apports la commande.} sr {Primeni izvedene izmene na komandu} pl {Zapisz zmiany tego polecenia} pt {Aplica as mudanas introduzidas ao comando} label delete_command sv {Radera det valda kommandot} en {Delete the marked command} de {Den ausgewhlten Befehl lschen.} it {Cancella il comando selezionato} fr {Dtruit la commande marque.} sr {Brie oznaenu komandu} pl {Usu oznaczone polecenie} pt {Elimina comando marcado} label command_content sv {Kommandots innehll} en {Content of command} de {Befehlszeile des ausgewhlten Men-Eintrags.} it {Contenuto del comando} fr {Contenu d'une commande.} sr {Sadraj komande} pl {Tre polecenia} pt {Contedo do comando} label switch_expression sv {Denna meny lter dig vlja mellan avancerade eller enkla uttryck} en {This menu lets you switch between advanced and basic expressions} de {Dieses Men erlaubt das Umschalten zwischen der einfachen und der fortgeschrittenen Version der Suchkriterien.} it {Menu che permette di scegliere tra espressioni avanzate e base} fr {Choisit entre expressions simples et avances.} sr {Ovde birate izmeu osnovnih i naprednih izraza} pl {To menu pozwala przechodzi midzy zaawansowanymi i podstawowymi wyraeniami} pt {Permutar modo de expresses} label save_expr_as sv {Om du vill att detta uttryck skall sparas fr framtida anvndning s skriv in namnet det skall sparas under hr och se till att knappen r markerad} en {If you wish to save this expression for future use, just write a label here and make sure the button is checked} de {Die in der Maske festgelegten Auswahl-Kriterien knnen gespeichert werden, so dass sie in Zukunft durch Auswahl aus dem Men verwendet werden knnen. Vergeben Sie hier einen Namen fr den Men-Eintrag, und aktivieren Sie die Option zum Sichern.} it {Se vuoi salvare questa espressione per utilizzi futuri, sufficiente inserire qui un identificatore e assicurarsi che il pulsante sia premuto} fr {Si vous voulez sauver cette expression, indiquez un nom et assurez vous que la case est coche.} sr {Ako elite da sauvate ovaj izraz za buduu upotrebu, upiite ovde naziv i proverite da li je opcija odabrana.} pl {Jeli chcesz zapisa wyraenie do przyszego wykorzystania, podaj tylko etykiet i upewnij si, e przycisk jest zaznaczony} pt {Se desejar gravar esta expresso para uso futuro, introduza um identificador neste espao e certifique-se que o boto est premido} label exp_ok sv {Stng uttrycksfnstret och maerka de brev som matchar uttrycket} en {Close Expression window and mark the messages which match the expression} de {Dieses Fenster schlieen. Die Nachrichten, die zu den angegebenen Kriterien passen, werden markiert.} it {Chiudi la finestra espressioni e seleziona i messaggi che corrispondono all'espressione} fr {Ferme cette fentre et marque les messages.} sr {Zatvori prozor za zadavanje izraza i oznai pisma koja se uklapaju u izraz} pl {Zamknij okno wyrae i oznacz wiadomoci, ktre zgadzaj si z wyraeniem} pt {Fecha esta janela e marca as mensagens que correspondem a esta expresso} label clear sv {Rensa alla flt} en {Clear all fields} de {Alle Eingaben lschen.} it {Cancella tutti i campi} fr {Vide tous les champs.} sr {Obrii sva polja} pl {Wyczy wszystkie pola} pt {Limpar todos os campos} label exp_basic_and sv {Alla raderna nedan mste matcha brevet fr att det skall markeras} en {All the lines below must match a message for it to get marked} de {Nachrichten, die alle folgenden Kriterien erfllen, werden markiert.} it {Tutte le linee qui sotto devono essere riscontrate in un messaggio perch questo sia selezionato} fr {Pour qu'un message soit marqu, il doit concorder avec toutes les lignes ci-dessous.} sr {Da bi pismo bilo oznaeno, sve linije ispod moraju da se uklope} pl {Wszystkie linie poniej musz si zgadza by wiadomo zostaa oznaczona} pt {Um mensagem ser marcada apenas se todas as linhas seguintes tiverem correspondncia} label exp_basic_or sv {Alla brev som matchar minst en av de icke tomma raderna nedan kommer att markeras} en {Any message which matches at least one of the non-empty lines below is marked} de {Nachrichten, die wenigstens eine der folgenden Kriterien erfllen, werden markiert.} it {I messaggi in cui vengono riscontrate almeno una delle linee qui sotto verranno selezionati} fr {Pour qu'un message soit marqu, il doit concorder avec au moins l'une des lignes ci-dessous.} sr {Bie oznaeno svako pismo koje se uklapa sa bar jednom od sledeih linija} pl {Kada wiadomo, ktra pasuje do cho jednej z nie-pustych linii bdzie oznaczona} pt {Uma mensagem ser marcada se pelo menos uma das linhas seguintes tiver correspondncia} label exp_basic_field sv {Denna rad matchar om minst ett av orden i fltet finns i den specificerade huvudraden} en {This line matches if at least one of the words in the entry field can be found in the indicated header line} de {Das Kriterium gilt als erfllt, falls wenigstens eines der hier eingetragenen Wrter in der entsprechenden Kopfzeile der Nachricht enthalten ist.} it {Questa linea viene riscontrata solo se almeno una parola nel campo viene trovato nell'intestazione indicata} fr {Cette ligne concorde si au moins l'un des mots peut tre trouv dans l'en-tte dsign.} sr {Ova linija se uklapa ako se bar jedna od datih rei moe pronai u odgovarajuem polju zaglavlja} pl {Ta linia pasuje, jeli cho jeden z wyrazw w polu wejciowym moe by znaleziony w zaznaczonej linii nagwka} pt {Esta linha tem correspondncia se pelo menos umas das palavras introduzidas no campo for encontrada na linha de cabealho indicada} label exp_adv_exp sv {Hr kan du skriva in ett avancerat uttryck, man kan anvnda tangentbordet och/eller knapparna here. Exemple p giltiga uttryck r: Subject has foo Subject is "foo bar" and (To has maf or Cc has maf)} en {Here you can write your advanced expression, you can use the keyboard or the convenience buttons below. Examples of expressions are: Subject has foo Subject is "foo bar" and (To has maf or Cc has maf)} de {Fortgeschrittene Version der Suchkriterien in englischer Sprache. Beispiele fr mgliche Ausdrcke sind: Subject has buchhaltung Subject is "bitte prfen" and (To has mller or Cc has schulze) Die Eingabe dieser Ausdrcke wird durch die Schaltflchen unten erleichtert.} it {Qui puoi scrivere le espressioni avanzate. Si pu utilizzare la tastiera o i pulsanti rapidi qui sotto. Esempi di espressioni sono: Subject has foo Subject is "foo bar" and (To has maf or Cc has maf)} fr {Entrez ici une expression avance. Vous pouvez utiliser le clavier ou vous servir des boutons ci-dessous. voici deux exemples d'expressions avances : Subject has toto Subject is "toto tata" and ( To has titi or Cc has titi )} sr {Ovde moete upisati svoj napredni izraz koristei tastaturu ili pomonu dugmad ispod. Evo nekoliko primera: Subject has foo Subject is "foo bar" and (To has maf or Cc has maf)} pl {Tu moesz wpisa zaawansowane wyraenie, moesz uy klawiatury lub wygodnych przyciskw poniej. Przykady wyrae: Subject has foo Subject has "foo bar" and (To has maf or Cc has titi)} pt {Introduza neste espao a expresso avanada, utilize o teclado e/ou os botes seguintes. Exemplos de expresses (em ingls): Subject has foo Subject has "foo bar" and (To has maf or Cc has titi)} label exp_adv_fields sv {Dessa knappar representerar de olika huvudrader som vi kan matcha innehllet i} en {These buttons correspond to the different header fields we can match on} de {Diese Schaltflchen erzeugen den Namen der entsprechenden Kopfzeilen im Eingabefeld fr die Suchkriterien.} it {Questi bottoni corrispondono ai diversi campi delle intestazioni che possono essere riscontrati} fr {Ces boutons indiquent les diffrents en-ttes dont on peut comparer le contenu.} sr {Ova dugmad odgovaraju razliitim poljima zaglavlja iji se sadraj moe uporeivati} pl {Te przyciski odpowiadaj rnym nagwkom, do ktrych moemy porwnywa wyraenia} pt {Estes botes indicam os cabealhos onde possvel procurar correspondncias} label exp_adv_has sv {Huvudraden innehller henna strng och kan innehlla mer text} en {The field contains this string and may contain more text} de {Die Kopfzeile entlt den angegeben Text.} it {Il campo contiene al suo interno questa stringa} fr {Le champ contient au moins ce texte.} sr {Polje sadri ovaj niz znakova i moe imati jo teksta} pl {Pole zawiera ten napis i moe zawiera wicej tekstu} pt {O campo contm pelo menos este texto} label exp_adv_is sv {Huvudfltet matchar detta exakt} en {The header field matches this exactly} de {Die Kopfzeile ist identisch mit dem angegeben Text.} it {Il campo dell'intestazione deve essere riscontrato esattamente} fr {L'en-tte concorde exactement avec ce texte.} sr {Polje je ekvivalentno zadatom sadraju} pl {Pole nagwka zgadza si dokadnie} pt {O contedo do campo corresponde exactamente a este texto} label exp_adv_gt sv {Denna strre n symbol gr endast att anvnda tillsammans med storleksfltet} en {This greater-than symbol only makes sense with the Size field} de {Der Operator "grer als" ist nur zum Vergleich der Gre der Nachricht mit einem Wert sinnvoll.} it {Il simbolo di maggiore ha senso solo col campo Dimensione} fr {Le symbole "plus grand que" ne s'utilise qu'avec le champ "Taille".} sr {Ovaj znak "vee od" ima smisla samo za polje "Veliina"} pl {Ten znak wikszoci przydaje si tylko w przypadku pola wielkoci} pt {Este operador apenas faz sentido quando usado em conjunto com "Tamanho"} label exp_adv_lt sv {Denna minder n symbol gr endast att anvnda tillsammans med storleksfltet} en {This less-than symbol only makes sense with the Size field} de {Der Operator "kleiner als" ist nur zum Vergleich der Gre der Nachricht mit einem Wert sinnvoll.} it {Il simbolo di minore ha senso solo col campo Dimensione} fr {Le symbole "plus petit que" ne s'utilise qu'avec le champ "Taille".} sr {Ovaj znak "manje od" ima smisla samo za polje "Veliina"} pl {Ten znak mniejszoci przydaje s tylko w przypadku pola wielkoci} pt {Este operador apenas faz sentido quando usado em conjunto com "Tamanho"} label exp_adv_bool sv {Dessa logiska termer kan anvndas fr att kombinera uttryck} en {These logical terms can be used to combine expressions} de {Mit diesen booleschen Operatoren knnen Sie einzelne Suchkriterien miteinander verknpfen.} it {Questi termini logici possono essere usati per combinare espressioni} fr {Ces oprateurs logiques servent combiner des expressions.} sr {Ovi logiki operatori se mogu koristiti za kombinovanje izraza} pl {Te wyraenia logiczne mog by wykorzystywane do czenia wyrae} pt {Operadores lgicos para expresses combinadas} label exp_adv_p sv {Anvnd parenteser fr att gruppera uttryck} en {Use parentheses to group expressions} de {Setzen Sie Klammern, um die Reihenfolge der Verknpfung festzulegen.} it {Usa parentesi per raggruppare espressioni} fr {Utilisez des parenthses pour grouper les expressions.} sr {Koristite zagrade za grupisanje izraza} pl {Uyj nawiasw by pogrupowa wyraenia} pt {Use parentesis para agrupar expresses} label exp_list sv {En lista ver sparade uttryck} en {A list of saved expressions} de {Liste der abgespeicherten Suchmasken.} it {Lista di espressioni salvate} fr {Une liste des expressions sauves.} sr {Spisak sauvanih izraza} pl {Lisa zapisanych wyrae} pt {Uma lista de expresses gravadas} label exp_text sv {Uttryckets innehll} en {Content of expression} de {Inhalt des Ausdrucks} it {Contenuto dell'espressione} fr {Contenu de l'expression.} sr {Sadraj izraza} pl {Tre wyraenia} pt {Contedo da expresso} label exp_delete sv {Radera det valda uttrycket} en {Delete the marked expression} de {Markierten Ausdruck lschen} it {Cancella l'espressione selezionata} fr {Dtruire l'expression marque.} sr {Brisanje oznaenog izraza} pl {Usu oznaczone wyraenie} pt {Apaga a expresso marcada} label fs_show sv {Visa alla filer i det aktiva biblioteket} en {Show all files in the current directory} de {Dateien im aktuellen Verzeichnis auflisten.} it {Mostra tutti i file nella directory corrente} fr {Montre tous les fichiers du rpertoire courant.} sr {Pokai sve datoteke u tekuem direktorijumu} pl {Poka wszystkie pliki w folderze} pt {Mostra todos os ficheiros to directrio actual} label fs_old sv {Meny som innehller gamla bibliotek} en {Menu containing old directories} de {Zuletzt benutzte Verzeichnisse.} it {Menu contenente directory visitate} fr {Menu contenant les rpertoires prcdemment visits.} sr {Ranije korieni direktorijumi} pl {Menu zawierajce stare foldery} pt {Menu contendo directrios previamente visitados} label mode_menu sv {Denna meny lter dig pverka rttigheterna p filen} en {This menu lets you modify the permissions of the file} de {Zugangsberechtigungen fr Datei verndern} it {Questo menu permette di modificare i permessi del file} fr {Ce menu permet de modifier les permissions du fichier.} sr {Ovim menijem se podeavaju prava pristupa datoteci} pl {To menu pozwala modyfikowa prawa dostpu do pliku} pt {Permite-lhe alterar as permisses do ficheiro} label welcome_lang sv {Denna meny lter dig vlja sprk p applikationen} en {This menu lets you choose the user interface language} de {Whlen Sie hier die Sprache aus.} it {Questo menu permette di selezionare la lingua dell'interfaccia} fr {Ce menu permet de choisir le langage que TkRat utilise.} sr {Ovim menijem birate jezik koji koristi TkRat} pl {To menu pozwala wybiera jzyk interfejsu} pt {Permite-lhe escolher o idioma da interface do TkRat} label welcome_shutup sv {Denna meny lter dig vlja om du ven i fortsttningen vill bli informaerad om ndringar i TkRat (den r ven tillgnglig via installlningsfnstret)} en {This menu lets you choose if you want to be informed of changes in TkRat the next time they occur (it is also accessible from the Preferences window)} de {Mit dieser Option legen Sie fest, ob TkRat Ihnen beim ersten Start einer neu installierten Version des Programms die neuen Funktionen beschreiben soll oder nicht.} it {Questo menu permette di scegliere se si vuole essere informati di cambiamenti in TkRat la prossima volta che avvengono ( possibile cambiare questo valore anche dalla finestra delle preferenze)} fr {Ce menu vous permet d'tre prvenu lors d'un changement de version de TkRat. Aussi accessible par la fentre prfrence.} sr {Ovde birate da li elite da budete obaveteni o izmenama u TkRat-u kada se sledei put dese (takoe se moe regulisati u prozoru za podeavanja} pl {To menu pozwala ustawi, czy chcesz by powiadamiany o zmianach w programi TkRat (to jest take dostpne okna preferencji)} pt {Permite-lhe escolher se pretende ser informado de mudanas no programa quando iniciar uma verso mais recente (configurao tambm acessvel a partir da janela de preferncias)} label welcome_cont sv {Stng detta fnster och ppna TkRats huvudfnster} en {Close this window and open the main TkRat window} de {Dieses Fenster schlieen und TkRat fortsetzen.} it {Chiudi questa finestra e apri la finestra di TkRat} fr {Ferme cette fentre et ouvre la fentre de messages de TkRat.} sr {Zatvori ovaj prozor i otvori glavni panel TkRat-a} pl {Zamknij to okno i otwrz okno poczty TkRat} pt {Fecha esta janela e abre a janela principal de mensagens} label ok_and_apply sv {Stng fnster och applicera ndringarna} en {Close window and apply changes} de {Dieses Fenster schlieen und die Einstellungen bernehmen.} it {Chiudi la finestra e applica le modifiche} fr {Ferme cette fentre et applique les changements.} sr {Zatvori prozor i primeni izmene} pl {Zamknij okno i dokonaj zmian} pt {Fecha a janela e aplica as alteraes} label keydef_delete sv {Tryck p denna knapp och sedan ngon av tangendefinitionerna ovan fr att readera definitionen} en {Press this button and then one of the keys above to delete the indicated key} de {Drcken Sie diese Schaltflche und anschlieend eine der oben angegebenen Tastenbezeichnungen, um die entsprechende Zuordnung zu lschen.} it {Premi questo pulsante e uno dei tasti sopra per cancellare il tasto indicato} fr {Cliquez sur ce bouton puis l'une des touches ci-dessus pour dtruire la touche dsigne.} sr {Pritisnite ovo dugme i zatim jedno od gore prikazanih da biste ga izbrisali} pl {Nacinij ten przycisk a potem jeden z przyciskw powyej by usun wybrany klucz} pt {Clique neste boto e depois numa das definies acima para eliminar esse atalho} label keydef_add sv {Tryck p denna knapp fr att lgga till tangenter till denna hndelse. Efter att du tryckt p knappen s skall du trycka den nskade tangenten p tangentbordet} en {Press this button to add a new key to this event. After you have pressed the button, you should press the key on the keyboard.} de {Drcken Sie diese Schaltflche, um eine neue Tastenzuordnung fr diese Aktion hinzuzufgen. Drcken Sie anschlieend die gewnschte Taste bzw. Tastenkombination.} it {Premi questo pulsante per agiungere un nuovo tasto a questo evento. Dopo aver premuto il pulsante devi premere il tasto sulla tastiera} fr {Cliquez sur ce bouton pour ajouter une nouvelle touche cet vnement. Aprs avoir cliqu, saisissez la touche au clavier.} sr {Pritisnite ovo dugme da poveete novi taster sa ovom naredbom, a zatim treba da pritisnete eljeno dugme na tastaturi.} pl {Nacinij ten przycisk by doda nowy klucz do tego zdarzenia. Po naciniciu przycisku powiniene nacisn klawisz na klawiaturze} pt {Clique este boto para criar uma tecla de atalho associada a esta aco. Depois deste boto deve ento pressionar a tecla ou combinao no teclado} label keydef_def sv {Hr se du tangentkombinationerna som orsakar denna hndelse} en {Here you see the keys which will trigger this event} de {Tasten bzw. Tastenkombinationen, die die jeweilige Aktion auslsen.} it {Qui si possono vedere i tasti che causeranno questo evento} fr {Liste des touches qui dclenchent cet vnement.} sr {Ovde vidite tastere koji su povezani sa ovom naredbom} pl {Tu widzisz klawisze ktre zareaguj na zdarzenie} pt {Neste espao v os atalhos existentes para estas aces} label apply_prefs sv {Applicera ndringarna och spara de nya vrdena} en {Apply changes and save new values} de {nderungen bernehmen und speichern.} it {Applica le modifiche e salva i nuovi valori} fr {Applique les changements et sauvegarde les nouvelles valeurs.} sr {Primeni izmene i sauvaj nove vrednosti} pl {Dokonaj zmian i zapisz nowe wartoci} pt {Aplica as alteraes e grava novos valores} label pref_language sv {Sprket som anvndargrnssnittet anvnder. Om man ndrar denna instllningen s mste man starta om TkRat fr att det skall mrkas} en {The language of the user interface. A change to this option requires a restart of TkRat to take effect.} de {Die Sprache fr TkRat einstellen. Diese Einstellung wirkt sich erst nach einem Neustart aus.} it {La lingua dell'interfaccia utente. Perch questa opzione abbia effetto necessario far ripartire TkRat} fr {Langage utilis par TkRat. Le changement n'est effectif qu'aprs avoir redmarr TkRat.} sr {Jezik korisnikog interfejsa. Da bi nova vrednost stupila na snagu potrebno je ponovo pokrenuti TkRat.} pl {Jzyk interfejsu uytkownika. Zmiana tej opcji wymaga restartu TkRat} pt {Lngua utilizada pelo TkRat. A alterao s ser visvel aps reiniciar o programa} label pref_color_set sv {Vilken frgskala som TkRat skall anvnda} en {Which color scheme the application should use} de {Das gewnschte Farbschema fr TkRat.} it {Lo schema di colori di TkRat} fr {Quel jeu de couleur TkRat devrait utiliser.} sr {Skup boja koje TkRat koristi} pl {Ktrego schematu kolorw powinna uywa aplikacja} pt {O esquema de cores que pretende usar} label pref_iconic sv {Hr kan man sga t TkRat att starta i ikonifierat lge} en {Here you can instruct TkRat that it should start up iconified} de {Aktivieren Sie diese Option, um TkRat minimiert zu starten.} it {Determina se TkRat parte iconizzato} fr {Indique si TkRat doit dmarrer en icne.} sr {Da li TkRat treba da se pokree ikonifikovan} pl {Tu moesz ustawi, aby TkRat uruchamia si zikonizowany} pt {Indique se pretende que o TkRat inicie no modo iconificado} label pref_print_command sv {Kommando som skriver ut brev. Alla '%s' i kommandonamnet kommer att bytas ut mot namnet p filen som skall skrivas ut. Om det inte finns ngra '%s' s fr kommandot data som skall skrivas ut p stdin. Man br ocks ha med ett '%p' som kommer att bytas ut mot namnet p skrivaren} en {Command to use for printing. Any '%s' in the command name will be replaced with the name of the file to print. If no '%s' is present, then the program will get the data on its stdin. One should also include a '%p' which will be replaced by the name of the printer.} de {Befehlszeile zum Drucken. Ein '%s' wird durch den Namen der zu druckenden Datei ersetzt; Kommt kein '%s' vor, werden die Daten dem angegebenen Programm auf der "Standardeingabe" (stdin) bergeben. Ein '%p' wird durch den Namen des Druckers ersetzt. Kommt kein '%p' vor, kann der benutzte Drucker nicht ausgewhlt werden.} fr {Quelle commande utiliser pour imprimer. Les caractres %s seront remplacs par le nom du fichier imprimer. Si %s n'apparat pas, Le programme d'impression reoit les donnes sur son entre standard. Les caractres %p seront remplacs par le nom de l'imprimante. Si %p n'apparat pas, vous ne pourrez pas slectionner d'imprimante} sr {Koja se komanda koristi za tampanje. Svako '%s' u komandi e biti zamenjeno nazivom datoteke koja se tampa, a ako nema '%s' onda e podaci biti prosleeni programu preko stdin-a. Takoe treba umetnuti '%p' koje e biti zamenjeno nazivom tampaa.} pl {Polecenie do drukowania. Kade '%s" w tym poleceniu zostanie zastpione nazw pliku do wydrukowania. Jeli nie bdzie ani jednego '%s' program program otrzyma dane na stdin. Polecenie powinno take zawiera '%p', ktre zostanie zastpione nazw drukarki} pt {Comando a usar para impresso de mensagens. Qualquer '%s' no nome do comando ser substitudo pelo nome do ficheiro a imprimir. Caso contrrio, o programa recebe os dados na entrada padro (stdin), o mais comum. Deve tambm incluir-se '%p' que ser substitudo pelo nome da impressora} label pref_tmp sv {Biblioteket dr temporrfiler skapas} en {Directory to store temporary files} de {Verzeichnis fr temporre Dateien.} it {Directory per file temporanei} fr {Rpertoire o stocker les fichiers temporaires.} sr {Direktorijum za privremene datoteke} pl {Katalog do przechowywania plikw tymczasowych} pt {Directrio onde escrever ficheiros temporrios} label pref_fontsize sv {Normalstorleken p all text} en {The normal size of all text} de Standard-Schriftgre it {La normale dimensione del testo} fr {Taille normale du texte.} sr {Normalna veliina svog teksta} pl {Normalna wielko caego tekstu} pt {Tamanho normal do texto} label pref_terminal sv {Det kommando som anvnds nr TkRat skall skapa en interaktiv terminal fr de ingngar i mailcap-filerna som har egenskapen needsterminal. Kommandot frn mailcap-filen kommer att lggas till efter detta kommando} en {Which command to use when creating an interactive terminal for mailcap entries that have 'needsterminal' set. This command will be prepended to the View command.} de {Befehlszeile, mit der Hilfsprogramme gestartet werden sollen, fr die in der mailcap "needsterminal" gesetzt ist. Der Befehl fr das eigentliche Hilfsprogramm wird rechts angefgt.} it {Quale comando da utilizzare quando si crea un terminale interattivo per i comandi mailcap che hanno bisogno di un terminale. Questo comando verr messo prima del comando di visualizzazione} fr {La commande utiliser lors de la cration d'un terminal interactif pour une commande mailcap qui ncessite un terminal. Cette commande sera excute avant la commande de visualisation.} sr {Koja se komanda koristi za stvaranje interaktivnog terminala za mailcap zapise s postavljenim 'needsterminal'. Na ovu komandu e se nadovezati naredba za pregled.} pl {Ktrego polecenia uywa podczas tworzenia interaktywnego terminalu dla wpisw mailcap, ktre maj ustawienie 'needsterminal'. To polecenie bdzie doczone do opcji podgldu} pt {O comando a utilizar aps a abertura de um terminal para entradas mailcap que necessitam um terminal. Este comando ser executado antes do comando de visualizao} label pref_list_format sv {Denna instllning bestmmer hur listan ver brev ser ut. Den ser ut som ett argument till printf() frutom att det enda som fr komma efter ett '%' r (frutom ytterligare ett '%') ett heltal (mste ej finnas) som kan vara negativt och en av fljande bokstver. s - Brevets mne n - Namnet p avsndaren (eller epostadress om namnet ej r tillgngligt). m - Avsndarens epostadress. r - Mottagarens namn (eller epostadress om namnet ej r tillgngligt). R - Mottagarens epostadress. b - Ungefrlig storlek p brevet (i bytes). B - Som ovan men skrivet med max fyra tecken. d - Brevets datum (i lsbar form). D - Brevets datum (i sekunder sedan tidens brjan). S - Brevets status (max 5 tecken). Fljande statusbokstver anvnds: N - brevet r olst A - du har svarat p brevet D - brevet r markerat fr radering F - brevet rr markerat ("flagged") och tillhr drfr den aktiva gruppen. + - brevet var adresserat till dig personligen i - Brevets index i listan t - trdningsstrng} en {This option determines what the list of messages looks like. This string looks like an argument to printf() except that the only thing that may follow a '%' (except another '%') is an optional integer which may be negative and one of the following characters: s - Subject of message n - Name of sender (or mail address if there is no name) m - Mail address of sender r - Name of recipient (or mail address if there is no name) R - Mail address of recipient b - Approximate size of message in bytes B - As above but expressed in max four characters d - Message date (in human readable form) D - Message date (in seconds since the epoch) S - Message status (maximum 5 characters) The following states are used: N - this message is unread A - this message has been answered D - this message is marked for deletion F - this message is flagged and therefore a member of the currrent group + - this message was addressed to you personally i - Current message index t - Threading string} de {Beschreibung des Aussehens, das eine Liste von Nachrichten haben soll, in "printf()-hnlichem" Format. Es sind nach '%' nur eine (auch negative) ganze Zahl und die folgenden Formatbuchstaben zugelassen: s Betreff n Name des Absenders, falls keiner, seine Adresse m Adresse des Absenders r Name des Empfngers, falls keiner, seine Adresse R Adresse des Empfngers b Ungefhre Gre der Nachricht in Byte B Dito, aber auf vier Stellen beschrnkt d Absendedatum (lesbare Schreibweise) D Absendedatum (Unix-internes Format, m.a.W. die seit dem 1.1.1970 0:00 GMT vergangenen Sekunden S Status, maximal fnf Zeichen: N ungelesen A beantwortet D zum Lschen vorgemerkt F markiert (gehrt zur Gruppe) + an Sie persnlich adressiert i Laufende Nummer t Bezeichner fr den "Thread" (etwa: Diskussionsstrang) der Nachricht} fr {Cette option dtermine l'aspect de la liste des messages. Cette chane ressemble une chane pour printf(3). Le signe % ne peut tre suivi que d'un entier (ventuellement ngatif) et d'un des caractres ci-aprs ou du signe % : s - L'objet du message. n - Nom de l'expditeur (ou adresse lectronique s'il n'y a pas de nom). m - Adresse lectronique de l'expditeur. r - Nom du destinataire (ou adresse lectronique s'il n'y a pas de nom). R - Adresse lectronique du destinataire. b - Taille approximative du message en octets. B - Idem mais exprime en 4 caractres maximum. d - Date du message sous une forme lisible. D - Date en nombre de secondes coules depuis le dbut des temps. S - tat du message (pas plus de 5 caractres). TkRat utilise les abrviations suivantes. N - Le message n'a jamais t lu. A - Vous avez rpondu ce message. D - Ce message sera dtruit. F - Ce message fait partie du groupe courant. + - Ce message vous a t adress personnellement. i - Numro d'ordre du message. t - indicateur de fil conducteur.} sr {Ova opcija odreuje kako izgleda spisak pisama. Ovaj string je kao argument za printf(), osim to iza '%' (sem novog '%') moe da sledi samo opcioni celi broj (moe biti negativan) i jedan od ovih znakova: s - naslov pisma, n - ime poiljaoca (ili adresa ako nema imena), m - adresa poiljaoca, r - ime primaoca (ili adresa ako nema imena), R - adresa primaoca, b - priblina veliina pisma u bajtovima, B - kao 'b', ali izraeno s najvie 4 znaka, d - datum pisma (u formi razumljivoj za ljude), D - datum pisma (u sekundama od poetka "vremena"), S - status pisma, najvie 5 znakova. Postoje sledea stanja: N - pismo nije proitano, A - na pismo je poslat odgovor, D - pismo je oznaeno za brisanje, F - pismo je oznaeno kao deo grupe, + - pismo je bilo upueno lino na Vas. i - indeks tekueg pisma, t - opis povezanosti.} pl {Ta opcja ustala, jak wyglda lista wiadomoci. Ten acuch wyglda jak argument do printf() poza tym, e po '%' moe by (poza nastpnym '%') opcjonalna liczba, rwnie negatywna, lub jeden z poniszych znakw. s - Temat wiadomoci n - Nazwa nadawcy (lub adres jeli nie podano nazwy) m - Adres nadawcy b - Przybliona wielko wiadomoci w bajtach B - Jak wyej, ale wyraona w maksymalnie czterech znakach d - Data wiadomoci (czytelna dla ludzi) D - Data wiadomoci (w sekundach od pocztku epoki) S - Status wiadomoci (maksymalnie pi znakw) Ponisze stany s uywane: N - wiadomo nie przeczytana A - odpowiedziano na wiadomo D - wiadomo jest przeznaczona do usunicia F - wiadomo jest oflagowana i jest czonkiem grupy + - wiadomo zostaa zaadresowana do ciebie osobicie i - indeks wiadomoci t - acuch wtka} pt {Esta opo determina o aspecto da lista de mensagens. Este texto semelhante a um argumento para a conhecida instruo printf() com a excepo de o carcter '%' pode ser seguido apenas por um algarismo inteiro (opcional) e de um dos caracteres seguinte: s - Assunto da mensagem n - Nome do remetente (ou endereo, caso no exista um nome) m - Endereo do remetente r - nome do destinatrio (ou endereo, caso no exista um nome) R - Endereo do destinatrio b - Tamanho aproximado da mensagem em bytes B - O mesmo que 'b' mas no mximo quatro caracteres d - Data da mensagem (em formato legvel) D - Data da mensagem (em segundos, contados desde o incio dos tempos - 01/01/1970) S - Estado da mensagem (no mximo 5 caracteres) Os estados seguintes so passveis de ser usados: N - esta mensagem ainda no foi lida A - esta mensagem foi respondida D - esta mensagem foi marcada para remoo F - esta mensagem est marcada para aces de grupo + - esta mensagem foi endereada directamente ao utilizador i - Nmero de ordem da mensagem t - Texto indicador de sequncia "thread"} label pref_show_header_selection sv {Om man visar valda huvudflt s kommer bara de flt som nms i denna listan att visas} en {If we are showing the selected headers only, those header lines mentioned in this list will be shown} de {Eine Liste ausgewhlter Kopfzeilen. Nur diese Kopfzeilen werden vor dem Inhalt der Nachricht angezeigt, wenn unter "Anzeigen" oder beim Drucken die "ausgewhlten Kopfzeilen" selektiert werden.} it {Se vengono mostrati solo i messaggi selezionati, solo questi messaggi vengono mostrati} fr {Seuls les en-ttes dsigns dans cette liste seront affichs (si le mode d'affichage des en-ttes est 'Slectionns').} sr {Ovi delovi zaglavlja se uzimaju kao "odabrani"} pl {Jeli zostan pokazane tylko wybrane nagwki to wanie te z tej listy} pt {Apenas os cabealhos contidos nesta lista sero exibidos caso se pretendam mostrar apenas os cabealhos seleccionados (devem estar escritos em Ingls)} label pref_folder_sort sv {Standardvrde p sorteringsordning fr mappar} en {Default sort order for folders} de {Normale Sortierreihenfolge fr Ordner.} fr {Ordre de tri par dfaut des botes lettres.} sr {Kako se rea spisak pisama} pl {Domylny tryb sortowania folderw} pt {Ordem das mensagens numa pasta} label pref_start_selection sv {Vilket brev som skall vljas nr en mapp ppnas} en {Which message to select when a folder is opened} de {Welche Nachricht soll beim ffnen eines Ordners angezeigt werden?} it {Il messaggio da selezionare quando viene aperta una cartella} fr {Quels messages slectionner quand une bote lettres est ouverte.} sr {Koje pismo odabrati kada se skup otvori} pl {Ktr wiadomo zaznaczy, kiedy folder zostanie otworzony} pt {A mensagem a seleccionar quando se abre uma pasta} label pref_url_viewer sv {Vilket program som skall anvndas fr att visa URLer. Kan ocks vara en egendefinierad procedur} en {Which viewer to use for showing URLs. This can also be a userproc.} de {Diese Option legt fest, welches Programm zum Betrachten einer URL verwendet wird. Dies kann auch eine Benutzerprozedur sein.} it {Il visualizzatore da usare per vedere le URL. Pu essere anche una procedura utente} fr {Quel programme utiliser pour visualiser les URL. Cela peut aussi tre une procdure utilisateur.} sr {Program za prikazivanje URL-ova - to moe biti i userproc} pl {Ktrej przegldarki uywa do otwierania URLi. To moe by take userproc} pt {O programa a utilizar para visualizar o endereo URL. Pode ser igualmente um procedimento do utilizador} label pref_dbase_dir sv {Var databasen skall lagras} en {Where the database is stored} de {Wo soll die Datenbank abgespeichert werden?} it {Directory dove viene salvato il database} fr {O conserver la base de donnes.} sr {Gde se uva baza} pl {Gdzie jest przechowywana baza danych} pt {O directrio onde a base de dados mantida} label pref_def_extype sv {Vilken expireringstgrd som skall fresls} en {The default expiration action} de {Voreinstellung fr die Aktion beim Verfall.} it {Azione normale di scadenza} fr {Action excuter quand un message arrive expiration.} sr {Podrazumevana akcija za isticanje} pl {Domylna akcja pod koniec czasu przechowywania} pt {A aco a tomar por defeito aps termo da mensagem} label pref_def_exdate sv {Vilken expireringsdag som skall fresls (i dagar sedan brevet lades in i databasen)} en {The default expiration time (in days since inserted into database)} de {Voreinstellung fr das Verfallsdatum von Nachrichten, im Format "+N" fr den Verfall nach N Tagen.} it {Tempo normale di scadenza (in giorni dall'inserimento nel dbase)} fr {Dure de vie d'un message dans la base de donnes, en jours.} sr {Podrazumevano vreme za isticanje (u danima od ubacivanja u bazu)} pl {Domylny czas przechowywania (w dniach od wprowadzenia do bazy)} pt {A data de validade por defeito de uma mensagem na base de dados, em dias} label pref_dbase_backup sv {Biblioteket dr man lgger bev som har expirerat frn databasen med tgrden "arkivera". Breven lagras komprimerade} en {The directory to store messages which are expired from the database with the backup expiration option (the messages will be compressed)} de {Geben Sie hier das Verzeichnis an, in dem Sicherungskopien fr Nachrichten angelegt werden, wenn Sie dies als Aktion bei Erreichen des Verfallsdatums festlegen.} it {La directory dove mettere i messaggi scaduti dal database con l'opzione di backup di scadenza (i messaggi vengono compressi)} fr {Rpertoire o conserver les messages de la base de donnes ayant expirs et pour lesquels l'action excuter est l'archivage (le message sera compress).} sr {Direktorijum gde se uvaju pisma koja su istekla u bazi a nareeno je arhiviranje (pisma e biti komprimovana)} pl {Katalog do przechowywania wiadomoci, ktre usunito z bazy danych, a ktre maj termin przechowywania w archiwum (wiadomoci zostan skompresowane)} pt {Directrio onde colocar as mensagens da base de dados (aps a data de validade) cuja aco definida colocar em arquivo (com compresso)} label pref_watcher_enable sv {Om vktaren verhuvudtaget skall visas} en {If we should pop up the Watcher at all} de {Soll der Wchter aktiviert werden und neu ankommende Nachrichten melden?} it {Se si vuole vedere la finestra di controllo} fr {Si oui ou non la fentre de surveillance doit surgir ?} sr {Treba li uopte startovati nadzorni prozor} pl {Czy wcza obserwatora} pt {Se a janela de aviso de novas mensagens deve ou no aparecer} label pref_watcher_time sv {Hur ofta vktaren skall leta efter nya brev i olika mappar. Tiden som anges r intervallet mellan tester i sekunder. Det finns tv olika tider, en fr databasen och en fr alla andra (standard). Vrdet noll stnger av funktionen} en {How often the Watcher should check different folders for new messages. This is expressed as the interval between checks in seconds. There are two times, one for database folders and one for all others (std). A zero interval disables the checking.} de {Wie oft soll der Wchter Ordner auf neue Nachrichten berprfen? (Eine Einstellung fr normale und eine fr Datenbank-Ordner; 0 = nicht prfen)} it {La frequenza con cui la finestra di controllo verifica le varie cartelle per messaggi nuovi. Questa espressa come l'intervallo tra controlli in secondi. Ci sono due valori: uno per le cartelle database e uno per le altre. Uno zero disabilita la verifica} fr {Intervalle (en secondes) entre 2 relevs des diffrentes botes lettres. Il y a 2 intervalles. Un pour la bote lettres base de donnes, et un pour les autres. Zro dsactive la surveillance.} sr {Koliko esto nadzorni prozor treba da proverava ima li novih pisama. Ovo se zadaje kao interval izmeu dve provere u sekundama. Postoje dva vremena - jedno za baze, a drugo za sve ostale skupove (standard). Nulti interval iskljuuje provere.} pl {Jak czsto obserwator powinien sprawdza czy s nowe wiadomoci. Jest to wyraone w przedziale midzy sprawdzaniem w sekundach. S dwa czasy, jeden dla folderw bazy danych i drgi dla wszystkich innych (std). Zerowy przedzia wycza sprawdzanie} pt {Frequncia de verificao de novas mensagens (em segundos). Existem duas frequncias de verificao, uma para pastas comuns e outra para a base de dados. Um intervalo de zero desactiva a vigilncia chegada de novas mensagens} label pref_watcher_max_height sv {Hur hg listan ver nya brev fr bli} en {The maximum height (in lines) of the list of new messages} de {Die maximale Anzahl Zeilen, die das Wchter-Fenster haben soll.} it {L'altezza massima (in linee) della lista di nuovi messaggi} fr {Taille apparente (en lignes) de la liste des nouveaux messages.} sr {Maksimalna visina spiska pisama (u linijama)} pl {Maksymalna wysoko (w liniach) listy nowych wiadomoci} pt {Altura mxima (em linhas) da lista de novas mensagens} label pref_watcher_show sv {Vilka brev som skall visas} en {Which messages to show} de {Legen Sie fest, welche Nachrichten im Wchter-Fenster aufgelistet werden sollen.} it {Quali messaggi da visualizzare} fr {Quels messages montrer ?} sr {Koje poruke prikazivati} pl {Ktre wiadomoci pokazywa} pt {Mensagens a mostrar} label pref_watcher_format sv {Formatet p listan. Se hjlpen fr mappfnstret fr detaljer} en {The format of the list, see the help for the Folder window for details on the format} de {Legen Sie hier das Format der Nachrichtenliste im Wchter- Fenster fest. Die Ersetzungen werden wie beim Format der Nachrichtenliste vorgenommen (siehe Allgemeines).} it {Il formato della lista. Vedere l'aiuto per la finestra cartella per informazioni su questo formato} fr {Format de la liste. Voir l'aide de la fentre des botes lettres pour plus de dtails sur le format.} sr {Format liste - detalji su dati u pomoi za format spiska pisama} pl {Format listy - zobacz w pomocy nt okna folderu, szczegy formatu} pt {Formato da lista (veja a ajuda para a janela de pastas para mais detalhes no formato desta lista)} label pref_watcher_bell sv {Hur mnga gnger terminalens klocka skall ringa nr vktaren upptcker ny post. OBSERVERA att om anvndarproceduren RatUP_Bell finns s kommer detta vrde att vara lst och klockan rings ej} en {How many times the terminal bell should ring whenever the Watcher discovers new messages. NOTE if the userproc RatUP_Bell exists, then this value is locked and the bell will not be rung.} de {Wie oft soll beim Eintreffen neuer Nachrichten ein akustisches Signal gegeben werden? Wenn die Benutzerprozedur "RatUP_Bell" existiert, dann ist dieser Wert gesperrt und es wird kein akustisches Signal gegeben.} it {Quanti beep da effettuare quando la finestra di controllo trova nuovi messaggi. NOTA: se la procedura utente RatUP_Bell esiste, allora questo valore bloccato e non verranno fatti beep} fr {Nombre de sonneries quand le veilleur dcouvre de nouveaux messages.} sr {Koliko puta treba aktivirati terminalsko zvono kada nadzorni prozor otkrije novo pismo. Ako postoji korisnika procedura (userproc) RatUP_Bell onda se zvono ne koristi, a polje je zakljuano.} pl {Jak wiele razy dzwonek terminala powinien dzwoni kiedy obserwator znajdzie nowe wiadomoci} pt {Nmero de toques do terminal quando o sistema de vigilncia detecta a chegada de uma nova mensagem NOTA: no caso da funo de utilizador RatUP_Bell existir, este valor cancelado e o toque no ocorrer} label pref_bcc sv {Standardvrde fr blindkopiefltet (BCC)} en {Default value for Blind Carbon Copy field} de {Voreinstellung fr die in der Nachricht nicht genannten Empfnger (BCC).} fr {Destinataires masqus par dfaut (BCC).} sr {Podrazumevana vrednost za slepu kopiju (BCC)} pl {Domylna warto dla "lepej" kopii (BCC)} pt {Valor por defeito do campo Bcc: (Cpia cega)} label pref_reply_to sv {Standardvrdet fr Svara-Till (Reply-To) fltet i brev som skickas} en {The default Reply-To address} de {Voreinstellung fr die von der Absender-Adresse abweichende "Antworten an" Adresse (Reply-To).} it {L'indirizzo rispondi-a normale} fr {L'adresse Rpondre ࠻ par dfaut.} sr {Podrazumevana adresa za odgovore} pl {Domylny adres do odpowiedz (Reply-To)} pt {Valor por defeito do campo Responder-A:} label pref_compose_headers sv {Vilka huvudflt som anvndaren normalt kan ndra. Anvndaren kan dock ndra listan i "Skriva brev"-fnstret} en {Which headers the user may edit by default. The list of headers can be changed in the Compose window.} de {Welche Kopfzeilen sollen beim Schreiben einer Nachricht editiert werden drfen?} it {Quali intestazioni l'utente pu modificare normalmente. La lista di intestazioni pu essere cambiata nella finestra di composizione} fr {En-ttes prsents par dfaut lors d'une rdaction. Vous pourrez toujours changer d'avis pour chaque message individuellement dans chaque fentre de rdaction.} sr {Koje delove zaglavlja korisnik normalno moe da menja. Ovo se moe podesiti i u prozoru za sastavljanje pisma.} pl {Ktre nagwki uytkownik moe domylnie zmienia. Lista nagwkw moe by zmieniona w oknie edycji} pt {Cabealhos a mostrar e editar por defeito na janela de redaco. Esta lista pode ser alterada durante a composio de uma mensagem, isto , outros cabealhos podero ser editados} label pref_editor sv {Vilket editor som skall kras nr anvndaren trycker p knappen 'Extern editor'. Ett '%s' i kommandot kommer att bytas ut mot namnet p filen som skall editeras och ett '%x' mot kordinaterna fr vre vnstra hrnet av fnstret (+X+Y). "Skriva brev"-fnstret kommer att vara lst tills editorn avslutas} en {Which external editor to run if the user presses the 'External editor' button. A '%s' in this command will be exchanged with the name of the file to edit and a '%x' will be replaced with the coordinates of the upper left corner (+X+Y). The Compose window will be locked until this editor returns.} de {Der externe Editor, der gestartet wird, wenn Sie "Externer Editor" whlen. '%s' im Kommando wird durch den Namen der Datei ersetzt, die die Nachricht enthlt. '%x' wird durch die Koordinaten der linken oberen Ecke (+X+Y) ersetzt. Das "Nachricht schreiben" Fenster wird blockiert, solange der externe Editor luft.} fr { Quel diteur utiliser si vous cliquez sur le bouton 'diteur externe'. Les symboles %s seront remplacs par un nom de fichier. TkRat remplace aussi les caractres %x par la position du coin suprieur gauche de la fentre de rdaction (+x+y). L'diteur de TkRat restera bloqu jusqu' ce que l'diteur externe se termine.} sr {Koji spoljni editor koristiti kada korisnik klikne na dugme 'Spoljni editor'. '%s' u komandnoj liniji ?e biti zamenjeno imenom datoteke, a '%x' ?e biti zamenjeno koordinatama gornjeg levog ugla (+X+Y). Prozor za sastavljanje pisma ?e biti zaklju?an sve dok spoljni editor ne okon?a rad.} pl {Ktry z zewntrznych edytorw uruchomi, gdy uytkownik nacinie przycisk "Zewntrzny edytor". '%s' w linii polece bdzie zamienione na nazwe pliku, a '%x' na pozycj lewego grnego rogu (+X+Y). Okno edycji bdzie zablokowane do momentu zamknicia programu zewntrznego} pt {O editor externo a usar quando o boto de editores externos pressionado. O TkRat substitui as strings '%s' pelo nome do ficheiro a editar e '%x' pelas coordenadas do canto superior esquerdo da janela a abrir. A janela de redaco permanecer bloqueada enquanto o editor externo estiver em execuo} label pref_attribution sv {Denna raden (om den r definierad) lggs in frst i varje svar. Raden har samma syntax som "Listformat"-instllningen} en {This line (if defined) is prepended to every reply. It has the same syntax as the "List format" option.} de {Format der Kurzbeschreibung, die in Antwortnachrichten vor die zitierte Originalnachricht gesetzt wird. Die Ersetzungen werden wie in der Nachrichtenliste vorgenommen (siehe Allgemeines).} it {Questa linea (se definita) viene messa prima di ogni risposta. Ha la stessa sintassi dell'opzione 'Formato lista'} fr {Cette ligne sera ajoute au dbut de chaque rponse. Utilisez la mme syntaxe que pour le format de la liste des messages.} sr {Ova linija (ako je definisana) se dodaje na svaki odgovor. Ima istu sintaksu kao opcija "Format spiska".} pl {Ta linia (jeli zdefiniowano) jest doczana do kadej odpowiedzi. Ma tak sam skadni jak opcja 'Format listy'} pt {Se definida, esta linha ser sempre usada como prefixo de cada resposta. A sintaxe usada a mesma da opo 'Formato da lista'} label pref_forwarded_message sv {Denna raden lggs in fre varje inbddat brev som skickas vidare} en {This line is inserted before the inlined message when you choose to "Forward inline"} de {Zeile, die bei weitergeleiteten Nachrichten vor die Originalnachricht gesetzt wird.} it {Questa linea viene inserita prima del messaggio incluso} fr {Ligne qui sera insre avant le message inclus lors d'un r-acheminement de message en inclusion.} sr {Ova linija se umee ispred citiranog pisma kada naredite preusmeravanje} pl {Ta linia jest wstawiana przed wczon wiadomoci, kiedy zrobisz "Forward inline"} pt {Esta linha inserida no incio da mensagem reenviada} label pref_skip_sig sv {Denna instllningen kontrollerar om signaturen p brev man svarar p skall inkluderas i svaret} en {This option controls whether to include the signature of a message you are replying to in the reply or not} de {Wenn diese Option aktiviert ist, wird die Signatur der Originalnachricht nicht zitiert.} it {Questa opzione controlla se includere o meno la signature del messaggio a cui si sta rispondendo} fr {Indique s'il faut inclure dans la rponse, la signature de la lettre laquelle vous rpondez.} sr {Da li u odgovor na pismo treba ukljuiti potpis tog pisma ili ne} pl {Ta opcja decyduje, czy wcza sygnaturk listu, na ktry odpowiadasz do odpowiedzi, czy nie} pt {Indica se deve ou no inserir a assinatura na mensagem de resposta} label pref_signature sv {Namnet p filen som innehller anvndarens signatur} en {The name of the file containing the user's signature} de {Name der Datei, aus der die Signatur gelesen werden soll.} it {Il nome del file contenente la signature utente} fr {Nom du fichier contenant votre signature.} sr {Ime datoteke sa korisnikovim potpisom} pl {Nazwa pliku zawierajcego sygnaturk uytkownika} pt {Nome do ficheiro contendo a assinatura} label pref_copy_attached sv {Sant om TkRat skall kopiera bilagefilerna s fort de lggs till. Om det r falskt s antar TkRat att filerna finns kvar nr brevet skickas} en {True if TkRat should make a copy of an attached file as soon as the file is specified. If it is false, TkRat assumes that the file will still be present when the message is actually sent.} de {Soll TkRat von Dateien, die als Anhnge versandt werden sollen, sofort eine Kopie anfertigen? Wenn nicht, muss die Originaldatei bis zum tatschlichen Versand der Nachricht verfgbar bleiben!} it {Opzione che determina se TkRar deve fare una copia degli allegati non appena effettuato l'allegamento. Altrimenti TkRat andr a cercare l'allegato al momento della spedizione} fr {Si vrai, TkRat effectuera immdiatement une copie des fichiers qu'il joint un message. Si faux, TkRat est plus optimiste et suppose que les pices jointes seront encore l lorsqu'il en aura besoin au moment de l'expdition du message.} sr {Da li se dodaci uz pismo kopiraju im ih zakaite. U suprotnom, TkRat oekuje da datoteke budu prisutne na svojim mestima kad se naredi slanje.} pl {Tak, jeli TkRat powinien robi kopie zacznikw zaraz po ich wybraniu. Jeli ustawiono 'Nie' TkRat uzna, e pliki cigle bd istniay podczas wysyania listu} pt {Se o TkRat deve ou no fazer cpias de ficheiros anexos assim que estes so anexados mensagem. Em caso negativo, o TkRat assume que os ficheiros estaro presentes no momento do envio da mensagem} label pref_from sv {Stter vilken avsndaradress som skall anges i brevet. Vnligen observera att om inte avsndaradressen pekar p dig s kommer en extra avsndaradress att lggas in (Sender:)} en {Which value the From: line should have. Please note that, if the From: line doesn't point at you, then a Sender: will be added} de {Voreinstellung fr die "Von" Kopfzeile. (Wenn diese Kopfzeile nicht auf den tatschlichen Absender zu verweisen scheint, fgt TkRat automatisch eine "Sender" Kopfzeile hinzu.)} it {Il contenuto della linea Da:. Se Da: non si riferisce al mittente, allora una linea Mittente: verr aggiunta} fr {Valeur du champ d'en-tte De: . Si De: ne vous dsigne pas, alors un champ d'en-tte Expditeur: vous dsignant sera automatiquement ajout.} sr {Vrednost 'Od:' ('From:') polja u zaglavlju. Ako 'Od:' ne upuuje na Vas, u zaglavlje e biti dodato i polje 'Sender:'.} pl {Jak warto powinno mie pole 'From:'. Pamitaj, e jeli to pole nie bdzie wskazywao na ciebie, dodatkowe pole 'Sender:' zostanie dodane} pt {O valor do campo 'De:', por defeito. Caso no coincida com a sua identidade, um campo 'Expeditor:' ser automaticamente adicionado} label pref_sendprot sv {Hur TkRat skall skicka brev. Via SMTP eller ett program} en {How to send messages, via SMTP or a program} de {Legen Sie mit dieser Auswahl fest, ob Nachrichten mittels (E)SMTP oder einem bestimmten Programm versandt werden sollen.} it {La modalit di spedizione dei messaggi. Tramite SMTP o programma} fr {Comment expdier les messages : Par SMTP ou en utilisant un programme.} sr {Kako slati pisma: preko SMTP-a ili programa} pl {Jak wysya wiadomoci. Przez SMTP czy jaki program} pt {Modo de envio de mensagens: via SMTP ou outro programa} label pref_smtp_hosts sv {Om vi skickar brev via SMTP s r detta en lista ver maskiner som vi skall frska skicka brevet till. Varje maskinnamn kan sluta med :port dr port r nummret p den port p maskinen som man skall prata SMTP med (mellanslag skall anvndas som listseparator)} en {If we are sending messages via SMTP, this is a space-delimited list of hosts we should try to send to. Each hostname may be appended by :port where 'port' is the port number to contact the host at.} de {Geben Sie hier den (E)SMTP-Server an, ber den Nachrichten versandt werden, falls oben SMTP eingestellt ist. Sie knnen auch mehrere, durch Leerzeichen getrennte (E)SMTP-Server angeben, mit denen TkRat den Versand versuchen soll. Zum Servernamen kann jeweils eine Portnummer im Format "host:port" angegeben werden.} fr {Listes des serveurs SMTP contacter dans le cas d'expdition des messages par SMTP. Chaque nom d'hte peut tre suivi de :numro_de_port. Chaque nom d'hte doit tre spar des autres par un espace.} sr {Ako se pisma alju preko SMTP-a, ovo je spisak hostova koje treba pozvati. Uza svako ime reunara se moe dodati ':port', gde je 'port' broj porta na koji se treba povezati. Separator u listi jeste razmak.} pl {Jeli wysyamy przez SMTP, jest to lista hostw, przez ktre powinimy sprbowa przesa. Kady host moe zosta dodany przez ':port', gdzie port to numer portu kontaktu z hostem (spacja jest separatorem)} pt {Lista de servidores a contactar em caso de envio de mensagens por protocolo SMTP. Cada nome de servidor pode ser seguido por ':port' em que 'port' o nmero do porto a contactar no servidor (o separador de lista um espao)} label pref_sendprog sv {Det hr r kommandot som anvnds om man vljer att skicka brev via ett program. Programet frvntas ta mottagarnas adress som argument och sjlva brevet p stdin (dvs sendmail)} en {This is the program which will be used if we are sending messages via a program. It is expected to take the recipient address as an argument and the actual message on stdin (e.g. /usr/lib/sendmail).} de {Befehlszeile, mit der Nachrichten versandt werden, wenn oben der Versand mit einem Programm ausgewhlt wurde. Die Empfnger werden der Befehlszeile von rechts angefgt, die Nachricht wird ber die "Standardeingabe" (stdin) bergeben.} it {Il programma che viene utilizzato per spedire messaggi. Deve prendere l'indirizzo del destinatario sulla riga di comando e il messaggio su stdin (come sendmail)} fr {Programme utiliser pour l'expdition de messages par programme. Ce programme doit accepter en argument l'adresse du destinataire et lire le message sur son entre standard. (par exemple sendmail (sendmail -bm -odb -oi -t)).} sr {Program koji se koristi za slanje pisama ukoliko je odabrano slanje preko programa. Oekuje se da taj program uzme adresu primaoca kao argument, a sadraj poruke preko stdin-a (kao sendmail).} pl {Program uywany, jeli wybierzemy opcj wysyania listw przez program. Wymagane jest, by przyjmowa adres odbiorcy jako argument a wiadomo przez stdin (jak np. sendmail)} pt {Programa a usar para envio de mensagens (caso opo envio prefira programa). Este programa deve aceitar como argumento o endereo do destinatrio e ler a mensagem a enviar atravs da sua entrada padro (exemplo: sendmail)} label pref_sendprog_8bit sv {Definierar vilken typ av data som programmet kan hantera (sju eller ttabitarsdata)} en {This is the type of data the program can handle (seven- or eight-bit)} de {Geben Sie hiermit an, ob das Programm, mit dem der Versand erfolgen soll, 8-Bit Daten verarbeiten kann, oder auf 7-Bit Daten beschrnkt ist.} it {Il tipo di dati che il programma pu gestire (sette o otto bit)} fr {Type de donnes que le programme supporte (sept ou huit bits).} sr {Tip podataka koje program moe da primi (sedmo- ili osmobitni)} pl {Typ danych, jakim program moe si posugiwa (7 lub 8bit)} pt {Tipo de dados que o programa pode processar eficientemente (sete ou oito bits)} label pref_charset sv {Vilket typsnitt vi anvnder} en {Which character set we are using} de {Benutzter Zeichensatz.} it {Il set di caratteri da usare} fr {Le jeu de caractres que vous utilisez.} sr {Koji karakter-set se koristi} pl {Jakiej strony kodowania uywamy} pt {Conjunto de caracteres a usar} label pref_pgp_version sv {Vilken version av PGP som du anvnder} en {Which version of PGP you are using} de {Whlen Sie hier die PGP Version, die TkRat verwenden soll.} fr {La version de PGP utilise.} sr {Koju verziju PGP-a koristite} pl {Ktrej wersji PGP uywasz} pt {Verso de PGP utilizada} label pref_pgp_path sv {Namnet p biblioteket som innehller pgp-programmet} en {The name of the directory containing the PGP program} de {Das Verzeichnis, in dem PGP installiert ist.} fr {Nom du rpertoire contenant le programme PGP.} sr {Direktorijum u kome se nalazi PGP program} pl {Nazwa katalogu zawierajcego program PGP} pt {Directrio contento o programa PGP} label pref_pgp_args sv {Eventuella extra argument som mste ges till PGP-kommandot} en {Any extra arguments that must be given to the PGP program} de {Falls beim Aufruf von PGP zustzliche Parameter angegeben werden mssen, knnen Sie diese hier angeben.} it {Argomenti extra da passare al programma PGP} fr {Arguments supplmentaires fournir au programme PGP.} sr {Dodatni argumenti koje treba proslediti PGP programu} pl {Jakiekolwiek dodatkowe argumenty dla programu PGP} pt {Argumentos adicionais a fornecer ao programa PGP} label pref_pgp_keyring sv {Det fullstndiga namnet p din nyckelring (inklusive skvg och suffix)} en {The full name of your PGP keyring (including path and suffix)} de {Hiermit knnen Sie den gewnschten PGP Schlsselbund festlegen, indem Sie den vollstndigen Dateipfad des Schlsselbundes angeben.} fr {Nom complet de votre fichier porte-clefs (inclure chemin et extension)} sr {Puno ime Vae datoteke sa PGP kljuevima (ukljuujui stazu i sufiks)} pl {Pena nazwa dla twojego pku kluczy PGP (wczajc cierzk i suffiks)} pt {Nome completo do ficheiro porta-chaves PGP (incluindo caminho e extenso de ficheiro)} label pref_cache_pgp sv {Om vi skall komma ihg PGP-lsenord} en {If we should cache PGP passwords} de {Aktivieren Sie diese Option, wenn TkRat die PGP Passwrter nach der Eingabe fr eine bestimmte Zeit speichern soll.} it {Se mantenere password PGP} fr {Se souvenir des mots de passe PGP ?} sr {Da li treba keirati PGP ifre} pl {Czy hasa PGP powinny by zapamitane} pt {Se deve ou no memorizar palavras passe de PGP} label pref_cache_pgp_timeout sv {Hur lnge som TkRat skall komma ihg ditt PGP-lsenord (0=applikationens livstid)} en {How long we should remember the PGP password (0=lifetime of application)} de {Legen Sie fest, wie lange TkRat die PGP Passwrter speichern soll. Der Wert 0 bedeutet bis zum Beenden.} it {Per quanto tempo devono essere mantenute le password PGP (0=per la durata dell'applicazione)} fr {Combien de temps se souvenir des mots de passe PGP. (0 = pour toute la dure de TkRat.} sr {Koliko dugo treba pamtiti PGP ifru (0 znai koliko god TkRat bude radio)} pl {Jak dugo pamita haso PGP (0=czas pracy programu)} pt {Tempo mximo de memria da palavra passe PGP (0 = enquanto o programa permanecer em execuo)} label pref_pgp_encrypt sv {Startvrdet fr knappen kryptera i brevskrivningsfnstret} en {The default value of the Encrypt checkbox in the Compose window} de {Hiermit geben Sie an, wie beim Schreiben neuer Nachrichten die Verschlsseln-Option voreingestellt werden soll.} it {Il valore standard del pulsante di crittazione nella finestra di composizione} fr {Valeur par dfaut de la case cocher Chiffrer dans la fentre de rdaction.} sr {Podrazumevana vrednost za opciju "ifrovanje" u prozoru za sastavljanje} pl {Domylny stan przycisku "Szyfruj" w oknie edycji wiadomoci} pt {O valor por defeito do boto de escolha 'Cifrar' no menu 'Extra' na janela de redaco} label howto_display sv {Denna meny lter dig bestmma hur brevdelen skall visas} en {This menu lets you decide how to display this bodypart} de {Whlen Sie in diesem Men, wie dieser Teil des Inhalts angezeigt werden soll.} it {Questo menu permette di determinare come viene visualizzata la componente del messaggio} fr {Ce menu permet de choisir comment afficher cette portion de message.} sr {Ovim menijem birate kako e biti prikazan ovaj deo pisma} pl {To menu pozwala decydowa, jak bdzie wywietlnana tre} pt {Este menu permite-lhe escolher como mostrar esta parte da mensagem} label save_bodypart sv {Spara denna del av brevet p fil} en {Save the current bodypart to a file} de {Diesen Teil der Nachricht in einer Datei speichern.} it {Salva la componente del messaggio in un file} fr {Sauver la portion courante du message dans un fichier.} sr {Snima tekui deo pisma u datoteku} pl {Zapisz tre do pliku} pt {Gravar esta parte da mensagem em ficheiro} label view_as_text sv {Om denna knapp r markerad s visas brevdelen som om den vore text} en {If this button is checked, this bodypart is shown as if it were text} de {Diesen Teil der Nachricht als Text anzeigen.} it {Se questo pulsante premuto allora questa parte del messaggio viene mostrata come se fosse testo} fr {Si cette case est coche, cette portion de message sera affiche comme du texte.} sr {Ako je ova opcija odabrana, onda se ovaj deo pisma prikazuje kao da je u pitanju tekst} pl {Jeli przycisk jest zaznaczony, tre jest wywietlana jakby bya tekstem} pt {Caso este boto esteja seleccionado, esta parte da mensagem ser mostrada como se fosse texto} label view_source sv {Se denna brevdel som den skickades (i ett separat fnster)} en {View this bodypart as it was transmitted (in a separate window)} de {Diesen Teil der Nachricht in der Quellfassung anzeigen.} it {Visualizza questa parte come stata spedita (in una finestra a parte)} fr {Afficher dans une autre fentre cette portion de message telle qu'elle fut transmise.} sr {Prikaz ovog dela pisma (u zasebnom prozoru) onako kako je prenet} pl {Poka tre tak, jak zostaa przesana (w osobnym oknie)} pt {Visualizar esta parte da mensagem como foi enviada (numa janela em separado)} label add_to_keyring sv {Lgg till denna publika nykel till din nyckelring} en {Add this public key to your keyring} de {Diesen ffentlichen Schlssel zum eigenen Verzeichnis hinzufgen.} it {Aggiungi questa chiave pubblica al keyring} fr {Ajouter cette clef publique votre porte-clefs.} sr {Dodaj ovaj javni klju na va sveanj} pl {Dodaj ten klucz publiczny do twojego pku kluczy} pt {Adicionar esta chave pblica ao porta-chaves PGP} label run_mailcap sv {Kr det kommando som definieras i mailcap fr denna typ} en {Run the command specified in the mailcap for this type} de {Diesen Teil des Inhalts gem mailcap-Einstellungen anzeigen.} it {Esegue il comando specificato nel mailcap per questo tipo} fr {Excuter la commande mailcap correspondant ce type.} sr {Izvri komandu naznaenu u mailcap-u za ovaj tip} pl {Uruchom polecenie ustalone w mailcap-ie dla tego typu} pt {Executar o comando especificado no ficheiro MAILCAP para este tipo} label bodypart_entry sv {Denna ingng ger information om denna brevdel. Undermenyn ger mer information och lter dig utfra vissa operationer p den} en {This entry corresponds to one bodypart. The submenu displays information and lets you perform some operations on it.} de {Dieser Eintrag entspricht einem Teil der Nachricht. Das Men enthlt Informationen und einige Kommandos dazu.} it {Questa selezione corrisponde a una parte. Il sottomenu ne fornisce informazioni e permette di effettuare operazioni su di essa} fr {Cette entre correspond une portion de message. Ce sous-menu donne quelques informations et permet d'effectuer des oprations sur cette portion.} sr {Ova stavka odgovara jednom delu pisma. Podmeni daje informacije i nudi mogue operacije nad njim.} pl {Ten wpis odpowiada jednej czci treci listu. Podmenu zawiera informacje i pozwala wykona par czynnoci na tej treci} pt {Esta entrada corresponde a uma parte da mensagem. O submenu fornece informao adicional e permite executar algumas operaes sobre essa parte de mensagem} label bodypart_show sv {Denna knapp lter dig bestmma om denna brevdel skall visas eller inte} en {This checkbox lets you decide if this bodypart should be shown or not} de {Mit dieser Option legen Sie fest, ob der gewhlte Teil der Nachricht angezeigt werden soll.} it {Questo pulsante permette di determinare se questa parte deve essere mostrata oppure no} fr {Cette case cocher indique si cette portion devrait tre affiche ou non.} sr {Ovom opcijom odluujete da li ovaj deo pisma treba da se prikae ili ne} pl {Ta opcja decyduje, czy tre ma zosta pokazana, czy nie} pt {Este boto de escolha permite-lhe mostrar ou ocultar esta parte da mensagem} label bodypart_save sv {Spara brevdelen till fil} en {Save the bodypart to a file} de {Diesen Teil der Nachricht in einer Datei speichern.} it {Salva la parte in un file} fr {Sauve cette portion dans un fichier.} sr {Snimi deo pisma u datoteku} pl {Zapisz cz treci wiadomoci do pliku} pt {Guarda esta parte da mensagem em ficheiro} label dbs_and sv {Alla raderna nedan mste matcha brevet fr att det skall inkluderas} en {All the lines below must match a message for it to get included} de {Die gesuchten Nachrichten mssen alle der folgenden Kriterien erfllen.} it {Tutte le linee sotto devono essere riscontrate in un messaggio perch venga incluso} fr {Toutes les lignes ci-dessous doivent concorder avec un message pour que ce dernier soit inclus.} sr {Sve linije ispod moraju se uklopiti da bi pismo bilo ukljueno} pl {Wszystkie linie poniej musz si zgadza z wiadomoci, by zostaa doczona} pt {Todas as linhas abaixo devem ser encontradas para a mensagem ser includa} label dbs_or sv {Alla brev som matchar minst en av de icke tomma raderna nedan kommer att inkluderas} en {Any message which matches at least one of the non-empty lines below is included} de {Die gesuchten Nachrichten mssen mindestens eines der folgenden Kriterien erfllen.} it {Almeno una delle linee non vuote sotto deve essere riscontrato in un messaggio perch venga incluso} fr {Inclure tout message qui concorde avec au moins une ligne non vide ci-dessous.} sr {Bie ukljueno svako pismo koje se uklapa u bar jednu od linija ispod} pl {Kada wiadomo, ktra odpowiada przynajmniej jednej nie-pustej linii poniej jest doczana} pt {Inclui qualquer mensagem que mostre pelo menos uma das linhas seguintes (no vazias)} label dbs_keywords sv {Denna rad matchar om minst ett av dessa ord has satts som nyckelord p brevet} en {This line matches if any of these words have been set as keywords on the message} de {Die Stichwrter der Nachricht enthalten mindestens eines der angegebenen Wrter.} it {Questa linea viene riscontrata se una di queste parole stata selezionata come parola chiave del messaggio} fr {Cette ligne concorde si l'un des mots saisis est utilis comme mot-cl du message.} sr {Ova linija se uklapa ako je bilo koja od ovih rei zadata kao kljuna re u pismu} pl {Ta linia si zgadza, jeli ktrykolwiek z wyrazw zosta podany w sowach kluczowych wiadomoci} pt {Existe correspondncia nesta linha se qualquer uma das palavras foi utilizada como palavra chave da mensagem} label dbs_subject sv {Denna rad matchar om minst ett av dessa ord finns i brevets mne. Om du vill matcha en fljd av ord s omge den med ""} en {This line matches if any of these words can be found in the subject. If you wish to match a sequence of words, enclose them in "".} de {Der Betreff der Nachricht enthlt mindestens eines dieser Wrter. Mehrere Wrter knnen "mit Anfhrungszeichen" zu einem Wort zusammengefasst werden.} it {Questa linea viene riscontrata se una di queste parole viene trovata nel soggetto. Se si vogliono cercare combinazioni di parole, vanno delimitate da ""} fr {Cette ligne concorde si au moins l'un des mots apparat dans l'objet. Pour une suite de mots, les encadrer par des "".} sr {Ova linija se uklapa ako bilo koja od ovih rei moe da se nae u naslovu pisma. Niz rei moete zadati pod navodnicima "".} pl {Ta linia si zgadza, jeli ktrykolwiek z wyrazw moe by znaleziony w temacie wiadomoci. Jeli chcesz sprawdzi kilka wyrazw zamknij je w nawiasach ""} pt {Existe correspondncia se qualquer uma das palavras for encontrada no cabealho Assunto:. Se pretender encontrar uma sequncia especfica de palavras, inclua a sequncia entre ""} label dbs_complete_msg_text sv {Denna rad matchar om minst ett av dessa ord finns ngonstans i brevet Om du vill matcha en fljd av ord s omge den med ""} en {This line matches if any of these words can be found anywhere in the body of the message. If you wish to match a sequence of words, enclose them in "".} de {Der Inhalt der Nachricht enthlt mindestens eines dieser Wrter. Mehrere Wrter knnen "mit Anfhrungszeichen" zu einem Wort zusammengefasst werden.} it {Questa linea viene riscontrata se una di queste parole viene trovata nel messaggio. Se si vogliono cercare combinazioni di parole, vanno delimitate da ""} fr {Cette ligne concorde si l'un des mots apparat dans le corps du message. Pour une suite de mots, les encadrer par des "".} sr {Ova linija se uklapa ako bilo koja od ovih rei moe da se nae bilo gde u pismu. Niz rei moete zadati pod navodnicima "".} pl {Ta linia si zgadza, jeli ktrykolwiek z wyrazw moe by znaleziony gdziekolwiek w wiadomoci. Jeli chcesz sprawdzi kilka wyrazw zamknij je w nawiasach ""} pt {Existe correspondncia se qualquer uma das palavras for encontrada em qualquer parte da mensagem. Se pretender encontrar uma sequncia especfica de palavras, inclua a sequncia entre ""} label dbs_to sv {Denna rad matchar om minst ett av dessa ord finns i Till-raden Om du vill matcha en fljd av ord s omge den med ""} en {This line matches if any of these words can be found in the To: line. If you wish to match a sequence of words, enclose them in "".} de {Die Liste der primren Adressaten einer Nachricht enthlt mindestens eines dieser Wrter. Mehrere Wrter knnen "mit Anfhrungszeichen" zu einem Wort zusammengefasst werden.} it {Questa linea viene riscontrata se una di queste parole viene trovata tra i destinatari. Se si vogliono cercare combinazioni di parole, vanno delimitate da ""} fr {Cette ligne concorde si l'un des mots apparat dans le champ d'en-tte 'Pour:'. Pour une suite de mots, les encadrer par des "".} sr {Ova linija se uklapa ako bilo koja od ovih rei moe da se nae u 'Za:' ('To:') polju zaglavlja. Niz rei moete zadati pod navodnicima "".} pl {Ta linia si zgadza, jeli ktrykolwiek z wyrazw moe by znaleziony w polu 'To:'. Jeli chcesz sprawdzi kilka wyrazw zamknij je w nawiasach ""} pt {Existe correspondncia se qualquer uma das palavras for encontrada no campo To:. Se pretender encontrar uma sequncia especfica de palavras, inclua a sequncia entre ""} label dbs_from sv {Denna rad matchar om minst ett av dessa ord finns i Frn-raden Om du vill matcha en fljd av ord s omge den med ""} en {This line matches if any of these words can be found in the From: line. If you wish to match a sequence of words, enclose them in "".} de {Der Absender der Nachricht enthlt mindestens eines dieser Wrter. Mehrere Wrter knnen "mit Anfhrungszeichen" zu einem Wort zusammengefasst werden.} it {Questa linea viene riscontrata se una di queste parole viene trovata tra i mittenti. Se si vogliono cercare combinazioni di parole, vanno delimitate da ""} fr {Cette ligne concorde si l'un des mots apparat dans le champ d'en-tte 'De:'. Pour une suite de mots, les encadrer par des "".} sr {Ova linija se uklapa ako bilo koja od ovih rei moe da se nae u 'Od:' ('From:') polju zaglavlja. Niz rei moete zadati pod navodnicima "".} pl {Ta linia si zgadza, jeli ktrykolwiek z wyrazw moe by znaleziony w polu 'From:'. Jeli chcesz sprawdzi kilka wyrazw zamknij je w nawiasach ""} pt {Existe correspondncia se qualquer uma das palavras for encontrada no campo From:. Se pretender encontrar uma sequncia especfica de palavras, inclua a sequncia entre ""} label dbs_cc sv {Denna rad matchar om minst ett av dessa ord finns i Cc-raden Om du vill matcha en fljd av ord s omge den med ""} en {This line matches if any of these words can be found in the Cc: line. If you wish to match a sequence of words, enclose them in "".} de {Die Liste der sekundren Adressaten einer Nachricht enthlt mindestens eines dieser Wrter. Mehrere Wrter knnen "mit Anfhrungszeichen" zu einem Wort zusammengefasst werden.} it {Questa linea viene riscontrata se una di queste parole viene trovata tra le copie-carbone. Se si vogliono cercare combinazioni di parole, vanno delimitate da ""} fr {Cette ligne concorde si l'un des mots apparat dans le champ d'en-tte 'Copie Carbone:'. Pour une suite de mots, les encadrer par des "".} sr {Ova linija se uklapa ako bilo koja od ovih rei moe da se nae u 'Cc:' polju zaglavlja. Niz rei moete zadati pod navodnicima "".} pl {Ta linia si zgadza, jeli ktrykolwiek z wyraz moe by znaleziony w polu 'Cc:'. Jeli chcesz sprawdzi kilka wyrazw zamknij je w nawiasach ""} pt {Existe correspondncia se qualquer uma das palavras for encontrada no campo Cc:. Se pretender encontrar uma sequncia especfica de palavras, inclua a sequncia entre ""} label keywords sv {Nyckelord som beskriver brevet} en {Keywords describing the message} de {Stichwrter zur Nachricht.} it {Parole chiave che descrivono il messaggio} fr {Mots-cls dcrivant le message.} sr {Kljune rei koje opisuju pismo} pl {Sowa kluczowe okrelajce wiadomo} pt {Palavras chave descriptivas da mensagem} label exp_none sv {Expirera inte detta brev} en {Do not expire this message} de {Diese Nachricht verfllt nie.} it {Il messaggio non scade mai} fr {Le message n'expire jamais.} sr {Ova poruka ne istie} pl {Wiadomo nie traci wanoci} pt {A mensagem nunca expirar} label exp_remove sv {Radera detta brev nr det expirerar} en {Delete this message when it expires} de {Diese Nachricht nach ihrem Verfall lschen.} it {Cancella questo messaggio quando scade} fr {Dtruit ce message expiration.} sr {Obrii ovo pismo kad istekne} pl {Usu list, kiedy straci wano} pt {Apaga a mensagem aps expirar} label exp_incoming sv {Lgg till detta brev till den inkommande brevldan nr det expirerar} en {Add this message to the INBOX when it expires} de {Diese Nachricht nach ihrem Verfall in den Eingangsordner verschieben.} it {Aggiunge questo messaggio alla INBOX quando scade} fr {Ajoute ce message la bote lettres d'arrive lorsqu'il expirera.} sr {Dodaj ovo pismo u INBOX kad istekne} pl {Dodaj ten list do INBOX, kiedy utraci wano} pt {Move a mensagem para a pasta INBOX aps expirar} label exp_backup sv {Flytta detta brev till backup nr det expirerar} en {Move this message to backup when it expires} de {Diese Nachricht nach ihrem Verfall in eine Sicherungsdatei verschieben.} it {Muove questo messaggio nel backup quando scade} fr {Archive ce message lorsqu'il expire.} sr {Prebaci ovo pismo u rezervu kad istekne} pl {Przenie list do archiwum, kiedy utraci wano} pt {Move a mensagem para o arquivo aps expirar} label exp_date sv {Nr brevet skall expireras. Den fr tillfllet enda godknda formen r +N dr N r antalet dagar frn som brevet skall ligga i databasen innan det expireras} en {When to expire the message. Currently, the only supported form is +N where N is number of days the message should be in the database before it expires.} de {Legen Sie hier das Verfallsdatum der Nachricht fest. Geben Sie dazu die Anzahl der Tage N, in denen die Nachricht verfallen soll, in der Form "+N" an.} it {Quando il messaggio deve scadere. L'unico formato supportato al momento +N dove N il numero di giorni per cui il messaggio deve essere mantenuto nel database prima che scada} fr {Quand ce message expire-t-il ? La seule syntaxe supporte pour le moment est +N, ou N reprsente le nombre de jours pendant lesquels le message doit sjourner dans la base de donnes avant d'expirer.} sr {Kada e pismo istei. Jedini za sada podrani oblik jeste +N, gde je N broj dana koje pismo treba da provede u bazi pre isticanja.} pl {Kiedy wiadomo ma utraci wano. Obecnie moesz jedynie uy formy '+N', gdzie N to liczba dni od pojawienia si wiadomoci w bazie danych} pt {Quando uma mensagem deve expirar. De momento o nico formato suportado +N, em que N representa o nmero de dias que a mensagem deve permanecer na base de dados antes de expirar.} label vd_delete sv {Radera denna ingng (om det r en undermeny s kommer du att f frgan om du vill radera alla barnen)} en {Delete this entry (if this entry is a submenu, you will be asked if you want to delete all children)} de {Den Eintrag lschen. Bei Untermens erfolgt noch eine Sicherheitsabfrage.} it {Cancella questa cartella (se un sottomenu allora viene chiesto se cancellare tutte le sottocartelle} fr {Dtruire cette entre. S'il s'agit d'un sous-menu TkRat vous demandera si vous voulez dtruire son contenu.} sr {Ukloni ovu stavku (ako je to podmeni, TkRat e Vas pitati da li da obrie i sve to se nalazi u njemu).} pl {Usu element (jeli to jest podmenu, bdziesz zapytany czy chcesz usun take elementy podrzdne)} pt {Apaga esta entrada (se a entrada um submenu, ser-lhe- perguntado se deseja remover tambm todos as entradas internas-filho)} label vd_setinbox sv {Stter p/stnger av statusen som normal inbrevlda (efetersom man bara kan ha en inbrevlda s stngs detta av fr den fregende inbrevldan nr man stter p detta)} en {Toggles the default INBOX status of this folder (since you can only have one INBOX, this resets the INBOX state from the previous INBOX, if any)} de {Mit dieser Option knnen Sie den Ordner als Eingangsordner (INBOX) festlegen. Nur ein Ordner kann der Eingangsordner sein, die Markierung wird daher beim aktuellen Eingangsordner entfernt.} fr {Donne cette bote lettres le statut de bote lettres d'arrive (INBOX). Cela enlvera ventuellement ce statut une autre bote lettres, puisqu'on ne peut avoir qu'une bote lettres d'arrive.} sr {Postavi ovaj skup za podrazumevano potansko sandue (inbox)} pl {Ustawia status domylnej skrzynki dla poczty nadchodzcej dla tego foldra (moesz mie tylko jedn tak, wic opcja ta resetuje stan poprzedniej skrzynki poczty przychodzcej, jeli istnieje)} pt {Atribui o estatuto de INBOX pasta actual.} label vd_watch sv {Visa vktaren br det kommer nya brev i den hr mappen} en {Pop up the Watcher when new messages arrive in this folder} de {Aktivieren Sie diese Option, wenn der Wchter Sie ber neu in diesem Ordner ankommende Nachrichten informieren soll.} fr {Fait surgir la fentre de veille lors de l'arrive de nouveau message dans cette bote lettres.} sr {Aktiviraj nadzorni prozor kada nova pisma stignu u ovaj skup} pl {Otwrz obserwatora kiedy przyjdzie nowa wiadomo do tego foldera} pt {Abrir a janela de aviso quando novas mensagens chegam a esta pasta} label vd_sort sv {Mappens sorteringsordning} en {Sort order of folder} de {Sortierung des Ordners.} fr {Ordre de tri de la bote lettres. Si vous choisissez Par dfaut, c'est la valeur choisie dans la fentre prfrence onglet bote lettres/liste de messages qui sera utilise.} sr {Nain reanja pisama u skupu} pl {Kolejno sortowania w folderze} pt {Ordenamento de mensagens} label vd_host sv {Maskin som mappen finns p} en {Host the mailbox resides on} de {Auf welchem Rechner liegt der Ordner?} it {Il nome del server su cui si trova la cartella} fr {Nom de l'hte o la bote lettres se trouve} sr {Raunar na kome se nalazi potansko sandue} pl {Host, na ktrym jest skrzynka} pt {Servidor onde a caixa de correio se encontra} label vd_user sv {Anvndare vi skall hmta mappen som} en {User we should access the folder as} de {Auf Ordner zugreifen als dieser Benutzer.} it {Il nome utente con il quale accedere alla cartella} fr {Nom d'utilisateur pour accder cette bote lettres.} sr {Korisnik pod ijim se imenom pristupa skupu} pl {Uytkownik, ktry ma dostp do foldera} pt {Nome de utilizador para aceder a esta caixa de correio} label vd_mbox sv {Namnet p IMAP-mappen} en {Name of remote IMAP folder} de {Name des Ordners auf dem IMAP-Server.} it {Il nome della cartella IMAP remoto} fr {Nom de la bote lettres distante} sr {Ime udaljenog IMAP skupa} pl {Nazwa oddalonego folderu IMAP} pt {Nome da pasta IMAP remota} label vd_pattern sv {Inkludera bara mappar som matchar detta mnster} en {Only include folders matching this pattern} de {Nur Ordner anzeigen, die diese Suchmaske erfllen.} it {Includi solo le cartelle che corrispondono con questo schema} fr {N'importer que les fichiers correspondant ce modle} sr {Uzmi u obzir samo skupove koji se uklapaju u ovaj obrazac} pl {Docz tylko foldery odpowiadajce wzorcowi} pt {Incluir apenas pastas que correspondam a este modelo} label watcher sv {Tryck vnster musknapp fr att ppna TkRats huvudfnster, hger musknapp stnger detta fnster fr tillfllet} en {Press left mousebutton to pop up main TkRat window, right mousebutton dismisses this window for now} de {Mit der linken Maustaste knnen Sie TkRat wieder herstellen. Mit der rechten Maustaste schlieen Sie dieses Fenster.} it {Premi il tasto sinistro del mouse per far apparire la finestra principale di TkRat, premi il tasto destro per chiudere temporaneamente la finestra} fr {Appuyer sur le bouton de gauche pour afficher la fentre de TkRat. Le bouton de droite fait disparatre cette fentre.} sr {Pritisnite levo dugme mia da bi se pojavio glavni prozor TkRat-a, a desno dugme da se ovaj prozor za sada ukloni.} pl {Nacinij lewy przycisk by otworzy gwne okno TkRat, prawy przycisk zamyka to okno} pt {Pressione o boto esquerdo do rato para abrir a janela principal do TkRat, o boto direito far desaparecer esta janela por agora} label help_subjlist sv {Lista ver hjlptexternas mnen. Klicka med vnster musknapp p en av dem fr att se motsvarande hjlptext} en {List of help subjects. Press left mousebutton on a subject to view help} de {Liste der Hilfethemen. Whlen Sie ein Thema mit der linken Maustaste.} it {Lista di soggetti d'aiuto. Selezionare con il tasto sinistro del mouse il soggetto che si vuole vedere} fr {Liste des sujets d'aide. Cliquez sur un sujet pour faire apparatre l'aide correspondante.} sr {Spisak tema za pomo. Kliknite levim dugmetom mia na temu da biste je pregledali.} pl {Lista tematw pomocy. Nacinij lewym przyciskiem temat by zobaczy pomoc} pt {Tpicos de ajuda. Clique num nos tpicos para ver} label help_text sv {Hjlptext fr det valda mnet} en {Help text for the chosen subject} de {Der Hilfetext zum ausgewhlten Thema.} it {Testo di aiuto per il soggetto scelto} fr {Aide relative au sujet choisi.} sr {Tekst pomoi za odabranu temu} pl {Tekst pomocy na wybrany temat} pt {Texto de ajuda do tpico escolhido} label pref_expunge_on_close sv {Om TkRat automatiskt skall radera alla brev markerade fr radering varje gng du stnger mappen} en {If TkRat should automatically delete all messages marked for deletion whenever you close the folder} de {Wenn diese Option aktiviert ist, werden beim Schlieen eines Ordners alle zum Lschen vorgemerkten Nachrichten tatschlich gelscht.} it {Se TkRat deve automaticamente cancellare tutti i messaggi contrassegnati quando viene chiusa la cartella} fr {TkRat doit-il dtruire tous les messages marqus pour destruction la fermeture de la bote lettres ?} sr {Da li TkRat treba da automatski obrie sve poruke oznaene za uklanjanje kad god zatvorite skup} pl {Czy TkRat powinien automatycznie usuwa wszystkie wiadomoci zaznaczone do usunicia kiedy zamykasz folder} pt {Se o TkRat deve ou no apagar automaticamente as mensagens marcadas para remoo quando fecha a pasta} label pref_checkpoint_interval sv {TkRat skriver ut brevflaggorna till mappen med jmna mellanrum om detta vrde inte r noll} en {TkRat periodically writes the message flags to the folder if this value is non-zero} de {TkRat speichert die Markierungen, die an den Nachrichten vorgenommen wurden, sptestens nach der hier vorgegebenen Zeit ab, falls der Wert ungleich 0 ist.} fr {TkRat crit l'tat des messages dans la bote lettres priodiquement si cette valeur est non nulle.} sr {TkRat periodino upisuje statuse pisama u skup ako ova vrednost nije nula} pl {TkRat co jaki czas zapisuje flagi wiadomoci do foldera jeli ta warto jest nie-zerowa} pt {O TkRat escreve periodicamente o estado das mensagens na pasta se este valor for no nulo} label new_folder sv {ppnar den valda mappen i ett nytt mappfnster} en {Opens the selected folder in a new Folder window} de {Whlen Sie einen Ordner aus, der in einem neuen Fenster angezeigt werden soll.} fr {Cre une nouvelle fentre de bote lettre.} sr {Otvara izabrani skup u novom prozoru} pl {Otwiera wybrany folder w nowym oknie folderu} pt {Abre a pasta seleccionada numa nova janela} label close_folder sv {Stng detta mappfnster} en {Close this Folder window} de {Dieses Ordnerfenster schlieen.} fr {Ferme cette fentre.} sr {Zatvara ovaj prozor za rad sa skupom} pl {Zamyka okno folderu} pt {Fecha esta janela de pastas} label pref_tip sv {Ett litet tips} en {A small tip} de Hinweis fr {N'inclut que les botes lettres qui correspondent ce modle. (les caractres * ? sont permis.)} sr {Mali savet} pl {Mas porada} pt {Pequena sugesto} label show_generated_headers sv {Visa hur adressflten kommer att se ut i brevet nr det snds} en {Show what the address headers of the sent message will look like} de {Vorschau auf die Kopfzeilen, mit denen die Nachricht versandt wird.} fr {Montre ce que seront les enttes des messages expdis.} sr {Pokai kako e izgledati zaglavlje poslatog pisma} pl {Poka jak bd wyglday nagwki adresowe wysanej wiadomoci} pt {Mostra os cabealhos de mensagem que sero enviados} label editors sv {Hantera listan ver textredigerare} en {Manipulate list of editors} de {Die Liste der Editoren verwalten.} fr {Le bouton 1 fait surgir la fentre principale de TkRat. Le bouton 3 fait disparatre cette fentre.} sr {Rad sa spiskom editora} pl {Manipuluj list edytorw} pt {Edita a lista de editores externos} label use_charset sv {Vilket teckenset brevet skall skickas i} en {Which character set the outgoing message should be sent in} de {Whlen Sie den Zeichensatz, der beim Versand dieser Nachricht verwendet werden soll.} fr {Le jeu de caractres utilis pour l'expdition des messages.} sr {Koji skup znakova treba da imaju poslata pisma} pl {Jaka strona kodowa powinna by nadawana wysyanym wiadomociom} pt {O conjunto de caracteres a usar na mensagem a enviar} label name_of_editor sv {Namnet p redigeraren (anvnds bara i menyn)} en {Name of editor (for use in menu only)} de {Name des Editors im Men.} fr {Liste des sujets d'aide. Cliquez sur un sujet pour faire apparatre l'aide correspondante.} sr {Ime editora (samo za upotrebu u meniju)} pl {Nazwa edytora (dla uytku tylko w menu)} pt {Nome do editor (a usar no menu)} label editor_command sv {Sjlva kommandot som krs. Ett '%s' i kommandot kommer att bytas ut mot namnet p filen som skall editeras.} en {The actual command to run. A '%s' in this string will be exchanged with the name of the file to edit.} de {Befehlszeile fr den Editor. Ein '%s' wird durch den Namen der zu editierenden Datei ersetzt.} fr {Commande pour lancer l'diteur. un %s sera remplac par le nom du ficher diter.} sr {Komanda za pokretanje. '%s' u ovom nizu e biti zamenjeno imenom datoteke koju treba editovati.} pl {Komenda do uruchomienia. '%s' zostanie zamienione na nazw pliku do edycji.} pt {O comando a executar. Uma string '%s' ser substituda pelo nome do ficheiro a editar} label editor_charset sv {Vilken teckenkodning som redigeraren arbetar med} en {Which charset the editor works in} de {Geben Sie an, welchen Zeichensatz der Editor verwendet.} fr {Le jeu de caractres utilis par cet diteur.} sr {Koji skup znakova koristi editor} pl {W jakiej stronie kodowej pracuj edytory} pt {Conjunto de caracteres usado pelo editor} label pane_button sv {Dra denna knapp upp eller ner fr att ndra fnstrets utseende} en {Drag this button up or down to change the layout of the window} de {Verschieben Sie diesen Schalter mit der Maus, um die Spaltenbreiten anzupassen.} fr {TkRat doit-il dtruire tous les messages marqus pour destruction la fermeture de la bote lettres ?} sr {Prevucite ovo dugme gore ili dole da biste promeniti izgled prozora} pl {Przesu ten przycisk do gry lub na d by zmieni wygld okna} pt {Arraste este boto para alterar a forma da janela} label vd_pathname sv {Denna mapps skvg} en {Path name of this folder} de {Dateiname dieses Ordners.} fr {Chemin de cette bote lettres.} sr {Ime staze za ovaj skup} pl {cierzka do tego folderu} pt {Caminho completo at pasta} label file_browse sv {Visa en filvljare och anvnd den fr att ange skvgen} en {Pop up a file chooser and use it to specify the path} de {Eine Datei mit Hilfe eines Auswahldialogs bestimmen.} fr {Fait surgir une bote de dialogue recherche de fichiers.} sr {Prikai bira datoteka da bi se naznaila staza} pl {Okno dialogowe wyboru plikw by okreli cierzk} pt {Abre um selector de ficheiros para especificar o caminho} label vd_subscribed sv {Inkludera bara mappar som du prenumererar p} en {Only include subscribed folders} de {Nur abonnierte Ordner importieren.} fr {N'inclure que les botes lettres auxquelles vous avez souscrit.} sr {Uzmi u obzir samo potpisane skupove} pl {Docz tylko zasubskrybowane foldery} pt {Incluir apenas pastas subscritas} label pref_lynx sv {Kommando fr att starta lynx. Alla '%u' i kommandot kommer att bytas ut mot den URL som skall visas nr kommandot krs. Kommandot komemr inte att kras i ngot speciellt fnster, dvs det mste sjlvt starta eventuella fnster som behvs.} en {Command used to start Lynx. A '%u' in this command will be replaced with the URL to open when the command is invoked. This command will not be run in any special window, i.e. it must launch any needed windows by itself.} de {Mit dieser Kommandozeile wird Lynx gestartet. Ein '%u' wird durch die anzuzeigende URL ersetzt. Das Kommando muss dafr sorgen, dass ein neues Fenster geffnet wird, beispielsweise: xterm -e lynx "%u"} fr {Commande utilise pour lancer lynx. TkRat remplacera un %u par l'URL visualiser. S'il faut lancer un mulateur de terminal pour excuter lynx, vous devez le prciser dans cette commande (exple : xterm -e lynx %u).} sr {Komanda kojom se pokree lynx. Svako '%u' u ovoj komandi bie zamenjeno URL-om koji treba otvoriti. Ova komanda nee biti izvrena ni u jednom posebnom prozoru, tako da se potreban prozor mora eksplicitno zadati.} pl {Polecenie do uruchomienia Lynxa. '%u' zostanie zastpione URLem do otwarcia. To polecenie musi samo sobie otworzy jakie okno.} pt {Comando usado para iniciar o programa 'lynx'. A string '%u' ser substituda pelo URL a abrir quando o comando chamado. Ser necessrio lanar um programa de terminal para executar o programa (exemplo: 'xterm -e lynx %u')} label pref_other_browser sv {Kommando fr att starta en annan lsare. Alla '%u' i kommandot kommer att bytas ut mot den URL som skall visas nr kommandot krs. Kommandot kommer inte att kras i ngot speciellt fnster, dvs det mste sjlvt ppna eventuella fnster som behvs.} en {Command used to start any other browser. A '%u' in this command will be replaced with the URL to open when the command is invoked. This command will not be run in any special window, i.e. it must launch any needed windows by itself.} de {Mit dieser Kommandozeile wird ein beliebiger anderer Browser gestartet. Ein '%u' wird durch die anzuzeigende URL ersetzt. Das Kommando muss dafr sorgen, dass ein neues Fenster geffnet wird.} fr {Commande utilise pour lancer un autre navigateur. TkRat remplacera un %u par l'URL visualiser. S'il faut lancer un mulateur de terminal pour excuter cette commande, vous devez le prciser dans cette commande (exple : xterm -e navigateur %u).} sr {Komanda kojom se pokree bilo koji drugi ita. Svako '%u' u ovoj komandi bie zamenjeno URL-om koji treba otvoriti. Ova komanda nee biti izvrena ni u jednom prozoru, tako da se potreban prozor mora eksplicitno zadati.} pl {Polecenie do uruchomienia innej przegldarki. '%u' zostanie zastpione URLem do otwarcia. To polecenie musi samo sobie otworzy jakie okno.} pt {Comando a usar para iniciar qualquer outro browser. A string '%u' ser substituda pelo URL a abrir quando o comando chamado. Se este comando executar em terminal, ser necessrio lanar simultaneamente um programa de terminal (ver lynx)} label bounce sv {Skicka vidare det aktuella brevet till en ny mottagare} en {Remail the current message to a new recipient} de {Die aktuelle Nachricht an einen neuen Empfnger umleiten.} fr {Rexpdier le message un autre destinataire.} sr {Prebaci tekue pismo na novog primaoca} pl {Wylij t wiadomo do nowego adresata} pt {Reencaminha a mensagem a um novo destinatrio} label store_passwd sv {Detta sparar ditt lsenord p disk s att du aldrig behver skriva in det igen. Det kan vara en stor risk associerade med detta. Ngon kan komma ver ditt hemliga lsenord.} en {This stores your password on disk so you never have to type it in again. There may be great risks associated with this, somebody may learn your secret password.} de {Passwrter in einer Datei speichern. Danach muss man sie nie wieder eingeben - und genauso unsicher ist die Sache dann auch!} fr {Stocke votre mot de passe sur disque. C'est une manipulation potentiellement dangereuse. Il est possible que quelqu'un puisse lire votre mot de passe.} sr {Ovim se Vaa ifra snima na disk tako da vie nikad ne morate da je unosite. To, meutim, ukljuuje veliki rizik - neko bi mogao da sazna Vau tajnu ifru!} pl {Tu jest zapisane twoje haso, tak by nie musia go za kadym razem wpisywa. Istnieje jednak ryzyko, e kto moe pozna twoje haso.} pt {Armazena em disco a sua palavra passe, de forma a no ter de a introduzir novamente. H diversos riscos associados, outros podero ter acesso sua palavra passe. De preferncia, no memorize a palavra passe} label purge_pwcache sv {Detta raderar de sparade lsenorden (som anvnds fr imap och pop3). Det raderar bde listan i minnet och lsenord sparade p disk.} en {This will clear the password cache (which is used for IMAP and POP3). It will clear both the in-memory cache and any passwords stored on disk.} de {Gemerkte Passwrter (sowohl im Speicher als auch in Dateien) vergessen.} fr {efface de la mmoire les mots de passe pour les connexions IMAP et POP3. Les mots de passes stocks sur disque sont aussi effacs.} sr {Ovim se prazni ke za ifre (za IMAP i POP3).} pl {Opcja skasuje zapamitane hase (uywan do IMAP i POP3). Dotyczy to hase w pamici jak i na dysku.} pt {Apaga todas as palavras passe armazenadas. Tanto as palavras passe armazenadas em disco como em memria sero removidas} label pref_wrap_cited sv {Om TkRat skall bryta raderna i den citerade texten nr brev besvaras} en {If TkRat should wrap the lines in the cited text when constructing replies} de {Diese Option erlaubt es TkRat, den Zeilenumbruch des zitierten Textes automatisch zu verndern.} fr {Indique si TkRat devrait remettre en forme le texte cit lors de la prparation d'une rponse.} sr {Treba li prelomiti redove citiranog teksta kada se odgovara na pisma} pl {Czy TkRat powinien dzieli linie w cytowanym tekcie, kiedy tworzy odpowied} pt {Se o TkRat deve ou no dobrar as linhas da mensagem de resposta} label reset_changes sv {terstll till originalvrden} en {Reset to original values} de {Auf ursprngliche Werte zurckstellen.} fr {Revient aux valeurs prcdentes.} sr {Vrati poetne vrednosti} pl {Resetuj do oryginalnych wartoci} pt {Retomar os valores originais} label setup_netsync sv {ppnar ett fnster dr du kan stlla in vad som synkroniseras vid ntverkssynkronisering} en {Opens a window which lets you control what is synchronized by the Network sync} de {Den allgemeinen Netzabgleich konfigurieren.} fr {Fait surgir une fentre permettant de contrler ce qui est mis jour lors des synchronisations rseau.} sr {Otvara prozor u kome moete podesiti ta se deava prilikom mrenog sinhronizovanja} pl {Okno ustawie synchronizacji przez sie} pt {Abre uma janela que lhe permite controlar as aces tomadas aquando da re-sincronizao da rede} label netsync sv {Synkronisera ver ntverket. Detta kan inkludera: - Att skicka uppskjutna brev - Att synkronisera frikopplade mappar - Att kra ett externt program} en {Synchronize over the network. This may include: - Sending deferred messages - Synchronizing disconnected mailboxes - Running an external program} de {Synchronisiere ber das Netzwerk, d. h. je nach Einstellungen: - Sende verzgerte Nachrichten - Gleiche offline Ordner ab - Fhre ein externes Programm aus} fr {Synchronisation gnrale travers le rseau. Cela peut inclure : - l'envoi des messages diffrs, - la synchronisation des botes lettres dconnectes - l'excution d'un programme externe.} sr {Mreno sinhronizovanje moe da ukljui: - slanje odloenih pisama, - sinhronizovanje udaljenih potanskih sanduia, - pokretanje eksternog programa.} pl {Synchronizacja przez sie. Moe zawiera: - Wysyanie odoonych wiadomoci - Synchronizacja nie poczonych skrzynek pocztowych - Uruchomienie zewntrznego programu} pt {Retomar sincronia de rede. Isto pode incluir: - Envio de mensagens deferidas - Sincronizao de pastas 'desligadas' - Executar um programa externo} label netsync_folder sv {Synkronisera den aktiva mappen ver ntverket (Denna operation r bara meningsfull p frikopplade mappar)} en {Synchronize the current folder over the network. (This operation only makes sense on disconnected folders.)} de {Synchronisiere diesen Ordner ber das Netzwerk (nur fr offline Ordner sinnvoll).} fr {Synchronise la bote lettres courante travers le rseau. Cela n'a de sens que pour les botes lettres dconnectes.} sr {Sinhronizuj tekui skup preko mree (ovo ima smisla samo za udaljene skupove)} pl {Synchronizuj folder przez sie (Ta opcja ma sens jedynie dla nie poczonych folderw)} pt {Sincronizar a pasta actual na rede (esta operao apenas faz sentido em pastas desligadas)} label pref_re_regexp sv {Reguljruttryck som anvnds fr att hitta svarsmarkeringen (normalt Re:) i mnesraden. Stora och sm tecken splar ingen roll hr.} en {Regular expression used to find the reply marker (normally Re:) in subjects. Case does not matter in this expression.} de {Geben Sie hier einen "regulren Ausdruck" an, mit dem das Antwort-Kennzeichen im Betreff von Nachrichten erkannt wird. blicherweise werden dafr "Re:" oder "AW:" verwendet, der entsprechende Eintrag lautet "re:|aw:". Gro-/Kleinschreibung wird bei der Auswertung nicht beachtet.} fr {Expression utilise pour trouver les indicateurs de rponse dans les objets (normalement Re:). La casse est ignore dans cette expression.} sr {Regularni izraz kojim se prolazi oznaka za odgovor (obino 'Re:') u naslovima pisama. Mala i velika slova se ovde ne razlikuju.} pl {Wyraenie oznaczajce, e list jest odpowiedzi (normalnie 'Re:') Wielko liter nie ma znaczenia} pt {Expresso regular usada para encontrar o marcador de resposta (normalmente Re:) na linha de assunto (maiscula no relevante)} label do_wrap_cited sv {Radbryter den citerade delen av brevet som komponeras} en {Wraps the cited parts of the message being composed} de {Fr den zitierten Text wird ein Zeilenumbruch vorgenommen.} fr {Introduit des csures dans le texte cit du message que vous rdigez.} sr {Prelama citirane delove pisma koje se sastavlja} pl {Dzieli cytowane fragmenty edytowanej wiadomoci} pt {Dobrar as linhas correspondentes a partes citadas da mensagem} label print_setup sv {ppnar en dialog dr du kan gra diverse utskriftsinstllningar} en {Opens a dialog where you can modify various print settings} de {ffnet ein Fenster zum Einstellen diverser Drucker-Einstellungen.} fr {Ouvre la bote de dialogue des rglages de l'impression.} sr {Otvara dijalog za podeavanje raznih opcija tampanja} pl {Okno ustawie druku} pt {Abre uma caixa de dilogo onde poder modificar vrias configuraes de impresso} label print_headers sv {Bestmmer vilka huvudrader frn bevet som skrivs ut} en {Determines which headers of the message to print} de {Mit dieser Auswahl legen Sie fest, welche Kopfzeilen im Ausdruck erscheinen sollen.} fr {Indique quels en-ttes imprimer.} sr {Odreuje koje delove zaglavlja treba tampati} pl {Decyduje, ktre nagwki drukowa} pt {Indica os cabealhos da mensagem a imprimir} label print_bodypart sv {Hrkan du kontrollera exakt vilka delar av brevt som skriv ut} en {Here you can control exactly which parts of the message get printed} de {Hier bestimmen Sie, welche Teile der Nachricht gedruckt werden sollen.} fr {Choix des parties du message imprimer.} sr {Ovde moete odrediti tano koje delove pisma treba odtampati} pl {Tu moesz bezporednio ustali, ktre fragmenty listu zostan wydrukowane} pt {Escolha das partes da mensagem a imprimir} label print_attachments sv {Om denna knappen r aktiv s skrivs ven bilagorna till de valda breven ut} en {If this button is checked, then the attachments to the selected messages will be printed as well} de {Drucke auch die Anhnge der Nachricht.} fr {Si vous cochez cette case, les pices jointes du message seront galement imprimes.} sr {Ako je ovo potvreno, onda e i svi dodaci uz izabrano pismo biti odtampani} pl {Jeli przycisk jest zaznaczony zaczniki rwnie zostan wydrukowane} pt {Caso este boto esteja seleccionado, os ficheiros anexos mensagem tambm sero impressos} label dest_printer sv {Skicka utskriften till skrivaren med detta namnet} en {Send output to the printer with this name} de {Whlen Sie den Drucker aus, auf dem der Ausdruck erfolgen soll.} fr {Nom de l'imprimante.} sr {Izlaz se alje na tampa sa ovim imenom} pl {Wylij wszystko do drukarki o tej nazwie} pt {Nome da impressora a usar} label dest_file sv {Skicka utskriften till filen med detta namn} en {Send output to the file with this name} de {Whlen Sie den Namen der Datei, in der der Ausdruck gespeichert werden soll.} fr {Imprimer dans un fichier portant ce nom.} sr {Izlaz se alje u datoteku sa ovim imenom} pl {Wylij wszystko do pliku o tej nazwie} pt {Imprimir para este ficheiro} label paper_size sv {Detta val informerar TkRat om hur stort pappret r} en {This choice informs TkRat how big the paper is} de {Das Papierformat fr den Ausdruck.} fr {Taille du papier} sr {Ova vrednost obavetava TkRat koliko je velik papir} pl {Ustal rozmiar papieru} pt {Tamanho do papel} label orientation sv {Orienteringen av utskriften} en {The orientation of the output} de {Die Seitenorientierung fr den Ausdruck.} fr {Orientation de l'impression} sr {Orijentacija izlaza} pl {Orientacja papieru} pt {A orientao da impresso} label print_font sv {Vilket typsnittsfamilj som skall anvndas} en {Which font family to use} de {Die Schriftart fr den Ausdruck.} fr {Quelle police de caractres utiliser.} sr {Koju grupu fontova treba koristiti} pl {Jakiej czcionki uy} pt {A famlia de fonte a usar} label print_fontsize sv {Storleken p texten i utskriften} en {The size of the text on the output} de {Gre der Schrift fr den Ausdruck.} fr {Taille des caractres} sr {Veliina teksta na izlazu} pl {Rozmiar tekstu na wyjciu} pt {Tamanho da fonte a usar na impresso} label print_res sv {Upplsningen p bilder som skrivs ut i punkter per inch. Ett hgre vrde ger mindre bilder} en {Resolution of printer images in Dots Per Inch. A higher value means smaller images.} de {Auflsung des Druckers in Punkten pro Zoll. Eine hhere Auflsung bewirkt kleinere Ausdrucke.} fr {Rsolution des images sur l'imprimante en PPP. Plus la valeur est grande, plus les images seront petites.} sr {Rezolucija za tampanje slika u takama po inu (DPI). Vea vrednost proizvodi manje slike.} pl {Rozdzielczo drukarki w punktach na cal (DPI). Wysza warto oznacza mniejsze obrazki} pt {Resoluo da impresso em dpi - pontos por polegada. Um valor mais elevado significa imagens mais pequenas} label vd_monitor sv {Denna knapp gr s att TkRat alltid ppnar brevldan och visar antalet brev i brevlistan} en {This button makes TkRat always keep this folder open and show the number of messages in the Folder menu} de {Diesen Ordner stndig im Zugriff halten und die Zahl der Nachrichten darin im Men anzeigen.} fr {Si cette case est coche, la bote lettres sera observe. TkRat la gardera toujours ouverte et indiquera en permanence le nombre de messages dans tous les menus de botes lettres.} sr {Ovo dugme ini da TkRat uvek dri ovaj skup otvorenim i prikazuje broj pisama u meniju za skupove.} pl {Ten przycisk powoduje, e folder jest zawsze otwarty i pokazuje liczb wiadomoci w menu foldera} pt {Caso este boto esteja activo, a pasta permanecer aberta e vigiada e o TkRat exibir o nmero de mensagens no menu 'Pastas} label pretty_ps sv {Skriv ut brev som snygga postscript-filer} en {Print messages as Pretty PostScript files} de {Den Text mit Hervorhebungen und Titelleiste ausdrucken.} fr {Imprime les messages en postscript avec mise en forme.} sr {tampaj pisma kao lepe PostScript datoteke} pl {Drukuj listy jako pliki PostScript} pt {Imprime mensagem em PostScript, formatada} label plain_text sv {Skriv ut brev som ren text} en {Print messages as plain text} de {Den Text ohne Auszeichnungen ausdrucken.} fr {Imprime les messages en texte brut, sans mise en forme.} sr {tampaj pisma kao obian tekst} pl {Drukuj listy jako czysty tekst} pt {Imprime a mensagem em texto simples, sem qualquer formato} label mark_nowrap sv {Denna tryckknapp lter dig se vilka delar av texten som r markerade som icke automatiskt radbrytningsbara. Dessa delar av texten stryks under nr knappen r aktiv} en {This checkbox lets you see which parts of the text are marked as not wrappable. Those parts are underlined when this button is marked.} de {Teile des Textes, auf die kein automatischer Zeilenumbruch angewendet wird, werden unterstrichen, sofern diese Option aktiviert ist.} fr {Avec cette option, TkRat souligne les parties de texte qui sont inscables.} sr {Kada je ovo dugme potvreno, delovi teksta koji se ne mogu prelomiti se prikazuju podvueno} pl {Przycisk informuje, ktre czci tekstu s oznaczone jako nie do podzielenia. Te czci s podkrelone jeli zaznaczysz przycisk.} pt {Caso este boto esteja activado, o programa sublinha as linhas da mensagem que no so dobrveis} label pref_reply_bottom sv {Denna option kontrllerar var textmarkren placeras nr du skall brja skriva ett svar p ett brev} en {This option controls where the insertion cursor is placed in the edit buffer during a reply} de {Wenn diese Option aktiviert wird, erscheint der Cursor zum Schreiben der Antwort unterhalb des Zitats.} fr {Cette option contrle l'endroit o le curseur est plac lors de la rdaction d'une rponse.} sr {Ova opcija odreuje gde treba da se postavi kurzor unutar prozora za editovanje pri sastavljanju odgovora} pl {Opcja decyduje, gdzie postawi kursor podczas edycji odpowiedzi} pt {Esta opo controla o ponto de insero do cursor na redaco de uma mensagem de resposta} label show_no_wrap sv {Bryt inte rader nr brev visas} en {Do not wrap lines when showing messages} de {Die Nachrichten werden ohne Zeilenumbruch angezeigt.} fr {Ne pas introduire de csures dans les messages affichs.} sr {Ne prelamaj redove prilikom pregleda pisama} pl {Nie dziel linii podczas wywietlania wiadomoci} pt {No dobrar linhas nas mensagens visualizadas} label show_wrap_char sv {Bryt raderna exakt vid den hgra marginalen nr brev visas} en {Wrap lines at exactly the right margin when showing messages} de {Wrter knnen beim Zeilenumbruch zertrennt werden.} fr {Insrer une csure l'endroit prcis de la marge droite lors de l'affichage d'un message.} sr {Prelomi redove tano na desnoj margini prilikom pregleda pisama} pl {Dziel linie dokadnie przy marginesie podczas wywietlania wiadomoci} pt {Dobrar as linhas na exacta margem direita nas mensagens visualizadas} label show_wrap_word sv {Bryt raderna vid en ordgrns nr brev visas} en {Wrap lines at a word boundary when showing messages} de {Der Zeilenumbruch trennt keine Wrter auf.} fr {Insrer les csures aux limites de mots lors de l'affichage des messages.} sr {Prelomi redove na razmaku izmeu rei prilikom pregleda pisama} pl {Dziel linie midzy wyrazami podczas wywietlania wiadomoci} pt {Dobrar linhas no limite das palavras nas mensagens visualizadas} label show_menu sv {Denna meny lter dig kontrollera hur innehllet i breven visas} en {This menu lets you control how the contents of the messages are displayed} de {Einstellungen zur Anzeige des Inhalts von Nachrichten.} fr {Diffrents rglages concernant l'apparence du contenu des messages.} sr {Ovim menijem odreujete kako se prikazuje sadraj pisama} pl {Menu pozwala kontrolowa sposb wywietlania zawartoci listw} pt {Este menu permite-lhe controlar a visualizao dos contedos da mensagem} label pref_watcher_font sv {Detta typsnitt anvnds i vktaren.} en {This font is used in the Watcher popup window} de {Diese Schrift wird im Wchter-Fenster verwendet.} fr {Police utilise dans la fentre du veilleur.} sr {Ovaj font se koristi za nadzorni prozor} pl {Czciona jest uywana w oknie obserwatora} pt {Fonte usada na janela de aviso} label attach_fname sv {Det filnamn som mottagren kommer att se} en {The filename the recipient will see} de {Dateiname, den der Empfnger angezeigt bekommt.} fr {Nom de fichier visible par le destinataire.} sr {Ime datoteke koje e videti primalac} pl {Nazwa pliku, jak zobaczy adresat} pt {O nome do ficheiro visvel destinatrio} label vd_debug_cclient sv {Stter p avlusningsutskrifter av brev-mappar. OBSERVERA att ditt lsenord till POP3 och IMAP-mappar kommer med strsta sannolikhet att sparas i den genererade filen.} en {Enables debugging of mail folders. OBSERVE that your password for remote folders will most probably be stored in the resulting output file.} de {Ermglicht das Debugging von Ordnern. BEACHTEN Sie, dass ihr Passwort fr das Postfach hchstwahrscheinlich in der Protokolldatei gespeichert wird.} fr {Active le dverminage des botes lettres. Remarquez que les mots de passes des botes lettres distantes, seront trs probablement stocks dans le fichier de dverminage.} sr {Omogu?ava debagovanje skupova s po?tom. PA?NJA! Va?e ?ifre za skupove na drugim serverima ?e najverovatnije biti zapisane u rezultuju?oj debug-datoteci!} pl {Wcza debugowanie folderw z poczt. UWAAJ: hasa do zdalnych folderw bd zapisane w plikach logw.} pt {Activa 'debug' de pastas. Tenha em ateno que a palavra passe de pastas remotas ser provavelmente armazenada no ficheiro de registos de 'debug'} label pref_useinputmethods sv {'Input methods' ser till s att tk frstr sig p dda och compose-tangenter. Dessa tangenter anvnds normalt fr att skriva in tecken som inte finns i i us-ascii. Detta kan dock i vissa fall ge mycket dliga prestanda, vilket oftast kan lsas genom att kompilera om tk utan trdning.} en {Input methods makes tk understand the concept of dead- and compose-keys. These are normally used to allow entry of non us-ascii characters. This option may cause a performance penalty, which normally can be solved by recompiling tk without threads.} de {Diese Option sorgt dafr, dass Tk tote Tasten und Compose-Tasten verarbeitet. Sonderzeichen, z. B. Vokale mit Akzent, knnen dann aus einzelnen Zeichen zusammengesetzt werden. Diese Option verursacht mglicherweise ein Performance-Problem, das normalerweise durch erneutes bersetzen von Tk ohne Threads beseitigt werden kann.} fr {Grce aux input methods, Tk peut utiliser les touches mortes et la touche compose. Ces mcanismes sont utiliss pour saisir des caractres accentus. Il est possible que cette option ait une influence sur les performances, ce qui peut se corriger en recompilant Tk sans support pour les fils d'excution (threads).} sr {"Input methods" omoguavaju da TK shvati koncept "mrtvih" i sastavnih tastera, koji se obino koriste za unos ne-ASCII znakova. Ova opcija moe izazvati pad performansi, to se obino reava kompajliranjem TK bez podrke za niti (threads).} pl {'Input methods' pozwalaj Tk zrozumie koncept klawiszy martwych i edycji. S one normalnie uywane by umoliwi wprowadzanie znakw spoza us-ascii. Ta opcja moe obniy wydajno, cho mona to rozwiza kompilujc Tk bez wtkw.} pt {O mtodo de entrada de caracteres especiais foram o Tk a compreender o conceito de teclas mortas e/ou de composio. Estas teclas so normalmente usadas na insero de caracteres no includos no conjunto US-ASCII. Esta opo pode causar uma diminuio de performance, geralmente resolvida re-compilando o TclTk por forma a no usar 'threads'.} label do_check_spelling sv {Kontrollera stavningen} en {Check your spelling} de {Rechtschreibung dieser Nachricht berprfen.} label ssh_command sv {Detta r en mall fr hur ssh-kommandot kommer att konstrueras. Standardvrdet r normalt "%s %s -l %s exec /etc/r%sd", dr de olika %s blir ersatta med fljande: Skvg till SSH-kommandot Namnet p maskinen vi vill koppla upp oss till Anvndarnamnet Tjnst (imap eller pop3). } en {This is the template for how the command is constructed. The default is normally "%s %s -l %s exec /etc/r%sd", where the different %s variables get replaced with the following: Path to SSH command Name of remote host User to connect as Remote service (IMAP or POP3) } de {Dies ist die Schablone, mit der der Befehl erzeugt wird. Voreinstellung ist normalerweise "%s %s -l %s exec /etc/r%sd", wobei die %s jeweils mit den folgenden Parametern belegt werden: dem SSH-Programm (Pfadangabe) dem Namen des Hosts dem Benutzernamen dem Dienst (IMAP oder POP3)} fr {Modle pour construire la commande ssh approprie. La valeur par dfaut est "%s %s -l %s exec /etc/r%sd", o les %s sont tour tour remplacs par : Chemin vers la commande SSH Nom de l'hte distant nom d'utilisateur distant Service distant (IMAP ou POP3) } pl {Szablon konstrukcji polecenia SSH. Domylnie jest "%s %s -l %s exec /etc/r%sd", gdzie poszczeglne %s s zastpowane nastpujco: cieka do polecenia SSH Nazwa zdalnego hosta Nazwa uytkownika do zalogowania Serwis (imap lub pop3)} pt {Modelo a usar para a construo do comando ssh. A configurao por defeito '%s %s -l %s exec /etc/r%sd', em que as strings '%s' so substitudas sucessivamente por: Caminho para o comando ssh Nome do servidor Nome do utilizador (userid) no servidor Servio a usar (imap ou pop3) } label go_online sv {ndrar mod p TkRat till online. Detta betyder att alla uppskjutna brev kommer att skickas och att alla ppna avkopplade-mappar kommer att synkroniseras.} en {This changes TkRat into Online mode. This means that all deferred messages will be sent and all open disconnected folders will be synchronized. Then, while we are online, all new messages are sent immediately and all open folders are resynchronized.} de {TkRat in den online Modus schalten. Alle verzgerten Nachrichten werden gesendet, und alle geffneten offline Ordner werden synchronisiert. Im online Modus werden alle Nachrichten sofort gesendet und alle geffneten Ordner werden automatisch synchronisiert.} fr {Bascule TkRat en mode En ligne . TkRat synchronisera les botes lettres dconnectes et expdiera les messages diffrs. En mode Enligne tous les messages seront expdis immdiatement, et les botes lettres distantes seront gres de manire interactive.} pl {Przecza TkRat w tryb Online. To znaczy, wszystkie odoone wiadomoci zostan wysane i wszystkie otwarte rozczone foldery zostan zsynchronizowane. Ponadto gdy jestemy online, wszystkie nowe wiadomoci s automatycznie wysyane i otwierane foldery s synchronizowane.} pt {Permuta o TkRat para o modo 'online'. As mensagens diferidas sero enviadas e as pastas 'desligadas' sero sincronizadas. Enquanto permanecer 'online', as mensagens a enviar sero imediatamente enviadas e as todas pastas abertas sero mantidas em sincronia.} label go_offline sv {ndrar mod p TkRat till offline. Detta betyder att alla brev som skickas kommer att lagras och att alla avkopplade-mappar kommer att operera i disconnected-mod.} en {This changes TkRat into Offline mode. This means that outgoing messages will be deferred and disconnected folders will operate in Disconnected mode.} de {TkRat in den offline Modus schalten. Abgehende Nachrichten werden verzgert, und offline Ordner werden nicht synchronisiert.} fr {Bascule TkRat en mode hors ligne . Les messages expdier seront diffrs, et les botes lettres dconnectes fonctionneront en mode dconnect.} pl {Przecza TkRat w tryb Offline. To znaczy, wszystkie wysyane wiadomoci zostan odoone do wysania, a rozczone foldery nie bd synchronizowane.} pt {Permuta o TkRat para o modo 'offline'. Mensagens sero enviadas em diferido e pastas 'desligadas' sero operadas em modo 'desligado'} label mail_server sv {Namnet p epostservern som denna mapp finns p. Du kan bara vlja frdefinierade servers hr. Om du vill anvnda en annan server s mste du definiera den frst.} en {The name of the mailserver this mailbox resides on. You can only select among predefined servers here. If you want to use a server which is not predefined, you must first define it.} de {Der Name des E-Mail Servers, auf dem dieser Ordner liegt. Sie knnen hier nur einen der Server auswhlen, die Sie vorher angelegt haben.} fr {Le nom du serveur o la bote lettres se trouve. Vous ne pouvez utiliser ici que les noms prdfinis. Pour utiliser un autre serveur, vous devez d'abord le dfinir} pl {Nazwa serwera, na ktrym jest skrzynka pocztowa. Moesz wybiera tylko wrd predefiniowanych serwerw. Jeli chcesz uywa serwera, ktry nie jest zdefiniowany, musisz go najpierw zdefiniowa.} pt {O nome do servidor onde se encontra esta caixa de correio. Pode apenas usar um dos servidores pr-definidos. Caso pretenda usar um novo servidor, deve completar primeiro a sua definio} label use_as_disconnected sv {Anvnd denna mapp som en avkopplad mapp. Detta betyder att en kopia av mappen kommer att sparas p din lokala dator. Det r sedan mjligt att arbeta mot denna lokala kopia utan att vara uppkopplad mot IMAP-servern.} en {Use this folder as a disconnected folder. That means that a copy of all content is kept on your local computer. This means that you can continue to work on this folder while offline from the IMAP server.} de {Diesen Ordner als offline Ordner anlegen. Der Inhalt des Ordners wird auf ihren Computer kopiert. Sie knnen diesen Ordner bearbeiten, whrend die Verbindung zum IMAP Server getrennt ist.} fr {Le nom du serveur o la bote lettres se trouve. Vous ne pouvez utiliser ici que les noms prdfinis. Pour utiliser un autre serveur, vous devez d'abord le dfinir} pl {Uywaj tego foldera jako rozczony. A wic lokalna kopia wszystkich wiadomoci bdzie przechowywana na twoim komputerze. Oznacza to, e moesz kontynuowa prac w tym folderze nawet w trybie offfline, nie bdc poczonym z serwerem IMAP.} pt {Usa esta pasta como uma pasta 'desligada'. Uma cpia local de todo o contedo da pasta mantida no seu computador. assim possvel alterar os contedos da pasta enquanto 'offline'.} label reimport_all sv {Fr TkRat att importera om alla importerade brevldedefinitioner} en {Makes TkRat reimport all imported folder definitions} de {TkRat liest alle Ordner-Definitionen erneut ein.} fr {TkRat rimportera toutes les dfinition des bote lettres} pl {Powoduje ponowne zaimportowanie wszystkich importowanych definicji folderw} pt {Fora o TkRat a importar todas as definies das pastas} label vd_tcp_default sv {Kontakta servern mha TCP via standardporten (143 fr imap och 110 fr pop3)} en {Connect to the server using TCP to the default port (143 for IMAP and 110 for POP3)} de {Verbindungen mit dem Server auf dem TCP Standardport des Dienstes herstellen (IMAP: 143, POP3: 110).} fr {La connexion s'effectuera sur le port TCP par dfaut. 143 pour IMAP et 110 pour POP3.} pl {Pocz z serwerem uywajc TCP na domylnym porcie (143 dla IMAP i 110 dla pop3)} pt {Ligar ao servidor usando o porto TCP por defeito (143 para IMAP e 110 para POP3)} label vd_tcp_custom sv {Kontakta servern mha TCP via en egen port} en {Connect to the server using TCP to a custom port} de {Verbindungen mit dem Server auf einem anderen TCP Port herstellen.} fr {Permet de choisir le port TCP de la connexion.} pl {Pocz z serwerem uywajc TCP na wybranym porcie} pt {Ligar ao servidor usando um porto pr-definido} label vd_rsh sv {Kontakta servern via ssh eller rsh. Detta krver att du kan logga in p servern.} en {Connect to the server using SSH or RSH. This requires that you can log in on the server.} de {Verbinde mit dem Server mittels SSH oder RSH. Es ist erforderlich, dass Sie sich auf dem Server einloggen knnen.} fr {Effectue la connexion par SSH ou RSH. Cela implique que vous puissiez vous loger sur le serveur par l'un de ces protocoles.} pl {Pocz z serwerem uywajc ssh lub rsh. Wymaga moliwoci zalogowania si na serwerze.} pt {Ligar ao servidor usando rsh ou ssh. Implica que possa realizar o login com um destes mtodos} label vd_flag_ssl sv {Anvnd SSL-kryptering nr servern kontaktas} en {Use SSL encryption when connecting to the server} de {Verwende Verschlsselung mit SSL fr Verbindungen mit dem Server.} fr {Utilise un chiffrement SSL lors de la connexion au serveur.} pl {Uywaj szyfrowania SSL podczas poczenia} pt {Usa criptografia SSL na ligao ao servidor} label vd_flag_checkc sv {Kontrollera servern certifikat nr SSL anvnds. Detta krver att du har CA-certiofikatet som anvndes fr att signera serverns certifikat installerat lokalt. Detta fungerar inte med sjlvsignerade certifikat.} en {Check the server certificate when using SSL. This requires that you have a CA-signed certificate on the server, it will not work with self-signed server certificates.} de {Prfe das Zertifikat des Servers bei Verbindungen mit SSL. Dazu mssen Sie das CA-Zertifikat, mit dem das Zertifikat des Servers signiert ist, lokal installiert haben. Eigen- Signaturen knnen nicht verwendet werden.} fr {Vrifie le certificat du serveur lors de la connexion. Vous devez possder sur votre ordinateur le certificat CA qui a servi signer le certificat du serveur. Cela ne fonctionnera pas avec les certificats auto-signs.} pt {Verifica o certificado do servidor quando usa SSL. necessrio ter certificado CA do servidor instalado localmente. Esta funo no operar com certificados auto-assinados.} label vd_action_on_create sv {Om denna option r vald s kommer en ny IMAP-import-mapp att skapas nr du trycker p "Skapa". Du kommer genast att f editera den nya mappen.} en {If this checkbox is checked, then a new IMAP folder will be created as soon as you press "Create." An Edit window for this new folder will then be opened.} de {Wenn Sie diese Option aktivieren, wird ein neuer IMAP Ordner angelegt, sobald Sie "Erstellen" aktivieren. Zum Bearbeiten der Eigenschaften des Ordners wird ein neues Fenster geffnet.} fr {Si vous choisissez cette option, un sous menu d'importation des botes lettres IMAP sera cr l'instant o vous cliquer le bouton crer . Une fentre d'dition relative ce sous menu apparatra alors.} pl {Jeli zaznaczone, nowy folder importu IMAP zostanie stworzony po naciniciu "Utwrz". Po utworzeniu nowego foldera pojawi si jego okno edycji.} pt {Caso esta opo esteja activada, uma nova pasta IMAP ser criada assim que pressionar 'Criar', bem como uma janela onde proder editar as definies da pasta} label vd_rei_session sv {Importera om denna mappen en gng per session} en {Automatically reimport this folder once every session} de {Diesen Ordner in jeder Sitzung automatisch neu einlesen.} fr {Rimporter automatiquement cette bote lettres une fois par session} pl {Automatycznie reimportuj folder podczas kadej sesji} pt {Reimportar esta pasta automa- ticamente uma vez por sesso} label vd_rei_manually sv {Importera inte om denna mappe automatiskt} en {Do not automatically reimport this folder} de {Diesen Ordner nicht automatisch neu einlesen.} fr {Ne pas rimporter cette bote lettres} pl {Nie reimportuj tego folderu} pt {No reimportar esta pasta automaticamente} label vd_import_on_create sv {Importera denna mappen s snart som den skapas} en {Perform an import of this folder as soon as it is created} de {Diesen Ordner einlesen, sobald er erstellt wurde.} fr {Importer cette bote lettres lors de sa cration} pl {Wykonaj import tego folderu zaraz po stworzeniu} pt {Importar esta pasta assim que for criada} label pref_start_online_mode sv {Vilken ntverks-mod som skall anvndas vid uppstart} en {Which network mode to use when starting} de {Verbindungsmodus beim Start von TkRat.} fr {Quel mode rseau utiliser au dmarrage de TkRat} pl {Domylny tryb podczas startu TkRat} pt {O modo de rede a usar no arranque do TkRat} label pref_ssh_path sv {Full skvg till SSH (skall inkludera kommandonamnet)} en {Full path of SSH command (including the SSH command itself)} de {Vollstndiger Pfad des SSH-Programms (einschlielich Programmname).} fr {Chemin complet de la commande SSH, y compris ssh} pl {Pena cieka polecenia SSH (razem z poleceniem)} pt {Caminho completo do comando SSH (incluindo o nome do comando)} label vd_secure sv {Om denna option r aktiv s vgrar TkRat att skicka ditt lsenord ver en okrypterad ntverksfrbindelse. Dvs ditt lsenord skickas endast vern en SSL-frbindelse} en {If this is set, then TkRat will refuse to send your password over an unencrypted network connection, i.e. the password will only be sent over an SSL-encrypted session} de {Wenn diese Option aktiviert ist, sendet TkRat ihr Passwort ausschlielich ber Verbindungen, die mit SSL verschlsselt sind, jedoch nicht ber unverschlsselte Verbindungen.} fr {Si vous choisissez cette option, TkRat n'enverra jamais votre mot de passe au travers d'une connexion non chiffre. Le mot de passe ne sera envoy que sur une connexion SSL.} pl {Jeli ustawione, TkRat bdzie odmawia wysania twojego hasa nie szyfrowanym poczeniem. Np. haso bdzie wysyane tylko podczas sesji SSL} pt {Caso esta opo esteja activada, o TkRat recusar-se- a enviar a sua palavra passe atravs de uma ligao no cifrada. A palavra passe ser transmitida apenas atravs de uma ligao SSL com criptografia} label vd_priv_ssl sv {Anvnd detta om servern krver SSL, vilket betyder att TkRat mste stta upp SSL- frbindelsen innan vi brjar prata IMAP eller POP3. Detta kommer att ndra standarporten vi TCP- frbindelser till 993 eller 995.} en {Use this if the server requires SSL, which means that TkRat has to negotiate the SSL connection before the IMAP or POP3 protocol starts. This will change the default port used when connecting to 993 or 995.} de {Verwenden Sie dies, falls der Server SSL fordert. TkRat baut dann zunchst eine SSL Verbindung vor der IMAP bzw. POP3 Verbindung auf. Die Standardports werden automatisch angepasst (IMAPS: 993, POP3S: 995).} fr {Utiliser cette option si le serveur impose une connexion SSL. TkRat devra alors ngocier la connexion SSL avant d'entamer le dialogue IMAP ou POP3. TkRat utilise alors respectivement pour ces protocoles les port 993 et 995.} pl {Wcz, jeli serwer wymaga SSL. To znaczy, e TkRat przed protokoem IMAP lub POP3 bdzie negocjowa poczenie SSL. Domylnym portem bdzie wtedy 993 lub 995} pt {Use esta opo se o seu servidor exije uma ligao SSL, devendo o TkRat negociar a ligao antes dos protocolos IMAP ou POP3 comearem. } label vd_priv_tls sv {Anvnd detta om du vill sl p kryptering bara om server stde det (STARTTLS-funktionen).} en {Use this if you want to enable encryption only if the server supports it (the STARTTLS extension)} de {Verwenden Sie diese Option, falls Sie Verschlsselung nur dann aktivieren wollen, wenn der Server diese untersttzt (STARTTLS Erweiterung).} fr {N'active le chiffrement de la connexion que si le serveur le supporte. C'est l'extension STARTTLS.} pl {Wcz, jeli chcesz wczy tylko szyfrowanie, jeli serwer je obsuguje (funkcja STARTTLS)} pt {Active esta opo se pretender usar criptografia apenas se o servidor a suportar (extenso STARTTLS)} label vd_priv_none sv {Frsk inte anvnda kryptering alls. Notera att frbindelsen kan vara krypterad nd om SSH anvnds.} en {Do not try to use any encryption at all. Note that the session may still be encrypted if SSH is used as the transport.} de {Verwende keine Verschlsselung. Die Verbindung kann allerdings dennoch verschlsselt sein, wenn SSH verwendet wird.} fr {Ne pas utiliser de chiffrement. Remarquez que la session peut toujours tre chiffre si le protocole de transport est SSH.} pl {Nie uywaj szyfrowania. Jednak sesja wci moe by szyfrowana, jeli do transportu wybrano SSH} pt {No utilizar criptografia. Note que a sesso pode sempre ser cifrada se usar SSH como protocolo de transporte} label create_mailbox_on_server sv {Koppla upp mot IMAP-servern och se till att brevldan verkligen finns. Om den inte gr det s skapas en ny.} en {Connect to the IMAP server and make sure that the mailbox actually exists. Create it if it does not already exist.} de {Stelle eine Verbindung zum IMAP Server her und stelle sicher, dass der Ordner existiert. Lege ggf. einen neuen Ordner an.} fr {Se connecter au serveur IMAP et s'assurer que la bote lettres existe. La crer si tel n'est pas le cas.} pl {Pocz z serwerem IMAP i upewnij si, e skrzynka istnieje. Jeli nie, utwrz j.} pt {Liga ao servidor IMAP e assegura-se que a caixa de correio existe. Caso ainda no exista, a caixa de correio criada} label pref_name sv {Namnet p rollen} en {The name of the role} de {Der Name fr diese Rolle.} fr {Le nom du rle} pt {O nome da identidade} label folder_select_role sv {Vljer vilken roll vi skall anvnda oss av fr tillfllet} en {Selects which role we should use at the moment} de {Whlt die Rolle, die Sie verwenden mchten.} fr {Slectionne le rle qu'il faut utiliser maintenant} pt {Selecciona a identidade a usar neste momento} label vd_role sv {Roll som anvnds nr mappen visas (kan ndras manuellt)} en {Role to use when viewing folder (can be overridden)} de {Die Rolle, die zum Betrachten dieses Ordners eingestellt werden soll. Diese Voreinstellung lsst sich spter bergehen.} fr {Rle utiliser lors de la consultation de la bote lettres} pt {Identidade a utilizar durante a consulta a esta pasta} label pref_save_outgoing sv {Denna meny lter dig vlja om och i s fall i vilken brevlda som utgende brev kopieras till.} en {This menu lets you select if and to which folder outgoing messages are automatically copied} de {Hier knnen Sie festlegen, in welchem Ordner Kopien der abgehenden Nachrichten abgelegt werden sollen.} fr {Ce menu permet de choisir si et dans quelle bote lettre les messages expdis sont automatiquement copis} pl {Menu wyboru czy i w ktrym folderze zapisywa wiadomoci wychodzce.} pt {Este menu permite-lhe escolher se e em qual pasta se devem gravar as mensagens enviadas com esta identidade} label pref_color_scheme sv {Vilken frgkarta som TkRat skall anvnda.} en {Which color scheme to use for TkRat.} de {Legt das Farbschema fr TkRat fest.} #fr {Quel thme de couleur utiliser pour TkRat. #Si l'option Prfrer les couleurs de TkRat #est faux dans Options->Avanc->Apparence, #les ressources X11 seront utilises.} #pl {Wybierz schemat kolorw TkRat. #Moe by nadpisane przez zasoby Xw, wtedy #naley wyczy "Preferuj wasne kolory" w #Opcje->Zaawansowane->Wygld} #pt {O esquema de cores a usar pelo TkRat. Esta #escolha pode ser alterada pela configurao do #servidor X, sendo nesta caso deseleccionar a #opo "Preferir cores personalizadas" em #Opes->Avanado->Aspecto} label pref_html_show_images sv {Om bilder i HTML-brev skall visas eller inte. Detta kan g mycket lngsamt. Det kan ocks innebra en skerhetsrisk eftersom folk kan spra nr du lser ett visst brev.} en {Whether to show images in HTML messages. This can slow things down considerably, it may also constitute a security risk; for example, people can track if you read a message.} de {Diese Option legt fest, ob Bilder in HTML Nachrichten angezeigt werden sollen. Das Laden der Bilder kann die Anzeige deutlich verzgern. Auerdem stellt das Laden der Bilder ein potentielles Sicherheitsrisiko dar, da es dadurch mglich wird, festzustellen, ob Sie eine bestimmte Nachricht gelesen haben.} fr {Afficher ou non les images dans le HTML. Ceci peut ralentir l'application considrablement; cela peut galement comporter un risque de scurit o il est possible de savoir si vous avez lu le message.} pl {Czy pokazywa obrazki w wiadomociach HTML. Ta opcja moe strasznie spowolni program, oraz zmniejszy bezpieczestwo, poniewa mona sprawdzi czy czytasz t wiadomo.} pt {Mostrar ou no imagens em mensagens com HTML. Esta opo pode dimuir a velocidade de execuo consideravelmente. Alm disso, constitui uma diminuio da sua privacidade na medida em que o remetente da mensagem pode verificar quando exactamente a mensagem foi lida} label pref_html_min_image_size en {The minimum size an image must be to be fetched. This helps prevent spam where images of 1x1 pixels are used to validate email addresses.} de {Die minimale Gre fr Bilder. Mit Hilfe dieser Einstellung wird verhindert, dass winzige Bilder (z. B. 1x1 Pixel) zur Verifikation ihrer Adresse verwendet werden knnen.} fr {La taille minimale d'une image a tlcharger. Ceci empche un type de pourriel o des images de 1x1 point sont utilises pour valider des adresses de courriel.} sv {Den minsta storleken som en bild mste ha fr att hmtas. Detta hjlper mot skrppost dr bilder av storlek 1x1 anvnds fr att kontrollera epostadresser.} label pref_html_proxy_host en {Name of the proxy server. Leave empty if you are not using a proxy server.} de {Name des Proxy Servers. Lassen Sie diesen Eintrag frei, falls sie kein Proxy verwenden.} fr {Nom du serveur mandataire. Laisser vide si vous n'utilisez pas de serveur mandataire.} sv {Namn p proxy-server. Lmna tomt om du inte anvnder en proxy-server.} label pref_html_proxy_port en {Port of the proxy server.} de {Portnummer des Proxy Servers.} fr {Port du serveur mandataire.} sv {Port p proxy-servern.} label pref_html_timeout en {How long to wait (in milliseconds) before giving up fetching an image.} de {Das Laden eines Bildes wird nach Ablauf dieser Zeitspanne in Millisekunden abgebrochen.} fr {Combien de temps (en milisecondes) faut-il attendre avant d'abandonner le chargement d'une image.} sv {Max vntetid (i millisekunder) p en bild.} label pref_pgp_keyid sv {ID p den PGP-nyckel som skall anvndas vid signeringar} en {ID of PGP key to use when signing messages} de {PGP Schlssel, mit dem Sie abgehende Nachrichten unterschreiben mchten.} fr {Identifiant de la clef PGP utilis pour signer un message} pt {Identificador da chave PGP a usar na assinatura de mensagens} label pref_uqa_domain sv {Denna domn lggs till till adresser som saknar domn. Om denna r blank s tas domnen i din "Frn"-adress, om den inte finns s frsker vi med maskinens domn.} en {This domain gets added to all addresses which do not already have a domain. If this is blank, we try to get the domain from the From: address; if there is none, we try the hostname.} de {Diese Domne wird allen Adressen angefgt, bei denen die Domnen- angabe fehlt. Wenn dieser Eintrag frei bleibt, wird die Domne aus der Von-Adresse bestimmt. Falls dies nicht mglich ist, wird sie aus Ihrem Rechnernamen (hostname) abgeleitet.} #fr {TkRat ajoutera ce nom de domaine toutes les adresses qui n'ont #pas de domaine. Si ce champ est vide, TkRat tentera tour tour #d'obtenir le nom de domaine en utilisant la valeur de # Utiliser l'adresse De: dfinie dans la section adresse des #rles, puis le nom d'hte de votre ordinateur, et finalement #l'option domaine dans la section options/Avanc/Rseau.} #pt {Este domnio adicionado a todos os endereos que no #tm ainda um domnio. Caso este espao esteja em branco, #o TkRat tentar extrair o domnio a partir do campo 'De:', #se este no existir, tentar o nome de servidor e em ltimo #caso usar a opo 'Domnio'.} label pref_smtp_helo sv {Hr kan du ange vilket maskinnamn som skall anvndas i HELO/EHLO-dialogen i SMTP} en {Here you can override the default hostname used in the SMTP HELO/EHLO dialog} de {Hier knnen Sie festlegen, mit welchem Namen sich TkRat beim HELO/EHLO des SMTP vorstellt. Bleibt das Feld frei, wird der Name Ihres Rechners verwendet.} fr {Cette option permet de changer le nom d'hte utilis dans le dialogue SMTP HELO/EHLO.} pt {Esta opo permite-lhe alterar o valor nome de servidor usado no dilogo de estabelecimento de ligao HELO/EHLO} label icon_offline sv {Denna ikon representerar det faktum att TkRat fr tillfllet befinner sig i offline mod. Genom att trycka p den kommer du att g ver i online mod. Offline mod betyder att alla brev som skickas kommer att lagras och att alla avkopplade-mappar kommer att operera i disconnected-mod. Alla lagrade brev kommer att skickas nr du gr ver i online mod. Alla ppna disconnected-mappar kommer ocks att synkroniseras.} en {This icon represents the fact that TkRat currently is in Offline mode. By pressing it, you will switch into Online mode. Offline mode means that outgoing messages will be queued (deferred) and disconnected folders will operate in Disconnected mode. Any queued messages will be sent when you switch into Online mode. Doing so will also synchronize all open disconnected folders.} de {Dieses Symbol zeigt an, dass TkRat sich gegenwrtig im offline Modus befindet. Ein Mausklick auf das Symbol schaltet in den online Modus. Im offline Modus werden abgehende Nachrichten verzgert, und offline Ordner werden nicht synchronisiert. Wenn Sie in den online Modus schalten, werden alle verzgerten Nachrichten verschickt und alle geffneten offline Ordner werden synchronisiert.} fr {Cette icne indique que TkRat est en mode Hors ligne . En cliquant dessus, vous basculerez en mode En ligne . Hors ligne signifie que les messages sont expdis en diffr et que les botes lettres dconnectes sont en mode dconnect. Si vous passez en mode En ligne, les messages diffrs seront envoys et les botes dconnectes seront synchronises.} pt {Este cone indica que de momento o TkRat est em modo 'offline'. Um clique sobre o cone permutar para o modo 'online'. Em modo 'offline' as mensagens a enviar so colocadas em lista de espera para envio em diferido e pastas 'desligadas' operam em modo desligado. Todas as mensagens a enviar em espera so enviadas assim que permutar para o modo 'online'. As pastas 'desligadas' so igualmente sincronizadas com a rede.} label icon_online sv {Denna ikon representerar det faktum att TkRat fr tillfllet befinner sig i online mod. Genom att trycka p den kommer du att g ver i offline mod. I online mod s skickas alla brev direkt.} en {This icon represents the fact that TkRat currently is in Online mode. By pressing it, you will switch into Offline mode. In Online mode, all new messages are sent immediately and all open folders are kept in sync.} de {Dieses Symbol zeigt an, dass sich TkRat gegenwrtig im online Modus befindet. Ein Mausklick auf das Symbol schaltet in den offline Modus. Im online Modus werden alle Nachrichten sofort gesendet und alle geffneten Ordner werden automatisch synchronisiert.} fr {Cette icne indique que TkRat est en mode En ligne . En cliquant dessus, vous basculerez en mode Hors ligne . En mode En ligne, les messages sont envoys immdiatement et les botes lettres dconnectes sont synchronises en permanence.} pt {Este cone indica que o TkRat est em modo 'online'. Um clique sobre o cone permutar para o modo 'offline'. No modo 'online' as mensagens a enviar so enviadas de imediato e todas as pastas abertas so mantidas em sincronia} label cant_delete_inbox sv {Du kan inte radera din INBOX!} en {You can not delete the INBOX!} de {Sie knnen die INBOX nicht lschen!} fr {Vous ne pouvez pas dtruire la bote INBOX !} pl {Moesz teraz usun INBOX!} pt {No pode remover a pasta INBOX!} label cant_delete_used sv {Du kan inte radera denna brevldan eftersom den anvnds i rollerna} en {You can't delete the folder since it is used in roles} de {Dieser Ordner kann nicht gelscht werden, weil er in bestimmten Rollen verwendet wird.} fr {Vous ne pouvez pas dtruire cette bote ; elle est utilise par des rles.} #pt {No possvel destruir a pasta porque usada nas identidades seguintes} label pref_validate_cert en {If checked: Check that the certificate used by the server in SSL- connections is valid. This does not work with self-signed certificates.} de {Wenn Sie diese Option aktivieren, wird das Zertifikat des Servers by SSL-Verbindungen geprft. Dies funktioniert nicht mit selbst-signierten Zertifikaten.} sv {Om markerad: kontrollera att certifikatet som anvnds av servern vid SSL-frbindelser r giltigt. Detta fungerar inte med sjlvsignerade certifikat.} fr {Si active : Vrifie que le certificat que le serveur utilise lors de la connexion SSL est valide. Ceci ne fonctionne pas avec les certificats auto-signs.} label pref_same_sending_prefs en {If checked: any changes you do to the sending preferences here will be applied to all other roles as well} de {Wenn Sie diese Option aktivieren, werden die hier vorgenommenen Einstellungen auf alle Rollen angewendet.} sv {Om markerad: alla ndrigar du gr i skickainstllningarna hr kommer att appliceras p alla andra roller ocks} fr {Si activ : Tout changement dans les prfrences d'expdition sera rpercut aux autres rles.} label pref_smtp_user en {Username to use when authenticating to the SMTP-server} de {Dieser Benutzername wird bei der Anmeldung beim SMTP Server angegeben.} sv {Anvndarnamn fr att autentisera mot SMTP-servern} fr {Nom d'utilisateur pour l'authenti- fication auprs du serveur SMTP} label pref_smtp_passwd en {Password to use when authenticating to the SMTP-server. This password is stored in clear text in the configuration file.} de {Dieses Passwort wird bei der Anmeldung beim SMTP Server angegeben.} sv {Lsenord fr att autentisera mot SMTP-servern. Detta lsenord sparas i klartext in konfigurationsfilen.} fr {Mot de passe pour l'authentification auprs du serveur SMTP. Ce mot de passe est conserv en clair dans Le fichier de configuration de TkRat.} label pref_url_behavior sv {Bestmmer hur URLer skall ppnas} en {Select whether to open a URL in a new browser window, tab, or existing window.} de {Diese Option legt fest, ob und wie fr die URL im gewhlten Browser ein neues Fenster geffnet wird.} label pref_sign_as en {Which PGP key to use for signing. If no key is specified here then TkRat will try to locate a key based on the from address.} sv {Vilken PGP-nyckel som skall anvndas vid signering. Om ingen nyckel har valts hr s kommer TkRat att frska anvnda en nyckel baserat p frn-adresses.} de {Legt fest, welcher PGP-Schlssel zum Unterschreiben verwendet werden soll. Wird hier kein Schlssel ausgewhlt, versucht TkRat den Schlssel aus der Absenderangabe im Adressfeld "Von" zu bestimmen.} label pref_sign_outgoing sv {Startvrdet fr knappen signera i brevskrivningsfnstret} en {The default value of the Sign checkbox in the Compose window} de {Voreinstellung fr die "Unterschreiben" Option beim Schreiben einer Nachricht.} it {Il valore standard del pulsante di firma nella finestra di composizione} fr {Valeur par dfaut de la case cocher 'Signer' dans la fentre de rdaction.} sr {Podrazumevana vrednost za opciju "Potpis" u prozoru za sastavljanje} pl {Domylny stan przycisku "Podpisz" w oknie edycji wiadomoci} pt {O valor por defeito do boto de escolha de 'Assinar' no menu 'Extra' na janela de redaco} label addressbook en {Opens the address book window} sv {ppnar adressbooksfnstret} de {Adressbuch-Fenster ffnen.} label reread_addresses en {Rereads all defined address books} sv {Lser om innehllet i alla definierade adressbcker} de {Liest alle definierten Adressbcher neu ein.} label import_aliases sv {Ser p ett par standardplatser efter aliasfiler frn andra brevprogram och lser in dessa} en {Searches a few standard places for alias files from other mail programs and reads those it finds} de {Nach Aliassen anderer E-Mail-Programme suchen und diese Aliasse hinzufgen.} it {Cerca in alcuni posti standard per file alias di altri programmi di posta e li legge} fr {Recherche et lit des fichiers d'alias d'autres programmes de courriel dans quelques emplacements standards} sr {Pretrauje neka uobiajena mesta gde drugi programi uvaju aliase i ita ih} pl {Szuka w kilku typowych miejscach plikw z aliasami z innych programw pocztowych i odczytuje je} pt {Procura nalguns lugares standard por ficheiros de endereos de outros programas e l esses ficheiros} label alias_pgp_actions en {The default PGP action to take when you send email to this address} sv {PGP-tgrd som skall gras nr du skickar brve till denna adress} de {Wenn Sie eine neue Nachricht an diesen Empfnger verfassen, wird die Einstellung fr Verschlsselung bzw. Unterschrift mit PGP auf diesen Wert voreingestellt.} label alias_pgp_key en {PGP-key which should be used to encrypt messages to this address} sv {PGP-nyckel som skall anvndas fr att kryptera brev till denna adress} de {Nachrichten an diesen Empfnger werden mit dem hier gewhlten PGP Schlssel verschlsselt.} label dbs_all_addresses en {This line matches if any of these words can be found in any of the address lines. If you wish to match a sequence of words, enclose them in "".} sv {Denna rad matchar om minst ett av dessa ord finns i ngon av adressraderna. Om du vill matcha en fljd av ord s omge den med ""} de {Wenigstens eines der Adressfelder (An, Kopie, Von) enthlt eines dieser Wrter. Mehrere Wrter knnen "mit Anfhrungszeichen" zu einem Wort zusammengefasst werden.} label dbs_time_to en {Match messages dated before this time. You can give a specifc time or use relative value like "1 week ago"} sv {Matchar meddelanden daterade fre denna tidpunkt. Du kan ange en specifik tid eller anvnda ett relativt vrde som "1 week ago".} de {Enddatum des Intervalls. Geben Sie ein bestimmtes Datum oder einen relativen Ausdruck, z. B. "1 week ago", an.} label dbs_time_from en {Match messages dated after this time. You can give a specifc time or use relative value like "1 week ago"} sv {Matchar meddelanden daterade efter denna tidpunkt. Du kan ange en specifik tid eller anvnda ett relativt vrde som "1 week ago".} de {Anfangsdatum des Intervalls. Geben Sie ein bestimmtes Datum oder einen relativen Ausdruck, z. B. "1 week ago", an.} label copy_msg sv {Kopiera det aktiva brevet till en annan mapp} en {Copy the current message to another folder} de {Die angezeigte Nachricht in einen anderen Ordner kopieren.} label copy_group sv {Kopiera samtliga brev i gruppen till en annan mapp} en {Copy all messages in the group to another folder} de {Alle markierten Nachrichten in einen anderen Ordner kopieren.} label forward_separately en {Forward the flagged messages. Each flagged message is forwarded as an attachment in a separate email. All the emails will have identical body text and recipients, only the attachment will differ.} sv {Vidarebefodra de markerade breven. Varje markerat brev vidareskickas som en bilaga i ett separat brev. Alla breven kommer att ha samma brdtext och mottagare, det r bara bilagan som skiljer.} de {Leite die markierten Nachrichten weiter. Jede markierte Nachricht wird als Anhang in einer separaten E-Mail weitergeleitet. Diese E-Mails haben identischen Text und Empfnger, aber unterschiedliche Anhnge.} label forward_in_one en {Creates one message which has all the flagged messages attached.} sv {Skapr ett brev som har alla markerade brev som bilagor.} de {Alle markierten Nachrichten weiterleiten. Es wird nur eine E-Mail verschickt, an die alle markierten Nachrichten angehngt werden.} label bounce_messages en {Bounce all the flagged messages as separate emails to the spcified address.} sv {Studsa de markerade breven som separata brev till den angiva adressen.} de {Alle markierten Nachrichten umleiten.} label pref_prefer_other_over_html en {When there are multiple versions of the body prefer to show a text part over the html part (when html support is present)} sv {Fredra att visa den rena textversionen av en brdtext framfr html-versionen (nr html-std finns)} de {Falls es mehrere Fassungen der Nachricht gibt, wird eine Text-Fassung einer HTML-Fassung vorgezogen, sofern diese Option aktiviert wird. (Ohne die HTML-Erweiterung hat dies keinen Effekt.)} label pref_watcher_title en {Title of watcher window} sv {Title p vktarfnstret} de {Titelzeile des Wchter-Fensters.} label pref_def_spell_dict en {Default dictionary to use for spell checking} sv {Startordlista fr stavningskontroll} de {Dieses Wrterbuch wird zur Rechtschreibprfung voreingestellt.} label pref_auto_dicts en {Dictionaries to test in auto-mode} sv {Ordlistor att testa i automode} de {Aus diesen Wrterbchern wird automatisch eines ausgewhlt, wenn oben "Automatisch" als Wrterbuch eingestellt ist.} label folder_status en {New messages / Total number of messages / Size of folder} sv {Nya brev / Totalt aantal brev / Mappstprlek} de {Neue Nachrichten / Nachrichten insgesamt / Ordnergre} label no_spell sv {Tyvrr, hittade inte Aspell eller Ispell 3.1.xx (eller senare)} en {Sorry, did not find Aspell or Ispell 3.1.xx (or later)} de {Tkrat konnte weder Aspell noch Ispell 3.1.xx (oder hher) finden.} label no_dictionaries en {Did not find any dictionaries} sv {Hittade inga ordlistor} de {Keine Wrterbcher gefunden.} label postpone sv {Stng fnstret och spara brevet som ett utkast s att vi kan fortstta senare} en {Close window and store the message as a draft so we can continue later} de {Dieses Fenster schlieen und die Nachricht als Entwurf zur weiteren Bearbeitung speichern.} label pref_compose_backup en {Time between backups (0 means no backups). They are stored in the drafts folder after a crash.} sv {Tid mellan backuper (0 betyder inga backuper). De sparas i utkastmappen och kan hittas dr efter en krash.} de {Intervall fr die Speicherung einer Sicherungskopie (0 deaktiviert diese Funktion). Die Sicherungskopie findet sich nach dem Absturz von TkRat im Ordner Entwrfe.} label enter_search_text_here sv {Text att leta efter} en {Text to search for} de {Nach diesem Text suchen.} label sort_order_folder sv {Lter dig ndra sorteringsordningen bland breven i den aktiva mappen. ndringen sparas men mappar kan ha egna instllningar som vger tyngre.} en {Lets you change the sort order of the messages in the current folder. The change is saved for the future but individual folders may override the default sort order.} de {Hiermit knnen Sie festlegen, in welcher Reihenfolge die Nachrichten im aktuellen Ordner aufgelistet werden. Die gewhlte Einstellung wird gespeichert und gilt fortan als "Standard" Sortierung. Fr jeden einzelnen Ordner kann eine abweichende Voreinstellung fr die Sortierung unter "Administration / Ordner bearbeiten" bestimmt werden.} #fr {Permet de changer l'ordre de tri dans la bote lettres courante. #Ce rglage ne sera pas sauvegard.} #sr {Omoguava da promenite nain reanja pisama u #tekuem skupu. Ova promena nee biti zapamena.} #pl {Pozwala zmienia sposb sortowania wiadomoci w tym folderze. #Ta zmiana nie zostanie zapamitana} #pt {Permite mudar a ordem das mensagens nesta pasta. #Esta mudana no memorizada} label pref_compose_last_chance en {When the compose window is closed a backup of the message is made and kept in the drafts folder for this many seconds.} sv {Nr skriva-brev fnstret stngs s skapas en kopia av brevet i utkastmappen som sparas i s hr mnga sekunder.} de {Wenn Sie das Schreiben einer neuen Nachricht abbrechen, so wird eine Sicherungskopie dieser Nachricht im Ordner Entwrfe abgelegt und fr die hier angegebene Zeit dort aufbewahrt.} label show_addr_history en {The address history is a small popup window which appears when one starts to write an address. It shows possible completions from the last 500 addresses used to send emails.} sv {Adresshistorian r ett litet popupfnster vilket dyker upp nr man brjar skriva in en adress. Det visa mjliga adresser bland de sista 500 adresserna som det skcikades brev till.} de {Die Liste der zuletzt verwendeten Adressen erscheint unterhalb des Eingabefeldes, sobald Sie die Eingabe einer Adresse beginnen. Die mglichen Vervollstndigungen der Eingabe werden aus den letzten 500 Adressen gebildet, an die E-Mails verschickt wurden.} label mark_as_unread sv {Markerar det aktiva brevet som olst} en {Marks the current message as unread again} de {Kennzeichnet die ausgewhlte Nachricht als ungelesen.} fr {Marque le message comme non lu.} sr {Oznaava tekue pismo kao da nije proitano} pl {Oznacz jako nie przeczytan} pt {Marca a mensagem novamente como no lida} label mark_as_read sv {Markerar det aktiva brevet som lst} en {Marks the current message as read again} de {Kennzeichnet die ausgewhlte Nachricht als gelesen.} label mark_as_answered sv {Markerar det aktiva brevet som besvarat} en {Marks the current message as answered} de {Kennzeichnet die ausgewhlte Nachricht als beantwortet.} label mark_as_unanswered sv {Markerar det aktiva brevet som obesvarat} en {Marks the current message as unanswered} de {Kennzeichnet die ausgewhlte Nachricht als unbeantwortet.} label pref_font_family_prop en {Font family for proportional text} sv {Typsnittsfamilj fr normal text} de {Die Schriftart fr Text mit Zeichen variabler Breite.} label pref_font_family_fixed en {Font family for fixed width text} sv {Typsnittsfamilj fr fastviddstext} de {Die Schriftart fr Text mit Zeichen einheitlicher Breite.} label pref_font_size en {Base font size} sv {Grundstorlek fr typsnitt} de {Die Basisgre fr Schriften.} label pref_url_viewer_cmd en {Command to use when launching the browser. For Lynx and 'other' browser a '%u' will bre replaced with the URL.} sv {Kommando som anvnds fr att starta weblsaren, Fr Lynx och 'annan' s kommer '%u' att bytas ut mot den URL som skall ppnas.} de {Legt die Befehlszeile, mit der der Web-Browser gestartet wird, fest. Wenn Sie oben "Lynx" oder einen "anderen" Browser auswhlen, wird ein '%u' in der Befehlszeile durch die anzuzeigende URL ersetzt.} label pref_spell_path sv {Skvg eller namn p kommando fr stavningskotroll. Kommandon som stds r r ispell och aspell. Vrdet "auto" betyder att TkRat kommer att leta efter bde aspell och ispell.} en {Path to or name of spell checking command. Supported commands are ispell and aspell. The special value "auto" means that TkRat will look for both aspell and ispell.} de {Befehlszeile fr das Programm zur Rechtschreibprfung. Es werden sowohl ispell als auch aspell untersttzt. Wird dieses Feld auf den Wert "auto" gesetzt, so sucht TkRat selbst nach beiden Programmen.} label pref_date_format en {This variable controls how times are formatted in the list of messages. The following substitutions can be used: %a - abbreviated weekday name %A - full weekday name %b - abbreviated month name %B - full month name %c - default date format for the current locale %d - day of month (01-31) %e - day of month ( 1-31) %H - hour as 24-hour clock (00-23) %I - hour as 12-hour clock (01-12) %m - month number (01-12) %M - minute (00-59) %p - 'AM' or 'PM' %S - second (00-60) %V - week number %x - default date format without time %X - default time format %y - year without century (00-99) %Y - year including century } sv {Denna variabel styr hur tider skrivs ut i listan ver brev. Fljande variabler kan anvndas: %a - frkortat namn p veckodag %A - namn p veckodag %b - frkortat mnadsnamn %B - namn p mnad %c - standardformat fr tider %d - dag i mnaden (01-31) %e - dag i mnaden ( 1-31) %H - timme som 24-timmars klocka (00-23) %I - timme som 12-timmars klocka (01-12) %m - mnadsnummer (01-12) %M - minut (00-59) %p - 'AM' eller 'PM' %S - sekund (00-60) %V - veckonummer %x - standardformat fr datum %X - standardformat fr tid %y - r utan sekel (00-99) %Y - r inklusive sekel } de {Hier legen Sie das Datumsformat fr die Liste der Nachrichten fest. Die folgenden Ersetzungen werden dabei vorgenommen: %a - abgekrzter Wochentag %A - vollstndiger Wochentag %b - abgekrzter Monatsname %B - vollstndiger Monatsname %c - Datumsformat entsprechend den lokalen Prferenzen %d - Tag im Monat (01-31) %e - Tag im Monat ( 1-31) %H - Stunde im 24-Stunden Format (00-23) %I - Stunde im 12-Stunden Format (01-12) %m - Monat als Zahl (01-12) %M - Minuten (00-59) %p - 'AM' (vormittags) oder 'PM' (nachmittags) %S - Sekunden (00-60) %V - Kalenderwoche %x - Standard Datumsformat ohne Zeitangabe %X - Standard Datumsformat %y - Jahr ohne Jahrhundert (00-99) %Y - Jahr inklusive Jahrhundert } label pref_tnef sv {Vilket tnef-kommando som skall anvndas (kan inkludera full skvg). Detta kommando anvnds fr att avkoda winmail.dat-bilagor.} en {Tnef command to use (may include full path). This command is used to decode winmail.dat attachments.} de {Die Befehlszeile fr das tnef Programm zum Anzeigen von Anhngen im winmail.dat Format. Die Befehlszeile kann den vollstndigen Pfadnamen des Programms enthalten.} label find_prev sv {Hitta tidigare} en {Find previous} de {Rckwrts suchen.} label pref_always_editor sv {Om denna instllning r sann s kommer alltid den frsta externa editorn att anvndas.} en {If true, the first external editor will always be used to edit messages} #de {Mit dieser Option legen Sie fest, ob der externe Editor immer #zum Schreiben von Nachrichten verwendet werden soll.} #fr {Si vrai, l'diteur externe sera toujours #utilis pour rdiger de nouveaux messages.} #sr {Ako je potvreno, za rad sa pismima #e se uvek koristiti spoljni editor} #pl {Jeli tak, edytor zewntrzny bdzie uywany zawsze} #pt {Se seleccionado, o editor externo ser sempre #utilizado para redigir novas mensagens} label pgp_err sv {Misslyckades att verifiera signaturen} en {Failed to verify the signature} label pgp_abort sv {PGP operationen avbrts. Tryck fr att frska igen} en {PGP operation was aborted. Press to retry} label dbinfo en {Show information about how the message(s) are stored in the database} sv {Visa information om hur meddelandet r sparat i databasen} label reset en {Reset to default values} sv {terstll till standardvrden} label dbinfo_apply_msg en {Apply these values to the current message} sv {Applicera dessa vrden p det aktuella meddelandet} label dbinfo_apply_folder en {Apply these values to the current group of messages} sv {Applicera dessa vrden p den nuvarande gruppen av brev} label dbinfo_apply_first_msg en {Apply these values to the current group of messages} sv {Applicera dessa vrden p den nuvarande gruppen av brev} tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/changes.text000066400000000000000000000701561137544547100224040ustar00rootroot00000000000000################################################################# # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssen # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # variable changes label welcome_title sv {Introduktion till TkRat} en {Introduction to TkRat} de {Willkommen bei TkRat} it {Introduzione a TkRat} fr {Introduction TkRat} sr {Uvod u TkRat} pl {Wprowadzenie do TkRat beta} pt {Introduo ao TkRat} label language sv Sprk: en Language: de Sprache: it Lingua: fr {Langage :} sr Jezik: pl Jzyk: pt Lngua: label show_changes sv ndringsmeddelanden: en {Changes messages:} de {Mitteilungen ber nderungen:} it {Messaggi cambiamenti:} fr {Message de changement :} sr {Poruke o promenama:} pl {Zmie wiadomoci:} pt {Mensagens de mudanas:} label show sv Visa en {Do show} de Anzeigen it Mostra fr Montrer. sr Prikai pl Poka pt Mostrar label dont_show sv {Visa inte} en {Do not show} de {Nicht anzeigen} it {Non mostrare} fr {Ne pas montrer.} sr {Ne prikazuj} pl {Nie pokazuj} pt {No mostrar} label continue sv Fortstt en Continue de Weiter it Continua fr Continuer sr Dalje pl Dalej pt Continua label welcome sv { Vlkommen till TkRat Nedan s hittar du tv stycken valknappar. Den frsta lter dig vlja sprk som programmet anvnder. I fortsttningen s kommer du att f information om alla ndringar i TkRat den frsta gngen du startar en ny version. Om du inte vill ha den informationen s kan den stngs av med hjlp av den andra knappen nedan. Knappen mrkt 'Fortstt' tar bort detta fnster och startar sjlva TkRat. EGENSKAPER TkRat r ett grafisk epostprogram (MUA (Mail User Agent)) som kan hantera MIME. TkRat r huvudsakligen skrivet i C men anvndargrnssnittet r gjort i tcl/tk. Nedan fljer en icke komplett lista ver programmets egenskaper: * Flersprkigt grnssnitt * Stdjer MIME Frstr inkommande text/plain, image/gif och message/rfc822 brev- typer. Dessa avkodas (om Quoted-printable eller Base64) och visas om teckensetet r kompatibelt. Av multipart s hanteras multipart/ mixed och multipart/alternative, alla andra multipartbrev hanteras som mixed. Alla andra content-types visas som en ikon och kan antingen kras genom motsvarande mailcap-kommando, ses som de r eller sparas till fil. * Stdjer MIME i huvudraderna Inkommande huvudrader avkodas s mycket som teckensetet tillter och utgende huvudrader kodas. * Skriva nya brev Brev skrivs i den inkluderad texteditorn (tk's text widget plus mnga tillgg) eller valfri extern editor. Man kan skicka med bilagor. * Databas Brev kan lggas in i en databas. Nr man lgger in breven s kan man specificera nykelord expireringstider och vad som skall hnda nr brevet expirerar. Intern s sparas breven som enkla textfiler. * Virtuella mappar En virtuell mapp r ett namn som man har satt p antingen en vanlig mapp (mbox, mh, IMAP eller POP) eller ett skuttryck i databasen. Anvndaren kan definiera en menystruktur som innehller alla de virtuella mapparna och kan sedan anvnda den menyn till att ppna mappar eller flytta brev till mappar. * Lagra brev Man kan avbryta komponerandet av ett nytt brev och spara det ofrdiga brevet till senare. Man kan sedan fortstta skrivandet senare. Man kan avsluta TkRat emellan editeringstillfllena. Flera brev kan vara lagrade samtidigt. * Vktare Nr programmet r ikonifierat s kontrolleras den aktiva mappen regelbundet. Om ett nytt brev anlnder s poppas ett litet fnster som innehller en lista ver alla breven (eller bara de nya) in den aktiva mappen upp. Anvndaren kan d antingen trycka p hger musknapp i fnstret fr att f det att frsvinna och fortstta vnta p nya brev. Eller trycka p vnster musknapp fr att f upp huvudfnstret. * Grnssnitt till resten av epost-vrlden TkRat frstr fr tillfllet unix brevldor, mh, POP och IMAP. Brev skickas mha SMTP eller valfritt program (tex sendmail). * Stdjer Leveransstatus TkRat stdjer den helt nya DSN (Delivery Status Notifications) standarden. Denna lter dig veta om ditt brev har levererats till sin destination. Detta krver dock en MTA som klarar DSN extensionen till ESMTP (fr tillfllet s r sendmail-8.7 eller senare den enda som jag knner till som klarar det). /MaF } en { Welcome to TkRat Below, you find two option buttons. The first lets you select the language of the user interface. The button below that lets you disable those changes messages that will appear whenever you start a new version of TkRat. Probably you want to see them, but the choice is yours. The 'Continue' button removes this window and starts the real TkRat program. FEATURES TkRat is a graphical Mail User Agent (MUA) which handles MIME. It is mainly written in C but the user interface is done in tcl/tk. The following is a non-exhaustive list of the capabilities: * Multilingual interface * MIME support Understands incoming text/plain, image/gif and message/rfc822 content-types. These are decoded (if Quoted-printable or Base64) and shown (if the charset is compatible). Of multipart, the multipart/mixed and multipart/alternate types are handled, all other multiparts are treated as mixed. All other content types are represented by an icon and can be either run through an appropriate mailcap command, viewed as is or saved to a file. * Supports MIME in headers Incoming header lines are decoded as much as the character set will allow and outgoing headers are encoded. * Composing Messages are composed with the built-in editor (tk's text widget plus many extensions) or an external editor of your choice. You can attach files to your message. * Message database Messages can be inserted into a database. When inserting, you add keywords, expiration time, and what to do when the expiration time is reached. Internally, the messages are stored as flat text files. * Virtual folders A Virtual folder is a name that has been set on an ordinary folder (mbox, mh, IMAP or POP3) or a database search expression. The user can define a menu structure which holds all the Virtual folders and can then move messages to a folder or open a folder via the menu. * Message hold You can suspend the composing of a message by putting the message on hold. The composing can then be continued at a later time. You can stop the program in the meantime. Multiple messages can be on hold at the same time. * Watcher When the program is iconified, it checks the current mailbox regularly. If a new message arrives, a small window displaying a list of all messages (or only the new ones) in the mailbox is opened. The user can then either press the right mousebutton in this window to make it go away and continue watching for new messages, or press the left mousebutton to make the Watcher window go away and the main window to de-iconify. * Interface to the rest of the mail world The program currently understands UNIX mailboxes, POP3, IMAP and mh folders. Messages are sent via SMTP or any user-configured program (for example, Sendmail). * Supports Delivery Status Notifications TkRat supports the brand new DSN standard. This lets you see if your message arrived safely at the destination. Using this feature requires an MTA that handles the DSN ESMTP extension (currently, the only one I know of is Sendmail-8.7 or later). /MaF } de { Willkommen bei TkRat Unterhalb dieses Textes finden Sie zwei Einstellmglichkeiten. Mit der ersten legen Sie die Sprache der Oberflche fest. Die zweite Auswahlliste legt fest, ob Sie in Zukunft ber nderungen in TkRat informiert werden, wenn Sie eine neue Version starten. Ich nehme an, dass Sie diese Meldungen sehen mchten, aber Sie haben die Wahl. Wenn Sie den Schalter "Weiter" auswhlen, wird das eigent- liche TkRat Programm gestartet. FUNKTIONEN TkRat ist ein MIME-fhiges e-mail Programm (Mail User Agent) mit grafischer Oberflche. TkRat ist im wesentlichen in C implemen- tiert, die Oberflche ist jedoch in Tcl/Tk realisiert. Die fol- gende Liste der Funktionen ist nicht erschpfend: * Die Oberflche untersttzt verschiedene Sprachen. * MIME Untersttzung TkRat kann Nachrichten in den Formaten text/plain, image/gif und message/rfc822 empfangen. Die Nachrichten werden dekodiert (quoted-printable oder base64) und an- gezeigt (falls der Zeichensatz kompatibel ist). Die Inhaltstypen multipart/mixed und multipart/alternate werden untersttzt, alle anderen multipart-Formate wer- den als mixed behandelt. Fr alle anderen Inhaltstypen wird ein Symbol angezeigt. Diese knnen mit dem passen- den mailcap Programm verarbeitet, im Quelltext ange- zeigt oder in einer Datei gespeichert werden. * MIME Untersttzung fr Kopfzeilen MIME kodierte Kopfzeilen eingehender Nachrichten werden dekodiert, soweit der Zeichensatz dies zulsst. Ausge- hende Kopfzeilen werden kodiert. * Nachrichteneditor Abgehende Nachrichten werden entweder mit dem einge- bauten Editor (basierend auf dem Tk Text Widget) oder einem belibigen Editor ihrer Wahl erstellt. Beliebige Anhnge an eine Nachricht sind mglich. * Datenbank Nachrichten knnen in eine Datenbank bernommen werden. Beim Speichern in der Datenbank geben Sie Schlsselbe- griffe und ein Ablauf-Datum fr die Speicherung an. Weiterhin knnen Sie angeben, was nach Ablauf des Datums mit der Nachricht geschehen soll. Intern werden die Nachrichten als einfache Textdateien gehalten. * Virtuelle Ordner Ein virtueller Ordner bildet entweder ein normales Postfach (mbox, mh, IMAP oder POP) oder eine Anfrage an die TkRat-Datenbank ab. Sie knnen eine Baumstruk- tur fr diese virtuellen Ordner festlegen. Nachrich- ten knnen innerhalb dieses Baumes verschoben werden. * Nachrichten als Entwurf halten Sie knnen das Schreiben einer Nachricht unterbrechen und diese Nachricht zum spteren Bearbeiten als Entwurf ablegen. Diese Entwrfe stehen auch nach dem Beenden und Neustart von TkRat noch zur Verfgung. Beliebig viele Nachrichten knnen als Entwurf gehalten werden. * Wchter Wenn Sie TkRat ikonifiziert haben, prft es weiterhin regelmig den aktuellen Ordner. Wenn neue Nachrich- ten eintreffen, ffnet TkRat ein kleines Fenster mit einer Liste aller (oder nur der neuen) Nachrichten in diesem Ordner. Sie knnen dann entweder das Fenster mit der rechten Maustaste wegklicken oder mit einem Klick der linken Maustaste das Ordnerfenster von TkRat wiederherstellen. * Zusammenarbeit mit dem Rest der e-mail Welt TkRat verarbeitet zur Zeit UNIX Mailboxen, POP, IMAP und mh Ordner. Abgehende Nachrichten werden via SMTP oder einem beliebigen, vom Benutzer festgeleg- ten Programm verschickt (z. B. sendmail). * Besttigungen TkRat untersttzt DSN Betsttigungen (Delivery Status Notifications). Damit knnen Sie feststellen, ob ihre Nachrichten beim Empfnger ankommen. Dafr ist es er- forderlich, dass der Server zum Versenden der Nach- richten (Mail Transfer Agent) die DSN-Erweiterungen des ESMTP untersttzt. /MaF } it { Benvenuto in TkRat beta CARATTERISTICHE TkRat un programma per la gestione dell'email in modo grafico che riconosce il formato MIME. E' scritto principalmente in C, ma l'interfaccia stata realizzata con tcl/tk. Qui di seguito vengono elencate alcune delle caratteristiche salienti: * Interfaccia multilingue * Supporto per MIME Decodifica e mostra messaggi di tipo text/plain, image/gif and message/rfc822. Dei tipi multipart, multipart/alternate viene riconosciuto in modo nativo. Tutti gli altri tipi sono trattati come mixed. I tipi non riconosciuti vengono rappresentati da un'icona e possono essere visualizzati come testo o salvati in un file. * Supporto per MIME nelle intestazioni Le intestazioni dei messaggi in arrivo vengono decodificate secondo le capacit del gruppo di caratteri. Le intestazioni dei messaggi in uscita vengono codificate. * Composizione I messaggi possono essere composti con l'editor integrato o con un editor esterno a scelta. E' possibile allegare file al messaggio. * Database di messaggi I messaggi possono essere inseriti in un database. Quando vengono inseriti possibile indicare parole chiave, data di scadenza e cosa fare in caso di scadenza. Internamente i messaggi vengono salvati in formato testo. * Cartelle virtuali Una cartella virtuale un nome che viene dato ad una cartella normale (mbox, IMAP o POP) o ad una chiave di ricerca database. E' possibile salvare messaggi in una cartella virtuale. L'utente pu definire una struttura di menu che contiene tutte le cartelle virtuali. * Messaggi mantenuti E' possibile sospendere la composizione di un messaggio. La composizione pu essere continuata in seguito. I messaggi vengono mantenuti anche quando si esce dal programma. E' possibile mantenere pi di un messaggio alla volta. * Controllo Quando il programma iconizzato, controlla la cartella corrente a intervalli regolari. Se un nuovo messaggio arriva, allora una piccola finestra contenente la lista di nuovi messaggi appare. L'utente pu premere il pulsante destro del mouse nella finestra per farla sparire, oppure pu premere il pulsante sinistro per aprire il programma e leggere il messaggio. * Interfaccia con vari formati email Il programma riconosce caselle unix, POP e IMAP. I messaggi vengono spediti usando SMTP o qualsiasi altro programma definito dall'utente. * Supporto per notifica consegne TkRat supporta il nuovo standard DSN, che permette di sapere se un messaggio giunto a destinazione. Questa funzione richiede un MTA che riconosca l'estensione EMSTP DSN (al momento solo sendmail 8.7). /MaF } fr { Bienvenue ! Le premier des deux boutons d'options ci dessous vous permet de choisir le langage utilis par l'interface de TkRat. Le second permet de dsactiver l'apparition de messages vous informant des amliorations des nouvelles versions lorsque celles ci seront installes. Le bouton 'continuer' fera disparatre cette fentre et dmarre le logiciel TkRat. FONCTIONNALITS TkRat est un programme graphique de gestion de couriel (MUA). TkRat comprend l'extension MIME du couriel. Il est crit principalement crit en C, et l'interface utilisateur est ralis avec Tcl/Tk. Voici une liste (non exhaustive) de ses capacits. * Interface multilingue Pour le moment, TkRat existe en anglais, sudois, italien et franais. Il est ais d'ajouter d'autres langues. * Dcodage MIME TkRat comprend naturellement les types text/plain, image/gif et message/rfc822. Ces trois types sont dcods (s'ils ont t cods en Quoted-printable ou Base64) et affichs si le jeu de caractre est compatible. Quant au "multipart", seuls les multipart/mixed et multipart/alternate sont traits. Les autres types sont reprsents par une icne et peuvent au choix tre sauvs dans un fichier, affichs tels quels (bon courage), ou passs au travers d'un filtre ou d'une commande "mailcap" approprie. * MIME dans les enttes Les enttes des messages arrivant sont dcodes si le jeu de caractre le permet. Les enttes des messages expdis sont encodes. * Rdaction des messages Les messages se rdigent l'aide d'un diteur intgr (le widget text de Tk muni de nombreuses extensions) ou d'un diteur externe de de votre choix. Il est possible de joindre des fichiers vos messages. * Base de donnes de messages Les messages peuvent tre transfrs dans une bote lettres de type base de donnes. Il faut alors leur attribuer des mots-cls, une date d'expiration, et spcifier que faire du message lorsqu'il expire. Dans ces bases de donnes, les messages restent stocks en interne sous forme d'un simple fichier texte. * Bote lettres virtuelle Une bote lettres virtuelle est un nom qui est attach une bote lettres classique (fichier, MH, POP, ou IMAP) ou une requte dans la base de donnes. Il est possible de dfinir un menu regroupant toutes les botes aux lettres virtuelles. Ce menu peut tre ensuite utilis pour dplacer des messages ou ouvrir les botes. * Suspension de la rdaction Il est possible de suspendre la rdaction d'un ou plusieurs messages et de la reprendre plus tard, mme d'une session TkRat une autre. * Surveillance Lorsque le programme est sous forme d'icne, il vrifie priodiquement dans la bote lettres courante la prsence de nouveaux messages. Lorsqu'un nouveau message arrive, une nouvelle petite fentre apparat. Elle contient (au choix) la liste de tous les messages ou juste celle des nouveaux messages. En cliquant dans cette fentre, on peut alors soit la faire disparatre (jusqu'au prochain nouveau message) l'aide du bouton3, soit rappeler (d-icnifier) la fentre principale de TkRat l'aide du bouton1. * Interface avec l'ensemble du systme de mail TkRat sait lire les botes lettres Unix, MH, IMAP, POP. Il peut expdier les messages par SMTP ou l'aide d'un programme du choix de l'utilisateur (sendmail par exemple) * Rapport de transmission (DSN) TkRat supporte le tout nouveau standard DSN. Ceci permet de savoir si un message est arriv jusqu' la machine de destination. Pour que cela fonctionne il faut un MTA qui reconnaisse l'extension ESMTP DSN. (actuellement seul sendmail 8.7 et + ) /MaF } sr { Dobrodoli u TkRat! Ispod se nalaze dva dugmeta. Prvo vam omoguava da izaberete jezik korisnikog interfejsa. Dugme ispod njega dozvoljava da uklonite prikaz poruka o izmenama svaki put kada pokrenete novu verziju TkRat-a. Moda bi valjalo da ih vidite, ali izbor je Va. Dugme 'Dalje' uklanja ovaj prozor i pokree glavni TkRat program. KARAKTERISTIKE TkRat je grafiki MUA (Mail User Agent - korisniki program za potu) koji podrava MIME. Uglavnom je napisan u C-u, ali je korisniki interfejs izveden u TCL/TK. Sledi nekompletan spisak njegovih mogunosti: * Viejeziki interfejs * Podrka za MIME TkRat razume ove tipove sadraja: text/plain, image/gif i message/rfc822. Oni se dekoduju (ako su Quoted-printable ili Base64) i prikazuju (ukoliko je karakter-set kompatibilan). Od viedelnih tipova su podrani multipart/mixed i multipart/alternate, a svi ostali se tretiraju kao mixed. Svi drugi tipovi su prikazani ikonicom i mogu se propustiti kroz odgovarajuu mailcap komandu, pogledati ili snimiti u datoteku. * Podrka za MIME u zaglavljima Dolazea zaglavlja se dekoduju koliko god karakter-set dozvoljava, a odlazea zaglavlja enkoduju. * Sastavljanje Pisma se sastavljaju pomou ugraenog editora (TK-ov text-widget sa mnogo ekstenzija) ili spoljnim editorom po Vaem izboru. Uz pisma moete zakaiti dodatke. * Baza pisama Pisma se mogu ubaciti u bazu podataka. Pri tom se dodaju kljune rei, vreme isticanja i ta initi kada zadati period istekne. Interno se poruke uvaju u obliku obine tekstualne datoteke. * Virtuelni skupovi Virtuelni skup je ime koje je dato obinom skupu pisama (datoteka, MH, IMAP ili POP) ili izrazu za pretragu baze. Korisnik moe da definie strukturu menija koja okuplja sve virtuelne skupove i da potom premeta pisma iz jednog skupa u drugi putem menija. * Zadravanje pisama Sastavljanje pisma se moe prekinuti ("zadrati"), pa zatim kasnije nastaviti - u meuvremenu se ak moe prekinuti rad programa. U jednom trenutku vie pisama moe biti zadrano. * Nadzorni prozor Kada se ikonifikuje, program redovno proverava tekue potansko sandue. Ako stigne novo pismo, pojavljuje se mali prozor sa spiskom svih (ili upravo pristiglih) pisama. Korisnik moe ili da pritisne desni taster mia kako bi izbrisao ovaj prozor (program nastavlja da prati nova pisma), ili da pritiskom na levi taster ukloni mali prozor i deikonifikuje glavni panel TkRat-a. * Komunikacija sa ostatkom sveta elektronske pote Program trenutno razume Unix potanske datoteke (mailbox), POP, IMAP i MH skupove (foldere). Pisma se alju preko SMTP-a ili konfigurisanog programa (kao to je sendmail). * Podrka za DSN (Delivery Status Notifications - poruke o dostavljanju) TkRat podrava novi DSN standard koji Vam omoguava da vidite da li je Vae pismo stiglo do svog odredita. Ovo zahteva MTA (Mail Transfer Agent, program za dostavljanje pote) koji radi sa DSN ESMTP proirenjima (jedini za koji ja trenutno znam jeste sendmail-8.7 ili noviji). /MaF } pl { Witamy w TkRat Poniej znajdziesz dwa przyciski. Pierwszy pozwala wybra jzyk interfejsu uytkownika! Niszy przycisk wyczy wywietlanie informacji o zmianach po kadym uruchomieniu TkRata. Pewnie chciaby je przeczyta, ale wybr naley do ciebie... Przycisk 'Kontynuuj' uruchomi TkRata. NOWOSCI TkRat jest graficznym klientem pocztowym (MUA), ktry obsuguje MIME. Jest napisany gwnie w C, ale interfejs uytkownika zrobiono w tcl/tk. Poniej jest czciowa lista moliwoci: * Wielojzyczny interfejs Obecnie dostpny jest jzyk Angielski, Szwedzki i Woski, ale nie jest trudno doda nowych jzykw. * Obsuga MIME Program rozumie typy text/plain, image/gif i message/rfc822. S one dekodowane (jeli Quoted-printable lub Base64) i wywietlane (jeli zgadza si kodowanie). Z wieloczciowych (multipart) obsugiwane s multipart/mixed i multipart/alternate, wszystkie pozostae s traktowane jako mixed. Wszystkie pozostae s reprezentowane przez ikony i mog by otwarte przez odpowiednie polecenie mailcap, przegldane, lub zapisane do pliku. * Obsuga MIME w nagwkach Linie nadchodzcego nagwka s dekodowane na tyle, na ile pozwala na to strona kodowa, a wychodzce nagwki s kodowane. * Edycja Wiadomoci s tworzone przy pomoc wbudowanego edytora (widget text z Tk i wiele rnych rozszerze) lub zewntrznego programu, ktry moesz wybra. Moesz dodawa zaczniki do listw. * Baza danych wiadomoci Wiadomoci mog by dodawane do bazy danych. Kiedy je dodajesz, ustalasz sowa kluczowe, dat wanoci i co zrobi, gdy wiadomo utraci dat wanoci. Wewntrznie wiadomoci s przechowywane jako paskie pliki tekstowe. * Wirtualne foldery Wirtualny folder to nazwa przyporzdkowana rzeczywistemu folderowi (mbox, mh, IMAP lub POP) lub wyraeniu do wyszukiwania w bazie danych. Uytkownik moe zdefiniowa struktur menu, ktra pomieci wszystkie wirtualne foldery i umoliwi prznoszenie wiadomoci do innych folderw lub otwierania folderw przez to menu. * Wiadomoci przechowywane Moesz przerwa edycj wiadomoci, przenoszc j do przechowalni. Edycja moe by wznowiona puniej. Moesz zamkn program w dowolnym momencie. Wiele wiadomoci moe by przechowywanych w ten sposb. * Obserwator Kiedy program jest zikonifikowany, regularnie sprawdza skrzynk poczty przychodzcej. W razie nadejcia nowyc wiadomoci otwierane jest mae okienko ze wszystkimi listami (lub tymi nowymi) w skrzynce. Uytkownik moe (przy pomocy prawego przycisku myszy) zamknc okno i czeka na inne nowe listy, lub (naciskajc lewy) otworzy gwne okno programu. * Interfejs do reszty skrzynek na wiecie Program obecnie potrafi obsugiwa skrzynki uniksowe, POP, IMAP i foldery mh. Wiadomoci s wysyane przez SMTP lub dowolny program zkonfigurowany przez uytkownika (np. sendmail). * Obsuga Zawiadamiania o Stanie Przesyki (DSN) TkRat obsuguje cakiem nowy standard DSN (Delivery Status Notifications). Pozwala on sprawdzi, czy wiadomoci dotary do celu przeznaczenia. Wymaga to jednak MTA obsugujcego rozszeenia DSN ESMTP (obecnie, z tego co wiem, tylko sendmail-8.7 i pniejsze). /MaF } pt { Benvindo ao TkRat O primeiro dos dois botes de opo a seguir permite-lhe selectionar a lngua da interface do utilizador. O segundo boto permite-lhe desactivar a aparecimento de mensagens informativas cada vez que iniciar uma nova verso do TkRat. O boto 'Continuar' fechar esta janela e dar incio ao programa propriamente dito. CARACTERSTICAS O TkRat um programa de e-mail (MUA - Mail User Agent) com interface grfica e suporte MIME. Grande parte do programa usa a linguagem C mas a interface grfica usa tcl/tk. Uma listagem no exaustiva das possibilidades do programa apresentada de seguida: * Interface em diversos idiomas * Suporte MIME Compreende os tipos text/plain, image/gif e mensagem rfc/822. Caso estes estejam codificados em 'Base64' ou 'Quoted-printable', so descodificados e mostrados (se o conjunto de caracteres for compatvel. Anexos multipart/mixed e multipart/alternate so igualmente processados, todos os outros so representados por um cone e podem ser alternativamente gravados em ficheiro ou passados para o comando 'mailcap' apropriado, caso exista. * MIME nos cabealhos da mensagem Linhas de cabealho de mensagens recebidas so descodificadas caso o conjunto de caracteres o permita. Os cabealhos das mensagens enviadas so sempre codificados. * Redaco As mensagens a enviar so compostas num editor interno (widget de texto do toolkit, ao qual se juntaram inmeras extenses) ou alternativamente num editor da escolha do utilizador. igualmente possvel anexar ficheiros a uma mensagem a enviar. * Base de dados de mensagens possvel inserir mensagens numa base de dados interna. Aquando da insero na base de dados requerido ao utilizador que fornea palavras chave, data de vencimento e/ou que aco operar quando esta data for atingida. Internamente as mensagens so armazenadas como ficheiros de texto. * Pastas de correio virtuais Um pasta virtual um nome que simplesmente est associado a uma pasta de correio comum (tipos mbox, mh, IMAP ou POP) ou a uma expresso de busca na base de dados. O utilizador pode definir uma estrutura em menu que agrupa todas as pastas virtuais e atravs do qual pode mover mensagens, abrir pastas, etc. * Suspenso da redaco Durante a composio de uma mensagem possvel suspend-la e continu-la mais tarde. possvel manter vrias mensagens suspensas em simultneo. O programa pode entretanto ser interrompido sendo as mensagens suspensas sempre preservadas. * Watcher (Vigia) Neste modo de funcionamento, quando o programa est na sua forma iconificada, a pasta de correio verificada periodicamente. Aps a chegada de uma nova mensagem, aberta uma pequena janela com uma lista das mensagens. O utilizador pode ento faz-la desaparecer pressionando o boto direito do rato sobre essa pequena janela e continuar a vigiar a chegada de novas mensagens, ou pressionar o boto esquerdo, o que causar o aparecimento da janela principal. * Interface com outros sistemas de e-mail At ao momento, o programa compreende pastas Unix, POP, IMAP e mh. As mensagens so enviadas via SMTP (Simple Mail Transfer Protocol) ou alternativamente atravs de um programa configurado pelo utilizador. (por exemplo 'sendmail'.) * Notificaes de transmisso O programa inclui suporte para o novo standard DSN (Delivery Status Notifications.) Isto permite ao utilizador verificar se uma mensagem por si enviada chegou ao destino. No entanto necessrio dispor de um MTA (Mail Transfer Agent) que suporte a extenso DSN ESMTP (por exemplo sendmail 8.7 e posteriores.) /MaF } tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/defs.tcl000066400000000000000000000030521137544547100215020ustar00rootroot00000000000000########################################################################### # List of languages # # Each language is represented by a list of three elements. The first is # # the shorthand name of the language (this is the name which is used in # # all the structures, filenames and procedures. The second is the name of # # the language (in the language itself) (this will be used in menus etc). # # The last element is the character set that should be used for this # # language. # ########################################################################### set languages { {sv Svenska iso8859-1} {en English ascii} {de Deutsch iso8859-1} {it Italiano iso8859-1} {fr Franais iso8859-1} {sr Srpski iso8859-2} {pl Polski iso8859-2} {pt Portugus iso8859-1}} # The following message is inserted at the top of every generated file set message {################################################################# # DO NOT EDIT! # # This file is automatically generated from files in the Text/ subdirectory # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssen # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # } # The following message is inserted at the end of every generated file set trailer {# # DO NOT EDIT! # This file is automatically generated from files in the Text/ subdirectory ############################################################################## } tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/dotext.tcl000077500000000000000000000071161137544547100221000ustar00rootroot00000000000000# dotext.tcl # # See README for information about what this program does. # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Parse arguments set warn 1 foreach a $argv { switch -exact -- $a { -nowarn { set warn 0 } -warn { set warn 1 } default { puts "Usage: $argv0 \[-nowarn\]" exit 1 } } } # Check tcl version if {[info tclversion] < 8.1} { puts "This script requires tclsh8.1 or later" exit 1 } # Directory where we should store the output files: set outdir ../.messages source defs.tcl # First we should create the language procedures foreach l $languages { set lang [lindex $l 0] proc $lang m "addmsg $lang \$m" set lang_name($lang) [lindex $l 1] } # variable -- # # Sets the variable name that the messages from this file will end up in. # # Arguments: # name - The variable name proc variable {name} { global varName set varName $name } # label -- # # Sets the label to be used for all subsequent language commands, that # is until the next call to this function. # # Arguments: # l - The label proc label {l} { global lab labels if { -1 != [lsearch -exact $labels $l]} { puts "*** Label '$l' is already defined" exit } lappend labels $l set lab $l } # addmsg -- # # Add a text string # # Arguments: # lang - The language # m - The message proc addmsg {lang m} { global lab text lang_name if {[info exists text($lang,$lab)]} { puts "Multiple definitions of $lab in $lang_name($lang)" } set text($lang,$lab) $m } # Now we should build the languages.tcl file, we start by definig the # procedures. set getLanguages "proc GetLanguages {} {return \"$languages\"}" set initMessages { # InitMessages -- # # Initializes a set of messages. # # Arguments: # lang - The language if the messages # var - The variable the messages are in proc InitMessages {lang var} { upvar \#0 currentLanguage_$var currentLanguage global ratCurrent set currentLanguage $lang set oldCharset $ratCurrent(charset) set ratCurrent(charset) utf-8 eval init_${var}_${lang} set ratCurrent(charset) $oldCharset } } set fh [open $outdir/languages.tcl w] puts $fh $message puts $fh "" puts $fh $getLanguages puts $fh "" puts $fh $initMessages close $fh # Source the text input files and write the apropriate output file after # each input file is read. foreach f [glob *.text] { set labels {} # Read data set fh [open $f r] fconfigure $fh -encoding binary eval [read $fh] close $fh # Write datafiles foreach lang $languages { set l [lindex $lang 0] set fh [open $outdir/text_${varName}_${l}.tcl w] fconfigure $fh -encoding binary puts $fh $message puts $fh "" puts $fh "# The following is the function which does the actual work" puts $fh "proc init_${varName}_${l} {} {" puts $fh "global $varName" foreach n $labels { if {[info exists text($l,$n)]} { set rl $l } else { if {$warn} { puts "Text $n not found in [lindex $lang 1] substituting English" } set rl en } puts $fh "set ${varName}($n) [list $text($rl,$n)]" } puts $fh "}" puts $fh $trailer close $fh # Convert datafile from local encding to utf-8 set fh [open $outdir/text_${varName}_${l}.tcl r] fconfigure $fh -encoding [lindex $lang 2] set data [read $fh] close $fh set fh [open $outdir/text_${varName}_${l}.tcl w] fconfigure $fh -encoding utf-8 puts $fh $data close $fh } catch {unset labels} catch {unset text} } tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/features.text000066400000000000000000000145471137544547100226140ustar00rootroot00000000000000################################################################# # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssen # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # variable features # After 2.1 label 1 en {There is a new mechanism to inform about new features. Added support for Mozilla & Galeon as external web-browsers. Users can now select how URLs are opened in browsers.} de {Neuerungen im Programm werden mit einer neuen Methode bekannt gemacht. Die Web-Browser Mozilla und Galeon werden untersttzt. Benutzer knnen jetzt festlegen, wie eine URL im Browser geladen wird.} sv {Det finns en ny mekanism fr att informera om nya egenskaper. Lade till std fr Mozilla & Galeon som externa weblsare. Anvndare kan nu vlja hur URLer ppnas i weblsarna.} fr {Nouveau mcanisme pour informer l'utilisateur des nouvelles fonctionnalits. Ajout du support pour les navigateurs externes Galeon et Mozilla Possibilit de choisir comment ouvrir les URL dans les navigateurs.} label 2 en {Added support for SMTP authentication.} de {SMTP mit Anmeldung beim Server (AUTH) wird untersttzt.} sv {Lade in std fr SMTP-autentisering.} fr {Support de l'authentification SMTP.} label 3 en {There is now a wizard to help defining new folders.} de {Beim Anlegen neuer Ordner hilft jetzt ein Assistent.} sv {Det finns nu en hjlpreda fr att hjlpa till att definiera nya mappar.} fr {Il y a maintenant un assistant de cration de botes lettres.} label 4 en {Implemented a "First use wizard" to help first-time users to get\ set up.} de {Neuen Benutzern hilft jetzt ein Assistent bei den ersten Schritten.} sv {Implementerade en Frstagngshjlpreda fr att hjlpa dem som kr\ TkRat fr frsta gngen att komma igg.} fr {Implment un assistant de premire utilisation pour aider la\ configuration lors de la premire utilisation.} label 5 en {You can now use shift-left-mousebutton or middle mousebutton to\ flag messages.} de {Nachrichten knnen jetzt mit der mittleren Maustaste oder bei\ gehaltener Umschalttaste mit der linken Maustaste markiert werden.} sv {Man kan nu anvnda shift-vnster-musknapp eller mittenknappen fr\ att markera meddelanden.} label 6 en {It is now possible to specify PGP keys and default actions for\ address book entries.} sv {Det r nu mjligt att ange PGP-nyckel och standardinstllning\ fr element i adressboken.} de {Fr Empfnger aus dem Adressbuch knnen jetzt PGP Schlssel\ und Voreinstellungen zum Verschlsseln und Unterschreiben festgelegt\ werden.} label 7 en {The database search window has been improved. It is now possible\ to specify a time interval to search within.} sv {Databasskningar har nu frbttrats avsevrt. Det r numera\ mjligt att ange ett tidsintervall att ska inom.} de {Die Mglichkeiten zur Suche in der Datenbank wurden erweitert.\ Es kann nun ein Datumsbereich zum Durchsuchen vorgegeben werden.} label 8 en {It is now possible to forward (separately or in one message) and\ bounce a group of messages.} sv {Det r nu mjligt att vidarebefodra (separat eller i ett brev)\ och studsa en grupp av meddelanden.} de {Eine Gruppe markierter Nachrichten kann nun (einzeln oder\ innerhalb einer einzigen Nachricht) weitergeleitet oder an einen\ neuen Empfnger umgeleitet werden.} label 9 en {Messages sent while in offline mode are placed in the "Outgoing"\ folder. There you can delete them or make changes by selecting\ "Continue composing" in the message menu.} sv {Brev som skickas medan du r nedkopplad lggs i "Utgende"-mappen. Dr kan du radera eller ndra brev genom att vlja "Fortstt skriv" i meddelandemenyn.} de {Nachrichten, die Sie im offline Modus absenden, werden im Ordner\ "Abgehende" gespeichert. Nachrichten in diesem Ordner knnen jetzt\ weiter bearbeitet oder auch gelscht werden.} label 10 en {Added in-line spelling checker to compose window. By default it\ will guess the language based on the first ten words you\ write. Misspelled words are underlined in red, right-click for\ replacements menu.} sv {Lade till stavningskontroll i "skriva brev"-fnstret. Normalt s\ gissar den sig till sprket baserat p de tio frsta orden du\ skriver. Felstavade ord rdmakeras, tryck hger musknapp fr en meny\ med alternativ.} de {Beim Schreiben einer Nachricht kann die Rechtschreibung geprft\ werden. In der Voreinstellung versucht TkRat anhand der ersten zehn\ Wrter die Sprache zu erraten. Rechtschreibfehler werden rot\ unterstrichen. Mit der rechten Maustaste lsst sich ein Men mit\ Korrekturvorschlgen anzeigen.} label 11 en {Now supports both aspell and ispell as spelling checker commands} sv {Stder nu bde ispell och aspell fr stavningskontroll} de {Sowohl ispell als auch aspell knnen zur Kontrolle der\ Rechtschreibung verwendet werden.} label 12 en {It is now possible to filter the list of messages} sv {Man kan nu filtrera listan av brev} de {Die Liste der Nachrichten in einem Ordner kann nun nach\ Suchbegriffen gefiltert werden.} label 13 en {There is now an address history list. It pops up automatically\ when you start writing an address and shows possible completions\ from the last 500 addresses you sent email to.} sv {Det finns nu en addresshistoria. Den dyker upp s fort man brjar\ skriva in en adress och visar mjliga adresser bland de senaste 500 du\ har skickat till.} de {Bei der Eingabe einer E-Mail Adresse steht jetzt eine Liste der\ zuletzt verwendeten Adressen zur Verfgung. Sobald Sie die Eingabe einer\ Adresse beginnen, erscheint eine Liste der mglichen Vervollstndigungen,\ welche aus den letzten 500 Adressen gebildet wird, an die E-Mails\ verschickt wurden.} label 14 en {It is now possible to change the way dates are formatted in the\ list of messages.} sv {Man kan nu ndra hur datum visas i brevlistan.} de {Das in der Nachrichtenliste verwendete Datumsformat kann\ jetzt eingestellt werden.} label 15 en {TkRat can now list and save the files contained in winmail.dat\ attachments (if the tnef command is installed).} sv {TkRat kan nu lista och spara filer som r packade i\ winmail.bat-bilagor (om kommandot tnef r installerat).} de {TkRat kann jetzt Anhnge im winmail.dat Format anzeigen und\ speichern, falls tnef installiert ist.} label 16 en {It is now possible to delete attchments from stored messages.} sv {Man kan nu radera bilagor frn lagradde meddelanden.} de {Anhnge an einer Nachricht lassen sich jetzt lschen.} tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/help.help000066400000000000000000000000001137544547100216450ustar00rootroot00000000000000tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/help.text000066400000000000000000003333761137544547100217320ustar00rootroot00000000000000################################################################# # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssen # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # variable help label title,intro sv Introduktion en Introduction de Einfhrung it Introduzione fr Introduction sr Uvod pl Wstp pt Introduo label intro sv { Hjlpsystem fr TkRat versikt TkRat r ett mailprogram (MUA efter Mail User Agent) avsett fr att lsa, skicka och spara elektronisk post. TkRat's huvudfnster r mappfnstret. Det r det fnstret som dyker upp nr du startar TkRat. I toppen av det hr fnstret finns en menyrad och en rad som beskriver den aktiva mappen. Nedanfr det finns en lista ver de brev som finns i den aktiva mappen. Du kan vlja ett brev i listan och se brevets innehll lngre ner. Nedanfr brevlistan kommer en statusrad och en rad med knappar. Nedanfr dem visas innehllet i det aktiva brevet. Innan du kan skicka ett brev mste du frst komponera det. Det finns flera stt att komma till kompositionsfnstret. Om du vill skapa ett nytt brev s trycker du p "Nytt brev" knappen. Om du vill svara p det aktiva brevet s trycker du p en av "Svara" knapparna. Normalt s visar mappfnstret den brevlda dr din inkommande post lagras (din INBOX). Nr du har lst (och eventuellt svarat p) ett brev s kan du vilja spara det i en annan mapp (fr att inte din INBOX skall svmma ver av gamla brev). Det finns flera olika typer av mappar som har olika egenskaper. Om det kommer ett nytt brev under tiden som programmet r ikonifierat s dyker ett litet vktarfnster upp. TkRat r ett mycket komplext program. Och trots att jag har frskt gra det enkelt att anvnda s syns komplexiteten p antalet saker som man kan stlla in. Allt skall ha bra vrden frn brjan. Mer information Information (p engelska) om den senaste versionen etc kan hitta p fljande URL: http://www.tkrat.org/ } en { Help system for TkRat Overview TkRat is a Mail User Agent (MUA) which is meant for reading, sending and managing electronic mail. The main window of TkRat is the Folder window. This is the window that appears when you start TkRat. At the top of this window is a menu bar and a line which describes the current folder. Below that is a listing of the messages in the folder, you can select messages in this list with the mouse to see the individual message below. Below the folder listing comes a message line and a row of buttons. Below that is the content of the current message shown. Before you can send a message, you must first compose (i.e. write) it. There are multiple ways to reach the Compose window. If you want to create a new message, you should press the Compose button. If you wish to reply to the current message, you should press one of the Reply buttons. By default, the Folder window shows your INBOX, that is the folder where your incoming mail is placed. When you have read (and perhaps replied to) a message, you might want to save it somewhere else (so as to not clutter up your INBOX). This is done by moving the message to another folder. There are many different types of folders with different properties. If a new message arrives when the program is iconified, a small Watcher window pops up. TkRat is a very complex program. Although I have tried to make it easy to use, the complexity shows in the number of different things you can customize. All the different options should have reasonable defaults. More information Information about the latest version, etc. can be found at the following URL: http://www.TkRat.org/ } de { Hilfe zu TkRat bersicht TkRat ist ein Mail User Agent (MUA), oft kurz "Mailer" genannt, zum Lesen, Versenden und Verwalten elektronischer Nachrichten (Email). Die wichtigsten Fenster von TkRat sind die Ordner-Fenster. Eines davon erscheint beim Start von TkRat. Am oberen Ende finden Sie eine Menleiste und eine einzeilige Beschreibung des angezeigten Ordners. Darunter befindet sich eine Liste der Nachrichten im Ordner. Um eine Nachricht im untersten Teil des Fensters anzuzeigen, klicken Sie sie mit der Maus in der Liste an. Zwischen Nachrichtenliste und aktueller Nachricht befinden sich eine Statuszeile und eine Reihe Schaltflchen. Es gibt mehrere Wege, ein Fenster zum Schreiben einer neuen Nachricht zu ffnen, insbesondere die "Schreiben" und "Antworten" Schaltflchen im Mittelteil. Nach dem Start zeigt TkRat Ihre INBOX, einen Ordner, den Sie als Ihren Haupt-Eingangsordner angegeben haben. Wenn TkRat ikonifiziert ist und neue Nachrichten in diesem Ordner eintreffen, ffnet der "Wchter" ein Fenster mit einem entsprechenden Hinweis. Gelesene und ggf. bearbeitete Nachrichten knnen in anderen Ordnern abgelegt werden. TkRat untersttzt eine groe Zahl von Ordner-Typen mit verschiedenen Eigenschaften. TkRat ist ein sehr komplexes Programm, und trotz all meiner Bemhungen um einfache Bedienbarkeit wird diese Komplexitt stellenweise sichtbar, insbesondere beim Konfigurieren. Die voreingestellten Werte sollten aber im Normalfall funktionieren. Weitere Informationen Informationen ber die aktuelle Version etc. sind unter der folgenden URL verfgbar: http://www.TkRat.org/ } it { Sistema d'aiuto per TkRat Introduzione TkRat e' un programma di posta elettronica (MUA da Mail User Agent), che pu gestire, ricevere e spedire messaggi email. La finestra principale di TkRat e' la "finestra cartella". Questa e' la finestra che appare all'avvio di TkRat. La barra dei menu e una linea di stato che descrive la cartella attiva si trovano in alto alla finestra. Al di sotto e' visualizzata la lista dei messaggi contenuti nella cartella, che possono essere selezionati con il mouse per vederne il contenuto nella parte inferiore della finestra. Sotto la lista dei messaggi c'e' un'altra linea di stato e una fila di bottoni. Infine, il contenuto del messaggio selezionato viene mostrato nella zona inferiore. Prima di spedire un messaggio e' necessario scriverlo. Ci sono vari modi per raggiungere la finestra di composizione. Se vuoi creare un nuovo messaggio, devi premere sul bottone Componi. Se invece vuoi rispondere al messaggio corrente, premi uno dei bottoni di risposta. Normalmente la finestra cartella mostra la tua INBOX, cioe' la cartella dove viene messa la posta in arrivo. Quando hai letto (e forse risposto) ad un messaggio, magari vuoi salvarlo da qualche altra parte, per mantenere "ordinata" la tua INBOX. Per fare ci bisogna muovere il messaggio in un'altra cartella. Se un nuovo messaggio arriva mentre il programma e' iconizzato, una piccola finestra di "osservazione" appare. TkRat e' un programma molto complesso. Nonostante abbia cercato di renderlo facile da usare, la complessit viene dimostrata dalle varie opzioni che e' possibile personalizzare. Tutte le varie opzioni dovrebbero avere valori standard "ragionevoli". Maggiori informazioni Informazioni riguardo la versione pi recente, ecc. si possono trovare all'URL http://www.TkRat.org/ } fr { Aide de TkRat Aperu TkRat est un MUA (Mail User Agent), ce qui signifie qu'il sert lire, expdier, et grer les messages lectroniques. La fentre principale de TkRat est la fentre des botes lettres. Cette fentre apparat au dmarrage du programme. Une barre de menus et une ligne de description de la bote lettres courante se trouvent en haut de cette fentre. En dessous, vous trouvez une liste des messages se trouvant dans la bote lettres. Vous pouvez en slectionner un par un clic de la souris pour voir son contenu apparatre dans la fentre dessous. Sous la liste se trouvent une barre d'tat et une ligne de boutons. Au-dessous, vous pouvez voir le contenu du message slectionn. Pour de plus amples renseignements sur la fentre bote lettres, slectionnez "Bote lettres" dans la liste des sujets. Avant d'expdier un message, il vous faut le crer. Il existe plusieurs faons de crer un message. Si vous voulez crer un nouveau message, cliquez sur le bouton "Nouveau message". Si vous voulez rpondre au message courant, cliquez sur "Rpondre". Par dfaut, la fentre bote lettres affiche le contenu de votre INBOX, c'est--dire la bote lettres o sont placs les messages arrivant. Aprs avoir lu un message, vous souhaiterez sans doute le classer ailleurs pour allger votre bote d'arrive. Cela s'accomplit en dplaant le message vers une autre bote lettres. Il existe de nombreux types de botes lettres diffrents avec diffrentes proprits. Slectionnez "Bote lettres" dans la liste des sujets pour en savoir davantage sur les botes lettres. Si un nouveau message apparat alors que TkRat se trouve sous forme d'icne, une petite fentre surgit. Slectionnez "Surveillance" dans la liste des sujets pour en savoir plus sur cette fentre. TkRat est un programme trs complexe. Pourtant j'ai essay de le rendre aussi simple utiliser que possible. La complexit apparat dans le grand nombre de paramtres qu'il est possible de personnaliser. Toutes les options doivent avoir des valeurs par dfaut convenables mais si vous pensez devoir changer quelque chose, consulter l'aide sur les "prfrences" pourra vous tre utile. Plus d'informations Les informations sur la dernire version, etc, peuvent tre trouves l'URL: http://www.TkRat.org/ } sr { TkRat-ov sistem za pomo Pregled TkRat je korisniki program za potu (MUA, Mail User Agent), tj. program za itanje, slanje i ureivanje elektronske pote. Glavni prozor TkRat-a jeste pogled na skup - to je prozor koji se pojavljuje kada pokrenete TkRat. Na vrhu ovog prozora se nalazi meni-linija i linija koja opisuje tekui skup. Ispod toga je spisak pisama u skupu sa koga moete miem odabrati pisma koja elite da pregledate. Ispod liste pisama je linija za statusne poruke i niz dugmadi, a ispod toga je prikazan sadraj trenutno odabrane poruke. Pre nego to poaljete pismo morate ga, naravno, sastaviti (napisati). Ima vie naina kako da se otvori prozor za sastavljanje. Ako elite da napiete novo pismo, treba da kliknete na dugme "Novo pismo"; ako, pak, elite da poaljete odgovor na trenutno odabrano pismo, kliknite na jedno od dugmadi za odgovaranje. U osnovnom stanju prozor za pregled skupa prikazuje INBOX, tj. skup u koji se smetaju pristigla pisma. Kada jednom proitate pismo (i eventualno odgovorite na njega), moda ete poeleti da ga sauvate negde drugde kako se INBOX ne bi zaguio. Ovo se postie tako to pismo premestite u neki drugi skup. Ima puno raznih vrsta skupova sa razliitim osobinama. Ako novo pismo stigne dok je program ikonifikovan, pojavljuje se mali nadzorni prozor. TkRat je vrlo kompleksan program, pa iako sam se trudio da ga nainim lakim za upotrebu, sloenost se vidi u broju stvari koje moete podesiti. Sve ove opcije bi trebalo da imaju razumne poetne vrednosti. Dodatne informacije Informacije o poslednjoj verziji itsl. mogu da se nau na ovom URL-u: http://www.TkRat.org/ } pl { System Pomocy TkRat Informacje oglne TkRat to Mail User Agent (MUA), czyli agent pocztowy, przeznaczony do czytania, wysyania i zarzdzania poczt elektroniczn. Gwne okno programu stanowi okno folderu. Okno to pojawia si po uruchomieniu TkRat. Na grze jest listwa menu i linijka opisujca aktualnie wybrany folder. Poniej znajduje si lista wiadomoci w folderze, moesz zaznaczy wybran wiadomo mysz w celu przeczytania jej. Pod list wiadomoci pokazany jest wybrany list. Zanim bdziesz mg wysya wiadomo musisz j utworzy (np. napisa). Jest wiele sposobw zrobienia tego. Jeli chcesz utworzy now wiadomo powiniene nacisn przycisk "Nowa wiadomo". Jeli chcesz odpowiedzie komu na jego list, nacinij jeden z przyciskw "Odpowiedz". Domylnie okno folderu pokazuje twoj skrzynke poczty przychodzcej (INBOX). Kiedy przeczytasz listy (i by moe odpowiesz na nie) moesz chcie je zapisa w jakiej innej skrzynce (by nie zapycha twojej skrzynki INBOX). Moesz to zrobi przenoszc wiadomo do innego folderu. Jest wiele rnych typw folderw z wieloma rnymi cechami. Jeli przyjdzie do ciebie nowa wiadomo, gdy program jest w postaci ikony, otwiera si mae okienko Obserwatora. TkRat to bardzo kompleksowe narzdzie. I pomimo maksymalnego uatwienia jego obsugi, jest wiele rnych opcji, ktre moesz swobodnie zmienia. Wszystkie te opcje powinny mie sensowne ustawienia domylne. Wicej informacji Informacje o ostatniej wersji itp. znajduj si na stronie: http://www.TkRat.org/ } pt { Sistema de ajuda do TkRat Viso geral O programa TkRat um MTA (Mail User Agent) cuja finalidade a leitura, envio e manuteno de correio electrnico. A janela principal do programa a janela de pasta de correio. Esta a janela que se abre quando o TkRat iniciado. No topo desta janela encontra-se uma barra de menus e uma linha que descreve a pasta aberta. Sob estas encontra-se uma listagem das mensagens contidas na pasta. possvel seleccionar mensagens da pasta com simple clique nesta listagem, mensagens estas que so visualizadas na parte inferior da janela. Imediatamente a seguir listagem da pasta surge uma linha com botes referentes a funes de mensagem. A mensagem propriamente dita surge a seguir a estes botes. Antes de poder enviar uma mensagem, necessrio redigi-la. H vrias maneiras de iniciar uma janela de composio de mensagem. Se deseja criar uma nova mensagem, basta pressionar o boto "Escrever". Se deseja responder a uma mensagem, deve ento pressionar um dos botes "Responder". Por defeito a janela de pasta mostra a sua caixa de correio principal (INBOX), isto , a pasta onde o correio recebido colocado em primeira instncia. Depois de ler este correio (e talvez respondido) poder eventualmente desejar armazen-lo noutro lugar (a fim de evitar sobrecarregar a pasta INBOX.) Isto conseguido movendo a mensagem para outra pasta. O TkRat dispoe de diversos tipos de pastas com diferentes propriedades. Se uma nova mensagem entra na sua caixa de correio quando o programa est em excuo em modo iconificado, surge uma pequena janela designada "watcher" (vigia) que o avisa da chegada de novo correio. O programa TkRat bastante complexo. Embora o autor tenha tentado faz-lo o mais simples possvel de utilizar, h um nmero aprecivel de opes que necessrio adaptar a cada caso e utilizador. Todas estas opes tm no entanto valores por defeito bastante gerais e apropriadas a um grande nmero de situaes. Mais informao Para mais informao sobre o programa, ltima verso, etc, pode ser encontrada no URL seguinte: http://www.TkRat.org } label title,deleting sv {Radera brev} en {Deleting messages} de {Lschen von Nachrichten} it {Cancellare messaggi} fr {Dtruire des messages} sr {Brisanje pisama} pl {Usuwanie wiadomoci} pt {Apagar mensagens} label deleting sv { Radera brev Att radera ett brev r en tvstegsoperation. Frst s markerar man brevet som raderat, och ngon gng senare s tas de mrkta breven bort. Brev som r markerade som raderade har ett 'D' bredvid sig i listan ver brev. Fr att markera det aktiva brevet som raderat trycker du helt enkelt p "Radera"-knappen. Du kan fortfarande gra vad du vill med brevet eftersom det inte har tagits bort nnu. Man kan ta bort raderat-mrket p det aktiva brevet genom att vlja "Ta bort radering" i Brev-menyn. Sjlva raderingen av de mrkta breven intrffar nr du synkroniserar mappen (via "Synkronisera mapp" i Administration-menyn) eller nr du stnger mappen (om du ppnar en annan mapp s stngs den aktiva mappen, mappen stngs ocks om du avslutar TkRat). } en { Deleting messages Deletion of messages is a two-step process. First, the messages are marked for deletion, and then at some later time the marked messages are actually removed from the folder. Messages marked for deletion are indicated by a 'D' next to them in the list of messages. To mark the current message for deletion, simply press the "Delete" button. You can perform all the usual operations on a message marked for deletion since the message is not actually deleted yet. You can remove the deletion mark from the current message by selecting "Undelete" in the message menu. The actual deletion of the marked messages occurs whenever you synchronize the folder (via "Synchronize folder" in the Admin menu) or when you close the folder (opening another folder causes the current folder to be closed, as does quitting TkRat). } de { Lschen von Nachrichten Nachrichten werden in zwei Schritten gelscht: Erst werden sie zum Lschen vorgemerkt, und zu einem geeigneten spteren Zeitpunkt aller solchermaen markierten Nachrichten auf einmal aus dem Ordner entfernt. Zum Lschen vorgemerkte Nachrichten tragen in der Liste den Kennbuchstaben 'D'. Solche Vermerke kann man mit der Schaltflche "Lschen" eintragen und mit dem Menpunkt "Wiederherstellen" im Nachricht-Men wieder entfernen. Da die Nachricht, abgesehen von der Markierung, noch in keiner Weise verndert wurde, kann sie uneingeschrnkt weiter bearbeitet werden. Das eigentliche Lschen erfolgt, wenn ein Ordner geschlossen oder der Menpunkt "Synchronisieren" im Admin-Men benutzt wird. Bitte beachten Sie, dass ein Ordner auch dann explizit geschlossen wird, wenn Sie TkRat beenden oder einen anderen Ordner in sein Fenster laden. } it { Cancellare messaggi La cancellazione dei messaggi avviene in due tempi. Prima i messaggi vengono contrassegnati per la cancellazione, e poi, pi tardi, i messaggi vengono rimossi fisicamente dalla cartella. I messaggi contrassegnati per la cancellazione sono indicati da una 'D' nella lista dei messaggi. Per contrassegnare un messaggio per la cancellazione e' sufficiente premere il bottone "Cancella". Tutte le normali operazioni sono possibili su tali messaggi fino a che non vengono rimossi definitivamente dalla cartella. E' possibile rimuovere il segno 'D' dal messaggio corrente selezionando la voce "Annulla cancella" dal menu "Messaggi". La cancellazione fisica dei messaggi contrassegnati avviene quando si sincronizza o aggiorna la cartella (tramite gli opportuni comandi del menu "Amministrazione", o quando si chiude la cartella (aprendo un'altra cartella chiude la cartella corrente. Ci accade anche quando si esce da TkRat). } fr { Dtruire des messages La destruction de messages s'effectue en deux tapes. Les messages sont d'abord marqus pour destruction, et, plus tard, ils sont effectivement dtruits. Les messages dtruire sont signals par un 'D' dans la liste. Pour marquer le message courant pour destruction, il suffit de cliquer le bouton 'Dtruire'. Vous pouvez continuer travailler normalement avec ce message puisqu'il sera dtruit plus tard. Il est possible d'enlever la marque de destruction, en choisissant "Annuler destruction" dans le menu message. La destruction physique des messages marqus s'effectue dans trois cas : o vous synchronisez la bote lettres avec la commande "synchroniser la bote lettres" du menu Administration, o vous ouvrez une autre bote lettres, o vous quittez TkRat. } sr { Brisanje pisama Brisanje pisama tee u dva koraka. Prvo se oznae pisma koja treba obrisati, a zatim se, u nekom docnijem trenutku, oznaena pisma zaista uklone iz skupa. Pisma odreena za brisanje imaju znak 'D' pored sebe na listi pisama. Da biste trenutno odabrano pismo oznaili za brisanje samo kliknite na dugme "Brii". Sve uobiajene operacije moete izvesti i sa pismom odreenim za brisanje zato to pismo jo nije stvarno uklonjeno. Oznaka za brisanje se moe ukloniti sa tekueg pisma pomou opcije "Poniti brisanje" iz menija "Pismo". Stvarno brisanje oznaenih pisama se odigrava svaki put kada sinhronizujete skup (opcija "Sinhronizuj skup" iz menija "Podeavanja") ili kada zatvorite skup (time to otvorite drugi skup ili tako to zavrite rad sa TkRat-om). } pl { Usuwanie wiadomoci Usuwanie wiadomoci to proces dwuetapowy. Najpierw wiadomo jest przeznaczana do usunicia, a w pniejszym czasie oznaczone wiadomoci s rzeczywicie usuwane z folderu. Wiadomoci przeznaczone do usunicia maj oznaczenie 'D' na licie wiadomoci. By zaznaczy wybran wiadomo do usunicia nacinij przycisk "Usu". Moesz wykonywa wszelkie typowe operacje na wiadomoci przeznaczonej do usunicia do czasu a zostanie ona rzeczywicie usunita. Moesz anulowa oznaczenie 'D' wybierajc polecenie "Przywr" w menu "Wiadomo". Wiadomoci s rzeczywicie usuwane podczas synchronizacji folderu (przez "Synchronizuj folder" w menu "Administracja") lub po zamkniciu folderu (ew. otwarciu innego folderu lub wyjciu z programu TkRat). } pt { Apagar mensagens O processo de destruio de mensagens consiste em dois passos. Primeiro, as mensagens devero ser marcadas para apagamento e posteriormente estas mensagens so efectivamente removidas da pasta. Mensagens marcadas para apagamento so marcadas com a presena da letra 'D' na lista de mensagens. Para marcar a mensagem actual para apagamento pressione o boto 'Apagar'. no entanto ainda possvel efectuar todas as operaes normais numa mensagem visto que ela ainda no est efectivamente apagada. igualmente possvel remover a marcao para apagamento selec- cionando 'No apagar' no menu 'Mensagem'. A destruio efectiva das mensagens marcadas ocorre quando sincro- niza a pasta ou quando fecha a pasta (da mesma forma, quando encer- ra o programa TkRat.) } label title,grouping sv {Gruppera brev} en {Grouping messages} de Nachrichtengruppen it {Raggruppare i messaggi} fr {Grouper les messages} sr {Grupisanje pisama} pl {Grupowanie wiadomoci} pt {Agrupar mensagens} label grouping sv { Gruppera brev Att gruppera brev r ett bekvmt stt att utfra samma operation p ett antal brev p en gng. Den aktiva gruppen bestr alltid av alla markerade brev i den aktiva mappen. Att ett brev r markerat visas mha 'F'-flaggan i brevlistan (Detta 'F' r egentligen en del av brevets statusflaggor s om du inte har med %S i ditt listformat s ser du det inte). Brev kan behlla sin markering nsta gng mappen ppnas. Fr tillfllet s kan du gra fljande operationer p en grupp (operationerna mste utfras frn gruppmenyn): * Radera - Markera breven fr radering * Ta bort radering - Ta bort raderingsmrkning frn breven * Flytta - Flytta breven till en annan mapp Det finns fem olika stt att lgga till/ta bort brev frn gruppen: * Anvnd "Ta bort gruppmarkering" ingngen i Grupp-menyn Detta tar bort samtliga gruppmarkeringar i den aktiva mappen. * Tryck hger musknapp p brevet i brevlistan Detta byter vrde (p/av) p gruppmarkeringen fr brevet. * Anvnd "Skapa i fnster..." ingngen i Grupp-menyn Detta skapar ett separat fnster som innehller en lista ver alla brev i den aktiva mappen dr du kan vlja brev. Nr du trycker p "Ok"-knappen s grs dina val om till den aktiva gruppen. * Anvnd "Skapa mha uttryck..." ingngen i Grupp-menyn Detta skapar ett fnster dr du kan skapa ett reguljruttryck och som sedan lgger in alla brev (i den aktiva mappen) som passar uttrycket i den aktiva gruppen. * Anvnd "Anvnd sparat uttryck" menyn i Grupp-menyn. Denna menyn innehller ingngar fr de uttryck som du tidigare har sparat. } en { Grouping messages Grouping messages is a convenient way of performing the same operation on multiple messages in just one step (after the group is created). The current group always consists of all flagged messages in the current folder. Flagged messages are indicated by a 'F' in the list of messages (this F is actually a part of the status flags, so if you do not have %S in your list format, you will not see it). Messages may keep their flagged status next time they are accessed. Currently, you can perform the following operations on a group (must be done via the Group menu): * Delete - Mark messages for deletion * Undelete - Remove deletion mark from messages * Move - Move messages to another folder There are currently five different ways to add/remove messages from a group: * Use the "Clear group marking" entry in the Group menu This will remove all group markings in the current folder * Press the right mousebutton on a message in the message list This will toggle the group flag for that message * Use the "Create in window..." entry in the Group menu This will create a separate window which contains a list of all messages in the current folder, you can select/deselect messages in it. Finally, when you press the OK button, your selection will become the group selection. * Use the "Create by expression..." entry in the Group menu This opens a window where you can give an expression, all messages in in the current folder that match that expression will be added to the current group * Use the "Use saved expression" submenu in the Group menu This menu contains entries for each expression you have saved earlier } de { Nachrichtengruppen Um mehrere Nachrichten auf einmal zu bearbeiten, kann man eine Nachrichtengruppe zusammenstellen. Die aktuelle Gruppe besteht aus den mit dem Kennbuchstaben 'F' markierten Nachrichten im aktuellen Ordner. Derzeit werden die folgenden Operationen auf Nachrichtengruppen untersttzt: * Lschen - Nachrichten zum Lschen vormerken * Wiederherstellen - Vormerkung rckgngig machen * Verschieben - Nachrichten in einem anderen Ordner ablegen * Drucken - Nachrichten ausdrucken Alle diese Operationen sind ber das Gruppen-Men anzuwhlen. Nachrichten zu einer Gruppe hinzuzufgen oder sie wieder daraus zu entfernen kann auf eine der folgenden fnf Arten geschehen: * Gruppen-Men, Punkt "Lsche Gruppenmarkierung" * In der Nachrichtenliste setzt bzw. lscht die rechte Maustaste den Kennbuchstaben bei einer einzelnen Nachricht * Gruppen-Men, Punkt "In Fenster erzeugen ...", ffnet ein separates Fenster mit der Liste der Nachrichten im aktuellen Ordner. Die Auswahl dort erfolgt mit der Maus und wird mit der "OK" Schaltflche in das Hauptfenster bernommen. * Gruppen-Men, Punkt "Nach Ausdruck erzeugen ...", ffnet ein Fenster, in dem man eine Suchmaske eingeben kann. Alle Nachrichten, die dieser Maske entsprechen, werden zur Gruppe hinzugefgt. * Gruppen-Men, Untermen "Benutze gespeicherten Ausdruck", enthlt eine Liste der Suchmasken, die Sie frher einmal eingegeben und abgespeichert haben. } it { Raggruppare i messaggi Raggruppare i messaggi e' un modo conveniente per eseguire certe operazioni su pi messaggi contemporaneamente. Il gruppo corrente consiste in tutti i messaggi selezionati nella cartella corrente. I messaggi selezionati sono contrassegnati da una 'F' nella lista dei messaggi (la F fa parte dello stato del messaggio, cosi' se il formato della lista non contiene %S, non sar visibile). I messaggi rimangono selezionati anche dopo un'operazione. Al momento le seguenti operazioni sono ammesse su un gruppo (tramite il menu "Gruppo"): * Cancella - Contrassegna i messaggi per la cancellazione * Annulla cancella - Recupera i messaggi contrassegnati per la cancellazione * Muovi - Muove i messaggi in un'altra cartella Ci sono cinque modi differenti per aggiungere/rimuovere messaggi da un gruppo: * Usando il comando "Cancella selezione di gruppo" dal menu "Gruppo". Questa opzione deseleziona tutti messaggi contrassegnati dalla 'F'. * Premendo il tasto destro del mouse su un messaggio nella lista dei messaggi. Questo seleziona/deseleziona il messaggio. * Usando il comando "Crea in finestra..." dal menu "Gruppo" Verr aperta una finestra a parte che contiene una lista di tutti i messaggi della cartella corrente in cui e' possibile selezionare e/o deselezionare messaggi. Premendo Ok i messaggi selezionati verrano raggruppati. * Usando il comando "Crea con chiave..." dal menu "Gruppo". Verr aperta una finestra dove e' possibile inserire una chiave secondo la quale verranno selezionati i messaggi. * Usando il comando "Usa chiavi salvate" dal menu "Gruppo". Questo menu contiene un sottomenu con le chiavi salvate in precedenza. } fr { Grouper les messages Grouper des messages permet d'effectuer sur plusieurs messages la mme opration en une seule commande (aprs que le groupe est cr). Le groupe courant est toujours compos de tous les messages signals dans la bote lettres courante. Les messages signals sont indiqus par un 'F' dans la liste des messages. (Ce 'F' fait partie des drapeaux d'tat, donc si vous n'avez pas %S dans votre format de liste vous ne le verrez pas). Les messages conservent leur tat signal aprs plusieurs accs. Pour le moment, il est possible d'effectuer les oprations suivantes sur un groupe, via le menu 'groupe' : * Dtruire - Marque les messages pour destruction. * Annuler destruction - Retire la marque de destruction. * Dplacer - Dplace les messages vers une autre bote lettres. Il existe 5 faons diffrentes d'ajouter une message dans un groupe ou de le retirer du groupe. * Utiliser Effacer le marquage de groupe dans le menu Groupe Cela vide totalement le groupe. * Utiliser le bouton 3 sur un message dans la liste. Cela inverse le marquage de groupe. * Utiliser Crer avec une expression dans le menu Groupe. Cela ouvre une fentre o vous pouvez donner des critres de slection. Tous les messages correspondant aux critres feront partie du groupe. Voyez le sujet "fentre expression" pour plus de dtails. * Utiliser Choisir dans une liste dans le menu Groupe. Vous pourrez slectionner les messages dans une nouvelle fentre et lorsque vous cliquez le bouton Ok, votre slection devient le groupe courant. * Utiliser le sous-menu Utiliser une expression sauvegarde dans le menu Groupe. Ce sous-menu contient une entre pour chacune des expression que vous avez dj utilise et sauvegarde. } sr { Grupisanje pisama Grupisanje pisama je pogodan nain da istu operaciju izvedete nad veim brojem pisama u samo jednom potezu (poto se grupa jednom kreira). Tekua grupa se uvek sastoji od svih oznaenih pisama u trenutno prikazanom skupu, a oznaena pisma su predstavljena znakom 'F' u spisku pisama (zapravo je ovo 'F' deo statusa pisma, pa ako niste uneli %S kao deo formata spiska neete ga videti). Pisma mogu zadrati oznaku kada im sledei put pristupite. Trenutno moete izvesti ove operacije nad grupom (moraju se izvesti preko menija "Grupa"): * Brii - oznaava pisma za brisanje * Poniti brisanje - uklanja oznaku za brisanje sa pisama * Premesti - pomera pisma u drugi skup Postoji pet naina kako pisma moete dodati u grupu ili ukloniti iz grupe: * Koristite opciju "Ukloni grupne oznake" iz menija "Grupa" Ovo e ukloniti oznake grupisanja sa svih pisama u tekuem skupu. * Pritisnite desno dugme mia nad pismom na spisku Ovim se oznaka za grupisanje naizmenino ukljuuje/iskljuuje. * Koristite opciju "Kreiraj u prozoru" iz menija "Grupa" Pojavljuje se poseban prozor sa spiskom pisama iz tekueg skupa, i u njemu moete odabrati/iskljuiti pisma. Kada najzad kliknete na "Ok" dugme, Va izbor postaje grupa. * Stavka "Kreiraj po izrazu" iz menija "Grupa" Otvara se prozor u kome moete zadati izraz, a zatim e grupi biti dodata sva pisma iz tekueg skupa koja se uklapaju u date uslove. Pogledajte temu "Prozor za izraze" da biste dobili jo informacija o ovome. * Koristite podmeni "Upotrebi snimljeni izraz" iz menija "Grupa" Ovaj meni sadri stavku za svaki izraz koji ste ranije snimili. } pt { Agrupar mensagens Utilizar agrupamentos de mensagens uma forma eficiente de realizar operaes em mensagens num s passo. O grupo actual consiste sempre em todas as mensagens indicadas com a letra 'F' (flag) na lista de mensagens (esta letra 'F' de facto parte das 'flags' de estado pelo que apenas estar visvel caso a sua configurao do formato da lista incluir %S.) As mensagens mantm os estados anteriormente fixados quando so acedidas de futuro. De momento, as seguintes operaes podem ser executadas num grupo (atravs do menu 'Grupo'): * Apagar - Marcar mensagens para apagamento * No apagar - Remover marcaes para apagamento * Mover - Mover mensagens para outra pasta H de momento cinco maneiras de adicionar/remover mensagens de um grupo: * Usando a entrada 'Eliminar marcaes de grupo' no menu 'Grupo' Todas as marcaes actuais da pasta sero removidas * Pressionando o boto direito do rato na lista de mensagens Esta aco permuta a marcao da mensagem corrente * Usando entrada 'Agrupar por lista' no menu 'Grupo' Esta aco cria uma janela separada que contm uma lista de todas as mensagens da pasta actual na qual esta mensagens podem ser (de)seleccionadas. Depois de pressionado o boto OK, a sua seleco torna-se um grupo. * Usando a entrada 'Agrupar por expresso' no menu 'Grupo' Esta aco abre uma janela onde possvel inserir uma dada expresso e de seguida agrupar todas as mensagens que contm essa expresso * Usando a entrada 'Usar expresso memorizada' no menu 'Grupo' Idntico ao anterior, numa expresso usada e gravada anteriormente } label title,folders sv Mappar en Folders de Ordner it Cartelle fr {Bote lettres} sr Skupovi pl Foldery pt Pastas label folders sv { Mappar En mapp r en samling av brev (noll eller flera). Det finns mnga olika stt att spara och komma t brev i en mapp. TkRat klarar fr tillfllet av fyra olika typer av mappar. Fr att ppna (vlja) en mapp s mste man veta vilken typ det r, och dessutom ha en del annan typspecifik information. Drfr har TkRat ngonting som jag kalla virtuella mappar (ocks knda som vmappar). Nr du definierar en vmapp s anger du vilken typ av mapp och all annan data som behvs, du mste dessutom ange ett namn p vmappen. Vmappens namn kommer sedan att synas i "Mappar"- och "Flytta"-menyerna. Fljande typer av mappar stdjs * Unix mailbox En UNIX mailbox bestr av en enkel textfil dr breven skiljs t av speciella skiljelinjer (som brjar med "From " eller "^A^A^A"). Detta r den vanligaste metoden att spara brev p unixsystem. Ndvndiga uppgifter: namnet p filen * IMAP4 IMAP (version 4) r ett protokoll fr att komma t sina mappar ver ntverk. En anvndare kan ha flera IMAP-mappar p en maskin. Om du vill anvnda rimap-autentisering s skall du lmna portnummer- fltet tomt. Ndvndiga uppgifter: namnet p maskinen som har mappen vilket anvndrnamn man skall anvnda ett lsenord (frgas efter vi ppning) mappens namn * Frikopplad En frikopplad mapp r en lokal kopia av en IMAP-mapp. Dessa mappar hlls sedan synkroniserade s att alla ndringar som grs i den ena speglas ver till den andra. Denna mapptyp r mycket anvndbar om man vill arbeta utan att vara uppkopplad mot ntet hela tiden. Ndvndiga uppgifter: namnet p maskinen som har mappen vilket anvndrnamn man skall anvnda ett lsenord (frgas efter vi ppning) mappens namn * POP3 POP (version 3) r ett protokoll fr att lsa sin post ver ntverk. Varje anvndare kan ha hgst en POP-mapp p en maskin. Ndvndiga uppgifter: namnet p maskinen som har mappen vilket anvndrnamn man skall anvnda ett lsenord (frgas efter vi ppning) * Databas En databasmapp r en samling av brev dr varje brev har nyckelord associerade till sig. Att ppna en databasmapp r samma sak som att ska i den (man kan ska p fler saker n nyckelord). Brev kan ocks ha en expireringstid, och nr den ns s hnder ngonting med brevet (det kan tex raderas). Varje anvndare kan bara ha en databas (man kan bara komma t sin egen databas). Ndvndiga uppgifter: ett skuttryck expireringstid och typ (nr man lgger in brev) * Dynamisk Detta r en specialform a "Unix mailbox". Den anges som ett bibliotek och nr man lgger in brev i mappen s sparas de i filmappar i biblioteket. Filmapparnas namn r frsta delen av avsndarens epostadress. Nr du ppnar en dynamisk mapp s fr du en lista ver alla filer som finns i biblioteket. Ndvndiga uppgifter: namnet p biblioteket. } en { Folders A folder is a collection of zero or more messages. There are a lot of different ways of storing and accessing the messages in a folder. TkRat currently understands four different types of folders. To open a folder, you need to know which type it is, and some additional type- specific data about it. To help you, TkRat has something called Virtual folders (also known as vfolders). When you define a vfolder, you have to specify a name, type, and all other data about the folder. This name will then appear in the "Folders" and "Move" menus for easy selection and message insertion. The following types of folders are supported * UNIX mailbox A UNIX mailbox consists of a flat text file where the messages are separated by a certain marker line (which starts with "From " or "^A^A^A"). This is the normal way of handling mail on UNIX systems. Data needed: The name of the file * IMAP4 IMAP (version 4) is a protocol for accessing mailboxes over a network. One user can have multiple IMAP mailboxes on one host. If you wish to use rimap-style authentication, you should make the port number field empty. Data needed: The name of the server Which user to access the folder as A password (asked for when opening the folder) The name of the mailbox * Disconnected A disconnected folder is a local copy of an IMAP folder. These folders are then synchronized so that any changes made to one of them are mirrored in the other. This is very useful for reading and answering your mail offline. Data needed: The name of the server Which user to access the folder as A password (asked for when opening the folder) The name of the mailbox * POP3 POP (version 3) is a protocol for accessing mailboxes over a network. Each user can have a maximum of one POP mailbox on one host. Data needed: The name of the server Which user to access the folder as A password (asked for when opening the folder) * Database A database folder is a collection of messages where each message has keywords associated with it. Opening the database is equivalent to searching it (you can search for attributes other than the keywords). Messages can also have an expiration time, and when this time is reached, something happens to the message (it may, for example. be deleted). Every user can have one database each (you can only access your own database). Data needed: A search expression Expiration time and type (for insertion) * Dynamic This is a special form of the File folder. It is specified as a directory, and when you insert messages into it, the message is actually placed in a file folder in that directory. The name of the file folder is the first part of the sender's email address. When you open a dynamic folder, you will see a list of the files in that directory. Data needed: The name of the directory } de { Ordner Ein Ordner ist eine Sammlung von Nachrichten. Es gibt sehr verschiedene Arten, eine solche Sammlung zu speichern und auf sie zuzugreifen. Zur Zeit untersttzt TkRat vier verschiedene Typen von Ordnern. Um einen Ordner zu ffnen, mssen Sie angeben, welchen Typ er hat, sowie je nach Typ weitere Einzelheiten angeben. Damit das nicht bei jedem Zugriff auf den Ordner erneut geschehen muss, werden diese Informationen nur einmal eingegeben und in den entsprechenden Auswahlmens erscheint dann ein sogenannter virtueller Ordner (vfolder). Die folgenden Typen von Ordnern werden untersttzt: * UNIX Mailbox Format In einer UNIX Mailbox werden die Nachrichten hintereinander als einfache Textdatei abgespeichert. Die Abtrennung der einzelnen Nachrichten voneinander erfolgt durch spezielle Markierungszeilen, die mit "From " oder "^A^A^A" beginnen. Dies ist der normale Ordnertyp auf einem UNIX-basierten Rechner. Bentigte Zusatzangaben: Name der Datei * IMAP4 Ordner IMAP Version 4 ist ein Protokoll, um ber das Netzwerk auf einen entfernten Ordner zuzugreifen. IMAP bietet die Mglichkeit, dass ein Benutzer mehrere Ordner auf einem Server verwaltet. Wenn der Zugang zum Server im Stile von RIMAP Authentifikation gesteuert wird, lassen Sie bitte das Feld "Port" leer. Bentigte Zusatzangaben: Name des Servers Name des Benutzers auf dem Server Passwort (wird beim ffnen des Ordners abgefragt) Name des Ordners * "Offline" IMAP Ordner Ein solcher Ordner ist eine lokal gehaltene Kopie eines IMAP Ordners, die gelegentlich mit dem Original abgeglichen wird, um nderungen in beide Richtungen weiterzuverbreiten. Mit einem solchen Ordner knnen Nachrichten bearbeitet werden, ohne dass dabei stndig eine Netzwerkverbindung verfgbar sein muss. Bentigte Zusatzangaben: Name des Servers Name des Benutzers auf dem Server Passwort (wird beim ffnen des Ordners abgefragt) Name des Ordners * POP3 Ordner POP Version 3 ist ebenfalls ein Protokoll zum Zugriff auf Ordner per Netzwerk, es ist (derzeit noch) verbreiteter, aber lter und untersttzt pro Benutzer und Server nur einen Ordner. Bentigte Zusatzangaben: Name des Servers Name des Benutzers auf dem Server Passwort (wird beim ffnen des Ordners abgefragt) * Datenbank-Ordner Jeder Benutzer kann eine (lokale) Datenbank anlegen. In ihr werden Nachrichten unter Stichworten abgespeichert, und beim ffnen eines Datenbank-Ordners wird nach diesen Stichworten oder anderen Textteilen gesucht und die Fundstellen aufgelistet. Ferner kann man fr die Datenbank einstellen, dass Nachrichten nach einer bestimmten Verweilzeit automatisch bearbeitet (gelscht, wieder vorgelegt, weitergeleitet, ...) werden. Bentigte Zusatzangaben (pro *Ordner*): Suchmaske Verweildauer und automatische Aktion * Dynamische Ordner Dynamische Ordner sind eine Sonderform der UNIX (oder Datei-)Ordner. Ein solcher Ordner ist ein Verzeichnis, in dem UNIX-Ordner als einzelne Dateien angelegt werden; Standardgem werden diese Dateien beim Abspeichern einer Nachricht nach dem Anfang der Absenderadresse benannt bzw. ausgewhlt. Bentigte Zusatzangaben: Name des Verzeichnisses } fr { Bote lettres Une bote lettres est une collection ventuellement vide de messages. Il existe de nombreuses formes de botes lettres diffrentes. TkRat connat 4 systmes diffrents. Pour ouvrir une bote lettres, vous devez connatre son type ainsi que des informations variables suivant le type. Pour simplifier, TkRat utilise ce qu'il appelle des botes lettres virtuelles. Lors de la dfinition d'une bote lettres virtuelle vous devez lui donner un nom, un type ainsi que tous les autres renseignements requis. Ce nom apparatra ensuite dans les menus "Bote lettres" et "Dplacer". Une fois cette dfinition effectue, vous pouvez travailler de la mme manire sur toutes vos botes lettres. Types de botes lettres connus : * bote lettres Unix Une bote lettres Unix est un fichier ASCII o les messages sont spars par une ligne particulire (commenant par "From " ou "^A^A^A"). C'est la manire standard de grer le courrier lectronique sous Unix. Donnes ncessaires : * nom du fichier * IMAP4 IMAP version 4 est un protocole qui permet d'accder des botes lettres travers un rseau. Un utilisateur peut avoir plusieurs botes lettres IMAP sur un mme hte. Pour utiliser une authentification rimap, Ne remplissez pas le champ port. Donnes ncessaires : * Nom du serveur * Nom d'utilisateur sous lequel contacter le serveur * Un mot de passe (ncessaire pour ouvrir la bote lettres) * Le nom de la bote lettres sur le serveur. * Le port (ventuellement) * Dconnect Une bote lettres Dconnect est une copie locale d'une bote lettres IMAP. lorsque vous synchroniser les botes lettres, tout changement effectu sur l'une des deux botes est rpercut sur l'autre. C'est trs pratique pour grer son courrier lorsque vous n'tes connects un rseau que de manire intermittente. Donnes ncessaires : * Nom du serveur * Nom d'utilisateur sous lequel contacter le serveur * Un mot de passe (ncessaire pour ouvrir la bote lettres) * Le nom de la bote lettres sur le serveur. * POP3 POP version 3 est un protocole qui permet d'accder des botes lettres travers un rseau. Un utilisateur peut avoir au maximum une bote lettres POP sur un serveur. Donnes ncessaires : * Nom du serveur * Nom d'utilisateur sous lequel contacter le serveur * Un mot de passe (ncessaire pour ouvrir la bote lettres) * base de donnes Une bote lettres base de donnes est une collection de messages o chaque message possde des mots-cls qui lui sont associs. Ouvrir une base de donnes revient effectuer une recherche (Les critres de recherche ne sont pas limits aux mots-cls). Les messages peuvent galement avoir une date d'expiration. lorsqu'un message expire, une action est automatiquement effectue (destruction par exemple). Chaque utilisateur peut avoir au plus une bote lettres base de donnes. Donnes ncessaires : * Un critre de recherche * Type et date d'expiration (lors de l'insertion de nouveaux messages) * Dynamique Il s'agit d'un cas spcial de fichier bote lettres : c'est en fait un rpertoire. Lorsqu'un message est insr, il est insr dans un fichier bote lettres dans ce rpertoire dont le nom est l'adresse de l'expditeur du message. Lors de l'ouverture d'une bote lettres dynamique, vous verrez la liste des fichiers dans ce rpertoire. Une bote lettres dynamique est une bote qui classe automatiquement les messages par expditeur. Donnes ncessaires : * nom du rpertoire. } sr { Skupovi Skup (folder) je niz od nula ili vie pisama. Postoji mnogo razliitih naina za uvanje i pristupanje pismima u skupu. TkRat trenutno razume etiri tipa skupova. Da biste otvorili skup, treba da znate kog je tipa i jo neto podataka koji zavise od njegove vrste. Da bi Vam pomogao u ovome, TkRat uvodi tzv. virtuelne skupove (vfolder). Kada definiete skup, morate mu dati ime, tip i ostale potrebne podatke. Ovo ime e se pojaviti u menijima "Skupovi" i "Premesti" radi lakeg biranja i prebacivanja pisama. Podrane su sledee vrste skupova: * Unix mailbox (Unix potansko sandue, datoteni skup) Unix-ovo sandue jeste prosta tekstualna datoteka u kojoj su pisma razdvojena odreenim marker-linijama (linija koja poinje sa "From: " ili "^A^A^A"). Ovo je osnovni nain za uvanje pote na Unix sistemima. Potrebni podaci: ime datoteke * IMAP4 IMAP (verzija 4) je protokol za pristup poti preko mree. Jedan korisnik moe imati vie imap-sanduia na istom hostu. Ako elite da koristite rimap nain autentikacije polje za broj porta treba da ostavite prazno. Potrebni podaci: ime servera ime korisnika pod kojim se pristupa skupu ifra (trai se onda kad se skup otvara) ime sandueta * Nepovezani skup Nepovezani skup jeste lokalna kopija IMAP skupa. Ovi skupovi se zatim sinhronizuju tako da se sve promene uinjene nad jednim preslikavaju na drugi. Ovo je vrlo korisno za itanje i odgovaranje na Vau potu off-line, tj. dok niste povezani sa IMAP serverom. Potrebni podaci: ime servera ime korisnika pod kojim se pristupa skupu ifra (trai se onda kad se skup otvara) ime sandueta * POP3 POP (verzija 3) je jo jedan protokol za rad sa potom preko mree. Svaki korisnik ima najvie jedno POP-sandue na jednom serveru. Potrebni podaci: ime servera ime korisnika pod kojim se pristupa skupu ifra (trai se onda kad se skup otvara) * Baza podataka Skup-baza jeste kolekcija pisama u kojoj svaka poruka ima niz povezanih kljunih rei. Otvaranje baze je isto to i pretraga po bazi (moete, naravno, traiti po vie kriterijuma osim kljunih rei). Pisma mogu imati i datum isticanja, i kada se taj dan dostigne sa pismom se neto deava (npr, moe biti izbrisano). Svaki korisnik moe da ima samo jednu bazu (moete pristupati samo svojoj bazi). Potrebni podaci: izraz za pretragu vreme isticanja i tip (za umetanje) * Dinamiki skup Ovo je posebna vrsta datotenog skupa. Zadaje se kao direktorijum, i kada ubacite pismo u njega, poruka se smeta u datoteku unutar tog direktorijuma. Ime datoteke jeste prvi deo adrese poiljaoca. Kada otvorite dinamiki skup, videete listu datoteka koje se nalaze u njemu. Potrebni podaci: ime direktorijuma } pt { Pastas Uma pasta uma coleco (de zero ou mais) mensagens. H diversas maneiras de armazenar e aceder s mensagens de uma pasta. O programa TkRat de momento implementa quatro tipos de pastas de correio electrnico. Antes de poder abrir uma pasta, necessrio conhecer alguns dados especficos do tipo de pasta. No intuito de simplificar este processo, o programa TkRat implementa as designadas 'pastas virtuais' (internamente designadas vfolder.) A definio de uma pasta virtual exige a especificao de um nome, tipo e restantes dados identificativos da pasta. Este mesmo nome aparecer no menu 'Pastas' e 'Mover' para simplificar a seleco e insero de mensagens. So suportados os tipos de pastas seguintes: * Caixa de correio Unix Uma pasta Unix consiste num simples ficheiro de texto onde as mensagens so separadas por uma linha marcador (que comea com "From " ou "^A^A^A"). Esta a maneira mais comum de manuteno de correio electrnico no sistema Unix. Dados necessrios: o nome do ficheiro * IMAP4 IMAP (verso 4) um protocolo de acesso a caixas de correio atravs de uma rede. Um nico utilizador pode ter vrias pastas imap no mesmo servidor. Se se desejar utilizar a autenticao rimap, deve ento deixar-se o campo do nmero do porto em branco. Dados necessrios: nome do servidor identificao do utilizador no servidor palavra passe (para abertura da pasta) o nome da caixa de correio * Desligada Uma pasta 'desligada' simplesmente uma cpia local de uma pasta IMAP. Estas pastas so sincronizadas de forma a que alteraes em qualquer delas reflectida na outra. Isto particularmente til para a leitura e resposta do correio electrnico no modo 'offline'. * POP3 POP (verso 3) igualmente um protocolo de acesso a caixas de correio atravs de uma rede. Cada utilizador pode ter um mximo de uma caixa de correio por servidor. Dados necessrios: nome do servidor identificao do utilizador no servidor palavra passe (para abertura da pasta) * Base de dados Uma pasta de tipo base de dados uma coleco de mensagens na qual cada mensagem tem palavras chave associadas. Abrir a base de dados equivalente a operar uma procura (alm de que possvel procurar mais que apenas as palavras chave.) As mensagens tm igualmente uma data de vencimento, aps a qual alguma aco operada na mensagem (por exemplo, a mensagem apagada.) Cada utilizador pode usar apenas uma base de dados. Dados necessrios: uma expresso de busca data de validade e tipo (para insero) * Dinmica Este um tipo especial de pasta tipo ficheiro. De facto, especificado como um directrio no qual insere mensagens, sendo na realidade inseridas num ficheiro nesse directrio. O nome do ficheiro a primeira parte do endereo do remetente. Quando abre uma pasta dinmica, poder observar uma lista de ficheiros nesse directrio. Dados necessrios: o nome do directrio } label title,folderdef sv {Definiera mappar} en {Defining folders} de {Anlegen von virtuellen Ordnern} it {Definizione cartelle virtuali} fr {Dfinition de botes lettres virtuelles} sr {Definisanje virtuelnih skupova} pl {Definiowanie wirtualnych folderw} pt {Definio de pastas virtuais} label folderdef sv { Definiera vmappar TkRat lter dig organisera dina mappar i en trdstruktur. Denna struktur anvnds sedan fr att bygga mapp- och flytta-menyerna. Nr du vljer 'Ny/ndra mapp' s dyker ett fnster upp dr du kan modifiera trdet. Man kan flytta om och skapa/ndra/radera mappar. Kom ihg att strukture endast finns innutin TkRat och den behver inte ha ngont som helst samband med hur saker och ting faktiskt ser ut p disken. Fnstret 'Definiera virtuell mappar' r uppdelat i tv delar. Den vnstra delen visar trdet och den hgra delen visar detaljer om den fr tillfllet valda noden i trdet. Trdet har tv noder p topnicn, en som innehller alla dina IMAP-servrar och en som innhller alla mappar. Man kan dra och slppa mappar i mappdelen av trdet. Och om man trycker ner hger musknapp s dyker det upp en liten meny som bland annat lter dig radera den aktuella noden. En av mapparna r mrkt 'INBOX', detta r den mapp som anvnds fr inkommande brev. Det betyder att detta r den mapp som TkRat visar nr programmet startar. Den kan ocks anvndas nr database expireras. Man kan bara ha en INBOX. Mappar kan anvndas fr att spara kopior av utgende brev. Vilken mapp som brev skall sparas i konfigureras separat fr varje roll i instllningsfnstret (se 'Roller'). Importera mappar Fr vissa typer av mappar (fil, imap och mh) s kan man importera en existerande strukture av mappat. Fr att gra detta, vlj motsvarande Import-typ av mapp. Ange sedan skvgen till strukturen som skall importeras. Vad du skall skriva beror p mapptyp och server, vanliga bra vrden r ingenting eller full skvg till ditt hembibliotek. Om du importerar IMAP-mappar s kan du ocks ange om de skall importeras som frnkopplade mappar (vilket innebr att TkRat skapar en lokal kopia av mapparna). Nsta flt att fylla i r 'Mnster'. Mnstret begrnsar vilka mappar som faktiskt importeras. Bara mappar som passar mnstret importeras. Det finns tv olika jokertecken som kan vara anvndbara. Ett '*' passar allt och fr importen att ocks titta i underbibliotek. Ett '%' passar alla mappar men tittar inte ner i underbibliotek. Man kan ocks begrnsa importen till mappar som man prenumererar p. Ledtrd: Om du frsker importera IMAP-mappar frn ett annat program s skall man frst frska att bara importera mappar som man prenumererar p. Ett annat tips r att om du r osker p vad som faktiskt kommer att importeras s anvnd jokern '%' fr att undvika att g igenom hela filsystemet. Det kan vara vr att notera att strukturen p importerade mappar inte kan ndras. Dvs du kan inte dra och slppa mappar i den. Och alla ndringar som grs p disken kommer att mrkas i TkRat nsta gng du importerar om mappen. } en { Defining folders TkRat lets you organize your folders in a tree-like structure. This structure is then used to build the Folders and Move menus. When you call "New/Edit folder" the vfolderdef window will appear. This window lets you modify the structure, add/edit/delete folders, etc. One important fact to remember is that the structure only exists within TkRat and does not necessarily reflect the actual layout on disk. The "Define virtual folder" window is divided into two parts. The left part shows the tree and the right part shows details about the currently selected node in the tree. The tree has two nodes at the top level, one which contains all your IMAP servers and one which contains all the folders. You can drag and drop folders in the folder part of the tree. And, pressing the right mousebutton gives you a small context menu which, among other things, allows you to delete the current node. One of the folders has the string 'INBOX' to the right of it, this is the designated inbox. It means that this is the folder TkRat opens by default when starting. It may also be used when expiring the database. You can have only one INBOX. Folders can be used to store outgoing messages. The folder to store the messages in can be configured separately for each role in the Preferences window (see also 'Roles'). Importing folders For some folder types (file, imap and mh) you can import an entire structure of folders. To do this, select the appropriate Import-type of a folder. Then, provide the path to the folders that will be imported. What you have to write here depends on the mailbox type and server, common good values are nothing and the full path to your home directory. For IMAP imports you can also specify if the folders should be imported as disconnected folders (which means that TkRat will keep a local copy of them). The next field to fill in is the pattern. The pattern limits which folders to actually import. Only folders matching the pattern will be imported. There are two wildcards which may be useful. A '*' matches everything and will cause the import to descend into matching subdirectories. A '%' matches all files but does not descend into subfolders. You can also limit the import to only subscribed folders. Hint: If you are trying to import your IMAP folders from another program, you should probably try with the 'Subscribed folders only' option enabled first. Another tip is that, when you are unsure what you actually will import, then try with pattern '%' to avoid recursing your entire file system. It may be worthwhile to note that the structure of imported folders is read-only. That is, you can not drag and drop among imported folders. Also, any changes you make to the layout on disk will be picked up the next time you re-import the folder. } fr { Dfinir les botes lettres TkRat permet d'organiser les botes lettres dans une structure arbores- cente. Cette structure est utilise ensuite pour construire les menus Botes lettres et Dplacer . Lorsque vous slectionnez Administration->Nouveau/dition des botes a lettres, la fentre des botes virtuelles apparat et vous permet de modifier cette structure, d'ajouter, de modifier, de dtruire des botes lettres. Un fait important dont il faut se souvenir est que cette structure n'a de sens que pour TkRat, et qu'elle n'est pas oblige de reflter une organisation physique quelconque. La fentre Dfinir une bote lettres virtuelle est divise en deux parties. La partie de gauche montre l'arborescence, tandis que la partie de droite montre les dtails de l'objet slectionn dans la partie gauche. L'arbre prsent en partie gauche a deux noeuds en haut gauche. Le premier contient tous les serveurs IMAP et le second contient toutes les botes lettres. vous pouvez faire un glisser/dposer dans la partie bote lettres de l'arbre pour rorganiser vos botes lettres. Le bouton 3 de la souris fait surgir un menu contextuel qui entre autres choses permet de dtruire l'objet auquel il s'applique. droite de l'une des botes lettres, se trouve le texte INBOX. Cela indique que cette bote lettres est ouverte par dfaut lorsque TkRat dmarre. Cette bote lettres peut aussi tre utilise lorsque TkRat examine la base de donnes pour en extraire les messages ayant expir. Vous ne pouvez avoir qu'une seule bote lettres marque INBOX. Les botes lettres peuvent tre utilise pour enregistrer les messages que vous expdiez. La bote utilise pour cela est une proprit des rles, dfinis dans la fentre Administration->Prfrences. (voir Rles). Importer des botes lettres. Vous pouvez importer dans TkRat une structure physique existante, pour les types de botes lettres suivant : Fichier, IMAP, MH. Pour raliser cela, slectionnez le type appropri, puis indiquez le chemin complet de votre bote lettres (ou de l'ensemble de botes lettres) que vous voulez importer. Les renseignements exacts que vous devrez saisir sont fonction du type import. Communment, de bonnes valeurs sont : rien du tout et le chemin de votre rpertoire HOME. Pour les importations de botes lettres IMAP, vous pouvez aussi prciser si les botes lettres doivent tre utilises en mode dconnect (TkRat en gardera une copie locale). Le champ suivant remplir est le modle. Le modle permet de choisir quelles botes lettres il faut importer. Seules les botes lettres dont le nom correspond au modle seront rellement importes. Deux jokers intressant : Une toile (*), correspond tout, et autorisera l'importation descendre dans les sous-botes lettres. Un '%' correspond tout, mais ne prendra pas en compte les sous botes lettres. Pour les botes lettres IMAP, vous pouvez aussi vous limiter aux botes lettres auxquelles vous avez souscrit. Conseil : Si vous tentez d'importer vos botes lettres IMAP d'un autre programme de courrier, vous devriez probablement d'abord essayer avec l'option bote lettres souscrites uniquement . Un autre conseil : Si vous n'tes pas certain de ce qui va tre import, commencer par essayer avec le modle '%', pour viter d'importer tout votre disque dur. Il est intressant de remarquer que les structures importes sont en lecture seule. Cela signifie qu'il est impossible de faire un glisser/dposer avec ces parties. De plus tout changement effectu sur la structure physique sera incorpor TkRat la prochaine fois que vous rimporterez cette bote lettres. } de { Ordner anlegen Mit TkRat knnen Sie Ihre Ordner in einer Baumstruktur anordnen. Diese Struktur erscheint im Men "Ordner" und in den "Verschieben" Mens. Das Fenster zum Bearbeiten der Ordner erscheint, wenn Sie "Ordner anlegen/bearbeiten ..." aus dem Men "Administration" whlen. In diesem Fenster knnen Sie die Baumstruktur anpassen und Ordner neu anlegen, ndern, lschen etc. Beachten Sie, dass der Ordner-Baum nur innerhalb von TkRat verwendet wird und nicht der Organisation der Ordner-Dateien in Verzeichnissen entsprechen muss. Das Fenster "virtuelle Ordner bearbeiten" ist zweigeteilt. Auf der linken Seite wird die Baumstruktur dargestellt, die rechte Seite zeigt Details des gerade ausgewhlten Knotens im Baum. Die Baumstruktur hat zwei Teile, die jeweils unter einem Wurzelknoten angeordnet sind. Im ersten Teil sind alle IMAP Server angeordnet, der zweite Teil enthlt alle Ordner. Die Knoten des Ordner-Teils im Baum knnen mit der Maus verschoben werden. Mit der rechten Maustaste knnen Sie zudem ein Kontextmen ffnen, welches unter anderem das Lschen des gewhlten Knotens erlaubt. Ein Ordner ist als "INBOX" markiert. Dieser Ordner wird als Eingangs- ordner betrachtet, und TkRat ffnet diesen Ordner beim Start. Die "INBOX" kann auch in Ablauf-Aktionen der Datenbank verwendet werden. Sie knnen nur einen Ordner als "INBOX" verwenden. Abgehende Nachrichten knnen als Kopie in einem Ordner abgelegt werden. In welchem Ordner diese Kopien abgelegt werden, knnen Sie fr jede Rolle einzeln im "Einstellungen" Fenster festlegen (siehe auch "Rollen"). Ordner einlesen Bei einigen Ordner-Typen (Datei, IMAP, MH) ist es mglich, einen Ordner-Baum komplett zu importieren. Whlen Sie dazu als Typ fr einen neuen Ordner den passenden "Importieren" Eintrag. Geben Sie dann den Pfad zu den Ordnern, die Sie importieren mchten, an. Die Angaben sind abhngig vom gewhlten Typ. Beim IMAP-Import knnen Sie zustzlich angeben, dass die Ordner als "offline Ordner" angelegt werden sollen (TkRat fhrt eine lokale Kopie von offline Ordnern). Das folgende Feld gibt das Muster an, mit dem Sie bestimmen knnen, welche Ordner tatschlich importiert werden. Nur die Ordner, die zu diesem Muster passen, werden eingelesen. Ein "*" passt dabei auf alles und bedeutet zustzlich, dass TkRat passende Unterverzeichnisse mit einbezieht. Ein "%" passt ebenfalls auf alles, Unterverzeichnisse werden jedoch nicht durchsucht. Sie knnen den Import auf abonnierte Ordner beschrnken. Tipp: Wenn Sie versuchen, Ihre IMAP Ordner aus einem anderen Programm zu importieren, versuchen Sie es zunchst mit der Option "nur abonnierte Ordner". Ein weiterer Tipp: Wenn Sie sich unsicher sind, was importiert werden wird, verwenden Sie das "%" Muster anstelle von "*", um zu vermeiden, dass das gesamte Dateisystem durchsucht wird. Beachten Sie, dass eine importierte Ordner-Struktur nicht verndert werden kann, d.h., in einer importierten Struktur knnen Sie die Knoten nicht mit der Maus verschieben. nderungen auerhalb von TkRat werden erst wirksam, wenn die Ordner das nchste Mal neu eingelesen werden. } pt { Definio de pastas O programa TkRat permite-lhe organizar as suas pastas numa estrutura tipo rvore, anloga a um sistema de ficheiros. Esta estrutura usada para construir os menus 'Pastas' e 'Mover'. Quando invoca a opo 'Criar/Editar pasta', a janela de definio de pastas ser lanada. Esta permite-lhe modificar a estrutura e operar diversas aces em pastas (criar, editar, apagar, etc.) Note bem que esta estrutura existe apenas no contexto do programa TkRat pelo que no reflecte necessariamente a disposio dos ficheiros em disco. A janela de definio de pastas encontra-se dividida em duas partes. A parte esquerda representa a estrutura em rvore das pastas; a parte direita mostra os detalhes relativos pasta seleccionada. A rvore tem duas folhas no nvel de topo, uma contendo os servidores IMAP definidos e outra contendo todas as pastas. possvel mover pastas de um local para outro com uma aco tipo 'drag & drop'. Para isto basta premir o boto esquerdo sobre um objecto e mant-lo premido enquanto se movimenta o rato. Para alm disto, pressionando o boto direito do rato surge um pequeno menu que, entre outras aces, lhe permite remover a folha (uma pasta ou um submenu.) Uma das pastas tem a palavra 'INBOX' sua direita, sendo esta a pasta de entrada por defeito. Quer isto dizer que o programa TkRat abrir esta pasta por defeito ao iniciar. Pode igualmente ser usada para receber mensagens da base de dados cujos prazos venceram (ver 'Base de Dados'.) Apenas uma pasta pode ser definida como pasta de entrada. As pastas podem ser igualmente utilizadas para guardar as mensagens enviadas. A pasta a utilizar neste caso pode ser definida na janela de Preferncias, separadamente para cada identidade (ver 'Identidades'.) Importar pastas Com determinados tipos de pastas (ficheiro, IMAP ou MH), possvel importar uma estrutura completa de pastas. Para isso, seleccione o tipo apropriado de pasta a importar e fornea o caminho para a mesma. Em relao ao caminho a indicar, isso depender dos tipos de caixa de correio e servidor; valores tpicos so deixar o espao em branco ou o directrio 'HOME'. Na importao tipo IMAP ainda possvel especificar se as pastas devero ser importadas como pastas desligadas (neste caso o TkRat ir manter uma cpia local.) O campo seguinte a preencher o 'padro'. Este permite limitar o nmero de pastas a importar. Apenas pastas em que o padro encontrado sero importadas. Dois 'wildcards' podem ser utilizados: 1) '*' corresponde a tudo e causa a importao de todos os ficheiros e subdirectrios; 2) '%' corresponde apenas a ficheiros pelo que subdirectrios no so importados. possvel ainda limitar a importao a pastas subscritas. Sugesto: Se pretender importar as pastas IMAP de outro programa, conveniente usar a opo 'Apenas pastas subscritas'. Para alm disso, se est inseguro quanto ao que deve de facto importar, use o wildcard % para evitar importar o seu sistema de ficheiros inteiro. Note que a estrutura de pastas importadas de leitura apenas, pelo que aces do tipo 'drag & drop' entre pastas importadas no so possveis. Qualquer outra mudana que introduza na estrutura em disco ser reflectida da prxima vez que importar a pasta. } label title,dbase sv Databasen en {The database} de {Die Datenbank} it {Il Database} fr {La base de donnes} sr {Baza podataka} pl {Baza danych} pt {A Base de Dados} label dbase sv { Databasen Databasen r ett stlle dr man sparar brev. Varje brev r associerat till ett antal nyckelord och en expireringstid (kan vara evigheten). Nr man lgger in ett brev i databasen s kan du ge fljande data: * nyckelord - en lista med nyckelord * expireringstid - tidpunkten nr det hr brevet expirerar frn databasen. Tiden skall ges som ett plustecken omedelbart fljt av ett nummer (inget mellanslag). Numret r antalet dagar som brevet skall ligga i databasen. Detta flt kan vara tomot och motsvarar d evigheten. * expireringshndelse - vad som skall hnda nr brevet expirerar. Mjliga vrden r: * Ta bort - radera brevet. * Inkommande - lgg in brevet i din INBOX. * Arkiv - arkivera brevet. Nr du vill hmta brev frn databasen s kan du ska p fljande flt: to, from, cc, subject, nyckelord och hela brev. Du kan f alla brev som matchar alla kriterier eller de som matchar minst ett. } en { The database The database is a place for storing messages. Each message is associated with a number of keywords and an expiration time (can be eternity). When you insert a message into the database, you can give the following information to be associated with it: * keywords - An optional list of keywords * expiration time - A time when the message expires from the database. The time is given as a plus sign and a number and the number is the number of days it should remain in the database. The filed may be blank and then the message never expires. * expiration action - What to do when the message expires. The possible actions are: * Remove - Delete the message * Incoming - Move the message to the incoming folder again * Backup - Move the message to some backup format When you want to fetch messages from the database, you can search the following fields: to, from, cc, subject, keywords and the entire message. You can find messages which match all criteria or just one. } de { Die Datenbank In der Datenbank knnen Sie Ihre Nachrichten speichern. Jeder Nachricht wird eine Anzahl von Schlsselwrtern und ein Ablauf-Datum zugeordnet (ewige Halt- barkeit ist mglich). Beim Speichern einer Nachricht in der Datenbank knnen Sie die folgenden Angaben machen: * Schlsselbegriffe - eine Liste von Schlsselwrtern, die leer sein kann. * Ablauf-Datum - das Datum, zu dem die Nachricht in der Datenbank ab- luft. Das Datum wird relativ zum aktuellen Datum mit einem '+' Zeichen und einer Zahl angegeben, welche die Anzahl Tage angibt, fr die die Nachricht in der Datenbank verbleiben soll. Wenn dieses Feld leer bleibt, bleibt die Nachricht fr immer haltbar, luft also nie ab. * Ablauf-Aktion - Wenn das Ablauf-Datum der Nachricht erreicht ist, wird die gewhlte Aktion ausgefhrt: * Keine - Die Nachricht verbleibt weiterhin in der Datenbank. * Entfernen - Die Nachricht wird gelscht. * Backup - Die Nachricht wird in ein Backup eingeordnet. Die Datenbank kann nach Nachrichten durchsucht werden. Dazu dient die Option "Datenbank durchsuchen ..." im Men "Ordner". Als Auswahlkriterien knnen die Kopfzeilen "Betreff", "An", "Von" und "Kopie", sowie die Schlsselbegriffe und schlielich Wrter aus der gesamten Nachricht ("Alle") angegeben werden. Werden mehrere Kriterien angegeben, so knnen diese logisch mit UND oder ODER ver- knpft werden. Datenbank-Abfragen nach Schlsselbegriffen knnen auch in der Struktur virtueller Ordner abgebildet werden. } it { Il database Il database e' un metodo per salvare messaggi. Ciascun messaggio e' associato con alcune parole chiave e una data di scadenza (che pu essere infinita). Quando un messaggio viene inserito nel database, e' possibile assegnarvi le seguenti informazioni: * parole chiave - una lista di parole chiave (opzionale) * data di scadenza - una data in cui il messaggio verr considerato scaduto. La data viene indicata con un segno "+" e un numero di giorni. Il campo pu rimanere vuoto: in questo caso il messaggio non scadr mai. * azione di scadenza - cosa accade quando il messaggio scade. Le scelte sono: * Rimouvi - rimuove il messaggio * Inserisci in entrata - muove il messaggio nella cartella in entrata * Muovi in archivio - muove il messaggio in qualche formato d'archivio Quando si vuole prelevare i messaggi dal database e' possibile effettuare una ricerca sui seguenti campi: a, da, copia, soggetto, parole chiave e l'intero messaggio. E' possibile prelevare messaggi che corrispondono a tutti i criteri di ricerca o solo a uno. } fr { La base de donnes La base de donnes est un bon endroit pour stocker les messages. Chaque message est associ un ou plusieurs mots-cls et une date d'expiration (qui peut tre infiniment loin dans le futur). Lorsque vous insrez un message dans la base de donnes, vous pouvez donner les renseignements suivants : * mots-cls - liste de mots-cls optionnels. * date d'expiration - La date est exprime sous la forme +N, o N est le nombre de jours pendant lesquels le message doit rester dans la base de donnes. Par exemple, +365 laissera le message dans la base de donnes pendant un an. Si le champ est laiss vide, le message n'expire jamais. * Action - Que faire lorsque le message arrive expiration * Dtruire - Le message est dtruit, * Arrive - Le message est remis dans la bote lettres d'arrive, * Archive - Le message est dplac et archiv (compression) Pour rechercher des messages dans la bote lettres, vous pouvez utiliser les champs suivants : De: Pour: Copie-Carbone: Objet: Mots-cls: ainsi que le message en entier. Il est possible de retenir les messages concordant avec au moins un des critres ou avec tous les critres. } sr { Baza podataka Baza je mesto za smetanje pisama. Svako pismo je povezano sa izvesnim brojem kljunih rei i vremenom isticanja (to moe biti i do veka). Kada ubacujete pismo u bazu, moete zadati ove podatke koji e biti povezani s porukom: * kljune rei - opciona lista kljunih rei, * vreme isticanja - vreme posle koga pismo istie iz baze. Vreme se zadaje kao znak plus i broj dana koje pismo treba da provede u bazi. Ako se ovo polje ostavi prazno, pismo nema rok isticanja, * akcija po isticanju - ta initi sa pismom kad mu istekne rok. Mogue akcije su: * Obrii - ukloni pismo, * Pristiglo - ubaci pismo u skup za dolazee poruke, * Arhiviraj - skloni pismo iz baze i sauvaj ga na pogodan nain, Kada elite da izdvojite pisma iz baze, moete je pretraivati po ovim poljima: Od, Za, Kopija, Naslov, Kljune rei i itava poruka. Moete izdvojiti pisma koja se uklapaju u sve uslove ili samo u jedan. } pt { A Base de Dados A base de dados um tipo particular de pasta de correio electrnico. Cada mensagem associada a um nmero de palavras chave informativas e uma data de validade (que pode ser para sempre.) Quando insere uma mensagem na base de dados. -lhe solicitado que fornea a seguinte informao, a qual ficar associada mensagem: * palavras chave - uma lista opcional de palavras chave * data de vencimento - o tempo que leva at esta mensagem expirar. Este tempo dado por um sinal mais (+) seguido de um nmero, sendo este o nmero de dias que esta mensagem dever permanecer na base de dados. este campo pode ser deixado e branco e neste caso a data de validade infinita. * aco aps expirar - destino a dar mensagem aps a data de vali- dade ter sido atingida. Aces possveis: * Nenhuma - no executa qualquer aco * Apagar - apaga esta mensagem * Inserir em inbox - coloca a mensagem de volta na caixa de correio de entrada Quando desejar encontrar determinadas mensagens da base de dados pode operar uma procura com base nos seguintes campos: De, Para, Cpia, Assunto, palavras chave e/ou a mensagem inteira. Pode basear a procura em todos os critrios em simultneo ou apenas um. } label title,userproc sv Userproc en Userprocs de Benutzerprozeduren it {Procedure Utente} fr {Procdure utilisateur} sr {Korisnike procedure} pl {Procedury uytkownika} pt {Procedimentos do utilizador} label userproc sv { Anvndardefinierade procedurer Anvndardefinierade procedurer r ett stt fr anvndaren att f mera kontroll ver vissa features. Procedurerna r vanliga tcl-procedurer (dvs de r skriva i tcl7.5). Ingen av procedurerna mste existera, de anvnds bara om de finns. Anvndaren kan definiera procedurer i ~/.ratatosk/userproc (eller vad userproc r satt till). Om procedurerna behver ngra globala variabler s mste dessa heta ngot som brjar med "ratUP_". Fljande procedurer kan definieras: RatUP_IsMe brevlda domn namn adl brevlda - Brevldans namn domn - Domndelen av adressen namn - Det personliga namnet (om angivet). adl - At-domain-list source route Denna proceduren skall avgra om den givna adressen pekar p mig eller inte. Den skall returnera ett sanningsvrde som skall vara sant om adressen pekar p mig. RatUP_Translate brevlda domn namn adl brevlda - Brevldans namn domn - Domndelen av adressen namn - Det personliga namnet (om angivet). adl - At-domain-list source route Nr man svarar p ett brev s kommer alla adresserna som vi skickar det till att kras genom denna proceduren. Den frvntas returnera en lista med fyra element {brevlda domn namn adl}. RatUP_Signature brev brev - Namnet p en global array som kan innehlla information om brevet som signaturen skall lggas in i. Denna proceduren skall returnera en textstrng som sedan kommer att lggas till i slutet av den frsta textdelen av brevet. Argumentet "brev" r namnet p en global array som KAN innhlla information om brevet. Se avsnittet om RatSend i interface-filen. Notera att denna proceduren anropas innan anvndaren har hunnit gra ngot med brevet s det enda fall d det finns ngon intressant information tillgnglig r d man svarar p eller skickar vidare ett brev. RapUP_Bell Denna funktion skall meddela anvndaren att ny post har kommit. Om inte denna funktionen finns s rings terminalklockan. RatUP_ShowURL url url - URLen som skall visas Denna funktion kan anropas nr anvndaren trycker ned vnster musknapp p en URL i ett brev. Den frvntas starta en webblddrare (i bakgrunden). RatUP_Citation brev brev - Handtaget fr det brev som skall citeras Denna funktion skall returnera en strng som kommer att lggas in frst p alla rader i bredtexten av det citerade brevet. Den anropas nr du svarar p ett bev. RatUP_NetsyncFolder spec anvndare protokoll spec - En ntverksfolderspecifikation {host:port}mailbox anvndare - Anvndare som vi skall koppla upp oss som protokoll - Protokoll (fr tillfllet alltid imap) Denna funktion skall returnera ett boolskt vrde som indikerar om den frikopplade mappen som vi fick som argument skall sykroniseras fr tillfllet eller inte. Detta betyder att funktionen kommer att anropas en gng fr varje frikopplad mapp varje gng du vljer "Ntverkssynk" i menyn. Den kommer INTE att anropas om du vljer "Ntverkssynk. mapp" ur "Administration"-menyn fr att synkronisera den aktiva mappen. } en { User defined procedures User defined procedures are a way for the user to have more control over certain features. The procedures are ordinary tcl procedures (i.e. they are written in tcl7.5). None of the procedures must exist, they will be used only if they exist. The user may define procedures in the ~/.ratatosk/userproc file (or whatever the userproc option is set to). If the procedures need any global variables, these must start with "ratUP_". The following procedures may be defined: RatUP_IsMe mailbox domain personal adl mailbox - Mailbox name domain - The domain part personal - The personal name phrase (if any) adl - At-domain-list source route This procedure should determine if the address is pointing to me or not. It should return a boolean value, which should be true if the address is pointing at me. RatUP_Translate mailbox domain personal adl mailbox - Mailbox name domain - The domain part personal - The personal name phrase (if any) adl - At-domain-list source route (probably empty) When we are replying to a message, all addresses we are sending to are run through this procedure. It is expected to return a list with four elements {mailbox domain personal adl}. RatUP_Signature message message - The name of a global array which may contain information about the message the signature will be appended to This function should return a text string which will later be appended to the first text part of the message. The message argument is the name of a global array which MAY contain information about the message. See the section on RatSend in the interface file). Note that this routine gets called before the user has had any chance to edit the message so there will not be any interesting information available at all for new messages. It only contains interesting things for replies and forwards. RatUP_Bell This function should notify the user that new mail has arrived. If this function does not exist, then the terminal bell is rung. RatUP_ShowURL url url - A URL to show This function may get called when the user presses the left mousebutton over a URL in a message. It is expected to invoke a browser (in the background). RatUP_Citation message message - The handler for the message which is being cited This function should return a string which will be prepended to all lines in the body of the cited message. It will be called when you reply to a message. RatUP_NetsyncFolder spec user prot spec - A network folder specification {host:port}mailbox user - User to connect as prot - Protocol (currently always imap) This function should return a boolean value which indicates if the disconnected folder given as an argument should be synchronized at this moment or not. That means that this function will be called once for each disconnected mailbox when you select "Network sync" from the menu. It will NOT be called when you select "Network sync. folder" from the Admin menu to synchronize the current folder. } de { Benutzerprozeduren Benutzerdefinierte Prozeduren geben Ihnen zustzliche Mglichkeiten der Kontrolle ber bestimmte Funktionen. Die Prozeduren mssen gewhnliche Tcl-Prozeduren sein (entsprechend Tcl 7.5). Die Prozeduren werden nur aufgerufen, falls sie existieren. Sie mssen diese Prozeduren also nicht definieren, wenn sie diese zustzlichen Funktionen nicht bentigen. Die Benutzerprozeduren werden in der Datei ~/.ratatosk/userproc definiert, bzw. in der durch die entsprechende Einstellung (Fenster "Einstellungen", Register "Dateien") bestimmten Datei. Falls in den Prozeduren globale Variablen verwendet werden, mssen deren Namen mit "ratUP_" beginnen. Die folgenden Prozeduren knnen definiert werden: RatUP_IsMe mailbox domain personal adl mailbox - Name des Postfachs domain - Der Domne-Teil personal - Persnlicher Name (falls vorhanden) adl - At-domain-list source route Diese Prozedure soll bestimmen, ob eine Adresse Ihnen gehrt oder nicht. Die Prozedur soll einen Wahrheitswert (boolean) zurckgeben, der wahr ist, falls die Adresse Ihnen gehrt. RatUP_Translate mailbox domain personal adl mailbox - Name des Postfachs domain - Der Domne-Teil personal - Persnlicher Name (falls vorhanden) adl - At-domain-list source route (vermutlich leer) Beim Antworten auf eine Nachricht werden alle Adressen an die wir senden mit dieser Prozedur "berssetzt". Die Prozedur soll eine Liste aus vier Elementen zurckgeben: {mailbox domain personal adl}. RatUP_Signature message message - Name einer globalen Array-Variablen, die Informationen ber die Nachricht enthalten kann, an die eine Signatur angehngt werden soll Diese Prozedur soll einen Text (string) zurckgeben, der spter an den ersten Textteil der Nachricht angefgt wird. Der Parameter message ist der Name einer globalen Array-Variablen, die Informationen ber die Nachricht enthalten KANN (vgl. den Abschnitt bezgl. Ratsend in der Datei interface). Beachten Sie, dass diese Routine aufgerufen wird, BEVOR der Benutzer Gelegenheit bekommt, die Nachricht zu bearbeiten. Fr neue Nachrichten wird es daher keine relevanten Informationen geben. Lediglich fr Antworten und weitergeleitete Nachrichten enthlt message relevante Informationen. RatUP_Bell Diese Prozedur soll den Benutzer benachrichtigen, dass neue Nachrichten eingetroffen sind. Falls diese Prozedur nicht existiert, wird ein akustisches Signal (terminal bell) gegeben. RatUP_ShowURL url url Anzuzeigender URL Diese Prozedur wird aufgerufen, wenn der Benutzer einen URL mit der linken Maustaste anklickt. Die Prozedur soll einen Browser im Hintergrund starten. RatUP_Citation message message - The handler for the message which is being cited message - Ein Handle der Nachricht, die zitiert wird Diese Prozedur soll eine Zeichenkette (string) zurckgeben, welche vor dem Text der zitierten Nachricht eingefgt wird. Die Prozedur wird auf- gerufen, wenn Sie auf eine Nachricht antworten. RatUP_NetsyncFolder spec user prot spec - Eine Netzwerk-Postfach Spezifikation {host:port}mailbox user - Benutzername fr die Anmeldung prot - Protokoll (gegenwrtig immer IMAP) Diese Prozedur soll einen Wahrheitswert (boolean) zurckgeben, der anzeigt, ob der angegebene Offline-Ordner jetzt synchronisiert werden soll. Diese Prozedur wird fr jeden Offline-Ordner einmal aufgerufen, wenn Sie "Netzwerksynchronisation" aus dem Men aufrufen. Die Prozedur wird NICHT aufgerufen, wenn Sie "Netzwerksynchronisation des Ordners" aus dem Men "Administration" aufrufen. } fr { Procdures utilisateur Les procdures utilisateur permettent d'avoir encore plus de contrle sur certaines fonctionnalits de TkRat. Les procdures sont des procdures ordinaires de tclsh. Aucune procdure utilisateur n'est obligatoire ; les procdures sont utilises automatiquement si elles existent. Vous pouvez dfinir vos procdures dans le fichier ~/.ratatosk/userproc (ou ailleurs suivant la valeur dans la fentre prfrences). Si les procdures ont besoin d'accder des variables globales, elles doivent commencer par "ratUP_" (pas plus clair en anglais, dsol). Les procdures suivantes peuvent tre dfinies : proc RatUP_IsMe {mailbox domain personal adl} { } mailbox - nom de la bote lettres, domain - Le domaine, personal - Le nom complet (si disponible), adl - At-domain-list source route. Cette procdure doit dterminer si l'adresse pointe vers vous ou non. une valeur boolenne doit tre retourne. proc RatUP_Translate {mailbox domain personal adl} { } mailbox - nom de la bote lettres, domain - le domaine personal - Le nom complet (si disponible), adl - At-domain-list source route (probablement vide) Lors d'une rponse faite un message toutes les adresses destinataires sont transmises cette procdure. elle doit retourner une liste de 4 lments {mailbox domain personal adl}. proc RatUP_Signature message { } message - nom d'un tableau global qui contient des informations relatives au message auquel la signature doit tre ajoute. Cette fonction doit retourner une chane de caractres correspondant la signature insrer dans le message. Le paramtre message est un descripteur de message qui peut servir obtenir des informations sur le message en cours de composition. Comme cette procdure est appele avant que l'utilisateur ait eu le temps de saisir quoi que ce soit, Il n'y aura des informations intressantes que si la composition en cours est une rponse un autre message ou un racheminement. Exemple de procdure RatUP_Signature # Procdure de signature. # J'ai 2 fichiers de signature # ~/.signature.ext est pour les courriels expdis l''extrieur # avec mon numro de tl etc... # ~/.signature. est usage interne, avec des trucs drles dedans. # Si j'envoie un message "quelq'un" ou a "quelqu'un@mondomaine" # La signature interne est utilise. # Sinon la signature externe est utilise. proc RatUP_Signature {message} { global env upvar #0 $message msg set dom {@mydomain} set localsig $env(HOME)/.signature set externalsig $env(HOME)/.signature.ext # La signature par dfaut est la signature interne set sig $localsig # Si une seule adresse dans les champs "Pour:" (To:) et # "Copie-Carbone" (Cc:), mais pas "Copie cache" (Bcc:) est # diffrente de "truc" ou "bidule@mondomaine", alors on # utilise la signature externe. foreach i [concat $msg(to) $msg(cc)] { if {[string first @ $i] && ![regexp -nocase $dom $i]} { set sig $externalsig break } } set fd [open $sig r] set text [read -nonewline $fd] return $text } proc RatUP_Bell {} { } Cette fonction doit avertir l'utilisateur qu'un message est arriv. Si cette procdure n'existe pas, le terminal sonne. Exemple de procdure RatUP_Bell : # Cette precdure joue un son au hasard parmis une liste de son. # pris dans un rpertoire donn: proc RatUP_Bell {} { # La liste des fichiers de sons (adaptez selon vos gots ) : set sounds { apert.wav apert2.wav applause.wav beam.wav beam2.wav cow.wav curve.wav drama.wav explos.wav falling.wav glasses.wav gong.wav horse.wav kling.wav kongas.wav laser.wav left.wav nature1.wav nature2.wav ok.wav pluck.wav roll.wav romans.wav soft.wav space.wav space2.wav space3.wav sparcle.wav strom.wav theetone.wav top.wav train.wav untie.wav ups.wav wallewal.wav } # Le rpertoire o se trouvent les fichiers sons. set dir /home/local/staroffice6.0/share/gallery/sounds # La commande utiliser pour jouer les sons set command /usr/bin/play # Pas de changement ncessaires ci dessous set i [expr int( rand() * [llength $sounds])] catch {exec /bin/sh -c "$command [file join $dir [lindex $sounds $i]] &" >&/dev/null} } proc RatUP_ShowURL {url} { } url - une URL afficher Cette fonction est active lorsque l'utilisateur clique sur une URL. Elle doit se charger de lancer un navigateur en tche de fond. proc RatUP_Citation message { } message - Un descripteur de message du message cit. Cette fonction doit retourner une chane de caractres qui sera ajoute au dbut de chaque ligne du message cit. proc RatUP_NetsyncFolder {spec user prot} { } spec - Dfinition de la bote distante {hte:port}bote__lettres user - Utilisateur distant prot - Protocole (toujours imap pour le moment) Cette fonction doit retourner une valeur boolenne qui indique si la bote lettres dconnecte donne en argument doit tre synchronise maintenant. Cette fonction est appele lorsque vous slectionnez Synchronisation rseau du menu TkRat. Elle n'est pas appele lorsque vous slectionnez Synchroniser bote lettres dconnecte du menu Administration. } sr { Korisnike procedure Korisnike procedure su nain da korisnik stekne dodatnu kontrolu nad odreenim mogunostima programa. Procedure su obine TCL procedure (tj. napisane su u TCL7.5). Nijedna od ovih procedura ne mora da postoji, ali bie koriene ako postoje. Korisnik moe da definie procedure u datoteci ~/.ratatosk/userproc (ili gde je ve postavljena opcija "Userproc-datoteka"). Ako procedure koriste globalne varijable, one moraju poinjati sa "ratUP_". Mogu se definisati sledee procedure: RatUP_IsMe mailbox domen ime adl mailbox - ime potanskog sandueta domen - domenski deo adrese fraza - lina fraza za imenovanje (ako je ima) adl - at-domain-list izvorna ruta Ova procedura treba da odredi da li adresa upuuje na mene ili ne. Treba da vrati logiku vrednost "tano" ako adresa zaista upuuje na mene. RatUP_Translate mailbox domen ime adl mailbox - ime potanskog sandueta domen - domenski deo adrese fraza - lina fraza za imenovanje (ako je ima) adl - at-domain-list izvorna ruta (verovatno prazna) Kada se odgovara na pismo, sve adrese na koje se alje se proputaju kroz ovu proceduru. Ona treba da vrati listu od etiri elementa {mailbox domen fraza adl}. RatUP_Signature pismo pismo - hendler za pismo na koje treba dodati potpis Ova funkcija treba da vrati niz znakova koji e kasnije biti dodat na prvi tekstualni deo pisma. Argument 'pismo' je hendler koji se moe koristiti da se dobiju podaci o pismu (pogledajte datoteku 'interface' za dodatna obavetenja). Ova rutina se poziva pre no to korisnik uopte dobije priliku da menja pismo, tako da za nova pisma nee biti nikakvih zanimljivih podataka - smislene stvari se mogu dobiti samo za odgovore i preusmerene poruke. RatUP_Bell Ova funkcija treba da obavesti korisnika da je stigla nova pota. Ako ova procedura ne postoji, onda se signal alje preko terminalskog zvona. RatUP_ShowURL url url - URL koji treba prikazati Ova funkcija se poziva kada korisnik klikne levim tasterom mia na URL unutar pisma i treba da pokrene odgovarajui ita (u pozadini). RatUP_Citation pismo pismo - hendler za pismo koje se citira Ova funkcija treba da vrati niz znakova koji e biti dodati na poetak svih redova u telu citiranog pisma. Poziva se kada odgovarate na pismo. RatUP_NetsyncFolder spec ime prot spec - specifikacija skupa na mrei {host:port}mailbox ime - ime korisnika pod kojim se pristupa skupu prot - protokol (za sada samo imap) Ova funkcija treba da vrati logiku vrednost koja govori da li nepovezani skup koji je dat kao argument treba da se sinhronizuje u datom trenutku ili ne. Ovo znai da e se funkcija pozivati po jednom za svaki nepove- zani skup kada odaberete "Mreno sinhronizovanje" iz menija. Ona se NEE pozivati kada naredite "Sinhronizuj skup preko mree" iz menija "Podeavanja" da bi se sinhronizovao tekui skup. } pt { Procedimentos definidos pelo utilizador Procedimentos definidos pelo utilizador so uma forma eficiente de se melhor controlar algumas caractersticas do programa. Os procedimentos so simples funes Tcl (isto , so escritos em Tcl7.5.) Nenhum dos procedimentos ter obrigatoriamente de existir, ao invs sero apenas usados caso estejam definidos. O utilizador pode definir estas funes num ficheiro de procedimentos (por defeito ~/.ratatosk/userproc). Caso estas funes necessitem alguma varivel global, ento devem comear com o prefixo "ratUP_". As seguintes funes podem ser definidas: RatUP_IsMe mailbox domain personal adl mailbox - nome da caixa de correio domain - domnio personal - parte de identificao pessoal adl - at-domain-list Esta funo dever determinar se o endereo utilizado pertence ou no ao utilizador. Deve fornecer um valor lgico como valor de sada, sendo verdadeiro se o endereo pertencer ao utilizador. RatUP_Translate mailbox domain personal adl mailbox - nome da caixa de correio domain - domnio personal - parte de identificao pessoal adl - at-domain-list RatUP_Bell Esta funo deve avisar o utilizador da chegada de novo correio electrnico. Se a funo no existir ento alternativamente toca a campainha do terminal. RatShowURL url url - um endereo url a abrir num browser Esta funo pode ser invocada quando o utilizador pressiona o boto esquerdo do rato sobre um endereo URL contido numa mensagem. esperado que o browser seja lanado para visualizao do URL. RatUP_Citation message message - a varivel que representa a mensagem a citar Esta funo deve fornecer uma 'string' que ser usada como prefixo em todas as linhas do corpo da mensagem citada. invocada quando o utilizador inicia a resposta a uma mensagem. RatUP_NetsyncFolder spec user prot spec - Especificao de pasta de rede tipo {host:port}mailbox user - Identificao do utilizador no servidor prot - Protocolo (de momento apenas IMAP) Esta funo dever fornecer um valor lgico que indica se uma pasta 'desligada' (o argumento da funo) dever ou no ser sincronizada. Quer isto dizer que esta funo ser invocada cada vez que seleccionar a entrada 'Sincronizar pasta' no menu 'Opes'. } label title,bugreport sv Felrapportering en {Reporting bugs} de Bugreports it {Rapporto malfunzioni} fr {rapport de bogue} sr {Izvetaji o grekama} pl {Zgaszanie bdw} pt {Relatrio de mau funcionamento} label bugreport sv { Felrapportering Felrapporter skickas till maf@TkRat.org. Detta grs bst genom att man vljer "Skicka felrapport..." ur hjlpmenyn (i mappfnstret). Detta kommer att skapa ett "Skriva brev"-fnster med en felrapportmall. Mottagaren r redan ifylld och en del av mnet. Du mste komplettera mnet och skriva en detaljerad version av hur du gjorde fr att komma til felet (och av felet) in brdtexten. TkRat har redan lagt till en bilaga som innehller information on din konfiguration. Denna bilagan innehller utskriften frn "uname -a" p ditt system, TkRats versionsnummer, tcl/tk versionsnummer och en kopa av alla dina instllningar. Om du vill se exakt vad som skickas s lagras bilagan i en fil som har ett namn p formen ~/.ratatosk/send/rat.X.Y (det r troligen den sista filen). Denna filen skapas nr du vljer "Skick felrapport..." och raderas nr rapporten skickas. } en { Reporting bugs You should send bug reports to maf@TkRat.org. This is best done by selecting "Send bug report..." in the Help menu (of the Folder window). This will open a Compose window with a template bug report. The recipient is already filled in as is part of the subject. You should complete the subject and write a detailed description of what you did to discover the bug (and the bug itself) in the body of the message. TkRat has already added an attachment which contains information about your configuration. This attachment contains the output from "uname -a" on your system, TkRat version number and date, tcl/tk version used, and a copy of all the settings in the Preferences window. If you wish to see exactly what is sent, then look at the contents of a file with a name like ~/.ratatosk/send/rat.X.Y (it is most probably the last file). This file gets created when you start "Send bug report..." and gets deleted when the report is sent. } de { Bugreports Bugreports sollten Sie an maf@TkRat.org senden. Am besten verwenden Sie dazu die Option "Schicke Bugreport..." im "Hilfe" Men des Hauptfensters. Damit ffnen Sie ein Fenster, in dem Sie die Beschreibung des Fehlers eingeben knnen. Geben Sie einen Betreff ein, der den Fehler stichwortartig beschreibt, und schreiben Sie eine detailierte Beschreibung des Problems in das dafr vor- gesehene Feld. Drcken Sie dann auf "Weiter". Ihre Eingaben werden in eine neue Nachricht bernommen. Der Empfnger ist bereits eingetragen. Weiterhin wird eine Beschreibung Ihrer Konfiguration als Anhang zu dieser Nachricht erstellt. Diese Beschreibung enthlt die Ausgabe des Befehls "uname -a" auf Ihrem System, die Versionsnummer und Datum von TkRat, die Versionsnummer von Tcl/Tk, sowie eine Kopie aller Einstellungen aus dem "Einstellungen" Fenster. Den Inhalt dieses Anhangs knnen Sie kontrollieren: Der Anhang ist in einer Datei gespeichert, deren Name die Form "~/.ratatosk/send/rat.X.Y" hat. Diese Datei wird erzeugt, wenn Sie die Option "Schicke Bugreport..." aufrufen, und sie wird gelscht, wenn die Nachricht verschickt wurde. } it { Rapporto malfunzioni Si dovrebbero spedire malfunzionamenti a maf@TkRat.org. Il modo migliore per fare ci e' di selezionare "Spedisci malfunzione..." dal menu d'Aiuto (della finestra cartella). In tal modo verr aperta una finestra di composizione con uno schema di malfunzione. Il destinatario e' gi inserito, cosi' come parte del soggetto. Si deve completare il soggetto e scrivere una descrizione dettagliata di cosa ha causato la malfunzione (e la malfunzione stessa) nel corpo del messaggio. TkRat include automaticamente un allegato contenente informazioni riguardo la configurazione. Questo allegato contiene l'output di "uname -a" del sistema, la versione e la data di TkRat, la versione di tcl/tk e una copia di tutte le preferenze selezionate. Se si vuole vedere esattamente cosa viene spedito si pu guardare il file ~/.ratatosk/send/rat.X.Y (e' probabilmente l'ultimo file). Questo file viene creato quando si seleziona "Spedisci malfunzione..." e viene rimosso quando la malfunzione viene spedita. } fr { Rapport de bogue Pour envoyer un description de bogue, contacter maf@TkRat.org (NDT: en sudois, anglais... mais pas en franais!). Le mieux est encore d'utiliser la commande Signaler une bogue du menu Aide de la fentre principale. Cela va ouvrir une fentre de rdaction avec un modle de description de bogue. Il suffit alors de remplir l'objet et de donner une description dtaille du bogue et comment le reproduire. TkRat va joindre au message ainsi cr une description de votre configuration qui contient le rsultat de "uname -a", la version de TkRat, la version de Tcl/Tk et une copie de tous les rglages de la fentre prfrence. Vous pouvez regarder le contenu de cette pice jointe. Il est cr dans un fichier ~/.ratatosk/send/rat.X.Y (c'est certainement le dernier fichier). Ce fichier est cr au moment o vous cliquez sur Signaler une bogue et est dtruit aussitt que le message est expdi. } sr { Izvetaji o grekama Izvetaje o grekama (bagovima) treba da aljete na maf@TkRat.org. Ovo je najpogodnije uraditi tako to odaberete stavku "Izvetaj o greci" iz menija "Pomo" (prozor za pregled skupa). Pojavie se prozor za pisanje poruka sa osnovnom formom izvetaja. Primalac je ve naznaen, kao i deo naslova. Treba da dopunite naslov i napiete detaljan opis ta je izazvalo pojavu greke i kako sama greka izgleda. TkRat je ve zakaio dodatak sa informacijama o konfiguraciji Vaeg sistema. Ovaj dodatak sadri izlaz komande "uname -a", datum i broj verzije TkRat-a, verziju TCL/TK koja se koristi i kopiju svih podeenih opcija. Ako elite da tano vidite ta se alje, pogledajte datoteku pod imenom nalik na "~/.ratatosk/send/rat.X.Y" (to je po svoj prilici poslednja takva datoteka). Ova datoteka se stvara kada pokrenete "Izvetaj o greci" i uklanja se kad se izvetaj poalje. } pt { Relatrio de mau funcionamento Os utilizadores devem enviar relatrios de erros e mau funcionamento ao autor do programa, contactvel por e-mail: maf@TkRat.org. A melhor maneira de o fazer seguir os passos que o programa sugere, seleccionando a entrada "Enviar relatrio de erro..." do menu "Ajuda" da janela principal. Uma janela de redaco de mensagem ser aberta j com um padro para o relatrio de mau funcionamento. O campo do destinatrio j est preenchido, bem como parte da linha Assunto. O utilizador deve completar a mensagem com uma descrio detalhada da aco que deu origem ao erro. Neste ponto, o programa j anexou entretanto um ficheiro com informao relativa sua configurao. Este ficheiro contm basicamente o resultado do comando 'uname -a' no seu sistema, o nmero e data da verso do TkRat, verso Tcl/Tk bem como uma cpia de todas as suas configuraes introduzidas na janela 'Preferncias'. Caso entenda consultar estes ficheiros anexos antes de os enviar, pode encontr-los sob o nome rat.X.Y no directrio ~/.ratatosk/send/ (provavelmente o ficheiro mais recente nesse directrio.) Este ficheiro automaticamente criado quando inicia a redaco da sua mensagem e imediatamente apagado assim que a mensagem for enviada. } label title,roles en Roles sv Roller de Rollen fr Rles label roles en { Roles The Roles feature is a way for you to have different identities. Associated with each role is address info and also settings on how to send email. You can then always change the role and the selected role will affect composed messages. You can also associate roles with folders so that, when working with a particular folder, you use a particular role. Roles are defined in the Preferences window. To create a new role, select 'Roles' in the tree and then press 'Create new role.' This will create a new role in the tree and you can then edit it as much as you want. In the 'Save outgoing' field, you may define a folder in which messages sent using this role should be saved by default. } sv { Roller Roller r ett stt att enkelt skifta mellan olika identiteter. Till varje roll r kopplat adressinformation samt instllningar fr hur brev skickas. Men kan sedan byta roll och den valda rollen kommer att pverkar brev som skrivs. Man kan ocks associera roller med mappar, s att nr du jobbar med en viss mapp s fr du automatiskt en viss roll. Roller definieras i instllningsfnstret. Fr att skapa en ny roll, vlj 'Roller' i trdet och tryck sen p 'Skapa ny roll'. Detta skapar en ny roll i trdet som du sedan kan anpassa. I fltet 'Spara utgende' s kan du stlla in i vilken mapp brev som skickas med denna rollen skall sparas i. } fr { Rles Les rles sont un moyen pour vous d'avoir plusieurs personnalits. Des informations sur votre adresse de courriel ainsi que les rglages concernant l'expdition des messages sont associs chaque rle. lorsque vous changez de rle, les nouveaux messages que vous rdigez en seront affects. Il est galement possible d'affecter un rle une bote lettre, de telle sorte que lorsque vous travaillez avec cette bote, vous utilisez automatiquement le rle qui lui est affect. Les rles sont dfinis dans la fentre de prfrence (menu Administration-> prfrence). Pour crer un nouveau rle, slectionnez Rles dans l'arborescence, puis Crer un nouveau rle . Cela cre un nouveau rle dans l'arbre que vous pouvez diter loisir. Dans le champ Copier les messages expdis dans , vous pouvez slectionner La bote lettre dans laquelle les copies des messages que vous expdiez iront se placer par dfaut. } de { Rollen Mit TkRat knnen Sie in verschiedenen Rollen auftreten. Mit einer Rolle sind Informationen zu Ihrer Absenderadresse und dem Versand von Nachrichten verbunden. Ein Rollenwechsel wirkt sich damit auf die Nachrichten, die Sie versenden, aus. Sie knnen einem Ordner eine Rolle zuordnen. Damit bearbeiten Sie die Nachrichten in diesem Ordner in der festgelegten Rolle. Rollen werden im "Einstellungen" Men angelegt und bearbeitet. Um eine neue Rolle anzulegen, whlen Sie "Rollen" aus, und drcken Sie dann auf die Schaltflche "Neue Rolle anlegen". Damit wird eine neue Rolle im Einstellungen-Baum angelegt, und Sie knnen diese entsprechend Ihren Wnschen anpassen. Unter "Sichere abgehende" knnen Sie fr jede Rolle einen Ordner angeben, in dem abgehende Nachrichten automatisch gesichert werden sollen. } pt { Identidades O programa permite-lhe a utilizao de diferentes identidades. A cada uma destas identidades devem ser associados um endereo de origem e ainda o modo de envio de mensagens. Depois de criada, a identidade pode ser alterada em qualquer momento. Aquando da redac- o de uma nova mensagem, a identidade a utilizar ser a que estiver activa nesse momento. igualmente possvel associar uma identidade a uma ou mais pastas, de forma que quando trabalha com uma determi- nada pasta, activa igualmente a respectiva identidade. As identidades so definidas na janela de Preferncias. Para criar uma nova identidade, seleccione 'Identidades' na rvore de opes e pressione o boto 'Criar nova identidade'. Esta aco criar uma nova folha de identidade na rvore que pode depois editar. ainda possvel designar a pasta onde mensagens enviadas com esta identidade devero ser gravadas por defeito. } tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/merge.tcl000077500000000000000000000041251137544547100216650ustar00rootroot00000000000000# merge.tcl # # Merges messages in one language from one file into another file # # Usage: tclsh merge.tcl file_to_merge_into lang file_to_merge_from # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Check tcl version if {[info tclversion] < 8.1} { puts "This script requires tclsh8.1 or later" exit 1 } source defs.tcl foreach l $languages { lappend langs [lindex $l 0] } # # Check arguments # if {3 != [llength $argv]} { puts "Usage: $argv0 file_to_merge_into lang file_to_merge_from" exit 1 } set basefile [lindex $argv 0] set sourcelang [lindex $argv 1] set sourcefile [lindex $argv 2] if {![file exists $basefile] || ![file writable $basefile]} { puts "$basefile does not exist or is not writable" exit 1 } if {-1 == [lsearch -exact $langs $sourcelang]} { puts "Unkown language $sourcelang, perhaps you need to modify defs.tcl" exit 1 } if {![file readable $sourcefile]} { puts "Can't open $sourcefile for reading" exit 1 } # Define label and variable proc proc label {label} { global current_label labels set current_label $label lappend labels $label } proc variable {v} { global varName set varName $v } # # Read definitions from sourcefile # foreach l $langs { proc $l t {} } proc $sourcelang m \ "global t current_label; set t($sourcelang,\$current_label) \$m" source $sourcefile # # Read definitions from basefile # foreach l $langs { proc $l m "global t current_label; set t($l,\$current_label) \$m" } proc $sourcelang m {} set labels {} source $basefile # # Write output # if {[catch {open $basefile w} fh]} { puts "Failed to open $basefile for writing: $fh" exit 1 } puts $fh $message puts $fh "" puts $fh "variable $varName" puts $fh "" foreach lab $labels { puts $fh "label $lab" foreach lang $langs { if {![info exists t($lang,$lab)]} { puts "$lang: missing $lab" } else { puts $fh [list $lang $t($lang,$lab)] } } puts $fh "" } close $fh tkrat_2.2cvs20100105-dfsg.orig/tkrat/Text/messages.text000066400000000000000000005515271137544547100226110ustar00rootroot00000000000000################################################################# # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssen # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # variable t label tkrat sv TkRat en TkRat de TkRat it TkRat fr TkRat sr TkRat pl TkRat pt TkRat label version sv Version en Version de Version it Versione fr version sr Verzija pl Wersja pt Verso label about sv Om en About de {ber TkRat} it Informazioni fr { propos} sr {O TkRat-u} pl {O programie} pt Acerca label quit sv Avsluta en Quit de Beenden it Esci fr Quitter sr {Kraj rada} pl Wyjd pt Sair label name sv Namn en Name de Name it Nome fr Nom sr Ime pl Nazwa pt Nome label size sv Storlek en Size de Gre it Dimensione fr Taille sr Veliina pl Rozmiar pt Tamanho label messages sv Brev en Messages de Nachrichten it Messaggi fr Messages sr Pisma pl Wiadomoci pt Mensagens label help sv Hjlp en Help de Hilfe it Aiuto fr Aide sr Pomo pl Pomoc pt Ajuda label ok sv Klar en OK de Ok it Ok fr Ok sr Ok pl OK pt OK label cancel sv Avbryt en Cancel de Abbrechen it Annulla fr Annuler sr Poniti pl Anuluj pt Cancelar label ratatosk sv {Ratatosk. I nordisk mytologi, ekorren som springer upp och ner i vrldstrdet, Yggdrasil, brandes frtal frn draken Nidhog vid roten till rnen i dess top, och vice versa.} en {Ratatosk (swift-tusked). In Norse mythology, the squirrel that runs up and down the cosmic or world tree, Yggdrasil, carrying abusive language from the dragon Nidhog at the bottom of the tree to the eagle at the top, and vice versa.} de {Ratatosk ("Nagezahn"). In der nordischen Mythologie der Name des Eichhrnchens, das an der Weltesche Yggdrasil auf und ab klettert und die feindlichen Worte zwischen dem an den Wurzeln nagenden Drachen Nidhog und dem Adler im Wipfel bermittelt.} it {Ratatosk. Nella mitologia norvegese, lo scoiattolo che corre su e gi per l'albero cosmico, Yggdrasil, portando insulti dal drago Nidhog, ai piedi dell'albero, all'aquila in cima, e viceversa.} fr {Ratatosk. Selon la mythologie nordique, l'cureuil qui court de haut en bas de l'arbre cosmique, Yggdrasil, transportant les injures du dragon Nidhog au pied de l'arbre, l'aigle au sommet, et vice versa.} sr {Ratatosk ("Brzozubi"): u nordijskoj mitologiji veverica koja tri gore - dole du kosmikog drveta Yggdrasil-a i prenosi rune rei od zmaja Nidhog-a sa dna drveta do orla na vrhu i natrag.} pl {Ratatosk. W norweskiej mitologii, wiewirka biegajca do gry i na d kosmicznym lub ziemskim drzewem, Yggdrasil, przekazujc obelywe wyzwiska od smoka Nidhoga na dole drzewa, do ora, na grze i vice versa} pt {Ratatosk. Na mitologia nrdica, o esquilo que constantemente sobe e desce a rvore csmica, Yggdrasil, transmitindo insultos do drago Nidhog, na base da rvore, para a guia no topo, e vice-versa.} label months sv {Jan Feb Mar Apr Maj Jun Jul Aug Sep Okt Nov Dec} en {Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec} de {Jan Feb Mr Apr Mai Jun Jul Aug Sep Okt Nov Dez} it {Gen Feb Mar Apr Mag Giu Lug Ago Set Ott Nov Dic} fr {Jan Fv Mar Avr Mai Jui Jul Ao Sep Oct Nov Dc} sr {Jan Feb Mar Apr Maj Jun Jul Avg Sep Okt Nov Dec} pl {Sty Lut Mar Kwi Maj Cze Lip Sie Wrz Pa Lis Gru} pt {Jan Fev Mar Abr Mai Jun Jul Ago Set Out Nov Dez} label move sv Flytta en Move de Verschieben it Muovi fr Dplacer sr Premesti pl Przenie pt Mover label delete sv Radera en Delete de Lschen it Cancella fr Dtruire sr Brii pl Usu pt Apagar label undelete sv {Ta bort radering} en Undelete de Wiederherstellen it {Annulla cancella} fr {Annuler destruction} sr {Poniti brisanje} pl Przywr pt {No apagar} label compose sv {Nytt brev} en Compose de Schreiben it Componi fr {Nouveau message} sr {Novo pismo} pl {Nowa wiadomo} pt Escrever label reply_sender sv {Svara till avsndare} en {Reply to sender} de {Absender antworten} it {Rispondi al mittente} fr {Rpondre l'expditeur} sr {Odgovor poiljaocu} pl {Odpowiedz nadawcy} pt Responder label reply_all sv {Svara till alla} en {Reply to all} de {Allen antworten} it {Rispondi a tutti} fr {Rpondre tous} sr {Odgovor svima} pl {Odpowiedz wszystkim} pt {Responder a todos} label here_is sv {Hr r ett objekt av typen} en {Here is an object of type} de {Dies ist ein Objekt vom Typ} it {Qui c' un oggetto di tipo} fr {Ceci est un objet de type} sr {Ovde je objekat tipa} pl {To jest obiekt typu} pt {Object do tipo} label show_all sv {Visa allt} en {Show all} de {Alles zeigen} it {Mostra tutti} fr {Montrer tout} sr {Pokai sve} pl {Poka wszystko} pt {Mostra todos} label old_dirs sv {Tidigare anvnda} en {Previously used} de {Zuletzt benutzt} it {Usato in precedenza} fr {Prcdemment utiliss} sr {Ranije korieno} pl {Ostatnio otwierane} pt {Usados anteriormente} label save_to_file sv {Spara till fil} en {Save to file} de {In Datei sichern} it {Salva file} fr {Sauver dans un fichier} sr {Snimi u datoteku} pl {Zapisz do pliku} pt {Gravar em ficheiro} label save_failed sv {Skrivningen misslyckades} en {Save failed} de {Sichern fehlgeschlagen} it {Impossibile salvare} fr {Erreur de sauvegarde} sr {Snimanje nije uspelo!} pl {Bd zapisu} pt {Erro ao gravar em ficheiro} label message sv Brev en Message de Nachricht it Messaggio fr Message sr Pismo pl Wiadomo pt Mensagem label print sv {Skriv ut} en Print de Drucken it Stampa fr Imprimer sr tampaj pl Drukuj pt Imprimir label folder_key_print sv {Skriv ut} en Print de Drucken it Stampa fr Imprimer sr tampaj pl Drukuj pt Imprimir label show_all_headers sv {Visa alla huvudrader} en {Show all headers} de {Alle Kopfzeilen zeigen} it {Mostra tutte le intestazioni} fr {Montrer tous les en-ttes} sr {Pokai celo zaglavlje} pl {Poka wszystkie nagwki} pt {Mostra todos os cabealhos} label show_selected_headers sv {Visa valda huvudrader} en {Show selected headers} de {Nur ausgewhlte Kopfzeilen zeigen} it {Mostra alcune intestazioni} fr {Montrer les en-ttes slectionns} sr {Pokai odabrane delove zaglavlja} pl {Poka wybrane nagwki} pt {Mostra cabealhos seleccionados} label show_no_headers sv {Visa inga huvudrader} en {Show no headers} de {Keine Kopfzeilen zeigen} it {Non mostrare intestazioni} fr {Ne montrer aucun en-tte} sr {Ne prikazuj zaglavlje} pl {Nie pokazuj nagwkw} pt {Esconde cabealhos} label compose_name sv {Skriva brev} en {Compose message} de {Nachricht schreiben} it {Componi messaggio} fr {Nouveau message} sr {Sastavljanje pisma} pl {Utwrz wiadomo} pt {Escreve mensagem} label edit sv Editera en Edit de Bearbeiten it Modifica fr dition sr Menjanje pl Edytuj pt Editar label edit_body sv Brdtexten en {Message body} de Nachrichtentext it {Corpo del messaggio} fr {Corps du message} sr {Sadraj pisma} pl {Tre wiadomoci} pt {Corpo da mensagem} label edit_headers sv Huvudflt en {Message headers} de Nachrichten-Kopfzeilen it {Intestazioni del messaggio} fr {En-ttes du message} sr {Zaglavlje pisma} pl {Nagwki wiadomoci} pt {Cabealhos da mensagem} label read_from_file sv {Ls brdtext frn fil} en {Read body from file} de {Aus Datei lesen} it {Leggi da file} fr {Lire un fichier} sr {Proitaj sadraj iz datoteke} pl {Odczytaj tre z pliku} pt {Ler corpo da mensagem a partir de ficheiro} label abort sv Avbryt en Abort de Abbrechen it Anulla fr Abandon sr Prekini pl Anuluj pt Cancelar label headers sv Huvudflt en Headers de Kopfzeilen it Intestazioni fr En-ttes sr Zaglavlje pl Nagwki pt Cabealhos label attachments sv Bilagor en Attachments de Anhnge it Allegati fr {Pices jointes} sr Dodaci pl Zaczniki pt Anexos label attach_file sv {Lgg till fil} en {Attach file} de {Datei anhngen} it {Allega file} fr {Joindre un fichier} sr {Zakai datoteku} pl {Dodaj zacznik} pt {Anexar ficheiro} label attach_special sv {Lgg till special} en {Attach special} de {Spezielles anhngen} it {Allega speciale} fr {Pice jointe spciale} sr {Posebni dodaci} pl {Dodaj specjalny} pt {Anexar especial} label detach sv {Ta bort} en Detach de Entfernen it Rimuovi fr Retirer sr Ukloni pl Usu pt {Remover anexo} label send sv Skicka en Send de Absenden it Spedisci fr Expdier sr alji pl Wylij pt Enviar label send_save sv {Skicka & Spara} en {Send & Save} de {Absenden & Sichern} it {Spedisci e Salva} fr {Expdier et sauver} sr {alji i sauvaj} pl {Wylij i zapisz} pt {Enviar & Gravar} label external_editor sv {Extern redigerare} en {External editor} de {Externer Editor} it {Editor esterno} fr {diteur externe} sr {Spoljni editor} pl {Zewntrzny edytor} pt {Editor externo} label running_ext_editor sv {Texten editeras i en extern redigerare...} en {Running external editor...} de {Starte externen Editor...} it {Eseguendo editor esterno...} fr {Excution de l'diteur externe...} sr {Pokreem spoljni editor...} pl {Uruchamiam zewntrzny edytor...} pt {Editor externo iniciado...} label filename sv Filnamn en Filename de Dateiname it {Nome file} fr {Nom de fichier} sr {Ime datoteke} pl {Nazwa pliku} pt {Nome de ficheiro} label description sv Beskrivning en Description de Beschreibung it Descrizione fr Description sr Opis pl Opis pt Descrio label id sv Identifierare en Identifier de Identifikation it Identificatore fr Identificateur sr Identifikator pl Identyfikator pt Identificador label type sv Typ en Type de Typ it Tipo fr Type sr Tip pl Typ pt Tipo label subtype sv Undertyp en Subtype de Untertyp it Sottotipo fr Sous-type sr Podtip pl Podtyp pt Subtipo label encoding sv Kodning en Encoding de Kodierung it Codifica fr Codage sr Kodiranje pl Kodowanie pt Codificao label current_encoding sv {Nuvarande kodning} en {Current encoding} de {Aktuelle Kodierung} it {Codifica corrente} fr {Codage courant} sr {Trenutno kodiranje} pl {Obecne kodowanie} pt {Codificao actual} label other sv annat en other de anderen it altro fr Autre sr Ostalo pl inna pt outro label custom_type sv {Egen typ} en {Custom type} de {Generischer Typ} it {Tipo utente} fr {Type personnalis} sr {Lini tip} pl {Wybrany typ} pt {Tipo personalizado} label need_to sv {Du mste ange minst en mottagare (Till:, Kopia:, Blind-kopia:)} en {You must specify at least one recipient (To:, Cc:, Bcc:)} de {Sie mssen mindestens einen Empfnger angeben (An:, Kopie:, Blindkopie:)} it {Devi specificare almeno un destinatario (A:, Copia:, Copia cieca:)} fr {Au moins un destinataire doit tre prcis (Pour:, Copie:, Copie cache:)} sr {Morate naznaiti bar jednog primaoca (Za:, Kopija:, Slepa kopija:)} pl {Musisz poda przynajmniej jednego nadawc (Do:, Cc:, Bcc:)} pt {Tem de especificar pelo menos um destinatrio (Para:, Cc:, Bcc:)} label admin sv Administration en Admin de Administration it Amministrazione fr Administration sr Podeavanja pl Administracja pt Opes label newedit_folder sv {Ny/ndra mapp} en {New/Edit folder} de {Ordner anlegen/bearbeiten} it {Nuova/Modifica cartella} fr {Nouveau/dition des botes lettres} sr {Rad sa skupovima} pl {Nowy/Edytuj folder} pt {Criar/Editar pasta} label update_folder sv {Uppdatera mapp} en {Update folder} de {Diesen Ordner aktualisieren} it {Aggiorna cartella} fr {Mise jour bote lettres} sr {Obnovi skup} pl {Aktualizuj folder} pt {Actualizar pasta} label continue sv Fortstt en Continue de Weiter it Continua fr Continuer sr Dalje pl Dalej pt Continuar label open_file sv {ppna fil} en {Open file} de {Datei ffnen} it {Apri file} fr {Ouvrir un fichier} sr {Otvori datoteku} pl Otwrz pt {Abrir ficheiro} label open_dbase sv {Sk i databasen} en {Search database} de {Datenbank durchsuchen} it {Cerca nel database} fr {Chercher dans la base de donnes} sr {Trai u bazi} pl {Przeszukaj baz} pt {Procurar na base de dados} label folders sv Mappar en Folders de Ordner it Cartelle fr {Botes lettres} sr Skupovi pl Foldery pt Pastas label and sv Och en And de Und it e fr et sr i pl i pt E label or sv Eller en Or de Oder it o fr ou sr ili pl lub pt Ou label not sv Icke en Not de Nicht it non fr non sr ne pl nie pt No label to sv Till en To de An it A fr Pour sr Za pl Do pt Para label subject sv mne en Subject de Betreff it Soggetto fr Objet sr Naslov pl Temat pt Assunto label keywords sv Nyckelord en Keywords de Stichwrter it {Parole chiave} fr Mots-cls sr {Kljune rei} pl {Sowa kluczowe} pt {Palavras chave} label all sv Allt en All de Alle it Tutti fr Tout sr Sve pl Wszystkie pt Todos label search sv Sk en Search de Suchen it Cerca fr Rechercher sr Trai pl Szukaj pt Procurar label emptyexp sv {Tomt skuttryck} en {Empty search expression} de {Leerer Suchausdruck} it {Chiave di ricerca vuota} fr {Effacer le critre de recherche} sr {Prazan izraz za pretragu} pl {Brak wyraenia do szukania} pt {Expresso de busca invlida} label to_file sv {Till fil} en {To file} de {In Datei} it {In file} fr {Dans un fichier} sr {U datoteku} pl {Do pliku} pt {Para ficheiro} label to_dbase sv {Till databasen} en {To database} de {In Datenbank} it {In database} fr {Dans la base de donnes} sr {U bazu} pl {Do bazy} pt {Para base de dados} label insert_into_dbase sv {Lgg till i databasen} en {Insert into database} de {In Datenbank einfgen} it {Inserisci in database} fr {Insrer dans la base de donnes} sr {Ubaci u bazu} pl {Wstaw do bazy} pt {Inserir na dbase} label exdate sv Expireringsdag en {Expiration date} de {Verfallsdatum} it {Data di scadenza} fr {Date d'expiration} sr {Datum isticanja} pl {Data wanoci} pt {Expira depois de} label extype sv Expireringstgrd en {Expiration action} de {Aktion beim Verfall} it {Azione di scadenza} fr {Action expiration} sr {Akcija po isticanju} pl {Akcja po utracie daty wanoci} pt {Aco aps expirar} label remove sv {Ta bort} en Remove de Entfernen it Rimuovi fr Dtruire sr Ukloni pl Usu pt Apaga label incoming sv {Lgg till inkommande} en {Insert into Incoming} de {In den Eingansordner verschieben} it {Inserisci in entrata} fr {Insrer dans la bote lettres d'arrive} sr {Ubaci meu pristigla pisma} pl {Wstaw do przychodzcych} pt {Insere em 'inbox'} label backup sv {Flytta till arkiv} en {Move to backup} de {Eine Sicherungskopie anlegen} it {Muovi in archivio} fr {Dplacer dans l'archive} sr {Premesti u arhivu} pl {Przenie do archiwum} pt {Move para arquivo} label insert sv {Lgg till} en Insert de Einfgen it Inserisci fr Insrer sr Ubaci pl Wstaw pt Inserir label already_vfolderdef sv {Det finns redan ett mappediteringsfnster} en {There is already one folder edit window} de {Es existiert bereits ein Arbeitsfenster} it {C' gi una finestra di modifica cartella} fr {Il y a dj une fentre d'dition des botes lettres.} sr {Ve postoji prozor za rad sa skupovima} pl {Jest ju jedno okno edycji foldera} pt {J existe janela de edio de pastas} label vfolderdef sv {Definiera virtuell mappar} en {Define virtual folder} de {Virtuelle Ordner bearbeiten} it {Definisci cartella virtuale} fr {Dfinir une bote lettres virtuelle} sr {Definii virtuelni skup} pl {Definiuj wirtualny folder} pt {Definir pasta virtual} label window sv Fnster en Window de Fenster it Finestra fr Fentre sr Prozor pl Okno pt Janela label close sv Stng en Close de Schlieen it Chiudi fr Fermer sr Zatvori pl Zamknij pt Fechar label folder sv Mapp en Folder de Ordner it Cartella fr {Botes lettres} sr Skupovi pl Folder pt Pasta label new_file sv {Ny filmapp} en {New file folder} de {Neuer Dateiordner} it {Nuova cartella file} fr {Nouveau fichier bote lettres} sr {Novi datoteni skup} pl {Nowy plik folderu} pt {Nova pasta} label new_mh sv {Ny mh-mapp} en {New mh folder} de {Neuer MH Ordner} it {Nuova cartella mh} fr {Nouvelle bote lettres MH} sr {Novi MH skup} pl {Nowy folder mh} pt {Nova pasta tipo MH} label new_dbase sv {Ny databasmapp} en {New database folder} de {Neuer Datenbankordner} it {Nuova cartella database} fr {Nouvelle bote lettres base de donnes} sr {Novi skup-baza} pl {Nowy folder bazy danych} pt {Nova pasta tipo dbase} label new_imap sv {Ny IMAP folder} en {New IMAP folder} de {Neuer IMAP Ordner} it {Nuova cartella IMAP} fr {Nouvelle bote lettres IMAP} sr {Novi IMAP skup} pl {Nowy folder IMAP} pt {Nova pasta tipo IMAP} label new_pop3 sv {Ny POP3 folder} en {New POP3 folder} de {Neuer POP3 Ordner} it {Nuova cartella POP3} fr {Nouvelle bote lettres POP3} sr {Novi POP3 skup} pl {Nowy folder POP3} pt {Nova pasta tipo POP3} label new_dynamic sv {Ny dynamisk folder} en {New dynamic folder} de {Neuer dynamischer Ordner} it {Nuova cartella dinamica} fr {Nouvelle bote lettres dynamique} sr {Novi dinamiki skup} pl {Nowy dynamiczny folder} pt {Nova pasta dinmica} label new_vfolder sv {Ny mapp} en {New vfolder} de {Neuer virtueller Ordner} it {Nuova cartella virtuale} fr {Nouvelle bote lettres virtuelle} sr {Novi virtuelni skup} pl {Nowy wirtualny folder} pt {Nova pasta virtual} label need_name sv {Du mste ange ett namn} en {You must specify a name} de {Sie mssen einen Namen angeben} it {Devi specificare un nome} fr {Vous devez donner un nom.} sr {Morate dati ime} pl {Musisz poda nazw} pt {Deve especificar um nome} label new_submenu sv {Ny undermeny} en {New submenu} de {Neues Untermen} fr {Nouveau sous-menu} pl {Nowe podmenu} pt {Novo submenu} label folder_name sv Mappnamn en {Folder name} de Ordnername it {Nome cartella} fr {Nom de la bote lettres} sr {Ime skupa} pl {Nazwa folderu} pt {Nome da pasta} label create sv Skapa en Create de Erstellen it Crea fr Crer sr Stvori pl Utwrz pt Criar label dont_create sv {Skapa inte} en {Do not create} de {Nicht erstellen} it {Non creare} fr {Ne pas crer} sr {Nemoj stvarati} pl {Nie twrz} pt {No criar} label failed_create sv {Misslyckades skapa biblioteket} en {Failed to create the directory} de {Konnte Verzeichnis nicht anlegen} it {Impossibile creare la directory} fr {Impossible de crer le rpertoire.} sr {Nisam uspeo da stvorim direktorijum!} pl {Bd podczas tworzenia katalogu} pt {No foi possvel criar o directrio} label do_without_dir sv {Jag skall frska att klara mig utan det, men vissa operationer kan misslyckas.} en {I will try to do without it, but be advised that some operations may fail.} de {Das Programm wird fortfahren, aber einige Funktionen werden fehlschlagen.} it {L'esecuzione continuer senza, ma certe operazioni potrebbero fallire.} fr {Je vais essayer de fonctionner sans, mais certaines oprations peuvent chouer.} sr {Pokuau da radim bez njega, ali znajte da neke operacije mogu da ne uspu.} pl {Sprbuj pracowa bez niego, ale pamitaj e mog wystpi bdy} pt {O programa continuar, mas algumas operaes podero falhar.} label error_in_userproc sv {Misslyckades med att lsa dina egendefinierade procedurer} en {There was an error in the userproc file} de {Es hab einen Fehler in einer Benutzerprozedur-Datei} it {C' un eroore nel file delle procedure utente} fr {Le fichier des procdures utilisateur contient une erreur.} sr {Greka u datoteci sa korisnikim procedurama!} pl {Wystpi bd w pliku procedury uytkownika} pt {Erro encontrado em procedimento do utilizador} label translate_error sv {RatUP_Translate genererar ett felaktigt svar vid fljande indata: %.100s} en {RatUP_Translate generated illegal output for the following input: %.100s} de {RatUP_Translate Fehlerhaffte Ausgabe fr folgenende Daten erzeugt: %.100s} it {RatUP_Translate ha generato un risultato incorretto per il seguente parametro: %.100s} fr {RatUP_Translate a gnr un rsultat incorrect pour le paramtre suivant: %.100s} sr {RatUP_Translate je generisao neispravan izlaz za ovaj ulaz: %.100s} pl {RatUP_Translate utworzy niepoprawne dane dla nastpujcego wejcia: %.100s} pt {RatUP_Translate produziu resultado incorrecto para os seguintes dados: $..100s} label reread_userproc sv {Uppdatera userproc} en {Reread userproc} de {Lies Benutzerprozedur-Datei erneut} it {Rileggi userproc} fr {Relire le fichier userproc} sr {Ponovo proitaj 'userproc'} pl {Ponownie czytaj 'userproc'} pt {Reler procedimento} label preferences sv Instllningar en Preferences de Einstellungen it Preferenze fr Prfrences sr Opcije pl Preferencje pt Preferncias label watcher sv Vktare en Watcher de Wchter it Controllo fr Veilleur sr {Nadzorni prozor} pl Obserwator pt Vigia label folderwindow sv Mappfnster en {Folder window} de Ordnerfenster it {Finestra cartella} fr {Fentre bote lettres} sr {Prikaz skupa} pl {Okno folderu} pt {Janela de pasta} label general sv Huvudinstllningar en General de {Allgemeines} it Generale fr Gnral sr Opte pl Gwne pt Geral label sending sv {Skicka brev} en Sending de Versenden it Spedizione fr Expdition sr Slanje pl Wysyanie pt Envio label under_construction sv Byggarbetsplats en {Area under construction} de {Bereich in Arbeit} it {Lavori in corso} fr {Zone en construction} sr {Radi se...} pl {W trakcie budowy} pt {Ainda em construo} label language sv Sprk en Language de Sprache it Lingua fr Langue sr Jezik pl Jzyk pt Lngua label show_changes sv ndringsmeddelanden en {Changes messages} de {nderungen am Programm} it {Messaggi cambiamenti} fr {Montrer les changements} sr {Poruke o izmenama} pl {Wiadomoci o zmianach} pt {Mostrar as mudanas} label show sv Visa en Show de Anzeigen it Mostra fr Afficher sr Pokai pl Widok pt Mostrar label dont_show sv {Visa inte} en {Do not show} de {Nicht anzeigen} it {Non mostrare} fr {Ne pas afficher} sr {Ne pokazuj} pl {Nie pokazuj} pt {No mostrar} label dbase_dir sv Databasbiliotek en {Database directory} de Datenbankverzeichnis it {Directory database} fr {Rpertoire de la base de donnes} sr {Direktorijum za bazu} pl {Katalog bazy danych} pt {Directrio da base de dados} label apply sv Applicera en Apply de Anwenden it Aggiorna fr Appliquer sr Primeni pl Zapisz pt Aplicar label reset sv terstll en Reset de Zurcksetzen it Azzera fr Rinitialiser sr Vrati pl Resetuj pt {Anular mudanas} label unapplied_changes_title sv {Oapplicerade ndringar} en {Unapplied changes} de {Nicht angewandte Vernderungen} it {Cambiamenti non aggiornati} fr {Changements non appliqus} sr {Neprimenjene izmene} pl {Nie zapisane zmiany} pt {Mudanas no aplicadas} label unapplied_changes sv {Du har gjort ndringar som du inte har applicerat. Vad vill du gra med dem} en {You have made changes which are not applied. What do you want to do with them?} de {Sie haben Vernderungen gemacht, die nicht angewandt wurden. Was wollen sie mit diesen machen?} it {Hai fatto dei cambiamenti che non sono stati aggiornati. Cosa vuoi farne} fr {Vous avez fait des changements que vous n'avez pas appliqus. Que voulez-vous en faire ?} sr {Nainili ste izmene koje nisu primenjene. ta treba da radim sa njima?} pl {Dokonae zmian, ktrych nie zapisano. Co zamierzasz z nimi zrobi?} pt {Ocorreram mudanas que no foram aplicadas. O que pretende?} label discard sv {Kasta bort} en Discard de Verwerfen it Scarta fr Oublier sr Odbaci pl Rezygnuj pt Esquecer label print_command sv Utskriftskommando en {Print command} de {Befehlszeile zum Drucken} it {Comando di stampa} fr {Commande d'impression} sr {Naredba za tampanje} pl {Polecenie druku} pt {Comando de impresso} label tmp_dir sv Temporrbibliotek en {Temporary directory} de {Temporres Verzeichnis} it {Directory temporanea} fr {Rpertoire temporaire} sr {Privremeni direktorijum} pl {Katalog tymczasowy} pt {Directrio temporrio} label window_geometry sv Fnsterplacering en {Window placement} de Fensterplazierung it {Posizionamento finestra} fr {Positionnement des fentres} sr {Ime prozora} pl {Pooenie okna} pt {Posicionamento da janela} label default_folder sv Startmapp en {Default folder} de Standardordner it {Cartella iniziale} fr {Bote lettres initiale} sr {Osnovni skup} pl {Domylny folder} pt {Pasta padro ou por defeito} label show_header_selection sv {Valda huvudflt} en {Selected headers} de {Ausgewhlte Kopfzeilen} it {Intestazioni selezionate} fr {En-ttes slectionns} sr {Odabrani delovi zaglavlja} pl {Wybrane nagwki} pt {Cabealhos seleccionados} label show_headers sv {Visa huvudflt} en {Show headers} de {Kopfzeilen anzeigen} it {Mostra intestazioni} fr {Montrer les en-ttes} sr {Pokai zaglavlje} pl {Poka nagwki} pt {Mostrar cabealhos} label sort_order sv Sorteringsordning en {Sort order} de Sortierung it Ordine fr {Ordre de tri} sr Redosled pl Sortowanie pt Ordem label sort_folder sv {Naturlig ordning} en {Natural order} de {Physische Reihenfolge} it {Ordine naturale} fr {Ordre naturel} sr {Prirodni red} pl Normalnie pt {Ordem natural} label sort_reverseFolder sv {Omvnd naturlig ordning} en {Reverse natural order} de {Umgekehrte physische Reihenfolge} it {Ordine naturale inverso} fr {Ordre naturel inverse} sr {Obrnuti prirodni red} pl {Odwrotnie normalnie} pt {Ordem natural, invertida} label sort_date sv Datumordning en {By date} de {Chronologisch} it {Per data} fr {Par date} sr {Po datumu} pl {Wg. daty} pt {Por data} label sort_reverseDate sv {Omvnd datumordning} en {By reverse date} de {Umgekehrt chronologisch} it {Per data inversa} fr {Par date inverse} sr {Obrnuto po datumu} pl {Odwrotnie wg. daty} pt {Por data, invertida} label sort_size sv Storleksordning en {By size} de {Aufsteigend nach Gre} fr {Par taille} sr {Po veliini} pl {Wg. rozmiaru} pt {Por tamanho} label sort_reverseSize sv {Omvnd storleksordning} en {By reverse size} de {Absteigend nach Gre} fr {Par taille inverse} sr {Obrnuto po veliini} pl {Odwrotnie wg. rozmiaru} pt {Por tamanho, invertida} label stdfolders sv standardmappar en {Standard folders} de Standardordner it {Cartelle normali} fr {Bote lettres normale} sr {Standardni skupovi} pl {Standardowe foldery} pt {Pastas por defeito} label dbase_folders sv Databasmappar en {Database folders} de Datenbankordner it {Cartelle database} fr {Botes lettres base de donnes} sr Skupovi-baze pl {Foldery bazy danych} pt {Pastas de base de dados} label width sv Bredd en Width de Breite it Larghezza fr Largeur sr irina pl Szeroko pt Laargura label lines sv rader en lines de Zeilen it linee fr lignes sr linija pl linie pt linhas label bell_ringings sv Klockringningar en {Bell rings} de {Anzahl akustischer Signale} it {Numero di beep} fr {Nombre de bips} sr {Broj zvona} pl Dzwonek pt {Nmero de toques} label show_messages sv {Visa brev} en {Show messages} de {Zeige Nachrichten} it {Mostra messaggi} fr {Montrer les messages} sr {Pokai pisma} pl {Poka wiadomoci} pt {Mostrar mensagens} label new sv Nya en New de Neu it Nuovo fr Nouveaux sr Novo pl Nowe pt Novo label signature_file sv Signaturfil en {Signature file} de Signaturdatei it {File signature} fr {Fichier de signature} sr {Datoteka s potpisom} pl {Plik sygnaturki} pt {Ficheiro de assinatura} label domain sv Domn en Domain de Domne it Dominio fr Domaine sr Domen pl Domena pt Domnio label smtp_hosts sv {SMTP maskin} en {SMTP host} de {SMTP Host} it {Host SMTP} fr {Htes SMTP} sr {SMTP host} pl {Host SMTP} pt {Servidor SMTP} label charset sv Teckenset en {Character set} de Zeichensatz it {Gruppo di caratteri} fr {Jeu de caractres} sr Karakter-set pl {Strona kodowa} pt {Grupo de caracteres} label attribution sv Kllhnvisning en Attribution de Bezug it Attribuzione fr Attribution sr Pripisivanje pl Przypisanie pt Atribuio label alias sv Alias en Alias de Alias it Alias fr Alias sr Alias pl Alias pt Alias label fullname sv {Lngt namn} en {Full name} de {Voller Name} it {Nome completo} fr {Nom complet} sr {Puno ime} pl {Pene nazwisko} pt {Nome Completo} label content sv Innehll en Content de Inhalt it Contenuto fr Contenu sr Sadraj pl Tre pt Contedo label need_alias_and_content sv {Jag behver bde ett alias och innehll} en {I need both an aliasname and content} de {Es werden sowohl Alias als auch Inhalt bentigt} it {Sono necessari sia un alias sia un contenuto} fr {Il faut la fois un nom d'alias et un contenu.} sr {Potrebni su i alias i sadraj!} pl {Podaj przynajmniej nazw aliasu i tre} pt {O programa requer um alias e um contedo} label dismiss sv Stng en Dismiss de Schlieen it Chiudi fr Fermer sr Izlaz pl Zamknij pt Fechar label save sv Spara en Save de Sichern it Salva fr Sauver sr Snimi pl Zapisz pt Gravar label save_out sv {Spara avsnt} en {Save outgoing} de {Sichere abgehende} it {Salva uscite} fr {Copier les messages expdis dans} sr {Snimi odlazee} pl {Zapisz wychodzce} pt {Guardar mensagens enviadas} label save_to sv {Spara till} en {Save to} de {Sichern in} it {Salva in} fr {Sauver dans} sr {Snimi u} pl {Zapisz do} pt {Guardar em} label newfolder sv {Ny mapp} en {New folder} de {Neuer Ordner} fr {Nouvelle bote lettres} pl {Nowy fodler} pt {Nova pasta} label dont_save sv {Spara inte} en {Do not save} de {Nicht sichern} it {Non salvare} fr {Ne pas sauver} sr {Ne snimaj} pl {Nie zapisuj} pt {No guardar} label found_aliases sv {Jag tittade i dina gamla filer och hittade} en {I scanned your old files and found a total of} de {Die Anzahl der gefundenen alten Dateien betrgt:} it {Nei file vecchi ho trovato} fr {Aprs analyse de fichiers existants, j'ai trouv} sr {Pregledao sam stare datoteke i pronaao ukupno} pl {Przejrzaem twoje stare pliki i znalazem razem} pt {Foram encontrados nos seus ficheiros antigos um total de} label num_aliases sv alias en aliases de Aliasse it alias fr alias sr alias(a) pl aliasy pt alias label see_log sv {Se gamla meddelanden} en {See old log messages} de {Zeige alte Meldungen} it {Mostra messaggi vecchi} fr {Voir les prcdents messages} sr {Pokai stare poruke} pl {Poka wewntrzne komunikaty} pt {Ver mensagens de log antigas} label seelog_title sv {Gamla meddelanden} en {Old messages} de {Alte Meldungen} it {Messaggi vecchi} fr {Prcdents messages} sr {Stare poruke} pl {Stare wiadomoci} pt {Mensagens de log antigas} label does_not_exist sv {finns inte} en {does not exist} de {existiert nicht} it {non esiste} fr {n'existe pas} sr {ne postoji} pl {Nie istnieje} pt {no existe} label no_folder sv {Ingen ppen folder} en {No open folder} de {Kein geffneter Ordner} it {Nessuna cartella aperta} fr {Pas de bote lettres ouverte} sr {Nema otvorenog skupa} pl {Brak otwartego folderu} pt {Nenhuma pasta aberta} label already_login sv {Det kr redan en login-process. Kr frdigt den frst} en {There is another login process running. Complete that one first.} de {Es luft bereits ein weiterer Loginprozess. Bitte benutzen sie diesen} it {C' gi un altro processo di login. Completa prima l'altro} fr {Il y a dj un processus de login ; veuillez le complter.} sr {Ve postoji jedan login-proces. Okonajte prvo njega.} pl {Jest jeszcze jeden proces loginu. Zakocz go najpierw} pt {Existe outro processo de login. Conclua esse primeiro} label login sv Login en Login de Login it Login fr Login sr Login pl Login pt Login label opening sv ppnar en Opening de ffne it Aprendo fr Ouverture sr Otvaram pl Otwieram pt {Abertura em curso} label mailbox_on sv {brevlda p} en {mailbox on} de {Mailbox auf} it {cartella su} fr {Bote lettres sur} sr {Sandue na} pl {Skrzynka na} pt {Correio em} label host sv Maskin en Host de Host it Host fr Hte sr Host pl Host pt Servidor label user sv Anvndare en User de Benutzer it Utente fr Utilisateur sr Korisnik pl Uytkownik pt Utilizador label passwd sv Lsenord en Password de Passwort it Password fr {Mot de passe} sr ifra pl Haso pt {Palavra de entrada - password} label mbox sv Skvg en {Mailbox path} de Maiboxpfad fr {Chemin complet} sr {Ime i staza sandueta} pl {Scierzka skrzynki pocztowej} pt {Caminho para pasta} label reply_to_which sv {Det hr brevet innehller inbddade brev. Vlj vilket du vill svara p} en {This message contains embedded messages. Choose which message you want to reply to.} de {Diese Nachricht enthlt eingebettete Nachrichten. Whlen sie die Nachricht, auf die sie antworten mchten} it {Questo messaggio contiene messaggi allegati. Scegli a quale vuoi rispondere} fr {Ce message contient d'autres messages. Choisissez auquel rpondre.} sr {Ovo pismo sadri ubaene poruke. Izaberite na koju poruku elite da odgovorite.} pl {Ta wiadomo zawiera osadzone wiadomoci. Wybierz, na ktr chcesz odpowiedzie} pt {Esta mensagem contm outras embebidas. Escolha a mensagem qual pretende responder.} label forward_which sv {Det hr brevet innehller inbddade brev. Vlj vilket du vill skicka vidare} en {This message contains embedded messages. Choose which message you want to forward.} de {Diese Nachricht enthlt eingebettete Nachrichten. Whlen sie die Nachricht, die sie weiterleiten mchten} it {Questo messaggio contiene messaggi allegati. Scegli quale vuoi rispedire} fr {Ce message contient d'autres messages. Choisissez lequel faire suivre.} sr {Ovo pismo sadri ubaene poruke. Izaberite koju poruku elite da preusmerite.} pl {Ta wiadomo zawiera osadzone wiadomoci. Wybierz, ktr chcesz forwardowa} pt {Esta mensagem contm outras embebidas. Escolha a mensagem que pretende reenviar.} label forwarded_message sv {Vidareskickat brev} en {Forwarded message} de {Weitergeleitete Nachricht} it {Messaggio rispedito} fr {Message rachemin} sr {Preusmereno pismo} pl {Przeforwardowana wiadomo} pt {Mensagem reenviada} label forward_as_attachment sv {Skicka vidare som bilaga} en {Forward as attachment} de {Als Anhang weiterleiten} it {Rispedisci come allegato} fr {Faire suivre en tant que pice jointe} sr {Preusmeri kao dodatak} pl {Forwarduj jako zacznik} pt {Reenviar em anexo} label forward_inline sv {Skicka vidare inbddat} en {Forward inline} de {Weiterleiten} it {Rispedisci come incluso} fr {Faire suivre en tant qu'inclusion} sr {Preusmeri kroz sadraj} pl Forwarduj pt Reenviar label empty_message sv {Tomt brev} en {Empty message} de {Leere Nachricht} it {Messaggio vuoto} fr {Message vide} sr {Prazno pismo} pl {Pusta wiadomo} pt {Mensagem vazia} label structure sv Struktur en Structure de Struktur it Struttura fr Structure sr Struktura pl Struktura pt Estrutura label view_source sv {Visa kllkod} en {View source} de {Quelltext zeigen} it {Mostra sorgente} fr {Montrer la source} sr {Prikai preneti oblik} pl {Poka rdo} pt {Ver cdigo} label source sv Kllkod en Source de Quelltext it Sorgente fr Source sr {Izvorni oblik} pl Zrdo pt Cdigo label help_window sv Hjlpfnster en {Help window} de Hilfefenster it {Finestra d'aiuto} fr {Fentre d'aide} sr {Prozor za pomo} pl {Okno pomocy} pt {Janela de ajuda} label site sv Adress en Site de Seite it Indirizzo fr Site sr Adresa pl Adres pt Endereo label access-type sv tkomsttyp en {Access type} de Zugriffsart it {Tipo d'accesso} fr {Type d'accs} sr {Vrsta adrese} pl {Rodzaj dostpu} pt {Tipo de acesso} label directory sv Bibliotek en Directory de Verzeichnis it Directory fr Rpertoire sr Direktorijum pl Katalog pt Directrio label server sv Server en Server de Server it Server fr Serveur sr Server pl Serwer pt Servidor label smtp sv SMTP en SMTP de SMTP it SMTP fr SMTP sr SMTP pl SMTP pt {SMTP - Simple Mail Transfer Protocol} label user_program sv {Anvndardefinierat program} en {User-defined program} de {Benutzerdefiniertes Programm} it {Programma definito dall'utente} fr {Programme dfini par l'utilisateur} sr {korisnikovog programa} pl {Program zdefiniowany przez uytkownika} pt {Programa definido pelo utilizador} label sendprot sv {Skicka via} en {Send via} de {Senden ber} it {Manda attraverso} fr {Expdier via} sr {alji preko} pl {Wylij przez} pt {Enviar via} label sendprog sv Sndprogram en {Sending program} de {Programm zum Versenden} it {Programma spedizione} fr {Programme d'expdition} sr {Program za slanje} pl {Program wysyajcy} pt {Programa de envio} label sendprog_8bit sv {Sndande program kan hantera ttabitarsdata} en {Sending program can handle 8-bit data} de {Das Programm zum Versenden kann 8-Bit Daten verarbeiten} label sort_subject sv {mne och tid} en {Subject by date} de {Nach Betreff und chronologisch} it {Soggetto per data} fr {Objet par date} sr {Po naslovu i datumu} pl {Temat wg. daty} pt {Assunto por data} label sort_threaded sv Trdad en Threaded de {Nach Thread} fr {Par fil conducteur} sr {Po odgovaranju} pl Wtki pt {Por sequncia "thread"} label import sv Importera en Import de Importieren it Importa fr Importer sr Uvezi pl Importuj pt Importar label import_directory sv {Importera bibliotek} en {Import directory} de {Verzeichnis importieren} it {Directory d'importazione} fr {Importation de rpertoire} sr {Uvezi direktorijum} pl {Importuj katalog} pt {Importar directrio} label cant_read sv {Kan ej lsa} en {Cannot read} de {Kann Datei nicht lesen} it {Non posso leggere} fr {Impossible de lire} sr {Ne mogu da proitam} pl {Nie mona odczyta} pt {Leitura impossvel} label ignored_not_file_not_dir sv {ignoreras eftersom den varken r en fil eller ett bibliotek.} en {is ignored, it is neither a file nor a directory} de {wird ignoriert, da weder Datei noch Verzeichnis.} it {viene ignorato non n un file n una directory.} fr {est ignor. Ce n'est ni un fichier ni un rpertoire} sr {se ignorie - to nije ni datoteka ni direktorijum.} pl {zignorowane - to jest plik, nie katalog.} pt {ignorado, no nem um ficheiro nem um directrio} label bounce_which sv {Det hr brevet innehller inbddade brev. Vlj vilket du vill studsa} en {This message contains embedded messages. Choose which message you want to bounce.} de {Diese Nachricht enthlt eine oder mehrere Nachrichten als Anhang. Whlen Sie die Nachricht aus, welche Sie umleiten mchten.} it {Questo messaggio contiene messaggi allegati. Scegli quale messaggio rimbalzare} fr {Ce message contient d'autres messages. Choisissez lequel rexpdier} sr {Ovo pismo sadri ukljuene poruke. Izaberite koju poruku elite da odbijete.} pl {Ta wiadomo zawiera osadzone wiadomoci. Wybierz, ktr chcesz odbi} pt {Esta mensagem contm outras embebidas. Escolha a mensagem que pretende reencaminhar (ressaltar).} label dbase sv Databasen en Dbase de Datenbank it Database fr {Base de donnes} sr {Baza podataka} pl {Baza danych} pt {Base de dados} label dbase_backup sv Arkivbibliotek en {Backup directory} de Backupverzeichnis it {Directory archivio} fr {Rpertoire d'archivage} sr {Arhiva za bazu} pl {Katalog archiwum} pt {Directrio de arquivo} label do_expire sv {Expirerar databasen} en {Expiring database} de {Prfung der Verfallsdaten in der Datenbank} it {Database di scadenza} fr {Base de donnes expirant} sr {Isticanje baze} pl {Data wanoci bazy} pt {Base de dados em vencimento} label expire sv Expirera en Expire de {Abgelaufene Eintrge lschen} it Spira fr Expire sr Isticanje pl {Data wanoci} pt Expirar label expire_result sv Expireringsresultat en {Expiration results} de {Ergebnisse der Verfallsdaten-Prfung} it {Risultati di scadenza} fr {Rsultat de l'expiration} sr {Rezultati isticanja} pl {Rezultat utraty wanoci} pt {Resultado do vencimento} label scanned sv Genomskta en Scanned de Analysiert it Scansionati fr Analys sr Pregledano pl Przejrzano pt Analisados label deleted sv Raderade en Deleted de Gelscht it Cancellati fr Dtruit sr Obrisano pl Usunito pt Apagadas label backedup sv {Flyttade till backup} en {Moved to backup} de Gebackuppt it {Messo nell'archivio} fr {Dplac vers l'archive} sr {Prebaeno u arhivu} pl {Przeniesiono do archiwum} pt {Movidas para o arquivo} label moved_to_inbox sv {Flyttade till inbox} en {Moved to INBOX} de {In die Inbox verschoben} it {Messo nella inbox} fr {Dplac dans la bote lettres principale} sr {Prebaeno u inbox} pl {Przeniesiono do poczty przychodzcej} pt {Movidas para a pasta principal de entrada} label db_expire sv {Expirerar databasen} en {Expiring database} de {Prfung der Verfallsdaten in der Datenbank} it {Database di scadenza} fr {Base de donnes expirant} sr {Isticanje baze} pl {Uniewanianie bazy} pt {Base de dados em vencimento} label packing_backup sv {Komprimerar arkivkopior} en {Compressing backup files} de {Komprimiere Backup Dateien} it {Comprimendo archivi} fr {Compression de l'archive...} sr {Komprimujem arhivu...} pl {Kompresuj pliki archiwum} pt {Compresso de ficheiros de arquivo em curso} label start_selection sv {Vlj vid mappppning} en {Select when open} de {Auswhlen beim ffnen} it {Seleziona all'apertura} fr {Slectionner l'ouverture} sr {Odaberi po otvaranju} pl {Wybierz po otwarciu} pt {Seleccionar na abertura} label first_message sv {Frsta brevet i mappen} en {First message in folder} de {Erste Nachricht im Ordner} it {Primo messaggio nella cartella} fr {Premier message de la bote lettres} sr {prvu poruku u skupu} pl {Pierwsza wiadomo w folderze} pt {Primeira mensagem na pasta} label last_message sv {Sista brevet i mappen} en {Last message in folder} de {Letzte Nachricht im Ordner} it {Ultimo messaggio nella cartella} fr {Dernier message de la bote lettres} sr {poslednju poruku u skupu} pl {Ostatnia wiadomo w folderze} pt {ltima mensagem na pasta} label first_new_message sv {Frsta olsta brev i mappen} en {First unread message in folder} de {Erste ungelesene Nachricht im Ordner} it {Primo messaggio non letto nella cartella} fr {Premier message non lu de la bote lettres} sr {prvu neproitanu poruku u skupu} pl {Pierwsza nie przeczytana wiadomo} pt {Primeira mensagem nova} label before_first_new_message sv {Brevet fre det frsta olsta} en {The message before the first unread} de {Die Nachricht vor der ersten ungelesenen} it {Il messaggio precedente il primo non letto} fr {Le message prcdant le premier non lu} sr {pismo ispred prvog neproitanog} pl {Ostatnia nie przeczytana wiadomo} pt {Mensagem precedente primeira no lida} label see_more_of_message sv {Se mera av det returnerade brevet} en {See more of returned message} de {Zeige mehr von zurckgekommener Nachricht} it {Mostra altro del messaggio di ritorno} fr {Voir davantage du message retourn} sr {Pokai jo vraenih poruka} pl {Zobacz reszt zwrconej wiadomoci} pt {Ver mais da mensagem de retorno} label extra sv Extra en Extra de Extra it Extra fr Extra sr Ostalo pl Ekstra pt Extra label cc sv Kopia en Cc de Kopie it Copia fr Copie sr Kopija pl Cc pt Cc label bcc sv Blind-kopia en Bcc de Blindkopie it {Copia cieca} fr {Copie cache} sr {Slepa kopija} pl Bcc pt Bcc label content_description sv Innehllsbeskrivning en Content-Description de Inhaltsbeschreibung it {Descrizione contenuto} fr {Description du contenu} sr {Opis sadraja} pl {Opis zawartoci} pt Descrio-contedo label reply_to sv Svara-till en Reply-To de {Antworten an} it Rispondi-A fr {Rpondre } sr {Odgovoriti na} pl Odpowiedz pt Responder-A label reply-to sv Svara-till en Reply-To de {Antworten an} it Rispondi-A fr {Rpondre } sr {Odgovoriti na} pl Odpowied pt Responder-A label comments sv Kommentarer en Comments de Kommentare it Commenti fr Commentaires sr Komentari pl Komentarz pt Comentrios label comment sv Kommentar en Comment de Kommentar it Commento fr Commentaire sr Komentar pl Komentarz pt Comentrio label from sv Frn en From de Von it Da fr De sr Od pl Od pt De label date sv Datum en Date de Datum it Data fr Date sr Datum pl Data pt Data label received sv Mottaget en Received de Empfangen it Ricevuto fr Reu sr Primljeno pl Otrzymano pt Recebido label message-id sv Meddelande-Id en Message-Id de Nachrichtenkennung it Id-Messaggio fr {Identifiant du message} sr {ID pisma} pl {ID wiadomoci} pt Id-da-mensagem label mime-version sv MIME-Version en MIME-Version de MIME-Version it Versione-MIME fr {Version MIME} sr {MIME verzija} pl Wersja-MIME pt Verso-MIME label content-type sv Innehll en Content-Type de Inhaltstyp it Tipo-Contenuto fr {Type de Contenu} sr {Vrsta sadraja} pl {Typ zawartoci} pt Tipo-contedo label auto-submitted sv Automatgenererat en Auto-Submitted de {Automatisch generiert} it AutoSpedito fr Auto-gnr sr Samogenerisano pl Auto-dostarczanie pt Auto-gerado label default_reply_to sv {Standard Svara-Till} en {Default Reply-To} de {Standard Antwort an} it {Rispondi-A Standard} fr {Par dfaut, rpondre } sr {Podrazumevano za odgovore} pl {Domylnie odpwiedz} pt {Responder-A, por defeito} label character_size sv Teckenstorlek en {Character size} de Zeichengre it {Dimensione carattere} fr {Taille des caractres} sr {Veliina znakova} pl {Rozmiar znakw} pt {Tamanho dos caracteres} label need_restart sv {Du mste starta om TkRat fr att dina ndringar skall n full effekt.} en {You need to restart TkRat for your changes to take full effect} de {Sie mssen TkRat neu starten, damit alle nderungen funktionieren.} it {Devi riiniziare TkRat perch le tue modifiche abbiano effetto.} fr {Il faut redmarrer TkRat pour que les changements soient pris en compte.} sr {Morate restartovati TkRat da bi promene stupile na snagu.} pl {Musisz zrestartowa TkRat, by zmiany odniosy skutek.} pt {Deve reinicializar o TkRat para as mudanas terem lugar.} label opening_folder sv {ppnar mapp} en {Opening folder} de {ffne Ordner} it {Aprendo cartella} fr {Ouverture de la bote lettres} sr {Otvaram skup} pl {Otwieram folder} pt {Abertura de pasta em curso} label read sv Ls en Read de Lesen it Leggi fr Lire sr itaj pl Czytaj pt Ler label write sv Skriv en Write de Schreiben it Scrivi fr crire sr Pii pl Zapisz pt Escrever label exec sv Kr en Exec de Ausfhren it Esegui fr Excuter sr Izvri pl Wykonaj pt Executar label perm_user sv Anvndare en User de Benutzer it Utente fr Utilisateur sr Korisnik pl Uytkownik pt Utilizador label perm_group sv Grupp en Group de Gruppe it Gruppo fr Groupe sr Grupa pl Grupa pt Grupo label perm_other sv Andra en Other de Andere it Altro fr Autre sr Ostali pl Inni pt Outro label mode sv Mod en Mode de Modus it Modo fr Mode sr Mod pl Tryb pt Modo label all_read sv {Alla ls} en {All read} de {Alle lesen} it {Tutti lettura} fr {Tout lu} sr {Svi itaju} pl {Wszyscy czytaj} pt {Todas lidas} label all_read_write sv {Alla ls & skriv} en {All read & write} de {Alle lesen & schreiben} it {Tutti lettura e scrittura} fr {Tout lu et crit} sr {Svi itaju i piu} pl {Wszyscy czytaj i zapisuj} pt {Todas lidas & escritas} label max_height sv {Max hjd} en {Max height} de {Maximale Hhe} it {Max altezza} fr {Hauteur max} sr {Maksimalna visina} pl {Maksymalna wysoko} pt {Altura mxima} label insert_failed sv {Misslyckades att lgga in brevet} en {Failed to insert message} de {Nachricht einfgen fehlgeschlagen} it {Impossibile inserire messaggio} fr {Impossible d'insrer le message.} sr {Nisam uspeo da ubacim pismo} pl {Bd przy wstawianiu wiadomoci} pt {Insero de mensagem falhada} label define_keys sv {Definiera tangenter} en {Define keys} de {Tasten festlegen} it {Definisci tasti} fr {Dfinir des raccourcis clavier} sr {Definii tastere} pl {Definiuj klawisze} pt {Definir teclas} label folder_key_find sv Sk en Find de Suchen it Ricerca fr Rechercher sr Nadji pl Znajd pt Procurar label folder_key_compose sv {Nytt brev} en Compose de Schreiben it Componi fr {Nouveau message} sr Sastavljanje pl Utwrz pt Escrever label folder_key_close sv Stng en Close de Schlieen it Chiudi fr Fermer sr Zatvori pl Zamknij pt Fechar label folder_key_openfile sv ppna en Open de ffnen fr Ouvrir sr Otvori pl Otwrz pt Abrir label folder_key_quit sv Avsluta en Quit de Beenden it Esci fr Quitter sr {Kraj rada} pl Wyjd pt Sair label folder_key_nextu sv {Nsta olsta} en {Next unread} de {Nchste ungelesene} it {Prossimo nuovo} fr {Prochain non lu} sr {Sledee novo pismo} pl {Nastpna nie przecztana} pt {Prxima nova} label folder_key_sync sv Synkronisera en Synchronize de {Lokaler Abgleich} it Sincronizza fr Synchroniser sr Sinhronizuj pl Synchronizuj pt Sincronizar label folder_key_netsync sv Ntverkssynk en {Network sync} de {Allgemeiner Netzabgleich} fr {Synchronisation rseau} sr {Mreno sinhronizovanje} pl {Synchronizacja sieciowa} pt {Sincronizar rede} label folder_key_update sv Uppdatera en Update de {Aktualisieren} it Aggiorna fr {Mettre jour} sr Obnovi pl Aktualizuj pt Actualizar label folder_key_delete sv Radera en Delete de Lschen it Cancella fr Dtruire sr Brii pl Anuluj pt Apagar label folder_key_undelete sv {Ta bort radering} en Undelete de Wiederherstellen it {Annulla cancella} fr {Annuler la destruction} sr {Poniti brisanje} pl Przywr pt {No apagar} label folder_key_flag sv {Markera brev} en {Flag message} de {Nachricht markieren} it {Contrassegna messaggio} fr {Marquer le message} sr {Oznai pismo} pl {Oflaguj wiadomo} pt {Marcar a mensagem} label folder_key_next sv Nsta en Next de Nchste it Prossimo fr Suivant sr Sledee pl Nastpny pt Prxima label folder_key_prev sv Fregende en Previous de Vorherige it Precedente fr Prcdent sr Prethodno pl Poprzedni pt Anterior label folder_key_home sv {Toppen av brevet} en {Top of message} de Nachrichtenkopf it {Inizio del messaggio} fr {Dbut du message} sr {Poetak pisma} pl {Pocztek listu} pt {Incio da mensagem} label folder_key_bottom sv {Slutet av brevet} en {End of message} de Nachrichtenende it {Fine del messaggio} fr {Fin du message} sr {Kraj pisma} pl {Koniec listu} pt {Fim da mensagem} label folder_key_pagedown sv {Sida ner} en {Page down} de {Seite runter} it {Pagina gi} fr {Page suivante} sr {Strana dole} pl {Strona w d} pt {Pgina seguinte} label folder_key_pageup sv {Sida upp} en {Page up} de {Seite rauf} it {Pagina su} fr {Page prcdente} sr {Strana gore} pl {Strona do gry} pt {Pgina anterior} label folder_key_linedown sv {Rad ner} en {Line down} de {Zeile runter} it {Linea gi} fr {Ligne suivante} sr {Red dole} pl {Linia w d} pt {Linha seguinte} label folder_key_lineup sv {Rad upp} en {Line up} de {Zeile rauf} it {Linea su} fr {Ligne prcdente} sr {Red gore} pl {Linia do gry} pt {Linha anterior} label folder_key_replya sv {Svara till alla} en {Reply to all} de {Allen antworten} it {Rispondi a tutti} fr {Rpondre tous} sr {Odgovori svima} pl {Odpowiedz wszystkim} pt {Responder a todos} label folder_key_replys sv {Svara till avsndaren} en {Reply to sender} de {Absender antworten} it {Rispondi al mittente} fr {Rpondre l'expditeur} sr {Odgovori poiljaocu} pl {Odpowiedz nadawcy} pt Responder label folder_key_forward_a sv {Skicka vidare som bilaga} en {Forward as attachment} de {Als Anhang weiterleiten} it {Rispedisci come allegato} fr {Faire suivre en tant que pice jointe} sr {Preusmeri kao dodatak} pl {Forwarduj zacznik} pt {Reenviar como anexo} label folder_key_forward_i sv {Skicka vidare inbddat} en {Forward inline} de {Weiterleiten} it {Rispedisci come incluso} fr {Faire suivre en tant qu'inclusion} sr {Preusmeri kroz sadraj} pl {Forwarduj inline} pt Reenviar label folder_key_bounce sv {Studsa brev} en {Bounce message} de Umleiten fr {Rexpdier le message} sr {Odbij pismo} pl {Odbij wiadomo} pt Reencaminhar label add_key sv Ny en Add de Hinzufgen it Aggiungi fr Ajouter sr Dodaj pl Dodaj pt Nova label press_key sv {Tryck p den nya tangenten} en {Press the new key} de {Drcken sie die neue Taste} it {Premi il nuovo tasto} fr {Appuyer sur la nouvelle touche} sr {Pritisnite novi taster} pl {Nacinij nowy klucz} pt {Pressione a nova tecla/combinao} label key_defined sv {Denna tangenten r redan definierad till} en {This key is already bound to} de {Diese Taste ist bereits gebunden an} it {Questo tasto gi assegnato a} fr {Cette touche est dj utilise par} sr {Ovaj taster je ve upotrebljen za} pl {Ten klucz jest ju w drodze do} pt {Esta tecla/combinao j utilizada por} label replace_key sv {Byt till ny definition} en {Replace with new binding} de {Ersetze durch neue Funktion} it {Cambia con nuovo assegnamento} fr {Remplacer avec la nouvelle dfinition} sr {Zameni novom funkcijom} pl {Zmie na nowe obramowanie} pt {Substituir por nova definio} label do_delete sv {Tryck p definitionen som skall bort} en {Press the binding to delete} de {Drcken sie die zu lsende Verbindung} it {Premi il tasto assegnato per cancellare} fr {Appuyer sur la touche dtruire} sr {Kliknite na definiciju koja se brie} pl {Wybierz obramowanie do usunicia} pt {Pressione a definio a remover} label nothing sv Ingenting en Nothing de Nichts it Niente fr Rien sr Nita pl {Daruj sobie} pt Nada label take_mail sv {Ta brev frn netscape} en {Take mail from Netscape} de {Post von Netscape stehlen} it {Prendi posta da Netscape} fr {Reprendre les messages netscape} sr {Uzmi potu od Netscape-a} pl {Zabierz poczt netscapeowi} pt {Retomar correio do programa Netscape} label import_IMAP sv IMAP-mappar en {IMAP folders} de IMAP-Ordner it {Cartelle IMAP} fr {Bote lettres IMAP} sr {IMAP skupovi} pl {Foldery IMAP} pt {Pastas IMAP} label import_DIS sv {Frikopplade mappar} en {Disconected folders} de {Offline Ordner} fr {bote lettres dconnecte} sr {Nepovezani skupovi} pl {Nie poczone foldery} pt {Pastas assncronas} label pattern sv Mnster en Pattern de Muster it Chiave fr Modle sr Uzorak pl Wzorzec pt Padro label host_required sv {Du mste ange en maskin} en {You must specify a host} de {Sie mssen einen Host angeben} it {Devi specificare un host} fr {Vous devez spcifier un hte.} sr {Morate dati host} pl {Musisz wybra jaki host} pt {Deve especificar um servidor} label import_failed sv {Importeringen misslyckades} en {Import failed} de {Import fehlgeschlagen} it {Importazione impossibile} fr {chec de l'importation} sr {Uvoz nije uspeo} pl {Bd importu} pt {Erro na importao} label warning_sendprog sv {VARNING: Det specificerade sndprogrammet finns inte eller r inte krbart} en {WARNING: The specified sending program does not exist or is not executable} de {WARNUNG: Das angegebene Sendeprogramm existiert nicht oder ist nicht ausfhrbar} it {ATTENZIONE: Il programma specificato per spedire non esiste o non eseguibile} fr {ATTENTION : Le programme d'expdition indiqu n'existe pas ou n'est pas excutable.} sr {UPOZORENJE: Dati program za slanje ne postoji ili nije izvriv!} pl {UWAGA: Wybrany program do wysyania poczty nie istnieje lub nie jest wykonywalny} pt {AVISO: O programa especificado no existe ou no executvel} label automatic_wrap sv {Automatisk radbrytning} en {Automatic line wrap} de {Automatischer Zeilenumbruch} it {A capo automatico} fr {Passage la ligne automatique} sr {Automatski prelom redova} pl {Zawijanie linii} pt {Dobrar linhas automaticamente} label need_keyword sv {Du mste ange minst ett nyckelord.} en {You must specify at least one keyword} de {Sie mssen mindestens ein Schlsselwort angeben.} it {Devi specificare almeno una parola chiave.} fr {Vous devez indiquer au moins un mot-cl.} sr {Morate dati bar jednu kljunu re} pl {Musisz poda cho jedno sowo kluczowe} pt {Deve especificar pelo menos uma palavra-chave} label opening_connection sv {ppnar frbindelse...} en {Opening connection...} de {ffne Verbindung...} it {Aprendo connessione...} fr {Ouverture de la connexion...} sr {Otvaram vezu...} pl {Otwieram poczenie...} pt {Estabelecimento da ligao...} label wait_greeting sv {Vntar p vlkomnande...} en {Waiting for greeting...} de {Warte auf Begrung...} it {In attesa di connessione...} fr {Attente de bienvenue...} sr {ekam na odziv...} pl {czekam na powitanie...} pt {Em espera do cumprimento...} label get_capabilities sv {Hmtar egenskaper...} en {Getting capabilities...} de {Empfange Fhigkeiten...} it {Richiedendo caratteristiche...} fr {Rception des caractristiques} sr {Oitavam mogunosti...} pl {Nabieram mocy...} pt {Recepo das caractersticas...} label send_envelope sv {Skicka kuvertinformation...} en {Sending envelope information...} de {Sende Umschlaginformationen...} it {Spedendo informazioni busta...} fr {Expdition des informations de l'enveloppe...} sr {aljem podatke iz omota...} pl {Wysyam informacj z koperty...} pt {Envio do envelope...} label send_from sv {Skickar avsndare...} en {Sending From address...} de {Sende Von: Adresse...} it {Mandando indirizzo destinatario...} fr {Expdition de l'adresse expditeur...} sr {aljem polaznu adresu...} pl {Wysyam adres nadawcy} pt {Envio De:...} label send_rcpt sv {Skickar mottagare %.100s...} en {Sending recipient %.100s...} de {Sende Empfnger %.100s...} it {Mandando destinatario %.100s...} fr {Expdition des adresses destinataires... %.100s...} sr {aljem adresu primaoca...} pl {Wysyam adresata %.100s...} pt {Envio do destinatrio %.100s...} label send_data sv {Skickar data...} en {Sending data...} de {Sende Daten...} it {Mandando dati...} fr {Expdition des donnes...} sr {aljem podatke...} pl {Wysyam dane...} pt {Envio dos dados em curso...} label wait_ack sv {Vntar p sndningskvittens...} en {Waiting for acknowledgement...} de {Warte auf Besttigung...} it {In attesa di conferma...} fr {Attente de confirmation...} sr {ekam potvrdu...} pl {Pokornie oczekuj uwag...} pt {Em espera de confirmao...} label closing sv {Stnker frbindelsen...} en {Closing channel...} de {Schliee Kanal...} it {Chiudendo canale...} fr {Fermeture de la connexion...} sr {Zatvaram vezu...} pl {Zamykam kana...} pt {Encerramento da ligao em curso...} label sending_message sv {Skickar brev...} en {Sending message...} de {Sende Nachricht...} it {Spedendo messaggio...} fr {Expdition du message...} sr {aljem poruku...} pl {Wysyam wiadomo} pt {Envio da mensagem...} label upgrade_dbase sv {Uppgradera databasen} en {Upgrade database} de {Update Datenbank} it {Aggiorna database} fr {Mise jour de la base de donnes} sr {Dogradi bazu} pl {Aktualizuj baz danych} pt {Actualizar a base de dados} label old_dbase sv {Du har en gammal version av databasen. Jag mste konvertera den fr att kunna anvnda den.} en {You have an old version of the database. I must convert it to be able to use it.} de {Sie haben eine alte Datenbankversion. Das Programm muss sie konvertieren, damit Sie sie benutzen knnen.} it {Hai una vecchia versione del database. Devo convertirlo prima di usarlo} fr {Vous avez une vieille version de la base de donnes. Je dois la convertir pour pouvoir l'utiliser.} sr {Imate staru verziju baze. Moram da je konvertujem da bi se mogla koristiti.} pl {Masz star wersj bazy danych. Musz j skonwertowa by si nam jeszcze do czego przydaa.} pt {A verso da base de dados antiga. Uma converso dever ter lugar para ser possvel utiliz-la.} label unlinked_messages sv {Oknda brev} en {Unlinked messages} de {Entkoppelte Nachrichten} it {Messaggio scollegato} fr {Messages sans index} sr {Pisma bez indeksa} pl {Wiadomoci bez indeksu} pt {Mensagens sem ndice} label unl_m1 sv {Det fanns} en {There were} de {Da waren} it C'erano fr {Il y avait} sr {Bilo je} pl Byo pt Haviam label unl_m2 sv {oknda brev i databasen. Det har flyttats till UnlinkedMessages-mappen (vilket r en filmapp med ~/UnlinkedMessages som fil).} en {messages sans index in the database. They can be found in the UnlinkedMessages folder (which is a file folder with ~/UnlinkedMessages as the file).} de {Nachrichten ohne Index in der Datenbank. Sie finden sie im EntkoppelteNachrichten Ordner (der Dateiordner in ~/UnlikedMessages)} it {messaggi scollegati nel database. Possono essere reperiti nella cartella UnlinkedMessages (che una cartella file nella directory ~)} fr {messages sans index dans la base de donnes. Vous les trouverez dans le fichier bote lettres ~/UnlinkedMessages} sr {pisama bez indeksa u bazi. Ona se nalaze u UnlinkedMessages skupu (koji je datoteni skup pod imenom ~/UnlinkedMessages).} pt {mensagens sem ndice na base de dados. Podero ser encontradas na pasta UnlinkedMessages (um arquivo no directrio ~)} label send_deferred sv {Skicka uppskjutna brev} en {Send deferred messages} de {Sende verzgerte Nachrichten} fr {Expdier les messages diffrs} sr {alji odloeno} pl {Wylij wstrzymane wiadomoci} pt {Enviar mensagens diferidas} label delivery_mode sv Leveranstyp en {Delivery mode} de Verzgerungsmodus it {Modo consegna} fr {Mode d'expdition} sr {Nain dostavljanja} pl {Tryb dostarczenia} pt {Modo de envio} label sending_deferred sv {Skicka uppskjutna brev} en {Sending deferred messages} de {Sende verzgerte Nachrichten} it {Spedendo messaggi differiti} fr {Expdition des messages diffrs} sr {aljem odloena pisma} pl {Wysyanie wstrzymanych wiadomoci} pt {Envio mensagens diferidas em curso} label to_send sv {Att skicka} en {To send} de {Zu senden} it {Da spedire} fr { expdier} sr {Za slanje} pl {Do wysania} pt {A enviar} label sent sv Skickade en Sent de Versandt it Spedito fr Expdis sr Poslato pl Wysano pt Enviadas label no sv Nej en No de Nein it No fr Non sr Ne pl Nie pt No label waiting_on_sender sv {Vntar p den sndande processen (den snder)...} en {Waiting on sender process (it is sending)...} de {Warte auf Sendeprozess (sendet gerade)...} it {In attesa del processo di spedizione (sta mandando)...} fr {Processus d'expdition en cours...} sr {ekam na proces za slanje (slanje u toku)...} pl {Czekam na proces wysyajcy (wysya)...} pt {Processo de envio em curso...} label illegal_file_spec sv {Otillten fil angiven} en {Illegal file specified} de {Ungltiger Dateiname} it {File non valido specificato} fr {Fichier indiqu non valide} sr {Zadata je pogrena datoteka} pl {Wybrano niepoprawny plik} pt {Arquivo indicado invlido} label mh_name sv MH-namn en {MH name} de MH-Name it Nome-MH fr {Nom MH} sr {MH ime} pl {Nazwa MH} pt {Nome MH} label need_mh_name sv {Du mste ange ett mh-namn} en {You must give a mh name} de {Sie mssen einen MH-Namen angeben} it {Devi dare un nome-mh} fr {Vous devez donner un nom MH.} sr {Morate dati MH ime} pl {Musisz poda nazw mh} pt {Deve especificar um nome MH} label failed_to_make_copy sv {Misslyckades att gra en privat kopia} en {Failed to make a local copy} de {Konnte keine lokale Kopie anlegen} it {Impossibile creare copia locale} fr {Impossible de crer une copie locale.} sr {Nisam uspeo da nainim lokalnu kopiju} pl {Nie udao si zrobi lokalnej kopii} pt {No foi possvel criar uma cpia local} label color_scheme sv Frgskala en {Color scheme} de Farbschema it {Schema colore} fr {Jeu de couleurs} sr {Grupa boja} pl {Schemat kolorw} pt {Esquema de cores} label #dde3eb sv Gr en Gray de Grau it Grigio fr Gris sr Sivo pl Szary pt Cinzento label PeachPuff2 sv Aprikos en Peach de Pfirsich it Albicocca fr Pche sr Breskva pl Brzoskwiniowy pt Pssego label bisque sv Emalj en Bisque de Rosa it Rosa fr Bisque sr Svetlo-smee pl Bisque pt Rosado label SlateBlue1 sv Bl en Blue de Blau it Blu fr Bleu sr Plavo pl Niebieski pt Azul label SteelBlue4 sv Stlbl en {Steel Blue} de {Stahl Blau} it {Blu acciaio} fr {Bleu acier} sr {Metalno plavo} pl Metalowo-niebieski pt {Azul metal} label SkyBlue1 sv Ljusbl en {Light blue} de Hellblau it Azzurro fr {Bleu clair} sr {Svetlo plavo} pl Jasno-niebieski pt {Azul claro} label aquamarine2 sv Ljusgrn en {Light green} de Hellfrn it {Verde chiaro} fr {Vert clair} sr {Svetlo zeleno} pl Jasno-zielony pt {Verde claro} label SpringGreen4 sv Grn en Green de Grn it Verde fr Vert sr Zeleno pl Zielony pt Verde label patterns_to_use sv {Mster att matcha hittade objekt mot} en {Patterns to match found objects against} de {Muster, gegen das gefundene Objekte verglichen werden sollen} it {Chiave di ricerca} fr {Modles de recherche} sr {Uzorak za poreenje} pt {Modelos de procura} label files sv Filer en Files de Dateien it File fr Fichiers sr Datoteke pl Pliki pt Ficheiros label directories sv Bibliotek en Directories de Verzeichnisse it Directory fr Rpertoires sr Direktorijumi pl Katalogi pt Directrios label incom_mbox sv {Inkommande brevlda} en {Incoming mailbox} de Eingangsordner fr {bote lettres d'arrive} sr {Izaberi kao inbox} pl {Poczta nadchodzca} pt {Pasta de correio de entrada} label group sv Grupp en Group de Gruppe it Gruppo fr Groupe sr Grupa pl Grupa pt Grupo label create_in_win sv {Skapa i fnster} en {Create in window} de {In Fenster erzeugen} it {Crea in finestra} fr {Choisir dans une liste} sr {Kreiraj u prozoru} pl {Utwrz w oknie} pt {Agrupar por lista} label create_by_expr sv {Skapa mha uttryck} en {Create by expression} de {Nach Kriterien erzeugen} it {Crea per chiave} fr {Crer avec une expression} sr {Kreiraj po izrazu} pl {Utwrz z wyraenia} pt {Agrupar por expresso} label use_saved_expr sv {Anvnd sparat uttryck} en {Use saved expression} de {Nach gespeicherten Kriterien} it {Usa chiave salvata} fr {Utiliser une expression enregistre} sr {Upotrebi snimljeni izraz} pl {Uyj zapisanego wyraenia} pt {Usar expresso memorizada} label clear_group sv {Ta bort gruppmarkering} en {Clear group marking} de {Lsche Gruppenmarkierung} it {Cancella selezione di gruppo} fr {Effacer le marquage de groupe} sr {Ukloni grupne oznake} pl {Czy oznaczenie grupy} pt {Eliminar marcaes de grupo} label select_all sv {Vlj alla} en {Select all} de {Alles auswhlen} it {Seleziona tutti} fr {Tout slectionner} sr {Izaberi sve} pl {Zaznacz wszystko} pt {Seleccionar todas} label deselect_all sv {Vlj bort alla} en {Deselect all} de {Nichts auswhlen} it {Deseleziona tutti} fr {Annuler toutes les slections} sr {Ukloni sve oznake} pl {Odznacz wszystko} pt {Anular todas as seleces} label edit_group sv {Editera grupp} en {Edit group} de {Gruppe bearbeiten} it {Modifica gruppo} fr {Modifier le groupe} sr {Izmeni grupu} pl {Edytuj grup} pt {Criar grupo} label create_exp sv {Skapa uttryck} en {Create expression} de {Auswahl-Kriterien festlegen} it {Crea chiave} fr {Crer une expression} sr {Kreiranje izraza} pl {Utwrz wyraenie} pt {Criar expresso} label basic_mode sv {Grundlggande mod} en {Basic mode} de Einfach it {Modo base} fr {Mode de base} sr {Osnovni nain} pl {Tryb podstawowy} pt {Modo simples} label advanced_mode sv {Avancerad mod} en {Advanced mode} de Fortgeschritten it {Modo avanzato} fr {Mode avanc} sr {Napredni nain} pl {Tryb zaawansowany} pt {Modo avanado} label save_as sv {Spara som} en {Save as} de {Sichern als} it {Salva come} fr {Sauver sous} sr {Snimi kao} pl {Zapisz jako} pt {Guardar como} label fields sv Flt en Fields de Felder it Campi fr Champs sr Polja pl Pola pt Campos label operators sv Operatorer en Operators de Operatoren it Operatori fr Oprateurs sr Operatori pl Operatory pt Operadores label booleans sv Logiska en Booleans de {Boolesche Operatoren} it Booleani fr Boolens sr Logiki pl Logiczne pt Lgicos label grouping sv Gruppering en Grouping de Gruppierung it Raggruppamento fr Regroupement sr {Za grupisanje} pl Grupowanie pt Associar label sender sv Avsndare en Sender de Absender it Mittente fr Expditeur sr Poiljalac pl Nadawca pt Expeditor label has sv Har en Has de Enthlt it ha fr contient sr sadri pl zawiera pt Contem label is sv r en Is de Ist it fr est sr jeste pl jest pt label clear sv Rensa en Clear de Lschen it Cancella fr Effacer sr Obrii pl Czy pt Limpar label error_underlined sv {Det r ett syntaxfel vid det understrykna ordet} en {There is a syntax error at the underlined word} de {Das unterstrichene Wort enthlt einen Syntaxfehler} it {C' un errore di sintassi} fr {Erreur de syntaxe sur le mot soulign} sr {Sintaksna greka kod podvuene rei} pl {Bd skadni przy podkrelonym wyrazie} pt {Erro de sintaxe na palavra sublinhada} label syntax_error_exp sv {Syntaxfel i uttrycket} en {Syntax error in expression} de {Syntaxfehler im Ausdruck} it {Errore di sintassi nella chiave} fr {Erreur de syntaxe dans l'expression} sr {Sintaksna greka u izrazu} pl {Bd skadni w wyraeniu} pt {Erro de sintaxe na expresso} label saved_expr sv {Sparade uttryck} en {Saved expressions} de {Gespeicherte Suchkriterien bearbeiten} it {Chiavi salvate} fr {Expressions sauvegardes} sr {Snimljeni izrazi} pl {Zapisane wyraenia} pt {Expresses memorizadas} label need_one_selected_exp sv {Du mste vlja ett uttryck} en {You must select one expression} de {Sie mssen einen Ausdruck auswhlen} it {Devi selezionare una chiave} fr {Vous devez slectionner une expression} sr {Morate odabrati izraz} pl {Musisz wybra jedno wyraenie} pt {Deve seleccionar uma expresso} label here_is_text sv {Hr kommer text kodad i} en {Here is text encoded in} de {Hier ist Text, der kodiert ist mit} it {Qui c' del testo codificato in} fr {Ceci est du texte encod en} sr {Ovo je teskt kodiran kao} pl {Tekst zakodowany w} pt {Texto codificado em} label which_cant_be_shown sv {som inte kan visas korrekt.} en {which can't be shown correctly} de {und deshalb nicht korrekt angezeigt werden kann.} it {che non pu essere visualizzato correttamente.} fr {qui ne peut tre affich correctement} sr {to ne moe da se prikae ispravno.} pl {ktry nie moe by pokazany poprawnie} pt {que poder no ser mostrado correctamente.} label show_sevenbit sv {Visa sjubitarsbokstver} en {Show seven-bit characters} de {Zeige ASCII Zeichen} it {Mostra caratteri a sette bit} fr {Montrer uniquement les caractres 7 bits} sr {Pokai sedmobitne znake} pl {Poka 7bit-owe znaki} pt {Mostrar unicamente caracteres de 7 bits} label convert_to_local_nl sv {Konvertera till lokala radslut} en {Convert to local newline conventions} de {Wandle in lokale neue-Zeilen-Konvention} it {Conversione metodo a-capo locale} fr {Convertir les fins de lignes} sr {Prevedi znake za novi red} pl {Konwertuj do lokalnych ustawie nowej linii} pt {Converter fim de linha} label startup_iconic sv {Starta i ikonifierat lge} en {Start iconified} de {Minimiert starten} label file sv Fil en File de Datei it File fr Fichier sr Datoteka pl Plik pt Arquivo label insert_file sv {Ls in fil} en {Insert file} de {Datei einfgen} it {Inserisci file} fr {Insrer un fichier} sr {Ubaci datoteku} pl {Wstaw plik} pt {Inserir ficheiro} label cut sv {Klipp ut} en Cut de Ausschneiden it Taglia fr Couper sr Iseci pl Wytnij pt Eliminar label cut_all sv {Klipp ut allt} en {Cut all} de {Alles ausschneiden} it {Taglia tutto} fr {Tout couper} sr {Iseci sve} pl {Wytnij wszystko} pt {Eliminar tudo} label copy sv Kopiera en Copy de Kopieren it Copia fr Copier sr Kopiraj pl Kopiuj pt Copiar label copy_all sv {Kopiera allt} en {Copy all} de {Alles kopieren} it {Copia tutto} fr {Tout copier} sr {Kopiraj sve} pl {Kopiuj wszystko} pt {Copiar tudo} label paste sv {Klistra in} en Paste de Einfgen it Incolla fr Coller sr Umetni pl Wklej pt Colar label wrap_paragraph sv {Bryt rader i stycke} en {Wrap paragraph} de {Paragraphen umbrechen} fr {Remise en forme paragraphe} sr {Prelomi pasus} pl {Zawijaj akapity} pt {Dobrar pargrafo} label compose_key_wrap sv {Bryt rader i stycke} en {Wrap paragraph} de {Paragraphen umbrechen} fr {Remise en forme paragraphe} sr {Prelomi pasus} pl {Zawijaj akapity} pt {Dobrar pargrafo} label run_through_command sv {Kr genom kommando} en {Run through command} de {Durch Programm filtern} it {Filtra con comando} fr {Filtrer par une commande} sr {Propusti kroz komandu} pl {Przepu przez program} pt {Filtar atravs de comando} label undo sv ngra en Undo de Rckgngig it {Annulla modifica} fr Annuler sr {Poniti izmenu} pl Cofnij pt {Corrige ltima aco} label redo sv {Gr om} en Redo de Wiederholen it Ripristina fr Refaire sr {Obnovi izmenu} pl Ponw pt {Retoma ltima aco} label compose_key_redo sv {Gr om} en Redo de Wiederholen it Ripristina fr Refaire sr {Obnovi izmenu} pl Ponw pt {Retoma ltima aco} label failed_to_open_file sv {Misslyckades att ppna filen: %.100s} en {Failed to open file: %.100s} de {Datei ffnen fehlgeschlagen: %.100s} it {Non possibile aprire il file: %.100s} fr {Impossible d'ouvrir le fichier: %.100s} sr {Nisam uspeo da otvorim datoteku} pl {Bd otwarcia pliku: %.100s} pt {No foi possvel abrir ficheiro: %.100s} label no_text_selected sv {Ingen text vald} en {No text selected} de {Kein Text ausgewhlt} it {Nessun testo selezionato} fr {Pas de texte slectionn} sr {Nema izabranog teksta} pl {Nie zaznaczono tekstu} pt {Nenhum texto seleccionado} label command_failed sv {Kommandot misslyckades} en {Command failed} de {Programm fehlgeschlagen} it {Comando fallito} fr {La commande a chou} sr {Komanda nije uspela} pl {Bd polecenia} pt {Comando falhou} label command sv Kommando en Command de Programm it Comando fr Commande sr Komanda pl Polecenie pt Comando label command_is_running sv {Kommandot kr} en {Command is running} de {Programm luft} it {Comando in esecuzione} fr {La commande s'excute} sr {Komanda se izvrava} pl {Polecenie w trakcie dziaania} pt {Comando em execuo} label folder_key_cycle_header sv {Rotera visade huvudrader} en {Cycle shown headers} de {Anzeige Kopfzeilen ndern} it {Scorri intestazioni visibili} fr {Permuter les en-ttes visibles} sr {Menjaj prikaz zaglavlja} pl {Cykl pokazanych nagwkw} pt {Permutar cabealhos visveis} label watcher_enable sv {Vktare aktiv} en {Watcher enable} de {Wchter einschalten} it {Abilita controllo} fr Surveillance sr {Nadzorni prozor} pl {Wcz obserwatora} pt {Vigia pasta} label enabled sv Pslagen en Enabled de Aktiviert it Abilitato fr Active sr Omoguen pl Wczony pt Activo label disabled sv Avslagen en Disabled de Deaktiviert it Disabilitato fr Dsactive sr Iskljuen pl Wyczony pt Desactivo label command_list sv Kommandolista en {Command list} de Befehlsliste it {Lista comandi} fr {liste de commandes} sr {Lista komandi} pl {Lista polece} pt {Lista de comandos} label compose_key_send sv Skicka en Send de Senden it Spedisci fr Expdier sr alji pl Wylij pt Enviar label compose_key_abort sv Avbryt en Abort de Abbrechen it Anulla fr Abandonner sr Odustani pl Zamknij pt Cancelar label compose_key_editor sv {Extern editor} en {External editor} de {Externer Editor} it {Editor esterno} fr {diteur externe} sr {Spoljni editor} pl {Zewntrzny edytor} pt {Editor externo} label compose_key_undo sv {Gr ogjort} en Undo de Rckgngig it {Annulla modifica} fr {Annuler la frappe} sr {Poniti izmene} pl Cofnij pt {Anula modificao} label compose_key_cut sv {Klipp ut} en Cut de Ausschneiden it Taglia fr Couper sr Iseci pl Wytnij pt Eliminar label compose_key_cut_all sv {Klipp ut allt} en {Cut all} de {Alles ausschneiden} it {Taglia tutto} fr {Tout couper} sr {Iseci sve} pl {Wytnij wszystko} pt {Eliminar tudo} label compose_key_copy sv Kopiera en Copy de Kopieren it Copia fr Copier sr Kopiraj pl Kopiuj pt Copiar label compose_key_paste sv {Klistra in} en Paste de Einfgen it Incolla fr Coller sr Umetni pl Wklej pt Colar label sig_cmd_failed sv {RatUP_Signature misslyckades} en {RatUP_Signature failed} de {RatUP_Signature fehlgeschlagen} it {RatUP_Signature fallito} fr {chec de RatUP_Signature} sr {RatUP_Signature nije uspelo} pl {Bd RatUP_Signature} pt {RatUP_Signature falhou} label bell_cmd_failed sv {RatUP_Bell misslyckades} en {RatUP_Bell failed} de {RatUP_Bell feehlgeschlagen} it {RatUP_Bell fallito} fr {chec de RatUP_Bell} sr {RatUP_Bell nije uspelo} pl {Bd RatUP_Bell} pt {RatUP_Bell falhou} label sig_cmd_takes_precedence sv {Du har definierat RatUP_Signature som har frtur ver signaturfilen!} en {You have defined a RatUP_Signature which takes precedence over the signature file!} de {Sie haben RatUP_Signature festgelegt, welche die Signaturerzeugung bernehmen wird!} it {Hai definito una RatUP_Signature che ha precedenza sul file firma!} fr {Vous avez dfini une procdure RatUP_Signature qui remplace le fichier signature.} sr {Definisali ste RatUP_Signature to ima prednost nad datotekom s potpisom!} pl {Zdefiniowae RatUP_Signature, ktra jest nadrzdna wobec pliku sygnaturki} pt {O procedimento RatUP_Signature tem precedncia sobre o ficheiro assinatura!} label more sv Mera en More de Mehr it Altro fr Plus sr Jo pl Wicej pt Mais label compose_sessions sv {Du har fortfarande kvar minst ett aktivt "Skriva brev"-fnster} en {You still have at least one Compose window active} de {Sie haben mindestens ein Entwurffenster offen} it {C' almeno una finestra composizione aperta} fr {Vous avez au moins une fentre de rdaction active.} sr {Jo imate bar jedan aktivni prozor za sastavljanje pisama} pl {Masz cigle przynajmniej jedno okno tworzenia wiadomoci} pt {Existe pelo menos mais uma janela de redaco activa} label really_quit sv {Verkligen avsluta?} en {Really quit?} de {Wirklich beenden?} it {Esci davvero?} fr {Quitter vraiment ?} sr {Da zaista zavrim?} pl {Naprawd wyj?} pt {Confirma querer sair?} label dont_quit sv {Avsluta inte} en {Do not quit} de {Nicht beenden} it {Non uscire} fr {Ne pas quitter} sr Nemoj pl {Nie wychod} pt {No sair} label use_from_address sv {Anvnd avsndaradress} en {Use From address} de {Benutze Von-Adresse} it {Usa indirizzo mittente} fr {Utiliser l'adresse De: } sr {Koristi polaznu adresu} pl {Warto pola From} pt {Endereo a usar no campo De} label messages_shown sv {Antal visade brev} en {Messages shown} de {Nachrichten angezeigt} it {Messaggi visualizzati} fr {Messages montrs} sr {Prikazana pisma} pl {Pokazych wiadomoci} pt {Mensagens visualizadas} label dbase_error sv {Fel i databasen! Kr databaskontrollen i TkRat-menyn: '%s'} en {Error in database! Run the database check in the TkRat menu: '%s'} de {Fehler in Datenbank! Benutzen sie den Datenbankcheck in Men: '%s'} fr {Erreur dans la base de donnes. Utilisez Vrifier la base de donnes dans le menu TkRat: '%s'} sr {Greka u bazi! Pokrenite proveru baze iz menija 'TkRat': '%s'} pl {Bd w bazie danych! Uruchom sprawdzanie bazy w menu TkRat: '%s'} pt {Erro na base de dados! Execute a verificao no menu TkRat: '%s'} label dbase_check sv Databaskontroll en {Database check} de {Datenbank check} it {Verifica database} fr {Vrification la base de donnes} sr {Provera baze} pl {Sprawdzanie bazy} pt {Verificao de base de dados} label check_dbase sv {Kontrollera databasen} en {Check database} de {Prfe Datenbank} it {Verifica database} fr {Vrifier la base de donnes} sr {Proveri bazu} pl {Sprawd baz} pt {Verificar a base de dados} label check_fix_dbase sv {Kontrollera & reparera databasen} en {Check & fix database} de {berprfe und repariere Datenbank} it {Verifica e ripara database} fr {Vrifier et rparer la base de donnes} sr {Proveri i ispravi bazu} pl {Sprawd i napraw baz} pt {Verificar & reparar a base de dados} label checking_dbase sv {Kontrollerar databasen} en {Checking database} de {berprfen Datenbank} it {Verificando database} fr {Vrification en cours} sr {Proveravam bazu} pl {Sprawdzam baz} pt {Verificao em curso} label num_malformed sv {Antal ogiltiga ingngar} en {Number of malformed entries} de {Anzahl der defekten Eintrge} it {Numero di entrate danneggiate} fr {Nombre d'entres malformes} sr {Broj neispravnih unosa} pl {Liczba znieksztaconych wpisw} pt {Nmero de entradas mal formadas} label num_nomessages sv {Antal ingngar utan tillhrande brev} en {Number of entries without messages} de {Anzahl der Eintrge ohne Nachrichten} it {Numero di entrate senza messaggi} fr {Nombre d'entres sans message} sr {Broj unosa bez pisama} pl {Liczba wpisw bez wiadomoci} pt {Nmero de entradas sem mensagens} label num_unlinked sv {Antal brev ej nmnda i index} en {Number of unlinked messages} de {Anzahl der entkoppelten Nachrichten} it {Numero di messaggi scollegati} fr {Nombre de messages sans index} sr {Broj pisama bez indeksa} pl {Liczba nie zlinkowanych wiadomoci} pt {Nmero de mensagens sem ndice} label total_size sv {Sammanlagd storlek av alla brev} en {Total size of all messages} de {Gesamtgre aller Nachrichten} it {Dimensione totale di tutti i messaggi} fr {Taille totale de tous les messages} sr {Ukupna veliina svih pisama} pl {Cakowity rozmiar wszystkich wiadomoci} pt {Tamanho total de todas as mensagens} label waiting_dbase_lock sv {Vntar p databaslset} en {Waiting on database lock} de {Warte auf Datenbankriegel} it {Attendendo sul blocco del database} fr {Attente du verrou de la base de donnes} sr {ekam zbog zakljuavanja baze} pl {Czekam na zablokowanie bazy} pt {Em espera da proteco da base de dados} label port sv Port en Port de Port it Porta fr Port sr Port pl Port pt Porto label imapport sv Port en Port de Port it Porta fr Port sr Port pl Port pt Porto label pop3port sv Port en Port de Port it Porta fr Port sr Port pl Port pt Porto label already_exists sv {finns redan} en {already exists} de {existiert bereits} it {esiste gi} fr {Existe dj} sr {ve postoji} pl {ju istnieje} pt {j existe} label overwrite sv {Skriv ver} en Overwrite de berschreiben it Sovrascrivere fr craser sr Prebrii pl Nadpisz pt Sobrescrever label file_exists sv {Filen finns} en {File exists} de {Datei existiert} it {Il file esiste gi} fr {Le fichier existe dj} sr {Datoteka postoji} pl {Plik istnieje} pt {O Ficheiro j existe} label type_description sv Typbeskrivning en {Type description} de Typbeschreibung it {Descrizione del tipo} fr {Description du type} sr {Opis vrste} pl {Opis typu} pt {Descrio do tipo} label view sv Visa en View de Ansicht it Visualizza fr Voir sr Prikai pl Poka pt Ver label view_as_text sv {Visa som ren text} en {View as plain text} de {Als normalen Text anzeigen} it {Visualizza come testo semplice} fr {Voir comme du texte} sr {Prikai kao obian tekst} pl {Poka jako tekst} pt {Ver como texto} label external_viewer sv {Externt program} en {External viewer} de {Externer Betracher} it {Programma esterno} fr {Commande de visualisation externe} sr {Spoljni program za pregled} pl {Zewntrzna przegldarka} pt {Comando de visualizao externa} label cant_pipe sv {Jag kan inte skicka data till stdin p program som anvnder terminalfnstret} en {I cannot pipe input into programs that use the terminal window} de {Kann keine Eingabe in ein Programm leiten, welches ein Terminal Fenster benutzt} it {Non possibile mandare l'input a programmi che usano la finestra terminale} fr {Je ne peux pas fournir les donnes un programme qui utilise un terminal.} sr {Ne mogu da prenesem podatke u program koji koristi terminalski prozor} pl {Nie mog przekaza (pipe) danych programom uywajcym okna terminala} pt {No possvel fornecer dados a um programa que use a janela terminal} label reread_mailcap sv {Ls om mailcap} en {Reread mailcap} de {Lies Mailcap nochmals} it {Rileggi mailcap} fr {Relire mailcap} sr {Ponovo proitaj mailcap} pl {Ponownie czytaj mailcap} pt {Reler 'mailcap'} label terminal_command sv Terminalkommando en {Terminal command} de {Befehlszeile fr ein Terminal} it {Comando terminale} fr {Commande terminal} sr {Terminalska komanda} pl {Polecenie terminala} pt {Comando de terminal} label vfolderedit sv {Editera virtuell mappar} en {Edit virtual folder} de {ndere virtuellen Ordner} it {Modifica cartella virtuale} fr {Modifier la bote lettres virtuelle} sr {Izmeni virtuelni skup} pl {Edytuj wirtualny folder} pt {Modificar pasta virtual} label delete_imap sv {Radera sjlva IMAP-mappen ocks?} en {Delete the actual IMAP folder as well?} de {Lsche auch den aktuellen IMAP-Ordner?} it {Cancellare anche la cartella IMAP fisica?} fr {Dtruire Physiquement la bote lettres IMAP} sr {Da takoe obriem i stvarni IMAP skup?} pl {Usun rwnie aktywny folder IMAP?} pt {Apagar tambm a pasta IMAP?} label dont_delete sv {Radera inte} en {Do not delete} de {Nicht lschen} it {Non cancellare} fr {Ne pas dtruire} sr {Nemoj brisati} pl {Nie usuwaj} pt {No apagar} label selected sv Valda en Selected de Ausgewhlte it Selezionato fr Slectionns sr Izabrano pl Zaznaczone pt Seleccionados label extract_adr sv {Extrahera adresser} en {Extract addresses} de {Extrahiere Adressen} it {Estrai indirizzi} fr {Extraire les adresses} sr {Izdvoj adrese} pl {Pobierz adresy} pt {Extrair endereos} label add_aliases sv {Lgg till alias} en {Add aliases} de {Alias hinzufgen} it {Aggiungi alias} fr {Ajouter un alias} sr {Dodaj aliase} pl {Dodaj aliasy} pt {Adicionar alias} label use sv Anvnd en Use de Benutze it Usa fr Utiliser sr Koristi pl Uyj pt Usar label missing_alias_name sv {Saknar aliasnamn. En ingng blev inte anvnd.} en {Missing alias name. One entry will not be added.} de {Vermisse Aliasname. Ein Eintrag wird nicht hinzugefgt.} it {Nome alias mancante. Una entrata non verr aggiunta.} fr {Un nom d'alias manque. L'un des alias ne sera pas ajoute.} sr {Nedostaje ime aliasa. Jedan unos nee biti dodat.} pl {Brakuje nazwy aliasu. Jeno pole nie zostanie dodane.} pt {Nome de alias em falta. Uma entrada no ser introduzida.} label alias_chooser sv {Alias vljare} en {Alias chooser} de Aliaswhler it {Selettore alias} fr {Slecteur d'alias} sr {Bira aliasa} pl {Wybr aliasw} pt {Selector de alias} label save_outgoing sv {Spara utgende} en {Save outgoing} de {Sichere abgehende} fr {Reoit les messages expdis} sr {Sauvaj odlazee} pl {Zapisz wychodzce} pt {Guardar mensagens enviadas} label add sv {Lgg till} en Add de Hinzufgen it Aggiungi fr Ajouter sr Dodaj pl Dodaj pt Juntar label skip_sig sv {Inkludera inte orginalsignaturen vid svar} en {Do not include the original signature in replies} de {Die Signatur nicht zitieren} #it {Non includere la signature} #fr {Ne pas inclure la signature} #sr {ne ukljuuj potpis} #pl {Nie doczaj sygnatury} #pt {No incluir a assinatura} label keep_sig sv {Inkludera signaturen} en {Include the signature} de {Signatur zitieren} it {Includi signature} fr {Inclure la signature} sr {ukljui potpis} pl {Docz sygnatur} pt {Incluir a assinatura} label sort_subjectonly sv {Efter mne} en {By subject} de {Nach Betreff} it {Per soggetto} fr {Par objet} sr {Po naslovu} pl {Wg. tematu} pt {Por assunto} label sort_sender sv {Efter avsndare och datum} en {By sender and date} de {Nach Absender und chronologisch} fr {Par auteur et par date} sr {Po poiljaocu i datumu} pl {Wg. nadawcy i daty} pt {Por remetente e data} label sort_senderonly sv {Efter avsndare} en {By sender} de {Nach Absender} it {Per mittente} fr {Par auteur} sr {Po poiljaocu} pl {Wg. nadawcy} pt {Por remetente} label sign sv Signera en Sign de Unterschreiben it Firma fr Signer sr Potpii pl Podpisz pt Assinar label encrypt sv Kryptera en Encrypt de Verschlsseln it Cripta fr Chiffrer sr ifruj pl Zakoduj pt Cifrar label pgp_pass_phrase sv {PGP lsenordssekvens} en {PGP pass phrase} de {PGP Passphrase} it {Frase passaggio PGP} fr {Phrase clef PGP} sr {PGP lozinka} pl {Fraza kodujca PGP} pt {Frase passe PGP} label pgp_problem sv {PGP problem} en {PGP problem} de {PGP problem} it {Problema PGP} fr {Problme PGP} sr {Problem sa PGP-om} pl {Problem PGP} pt {Problema PGP} label retry sv {Frsk igen} en Retry de {Nochmal versuchen} it Riprova fr Ressayer sr Ponovi pl Ponw pt Repetir label sig sv Sig en Sig de Sig it Sig fr Sig sr Sig pl Sig pt Sig label signature sv Signatur en Signature de Signatur it Firma fr Signature sr Potpis pl Sygnatura pt Assinatura label none sv Ingen en None de Keine it Nulla fr Aucune sr Nita pl Nic pt Nenhum label pgp_part sv Del en Part de Teil it Parte fr Pice sr Deo pl Cz pt Parte label pgp_none sv Ingen en None de Keine it Nulla fr Aucun sr Nita pl nic pt No label pgp_unchecked sv ? en ? de ? it ? fr ? sr ? pl ? pt ? label pgp_good sv Ok en OK de Ok it Ok fr Ok sr Ok pl Ok pt Ok label pgp_bad sv Fel en Bad de Ungltig it Err. fr Err. sr Loe pl Zle pt Incorrecto label pgp_output sv {PGP utdata} en {PGP output} de {PGP Ausgabe} it {Output PGP} fr {Rsultat de PGP} sr {Izlaz PGP-a} pl {Wynik PGP} pt {Resultado de PGP} label warning_pgp_prog sv {VARNING: Det specificerade pgpprogrammet finns inte eller r inte krbart} en {WARNING: The specified PGP program does not exist or is not executable} de {WARNUNG: Das angegebene PGP Programm exisiert nicht oder ist nicht ausfhrbar} it {ATTENZIONE: Il programma pgp specificato non esiste o non eseguibile} fr {ATTENTION : Le programme PGP indiqu n'existe pas ou n'est pas excutable !} sr {UPOZORENJE: Naznaeni PGP program ne postoji ili nije izvriv!} pl {UWAGA: Podany program PGP nie istnieje lub nie jest wykonywalny!} pt {AVISO: O programa PGP especificado no existe ou no executvel} label pgp_version sv PGP-version en {PGP version} de {PGP Version} fr {Version de PGP} sr {Verzija PGP-a} pl {Wersja PGP} pt {Verso de PGP} label pgp_path sv PGP-skvg en {PGP path} de {PGP Verzeichnis} fr {Chemin vers PGP} sr {Staza do PGP-a} pl {Scieka PGP} pt {Caminho para programa PGP} label pgp_extra_args sv {PGP extra argument} en {PGP extra arguments} de {Zustzliche Parameter} it {Argomenti extra PGP} fr {Autres arguments pour PGP} sr {Dodatni argumenti za PGP} pl {Dodatkowe argumenty PGP} pt {Argumentos adicionais para PGP} label pgp_keyring sv {PGP nyckelring} en {PGP keyring} de {PGP Schlsselbund} fr {Porte-clefs PGP} sr {PGP sveanj kljueva} pl {Pk kluczy PGP} pt {Porta-chaves PGP} label decoded sv Avkodad en Decoded de Dekodiert it Decodificato fr Dcod sr Dekodovano pl Rozkodowano pt Descodificado label send_bug sv {Skicka felrapport} en {Send bug report} de {Schicke Bugreport} it {Rapporto malfunzione} fr {Signaler une bogue} sr {Izvetaj o greci} pl {Raport o bdach} pt {Enviar relatrio de erro} label bug_shortdesc sv {Kort (en rad) beskrivning} en {Short (one line) description} de {Kurze Beschreibung} fr {Description courte (une ligne)} sr {Kratak opis (u jednom redu)} pl {Krtki (jedna linia) opis} pt {Descrio curta do erro} label bug_description sv {Skriv en detaljerad beskrivning om vad som hnde nedan. Se till att du skriver vad du gjorde fr att orsaka felet} en {Write a detailed description of what happened below. Be sure to tell what action of yours caused the error.} de {Schreiben sie eine detailierte Beschreibung, was passiert ist, im Feld unten. Schreiben sie auch, wie man es reproduzieren kann!} fr {veuillez faire une description dtaille du problme. Indiquez Ce que vous avez fait qui a provoqu l'erreur.} sr {Napiite ispod detaljan opis ta se dogodilo. Obavezno navedite koje su Vae akcije proizvele greku.} pl {Podaj szczegowy opis tego co si stao poniej. Upewnij si, ktra z twoich czynnoci spowodowaa bd} pt {Elabore uma descrio detalhada do problema encontrado. Refira todas as aces que conduziram ocorrncia do erro} label configuration_information sv {Beskrivning av din konfiguration} en {Description of your configuration} de {Beschreibung Ihrer Konfiguration} it {Descrizione della tua configurazione} fr {Description de votre configuration} sr {Opis Vae konfiguracije} pl {Opis twojej konfiguracji} pt {Descrio da sua configurao} label send_bugs_etc sv {Vnligen skicka felrapporter och frslag till frbttringar till maf@tkrat.org} en {Please send bug reports and suggestions for improvements to maf@tkrat.org} de {Bitte senden sie Bugreports und Vorschlge fr Verbesserungen an maf@tkrat.org} it {Prego mandare errori e suggerimenti a maf@tkrat.org} fr {Veuillez expdier les descriptions de bogues et vos suggestions maf@tkrat.org} sr {Molim Vas, aljite izvetaje o grekama i predloge za poboljanja na maf@tkrat.org} pl {Prosz wysa raport o bdach i sugestie ulepsze do maf@tkrat.org} pt {Envie pf. relatrios de erros e sugestes a maf@tkrat.org} label sign_outgoing sv {Signera brev} en {Sign messages} de {Unterschreibe Nachrichten} it {Contrassegna messaggi} fr {Signer les messages} sr {Potpii pisma} pl {Podpisz wiadomoci} pt {Assinar mensagem} label encrypt_outgoing sv {Kryptera brev} en {Encrypt messages} de {Verschlssele Nachrichten} it {Cripta messaggi} fr {Chiffrer les messages} sr {ifruj pisma} pl {Szyfruj wiadomoci} pt {Cifrar mensagem} label true sv Sant en True de Ja it Vero fr Vrai sr Da pl Tak pt Sim label false sv Falskt en False de Nein it Falso fr Faux sr Ne pl Nie pt No label attach_pgp_keys sv {Bifoga PGP-nycklar} en {Attach PGP keys} de {Hnge PGP Schlssel an} it {Allega chiavi PGP} fr {Joindre une clef PGP} sr {Zakai PGP-klju} pl {Docz klucze PGP} pt {Anexar chaves PGP} label select_keys sv {Vlj nycklar} en {Select keys} de {Whlen sie Schlssel} it {Seleziona chiavi} fr {Choisir une clef} sr {Odaberite klju} pl {Wybierz klucze} pt {Seleccionar chaves} label bits sv Bitar en Bits de Bits it Bit fr Bits sr Bitovi pl Bity pt Bits label keyid sv NyckelID en KeyID de Schlssel-Nr. it IDchiave fr {Identifiant de clef} sr {ID kljua} pl {ID klucza} pt {Identificao da chave} label user_id sv Anvndarid en {User ID} de Benutzerkennung it {ID utente} fr {Identifiant d'utilisateur} sr {ID korisnika} pl {ID uytkownika} pt {Identificao do utilizador} label pgp_key sv PGP-nyckel en {PGP key} de {PGP Schlssel} fr {Clef PGP} sr {PGP klju za} pl {Klucz PGP} pt {Chave PGP} label embedded_pgp_keys sv {Hr r en eller flera PGP-nycklar bifogade} en {Here are one or more PGP keys attached} de {Hier sind ein oder mehrere Schlssel angehngt} it {Qui ci sono una o pi chiavi PGP allegate} fr {Voici une ou plusieurs clefs PGP en pice jointe.} sr {Ovde ima jedan ili vie zakaenih PGP kljueva} pl {Docz jeden lub wicej kluczy PGP} pt {Uma ou mais chaves PGP em anexo} label add_to_keyring sv {Lgg till i nyckelknippa} en {Add to keyring} de {Zu Schlsselbund hinzufgen} it {Aggiungi al keyring} fr {Ajouter au porte-clefs} sr {Dodaj na sveanj} pl {Dodaj do pku kluczy} pt {Adicionar ao porta-chaves} label press_return_to_dismiss sv {Tryck p retur fr att skicka bort fnstret} en {Press Return to dismiss window} de {Drcken sie Return, um das Fenster zu schlieen} it {Premi invio per chiudere la finestra} fr {Appuyer sur entre pour faire disparatre la fentre.} sr {Pritisnite Enter da se zatvori prozor} pl {Nacinij Enter by zamkn okno} pt {Pressione 'Enter' para sair} label decrypting sv Dekrypterar... en Decrypting... de Entschlssele... it Decriptando... fr Dcryptage... sr Deifrujem... pl Odszyfrowuj... pt {Deciframento em curso...} label seconds sv sekunder en seconds de Sekunden it secondi fr secondes sr sekundi pl sekundy pt segundos label days sv dagar en days de Tage it giorni fr jours sr dana pl dni pt dias label url_viewer sv {URL lsare} en {URL viewer} de {Web-Browser} it {Visualizzatore URL} fr {Visionneuse d'URL} sr {Za pregled URL-ova} pl {Przegldarka URL'i} pt {Visualizador de endereos URL} label userproc sv Egendefinierad en Userproc de Benutzerprogramm it {Procedura utente} fr {Procdure utilisateur} sr {Korisnika procedura} pl {Procedura uytkownika} pt Procedimento label addrbooks sv Adressbcker en {Address books} de Adressbcher it Agende fr {Carnets d'adresses} sr Adresari pl {Ksizki adresowe} pt {Livro de endereos} label addrbook sv Adressbok en {Address book} de Adressbuch it Agenda fr {Carnet d'adresses} sr Adresar pl {Ksika adresowa} pt {Livro de endereo} label use_system_aliases sv {Anvnd systemets alias} en {Use system aliases} de {Benutze Systemaliasse} it {Usa alias di sistema} fr {Utiliser les alias systmes} sr {Koristi sistemske aliase} pl {Uyj systemowych aliasw} pt {Usar alias de sistema} label move_to sv {Flytta till} en {Move to} de {Verschiebe nach} it {Muovi in} fr {Dplacer vers} sr {Premesti u} pl {Przenie do} pt {Mover para} label set_default sv {Stt standard} en {Set default} de {Setze Standard} it {Seleziona default} fr {Choisir par dfaut} sr {Postavi za osnovni} pl {Ustaw domylny} pt {Escolher padro} label need_writable_book sv {Du mste spara minst en skrivbar adressbok} en {You must leave at least one writable address book} de {Sie brauchen mindestens ein beschreibbares Adressbuch} it {Bisogna avere almeno un'agenda scrivibile} fr {Vous devez laisser au moins un carnet d'adresses en criture.} sr {Morate ostaviti bar jedan upisiv adresar} pl {Musisz zostawi przynajmniej jedn ksik do zapisu} pt {Deve haver pelo menos um livro de endereos actualizvel} label book_already_exists sv {Det finns redan en adressbook med det namnet} en {There already exists an address book with that name} de {Es gibt bereits ein Buch mit diesem Namen} it {Esiste gi un'agenda con quel nome} fr {Il y a dj un carnet d'adresses portant ce nom.} sr {Ve postoji adresar sa tim imenom} pl {Ju istnieje ksika o takiej nazwie} pt {J existe um livro de endereos com esse nome} label pgp sv PGP en PGP de PGP it PGP fr PGP sr PGP pl PGP pt PGP label find sv Sk en Find de Suchen it Ricerca fr Rechercher sr Trai pl Znajd pt Procurar label find_in sv {Sk i} en {Find in} de {Suchen in} it {Cerca in} fr {Rechercher dans} sr {Trai u} pl {Znajd w} pt {Procurar em} label message_list sv Brevlista en {Message list} de Nachrichtenliste it {Lista messaggi} fr {la liste des messages} sr {Lista pisama} pl {Lista wiadomoci} pt {Lista de mensagens} label message_body sv {Brevets innehll} en {Message body} de {Dieser Nachricht} it {Corpo messaggio} fr {le corps du message} sr {Sadraj pisma} pl {Tre wiadomoci} pt {Corpo da mensagem} label find_next sv {Sk nsta} en {Find next} de {Vorwrts suchen} it {Cerca prossimo} fr {Occurrence suivante} sr {Trai dalje} pl Nastpny pt {Procurar prximo} label out_of_order sv {Ur funktion} en {Out of order} de {Auer Betrieb} it {Non funzionante} fr {Hors service} sr {Ne radi} pl Uszkodzony pt {Mau funcionamento} label default_to_browse sv {Anvnd titta-mod i normalfallet} en {Default to Browse mode} de {Browsemodus als Standard} it {Sempre in modo titoli} fr {Mode papillon} sr {Podrazumeva se reim pregledanja} pl {Domylnie do trybu przegldania} pt {Modo de navegao por defeito} label browse_mode sv Titta-mod en {Browse mode} de Browsemodus it {Modo titoli} fr {Mode papillon} sr {Reim pregledanja} pl {Tryb przegldania} pt {Modo de navegao} label show_body sv {Visa texten} en {Show body} de {Zeige Inhalt} it {Mostra corpo} fr {Montrer le corps} sr {Pokai sadraj} pl {Poka tre} pt {Mostrar corpo} label balloon_help sv Ballonghjlp en {Balloon help} de Kontexthilfe it Aiuto-balloon fr {Bulles d'aide} sr Balon-pomo pl {Baloniki pomocy} pt {Bales de ajuda} label subjects sv mnen en Subjects de Themen it Soggetti fr Objets sr Naslovi pl Tematy pt Assuntos label warning sv Varning en Warning de Warnung it Attenzione fr Attention sr Upozorenje pl Ostrzeenie pt Aviso label do_not_show_again sv {Visa inte denna varning nsta gng} en {Do not show this warning again} de {Zeige diese Warnung nicht noch einmal} it {Non mostrare piu questo avviso} fr {Ne plus montrer ce message} sr {Ne pokazuj ovo upozorenje ponovo} pl {Nie pokazuj ju tego ostrzeenia} pt {No mostrar este aviso novamente} label expunge_on_close sv {Radera vid stngning} en {Expunge on close} de {Ordner beim Schlieen aufrumen} it {Rimuovi in chiusura} fr {Dtruire la fermeture} sr {Oisti pri zatvaranju} pl {Usuwanie po zamkniciu} pt {Apagar ao fechar} label do sv Gr en Do de Ja it {} fr Oui sr Da pl Wykonaj pt Sim label do_not sv {Gr inte} en {Do not} de Nein it Non fr Non sr Ne pl Nie pt No label mailbox_stolen sv {Ngon annan process har tagit vrt ls p en mapp} en {Some other process has stolen our lock on a folder} de {Ein anderer Prozess sperrt einen Ordner} fr {Un autre processus a crochet un verrou de bote lettres.} sr {Neki drugi proces je preoteo zakljuani skup} pl {Jaki inny proces przej kontrol nad folderem} pt {Outro processo quebrou a proteco a uma pasta} label checkpoint_interval sv {Spara flaggor intervall} en {Checkpoint interval} de {Prfpunkt Intervall} fr {Intervalle de points de reprise} sr {Interval za upis statusa} pl {Przedziay midzy punktami kontrolnymi} pt {Intervalo de verificaes} label new_folder sv {Nytt mappfnster} en {New folder window} de {Neues Ordnerfenster} fr {Nouvelle fentre bote lettres} sr {Novi prozor za skup} pl {Nowe okno folderu} pt {Nova janela} label empty sv Tom en Empty de Leer fr Vide sr Prazno pl Pusty pt Vazio label msglist sv Lista en Msglist de Nachrichtenliste fr {Liste de messages} sr {Lista pisama} pl Lista pt Lista label url sv URL en URL de URL fr URL sr URL pl URL pt URL label auto sv Automatiskt en Automatic de Automatisch fr Automatique sr Automatski pl Automatycznie pt Automtico label bad_charset sv {Den valda teckenkodningen kan inte uttrycka alla tecken i brevet} en {The chosen character set can not properly represent all characters in the message} de {Der ausgewhlte Zeichensatz kann nicht alle Zeichen dieser Nachricht abbilden} fr {Impossible de reprsenter tous les caractres du message avec le jeu de caractres choisi.} sr {Odabrani skup znakova ne moe da pravilno predstavi sve znake u pismu} pl {Wybrana strona kodowa nie moe poprawnie wywietla wszystkich znakw w tej wiadomoci} pt {O conjunto de caracteres escolhido no representa todos os caracteres da mensagem} label converting_dbase sv {Konverterar databasen... %d%%} en {Converting database... %d%%} de {Konvertiere Datenbank %d%%} fr {Base de donnes en cours de conversion... %d%%} sr {Konvertujem bazu... %d%%} pl {Konwertuj baz... %d%%} pt {Converso da base de dados... %d%%} label interpret_as sv {Tolka som} en {Intrepret as} de {Interpretieren als} fr {Interprter comme} sr {Interpretiraj kao} pl {Interpretuj jako} pt {Interpretar como} label address sv Adress en Address de Adresse fr Adresse sr Adresa pl Adres pt Endereo label method sv Metod en Method de Methode fr Mthode sr Metod pl Metoda pt Mtodo label return_path sv Avsndare en {Return path} de Retourpfad fr {Chemin de retour} sr {Staza za povratak} pl {Return path} pt Caminho-de-retorno label test_by sv {Du kan testa dessa vrden genom menyingngen "Visa genererade huvudrader" i skriva brev-fnstret} en {You can test these settings via the "Show generated headers" entry in the Compose window menus} de {Sie knnen diese Einstellungen per "Zeige erzeugte Kopfzeilen" aus dem "Erstellen"-Men berprfen} fr {Vous pouvez tester ces rglages grce l'option Montrer les en-ttes gnrs dans le menu de la fentre de rdaction} sr {Moete proveriti ova podeavanja putem naredbe "Pokai generisano zaglavlje" iz menija prozora za sastavljanje pisama} pl {Moesz testowa te ustawienia przy pomocy przycisku 'Poka wygenerowane nagwki' w menu okna tworzenia wiadomoci} pt {Pode testar estas configuraes atravs de 'Mostrar cabealhos gerados' nos menus da janela de redaco de mensagem} label tip sv Tips en Tip de Tip fr Astuce sr Savet pl Porada pt Sugesto label show_generated sv {Visa genererade huvudrader} en {Show generated headers} de {Zeige erzeugte Kopfzeilen} fr {Montrer les en-ttes gnrs} sr {Pokai generisano zaglavlje} pl {Poka wygenerowane nagwki} pt {Mostrar cabealhos gerados} label generated_header sv {Genererade adress huvudrader} en {Generated address header fields} de {Erzeugte Kopfzeilen} fr {Champs relatifs aux en-ttes d'adresse gnrs} sr {Generisane adrese u zaglavlju} pl {Wygenerowane pola adresowe} pt {Cabealho gerado} label update sv Uppdatera en Update de Auffrischen fr {Mise jour} sr Obnovi pl Aktualizuj pt Actualizar label edit_exp sv {Rediger uttryck} en {Edit expression} de {Ausdruck bearbeiten} fr {diter l'expression} sr {Menjanje izraza} pl {Edytuj wyraenie} pt {Editar expresso} label editors sv Textredigerare en Editors de Editoren fr diteurs sr Editori pl Edytory pt Editores label create_simple sv {Skapa mapp (enkelt)} en {Create folder (simple)} de {Erzeuge Ordner (einfach)} fr {Crer une bote lettres (simple)} sr {Stvaranje skupa (jednostavno)} pl {Utwrz folder (proste)} pt {Criar pasta (simples)} label create_advanced sv {SKapa mapp (avancerat)} en {Create folder (advanced)} de {Erzeuge Ordner (fortgeschritten)} fr {Crer une bote lettres (avanc)} sr {Stvaranje skupa (napredno)} pl {Utwrz folder (zaawansowane)} pt {Criar pasta (avanado)} label options sv Instllningar en Options de Optionen fr Options sr Opcije pl Opcje pt Opes label definitions sv Definitioner en Definitions de Definitionen fr Dfinitions sr Definicije pl Definicje pt Definies label track_changes sv {Flj ndringar} en {Track changes} de {nderungen verfolgen} fr {Surveiller les changements} sr {Prati izmene} pl {Obserwuj zmiany} pt {Vigiar mudanas} label sort_default sv Standardvrde en Default de Standard fr {Par dfaut} sr Podrazumevano pl Domylne pt {Por defeito} label pathname sv Skvg en {Path name} de Pfadname fr Chemin sr Staza pl {nazwa cierzki} pt Caminho label browse sv Sk en Browse de Blttern fr Parcourir sr Pregled pl Przegldaj pt Navegar label select_file sv {Vlj fil} en {Select file} de {Datei auswhlen} fr {Slectionner un fichier} sr {Odaberite datoteku} pl {Wybierz plik} pt {Seleccionar ficheiro} label trace_changes sv {Flj ndringar} en {Trace changes} de {nderungen verfolgen} fr {Repre les changements.} sr {Prati izmene} pl {Obserwuj zmiany} pt {Indicar mudanas} label subscribed_only sv {Endast prenumererade mappar} en {Subscribed folders only} de {Nur abonnierte Ordner} fr {Botes lettres souscrites uniquement} sr {Samo potvreni skupovi} pl {Tylko zasubskrybowane foldery} pt {Pastas requeridas apenas} label auto_select sv {AUTOMATISKT VAL} en {AUTO SELECT} de {Automatische Auswahl} fr {Slection automatique} sr {AUTOMATSKI IZBOR} pl {AUTOMATYCZNY WYBOR} pt {Seleco automtica} label lynx_cmd sv {Lynx kommando} en {Lynx command} de {Lynx Befehl} fr {Commande Lynx} sr {Lynx komanda} pl {Polecenie Lynx'a} pt {Comando lynx} label other_browser_cmd sv {Annat blddrarkommando} en {Other browser command} de {Anderer Browser-Befehl} fr {Commande autre navigateur} sr {Komanda za drugi ita} pl {Polecenie innej przegldarki} pt {Outro 'Browser'} label bounce sv Studsa en Bounce de Umleiten fr Rexpdier sr Odbij pl Odbij pt Reencaminhar label delete_failed sv {Misslyckades radera mappen} en {Mailbox deletion failed} de {Mailbox lschen fehlgeschlagen} fr {Impossible de dtruire la bote lettres} sr {Brisanje sandueta nije uspelo!} pl {Bd usuwania skrzynki} pt {No foi possvel remover a pasta} label store_passwd sv {Spara lsenordet p disk} en {Store password on disk} de {Passwort auf Festplatte speichern} fr {Sauver le mot de passe sur disque} sr {Sauvaj ifru na disk} pl {Przechowuj haso na dysku} pt {Guardar a palavra passe em disco} label cache_passwd sv {Kom ihg lsenord} en {Cache passwords} de {Passwrter speichern} it {Mantieni password} fr {Mmoriser les mots de passe} sr {Keiranje ifri} pl {Cacheuj hasa} pt {Palavras passe} label cache_timeout sv {Kom ihg intervall} en {Cache timeout} de {Passwort-Speicherdauer} it {Timeout cache} fr {Dure de mmorisation} sr {Rok za keiranje} pl {Cacheuj timeout} pt Durao label purge_pwcache sv {Radera sparade lsenord} en {Purge cached passwords} de {Gespeicherte Passwrter lschen} fr {Purger les mots de passe en mmoire} sr {Isprazni ke za ifre} pl {Wyczy cacheowane hasa} pt {Destruir palavras passe memorizadas} label basic sv Normal en Basic de Einfach fr basique sr Osnovno pl Podstawowy pt Bsico label replies sv Svar en Replies de Antworten fr Rponses sr Odgovori pl Odpowiedzi pt Respostas label wrap_cited sv {Bryt citerad text} en {Wrap cited text} de {Den zitierten Text umbrechen} fr {Remettre en forme le texte cit} sr {Prelomi citirani tekst} pl {Zawijaj cytowany tekst} pt {Dobrar linhas de texto citado} label for sv fr en for de fr fr pour sr za pl dla pt para label mark_as_unread sv {Markera som olst} en {Mark as unread} de {Als ungelesen markieren} fr {Marquer comme non lu} sr {Oznai kao neproitano} pl {Oznacz jako nie przeczytane} pt {Marcar como nova} label folder_key_markunread sv {Markera som olst} en {Mark as unread} de {Als ungelesen markieren} fr {Marquer comme non lu} sr {Oznai kao neproitano} pl {Oznacz jako nie przeczytane} pt {Marcar como nova} label defaults sv Standardvrden en Defaults de Standard fr {Par dfaut} sr Podrazumevano pl Domylnie pt {Por defeito} label setup_netsync sv {Stll in ntverkssynk} en {Set up network sync} de {Netzabgleich Einstellungen} fr {Configuration de la synchronisation rseau} sr {Podesi mreno sinhronizovanje} pl {Konfiguruj synchronizacj sieciow} pt {Configurar sincronismo da rede} label run_command sv {Kr kommando} en {Run command} de {Programm starten} fr {Excuter une commande} sr {Izvri komandu} pl Uruchom pt {Executar comando} label netsync sv {Ntverkssyna allt} en {Network sync all} de {Allgemeiner Netzabgleich} #de Netzwerksynchronisation #fr {Synchronisation rseau} #sr {Mreno sinhronizovanje} #pl {Synchronizacja sieciowa} #pt {Sincronizar rede} label running_cmd sv {Kr externt program...} en {Running external program...} de {Externes Programm starten...} fr {Commande externe en cours...} sr {Pokreem spoljni program...} pl {Uruchamiam zewntrzny program...} pt {Execuo de comando externo em curso...} label netsync_folder sv {Ntverkssynk mapp} en {Network sync folder} de {Netzabgleich dieses Ordners} fr {Synchroniser bote lettre dconnecte} sr {Sinhronizuj skup preko mree} pl {Synchronizuj folder} pt {Sincronizao de pasta assncrona} label uidvalidity_changed sv {Mastermappens uidvalidityvrde har ndrats. Detta betyder att vi inte lngre kan synkronisera oss mot den. Men du kan fortfarande lsa de brev som ligger i den lokala kopian.} en {The master folders uidvalidity value has changed. This means that we are out of sync and that we can not synchronize any more. But, you can still read the local copy.} de {Der Ordner hat sich substanziell gendert. Das bedeutet, dass eine Synchronisation unmglich geworden ist. Sie knnen jedoch noch immer die lokale Kopie lesen.} fr {La valeur de l'uidvalidity de la bote lettres distante a chang, la bote lettres est dsynchronise et ne peut plus tre synchronise. La lecture de la copie locale reste possible.} sr {Promenjena je vrednost 'uidvalidity' za glavni skup. Ovo znai da nismo sinhronizovani i da je dalje sinhronizovanje nemogue, ali se lokalna kopija jo moe itati.} pl {Warto 'Uidvalidity' gwnych folderw zostaa zmieniona. To znaczy, e nie mona ju wicej synchronizowa danych. Ale wci moesz czyta lokaln kopi} pt {Fora de sincronismo. A leitura de cpias locais permanece possvel} label open sv ppna en Open de ffnen fr Ouvrir sr Otvori pl Otwrz pt Abrir label save_to_mh sv {Kan inte spara utgende post i MH-mappar} en {Cannot save outgoing mail in MH folders} de {Kann verschickte Nachrichten nicht in MH-Ordner sichern} fr {Impossible de sauvegarder les copies de messages dans une bote lettres de type MH.} sr {Ne mogu da snimim odlazeu potu u MH skup} pl {Nie mona zapisa wychodzcej poczty w folderach MH} pt {No possvel gravar correio enviado em pasta MH} label failed_to_create_file sv {Misslyckades att skapa filen %.100s: %.100s} en {Failed to create file %.100s: %.100s} de {Konnte Datei %.100s nicht anlegen: %.100s} fr {chec de cration de fichier %.100s: %.100s} sr {Nije uspelo stvaranje datoteke %.100s: %.100s} pl {Nie mona utworzy pliku %.100s: %.100s} pt {No foi possvel criar ficheiro %.100s: %.100s} label failed_to_unlink_file sv {Misslyckades att radera filen %.100s: %.100s} en {Failed to unlink file %.100s: %.100s} de {Konnte Datei %.100s nicht lschen: %.100s} fr {chec de destruction du fichier %.100s : %.100s} sr {Nije uspelo odvezivanje datoteke %.100s: %.100s} pl {Nie mona odlinkowa pliku %.100s: %.100s} pt {No foi possvel destruir o ficheiro %.100s: %.100s} label failed_to_move_to_file sv {Misslyckades flytta till fil '%.100s': %.100s} en {Failed to move to file '%.100s': %.100s} de {Konnte Datei %.100s nicht verschieben: %.100s} fr {chec du dplacement du fichier %.100s : %.100s} sr {Nije uspelo premetanje u datoteku '%.100s': %.100s} pl {Nie mona prznie do pliku '%.100s': %.100s} pt {No foi possvel mover para o ficheiro '%.100s': %.100s} label synchronizing sv {Synkroniserar '%s'} en {Synchronizing '%s'} de {Synchronisiere '%s'} fr {Synchronisation de %s} sr {Sinhronizujem '%s'} pl {Synchronizacja '%s'} pt {Sincronizao em curso} label uploading sv {Skickar upp lokala ndringar} en {Uploading local changes} de {Sende lokale nderungen} fr {Transmission des changements locaux} sr {Prenosim lokalne izmene} pl {Wysyanie lokalnych zmain} pt {Transmisso das mudanas locais} label downloading sv {Laddar ner brev %d av %d} en {Downloading message %d of %d} de {Lade Nachricht %d von %d} fr {%d messages sur %d rcuprs} sr {Prenosim pismo %d od %d} pl {Sciganie wiadomoci %d z %d} pt {Carregamento de mensagem %d de %d em curso} label downloading_flags sv {Laddar ner ndrade flaggor} en {Downloading flag-changes} de {Empfange entfernte nderungen} fr {rcupre l'tat des changements} sr {Prenosim statusne izmene} pl {Sciganie zmian flag} pt {Carregamento das mudanas de estado} label syntax_error sv {Syntaxfel i %.100s p rad %d} en {Syntax error in %.100s at line %d} de {Syntaxfehler in %.100s in Zeile %d} fr {%.100s : Erreur de syntaxe la ligne %d} sr {Sintaksna greka u %.100s na liniji %d} pl {Bd skadni w %.100s w linii %d} pt {Erro de sintaxe em %.100s na linha %d} label too_long_citation sv {Fr lng citering} en {Too long citation} de {Zitat zu lang} fr {Citation trop longue} sr {Predugaak citat} pl {Zbyt dugi cytat} pt {Citao demasiado longa} label illegal_regexp sv {Ogiltigt reguljruttryck i citeringsuttrycket: %.800s} en {Illegal regexp in citation expression: %.800s} de {Nicht erlaubter regulrer Ausdruck in Zitat: %.800s} fr {Expression rgulire de citation illgale : %.800s} sr {Nelegalan regularni izraz za citiranje: %.800s} pl {Niepoprawne wyraenie w cytacie: %.800s} pt {Expresso regular (regexp) invlida: %.800s} label reply_regexp sv Re:-uttryck en {Re: regexp} de {Re: regulrer Ausdruck} fr {Expression rgulire Re:} sr {'Re:' regularni izraz} pl {'Re:' regularne wyraenie} pt {Expresso regular 'Re:'} label re_regexp_error sv {Igiltigt Re:-uttryck} en {Illegal Re: regexp} de {Ungltiger regulrer Ausdruck in Re:} fr {Expression rgulire Re: illgale} sr {Nelegalni 'Re:' izraz} pl {Niepoprane 'Re:' regularne wyraenie} pt {Expresso regular 'Re:' invlida} label do_wrap_cited sv {Radbryt citerat brev} en {Wrap cited message} de {Zitat umbrechen} fr {Remettre en forme les messages cits} sr {Prelomi citirano pismo} pl {Zawijaj cytowan wiadomo} pt {Dobrar mensagem citada} label print_setup sv Utskriftsinstllning en {Print setup} de Druckereinstellungen fr {Paramtrage de l'impression} sr {Podeavanje tampe} pl {Ustawienia druku} pt {Configurao da impresso} label printer sv Skrivare en Printer de Drucker fr Imprimante sr tampa pl Drukarka pt Impressora label what_to_print sv {Att skriva ut} en {What to print} de {Was soll gedruckt werden} fr Imprimer sr {ta se tampa} pl {Co drukowa} pt {A imprimir} label print_attachments sv {Skriv ut bilagor} en {Print attachments} de {Anhnge drucken} fr {Imprimer les pices jointes} sr {tampaj dodatke} pl {Drukuj zaczniki} pt {Imprimir anexos} label attachment sv Bilaga en Attachment de Anhang fr {Pice jointe} sr Dodaci pl Zaczniki pt Anexo label body sv Brdtext en Body de Text fr Corps sr Sadraj pl Tre pt Corpo label destination sv Destination en Destination de Empfnger fr Destination sr Odredite pl Cel pt Destino label paper_size sv pappersstorlek en {Paper size} de Papiergre fr {Format de papier} sr {Veliina papira} pl {Rozmiar papieru} pt {Formato da pgina} label orientation sv Orientering en Orientation de Orientierung fr Orientation sr Orijentacija pl Orientacja pt Orientao label portrait sv Stende en Portrait de Hoch fr Portrait sr Portret pl Portret pt Retrato label landscape sv Liggande en Landscape de Quer fr Paysage sr Pejsa pl Pejza pt Paisagem label points sv Punkter en Points de Punkt fr Points sr pt pl pt pt pt label pic_res sv Bildupplsning en {Picture resolution} de Bildauflsung fr {Rsolution d'image} sr {Rezolucija slike} pl {Rozdzielczo obrazka} pt {Resoluo de imagem} label dpi sv DPI en DPI de DPI fr PPP sr DPI pl DPI pt dpi label d sv U en D de V fr D sr O pl W pt D label h sv L en H de H fr S sr Z pl E pt S label unprintable sv {Hr r en del med typen %s som inte kan skrivas ut} en {Here is a bodypart of type %s which can not be printed} de {Hier ist ein Teil vom Typ %s, welcher nicht gedruckt werden kann} fr {Ceci est est une partie de type %s qui ne peut tre imprime} sr {Ovaj deo pisma je tipa %s koji ne moe da se odtampa} pl {Tu jest cz treci typu %s ktra nie moe by wydrukowana} pt {Parte tipo %s, que no pode ser imprimida} label page sv Sida en Page de Seite fr Page sr Strana pl Strona pt Pgina label monitored sv vervakad en Monitored de berwacht fr Surveille sr {Pod nadzorom} pl Monitorowany pt Vigiada label monitor_mbox sv {vervaka mapp} en {Monitor folder} de {Ordner berwachen} fr {Surveiller la bote lettre} sr {Nadziri skup} pl {Folder monitorowany} pt {Vigiar pasta} label watched sv Vaktad en Watched de Wchter fr Observe sr Posmatrano pl Obserwowany pt Observada label watch_mbox sv {ppna vktaren vid nya brev} en {Pop up Watcher on new messages} de {Wchter bei neuen Nachrichten ffnen} fr {le veilleur surgit l'arrive de nouveaux messages} sr {Nova pisma pokreu nadzorni prozor} pl {Otwrz obserwatora w przypadku nowej wiadomoci} pt {Assinalar a chegada de novas mensagens} label ridiculously_long sv {Hittade en ljligt lng adress} en {Encountered ridiculously long address} de {Zu lange E-Mail-Adresse festgestellt} fr {Adresse ridiculement longue rencontre} sr {Naiao sam na besmisleno dugaku adresu} pl {Natrafiono na baardzo dugi adres} pt {Encontrado endereo incrivelmente longo} label recall sv terstll en Recall de Wiederherstellen fr Rappeler sr Opozovi pl Oddzwo pt Chamar label previous sv Fregende en Previous de Vorherige fr Prcdent sr Prethodni pl Poprzedni pt Anterior label plain_text sv {Ren text} en {Plain text} de {Einfacher Text} fr {Texte seul} sr {Obian tekst} pl {Czysty tekst} pt {Texto plano} label pretty_ps sv {Avancerad PostScript} en {Pretty PostScript} de {PostScript} fr {Mise en page Postscript} sr {Lepi PostScript} pl PostScript pt {Formatado em PostScript} label underline_nonwrap sv {Stryk under icke radbrytbar} en {Underline non-wrappable} de {Nicht Umbrechbares unterstreichen} fr {Souligner mots inscables} sr {Podvuci neprelomivo} pl {Podkrel nie-zawijalne} pt {Sublinhar palavras no dobrveis} label no_pp_in_print_command sv {Inget %p i utskriftskommandot. Detta betyder att skrivarvalet inte kommer att fungera.} en {No %p in print command. This means that the printer selection will not work.} de {Kein %p im Druckbefehl. Das bedeutet, dass die Druckerauswahl nicht funktioniert.} fr {La commande d'impression ne contient pas de %p. Il est impossible de choisir une imprimante.} sr {Nema %p u naredbi za tampanje. Ovo znai da izbor tampaa nee raditi!} pl {Brak %p w poleceniu druku. To znaczy, e nie zostanie wybrana drukarka} pt ZORRO label default_bcc sv {Frvald blind-kopia} en {Default Bcc} de {Standard Blindkopie} fr {copie cache par dfaut} sr {Podrazumevana bcc} pl {Domylne bcc} pt {Cpia cega (Bcc) por defeito} label alias_may_only_contain_chars sv {Aliasnamn fr endast innehlla bokstver och siffror} en {Aliasnames may only contain characters and digits} de {Aliasnamen drfen nur Zeichen und Ziffern enthalten} fr {Les noms d'alias ne peuvent contenir que des lettres et des chiffres} sr {Ime aliasa sme da sadri samo slova i cifre!} pl {Nazwy aliasw mog zawiera tylko litery i cyfry} pt {O alias pode ter apenas caracteres e digitos} label reply_bottom sv {Skriv svar under den citerade texten} en {Write reply below the cited text} de {Die Antwort unterhalb des zitierten Textes beginnen} label no_wrap sv {Ingen radbrytning} en {No line wrapping} de {Kein Zeilenumbruch} fr {Pas de csure} sr {Nema preloma redova} pl {Bez zawijania} pt {No dobrar linhas} label wrap_char sv {Radbryt vid tecken} en {Wrap at character} de Zeichenumbruch fr {Csure sur tout caractre} sr {Prelom na znaku} pl {Zawijaj dowolnie} pt {Dobrar linha no carcter} label wrap_word sv {Radbryt vid mellanrum} en {Wrap at word boundary} de Wortumbruch fr {Csure aux limites de mots} sr {Prelom iza rei} pl {Zawijaj midzy wyrazami} pt {Dobrar linha no limite da palavra} label appearance sv Utseende en Appearance de Erscheinungsbild fr Apparence sr Izgled pl Wygld pt Aspecto label misc sv Diverse en Miscellaneous de Diverse fr Divers sr Razno pl Rne pt Diversos label html sv HTML en HTML de HTML fr HTML sr HTML pt HTML label watcher_font sv Vktartypsnitt en {Watcher font} de {Schrift fr den Wchter} fr {Police du veilleur} sr {Font za nadzorni prozor} pl {Czcionka obserwatora} pt {Fonte do Observador} label bold sv Fetstil en Bold de Fett fr Gras sr Podebljano pl Pogrubienie pt Carregado label italic sv Lutande en Italic de Schrg fr Italique sr Kurziv pl Kursywa pt Itlico label underline sv Understruken en Underline de Unterstrichen fr Souligner sr Podvueno pl Podkrelenie pt Sublinhado label overstrike sv verstruken en Overstrike de berstrichen fr Barre sr Precrtano pl Przekrelenie pt Riscado label edit_font sv {ndra typsnitt} en {Edit font} de {Schrift ndern} fr {Choisir police} sr {Podesi font} pl {Edytuj font} pt {Editar fonte} label use_one_method sv {Anvnd en metod fr att ange typsnitt} en {Use one method to specify font} de {Whlen eine der folgenden Mglichkeiten, um die Schrift festzulegen:} fr {Utilisez l'une des mthode pour choisir une police} sr {Koristi jedan metod za oznaavanje fonta} pl {Uyj jednego sposobu by okreli czcionk} pt {Usar um dos mtodos para seleccionar uma fonte} label family sv Familj en Family de Familie fr Famille sr Porodica pl Rodzina pt Famlia label font sv Typsnitt en Font de Schrift it Carattere fr Police sr Font pl Czcionka pt Fonte label font_size sv Teckenstorlek en {Font size} de Schriftgre fr Taille sr {Veliina fonta} pl Rozmiar pt Tamanho label debug_cclient sv {Avlusa frbindelser} en {Debug connections} de {Debugge Verbindungen} fr {Dverminer les connexions} sr {Debaguj veze} pl {Debug pocze} pt {Debug ligaes} label name_occupied sv {Det namnet r redan taget} en {That name is already occupied} de {Der Name ist schon vergeben} fr {Ce nom est dj utilis.} sr {To ime je ve? upotrebljeno} pl {Ta nazwa jest ju zajta} pt {Nome j utilizado} label useinputmethods sv {Anvnd 'input methods'} en {Use Input methods} de {Benutze Eingabemethoden} fr {Utiliser input methods} sr {Koristi "input methods"} pl {Uywaj 'Input methods'} pt {Utilizar entrada caracteres especiais} label check_spelling sv {Kontrollera stavning} en {Check spelling} de {Rechtschreibung berprfen} fr {Vrifier l'orthographe} sr {Proveri spelovanje} pl {Sprawd pisowni} pt {Verificar ortografia} label unknown_word sv {Oknt ord} en {Unknown word} de {Unbekanntes Wort} fr {Mot inconnu} sr {Nepoznata re} pl {Nieznane sowo} pt {Palavra desconhecida} label replace_with sv {Byt ut mot} en {Replace with} de {Ersetzen durch} fr {remplacer par} sr {Zameni sa} pl {Zastp przez} pt {Substituir por} label replace sv {Byt ut} en Replace de Ersetzen fr Remplacer sr Zameni pl Zastp pt Substituir label replace_all sv {Byt ut alla} en {Replace all} de {Alles ersetzen} fr {Tout remplacer} sr {Zameni sve} pl {Zastp wszystkie} pt {Substituir tudo} label ignore sv Ignorera en Ignore de Ignorieren fr Ignorer sr Ignorii pl Ignoruj pt Ignorar label ignore_all sv {Ignorera alla} en {Ignore all} de {Alles Ignorieren} fr {Tout ignorer} sr {Ignorii sve} pl {Ignoruj wszytkie} pt {Ignorar tudo} label learn sv Lr en Learn de Lernen fr Apprendre sr Naui pl Naucz pt Aprender label spell_complete sv {Stavningskontroll klar} en {Spell-check complete} de {Rechtschreibprfung beendet} fr {Vrification orthographique termine} sr {Provera spelovanja zavrena} pl {Sprawdzanie pisowni zakoczone} pt {Verificao da ortografia concluda} label spell_cmd sv {Stavningskontroll} en {Spell command} de {Rechtschreib-Programm} label unsupported_folder_file sv {Oknd version p vfolderdef-filen. Versionen som lstes in r fr ny fr denna versionen av TkRat.} en {Unsupported format of vfolderdef file. The version encountered is too new to be parsed by this version of TkRat.} de {Das Format der Datei vfolderdef wird noch nicht untersttzt. Die vorliegende Version ist zu neu und kann von dieser TkRat-Version nicht verarbeitet werden.} fr {Format du fichier vfolderdef non reconnu. Sa version est trop rcente pour que TkRat puisse l'analyser.} pl {Nie obsugiwany format pliku vfolderdef. Obsuga tego foldera nie zostaa jeszcze zaimplementowana w TkRat.} pt {Formato do ficheiro vfolderdef no suportado.} label ssh_path sv SSH-kommando en {SSH command} de {SSH Befehlszeile} fr {commande SSH} pl {Polecenie SSH} pt {Caminho ssh} label ssh_command sv {SSH kommando} en {SSH command} de {SSH Befehl} fr {commande SSH} pl {Polecenie SSH} pt {Comando ssh} label ssh sv SSH en SSH de SSH fr SSH pl SSH pt SSH label go_online sv {Byt till uppkopplad-mod} en {Go online} de {Online Modus} fr {Mode En ligne } pl {Tryb Online} pt {Modo 'online'} label go_offline sv {Byt till nedkopplad-mod} en {Go offline} de {Offline Modus} fr {Mode Hors ligne } pl {Tryb Offline} pt {Modo 'offline'} label details sv Detaljer en Details de Einzelheiten fr Dtails pl Szczegy pt Detalhes label imap sv IMAP en IMAP de IMAP fr IMAP pl IMAP pt IMAP label pop3 sv POP3 en POP3 de POP3 fr POP3 pl POP3 pt POP3 label mh sv MH en MH de MH fr MH pl MH pt MH label dynamic sv Dynamisk en Dynamic de Dynamisch fr Dynamique pl Dynamiczny pt Dinmico label dis sv Offline en Offilne de Offline fr Dconnecte pl Rozczony pt Desligado label struct sv Meny en Menu de Men fr Menu pl Menu pt Menu label apply_changes sv {Utfr ndringar} en {Apply changes} de {nderungen anwenden} fr {Appliquer les changements} pl {Sprawd certyfikat serwera czc si przez SSL. Wymaga } pt {Aplicar mudanas} label restore_values sv {terstll vrden} en {Restore values} de {Werte wiederherstellen} fr {Valeurs d'origine} pl {Przywr wartoci} pt {Restaurar valores} label mail_server sv Epostserver en {Mail server} de {E-mail Server} fr {Serveur de courrier} pl {Serwer poczty} pt Servidor label pop_servers sv POP3-servrar en {POP3 servers} de {POP3 Server} fr {Serveurs POP3} pl {Serwery POP3} pt {Servidores POP3} label imap_servers sv IMAP-servrar en {IMAP servers} de {IMAP Server} fr {Serveurs IMAP} pl {Serwery IMAP} pt {Servidores IMAP} label tcp_default sv {TCP-frbindelse till standardporten} en {TCP connection to the default port} de {TCP-Verbindung zum Standardport} fr {Connexion au port TCP par dfaut} pl {Poczenie TCP z domylnym portem} pt {Ligao TCP ao porto padro} label tcp_custom sv {TCP-frbindelse till en egen port} en {TCP connection to a custom port} de {TCP-Verbindung zu einem anderen Port} fr {Connexion un autre port TCP} pl {Poczenie TCP z wybranym portem} pt {Ligao TCP a um porto pr-definido} label rsh_ssh sv {RSH eller SSH} en {RSH or SSH} de {RSH oder SSH} fr {RSH ou SSH} pl {RSH lub SSH} pt {RSH ou SSH} label connect sv {Koppla upp} en Connect de Verbinden fr Connexion pl Pocz pt Ligar label privacy sv Integritet en Privacy de Privatsphre fr Confidentialit pl Prywatno pt Privacidade label use_ssl sv {Anvnd SSL vi uppkopplingen} en {Use SSL when connecting} de {Verwende SSL beim Verbinden} fr {Utiliser SSL lors des connexions} pl {Przy czeniu uywaj SSL} pt {Usar SSL na ligao} label try_tls sv {Frsk sl p SSL efter uppkopplingen} en {Try to enable SSL when connected} de {Versuche, SSL zu aktivieren, wenn verbunden} fr {Essayer d'utiliser SSL lors des connexion} pl {Prbuj wczy SSL w czasie poczenia} pt {Tentar usar SSL enquanto ligado} label no_encryption sv {Frsk inte anvnda kryptering} en {Do not try to use encryption at all} de {Verzichte auf Verschlsselung} fr {Ne pas utiliser de chiffrement} pl {Nie uywaj szyfrowania} pt {No usar cifragem} label ssl_check_cert sv {Verifiera servercertifikat (on SSL)} en {Verify server certificate (if using SSL)} de {Prfe das Zertifikat des Servers (bei Verwendung von SSL)} fr {Vrifier le certificat du serveur (SSL seulement)} pl {Sprawd certyfikat serwera (dot. SSL)} pt {Verificar certificado do servidor (para ligaes SSL)} label flags sv Flaggor en Flags de Schalter fr Drapeaux pl Flagi pt Marcaes label a_mailserver_named sv {En epostserver med namet} en {A mailserver named} de {Ein e-mail Server namens} fr {Un serveur de courrier nomm} pl {Serwer poczty o nazwie} pt {Um servidor chamado} label new_imap_server sv {Ny IMAP-server} en {New IMAP server} de {Neuer IMAP Server} fr {Nouveau serveur IMAP} pl {Nowy serwer IMAP} pt {Novo servidor IMAP} label new_mailbox sv {Ny brevlda} en {New mailbox} de {Neuer Ordner} fr {Nouvelle bote lettres} pl {Nowa skrzynka} pt {Nova pasta} label need_mail_server sv {Du mste ange vilken epostserver som skall anvndas} en {You must specify which mail server to use} de {Sie mssen einen e-mail Server angeben} fr {Vous devez indiquer quel serveur de courrier utiliser} pl {Musisz wybra serwer poczty} pt {Deve especificar qual o servidor a utilizar} label action_on_create sv {Att utfra nr servern har skapats} en {Action to take when server is created} de {Aktion nach dem Anlegen des Servers} fr {Action la cration du serveur} pl {Akcja po utworzeniu serwera} pt {Aco aps a criao do servidor} label action_imap sv {Importera brevldor frn denna server} en {Import mailboxes from this server} de {Ordner von diesem Server einlesen} fr {Importer les botes lettres du serveur} pl {Importuj skrzynki z serwera} pt {Importar pastas deste servidor} label reimport_when sv {Importera igen} en {Reimport when} de {Erneut einlesen falls} fr {quand rimporter} pl {Reimportuj gdy} pt {Reimportar quando} label reimport_manually sv Manuellt en Manually de manuell fr Manuellement pl Rcznie pt Manualmente label reimport_session sv {En gng fr varje session} en {Once every session} de {Einmal pro Sitzung} fr {Une fois par session} pl {Podczas kadej sesji} pt {Uma vez por sesso} label reimport_now sv {Importera nu} en {Reimport now} de {Jetzt neu einlesen} fr {Rimporter maintenant} pl {Reimportuj teraz} pt {Reimportar imediatamente} label import_on_create sv {Importera automatisk vid skapande} en {Automatically import on create} de {Automatisch beim Erstellen einlesen} fr {Automatiquement rimporter la cration} pl {Automatycznie importuj po utworzeniu} pt {Importar automaticamente ao criar} label mailserver_used sv {Denna epost-server anvnds av fljande brevldor och kan drfr inte raderas} en {This mail server is used by the following folders and can therefore not be deleted} de {Dieser Server kann nicht gelscht werden, weil er von den folgenden Ordner verwendet wird.} fr {Vous ne pouvez pas dtruire ce serveur de courrier car il sert les botes lettres suivantes} pl {Ten serwer poczty jest uywany przez nastpujce foldery i nie moe by usunity} pt {Este servidor usado pelas pastas seguintes e por isso no pode ser removido} label item_not_empty sv {Detta objekt innehller andra brevldor, r du sker att du vill radera dem ocks?} en {This item contains other mailboxes, are you sure you want to delete them, too?} de {Dieses Element enthlt weitere Ordner. Sind Sie sicher, dass Sie diese auch lschen mchten?} fr {Cet article contient d'autres botes lettres. Voulez vous les dtruire galement ?} pl {Ten element zawiera inne skrzynki, czy je te chcesz usun} pt {Este item contm outras pastas; quer eliminar tudo?} label delete_what_folder sv {Vill du radera sjlva brevldorna p disk eller servern ocks, eller vill du bara radera definitionen i TkRat?} en {Do you want to delete the actual mailboxes on disk or on the remote server as well, or just the mailbox definitions in TkRat?} de {Mchten Sie den Ordner auf der Festplatte bzw. auf dem Server auch lschen, oder mchten Sie nur den Ordner in TkRat lschen?} fr {Voulez vous dtruire la bote lettres physique, sur le disque ou le serveur, ou seulement sa dfinition dans TkRat ?} pl {Czy chcesz usun skrzynki z dysku czy rwnie z serwera, czy tylko definicje skrzynek z TkRat?} pt {Deseja eliminar as pastas em disco ou no servidor, ou apenas as definies no TkRat?} label delete_both sv {Radera bda} en {Delete both} de {Beide lschen} fr {Dtruire les deux} pl {Usu wszystkie} pt {Eliminar ambos} label only_in_tkrat sv {Bara i TkRat} en {Only in TkRat} de {Nur in TkRat} fr {La dfinition seulement} pl {Tylko w TkRat} pt {Apenas no TkRat} label reimport_all sv {Importera alla brevldor} en {Reimport all folders} de {Alle Ordner neu einlesen} fr {Rimporter toutes les botes lettres} pl {Reimportuj wszystkie foldery} pt {Reimportar todas as pastas} label som_last sv {Senaste mod} en {Last mode} de {Zuletzt verwendeter Modus} fr {Dernier mode utilis} pl {Ostatni tryb} pt {ltimo modo} label som_online sv Uppkopplad en Online de Online fr {En ligne} pl Online pt 'Online' label som_offline sv Nedkopplad en Offline de Offline fr {Hors ligne} pl Offline pt 'Offline' label network sv Ntverk en Network de Netzwerk fr Rseau pl Sie pt Rede label imap_secure sv {SKicka inte okrypterat lsenord} en {Do not send password unencrypted} de {Sende das Passwort nicht unverschlsselt} fr {Ne pas envoyer de mot de passe non chiffr} pl {Nie wysyaj nie szyfrowanego hasa} pt {No enviar a palavra passe no cifrada} label create_mailbox_on_server sv {Skapa brevlda p servern} en {Create mailbox on server} de {Erzeuge Ordner auf dem Server} fr {Crer la bote lettres sur le serveur} pl {Utwrz skrzynk na serwerze} pt {Criar pasta no servidor} label mailbox_create_failed sv {Misslyckades att skapa brevldan. Vill du fortstta och skapa brevldan i TkRat nd? Felmeddelandet var: } en {Failed to create the folder. Do you want to continue and create the folder in TkRat anyway? The error message was: } de {Das Erstellen des Ordners ist fehlgeschlagen. Mchten Sie fortfahren und den Ordner trotzdem in TkRat erzeugen? Die Fehlermeldung war: } fr {chec de la cration de la bote lettres. Voulez vous tout de mme crer sa dfinition dans TkRat ? Le message d'erreur tait : } pl {Bd podczas tworzenia folderu. Czy chcesz kontynuowa i mimo to utworzy folder w TkRacie? Nastpi bd: } pt {No foi possvel criar a pasta. Deseja continuar e criar a pasta definida no TkRat? A mensagem de erro foi: } label system_default_charset sv Systemstandard en {System default} de {Voreinstellung des Systems} fr {Rglages du systme} pl Domylny pt {Padro de sistema} label roles sv Roller en Roles de Rollen fr Rles pt Identidades label graphics sv Grafik en Graphics de Darstellung fr Graphiques pl Grafika pt Grficos label www sv WWW en WWW de WWW fr WWW pl WWW pt WWW label advanced sv Avancerade en Advanced de Erweitert fr Avanc pl Zaawansowany pt Avanado label behaviour sv Betende en Behavior de Verhalten fr Comportement pl Zachowanie pt Comportamento label caching sv Caching en Caching de Cache fr Caches pl Cacheowanie pt Cache label fonts sv Typsnitt en Fonts de Schriften fr Polices pl Czcionki pt Fontes label hide sv Gm en Hide de Ausblenden fr Masquer pl Ukryj pt Ocultar label default sv Normal en Default de Voreinstellung fr {Par dfaut} pl Domylny pt {Por defeito} label role_name sv Rollnamn en {Role name} de Rollenname fr {Nom du rle} pt {Nome da identidade} label roles_expl sv {Roller r ett stt att ha olika 'personligheter'. Man kan presentera olika adresser fr de olika rollerna och dessutom anvnda olika metoder att skicka breven} en {The Roles feature is a way to have different 'personalities.' You can present a different address for each role and also use different methods for sending email.} de {Rollen bieten die Mglichkeit, mit verschiedenen Benutzerprofilen zu arbeiten. Jeder Rolle kann eine andere Adresse zugeordnet werden. Ebenso kann die Methode zum Versenden von Nachrichten fr jede Rolle gesondert festgelegt werden.} fr {Les rles sont un moyens d'avoir diffrentes personnalits. Vous pouvez prsenter diffrentes adresses pour chacun des rles, et aussi utiliser diffrentes mthodes d'expdition.} pt {'Identidades' uma maneira de assumir diferentes personalidades. Pode apresentar um endereo diferente para cada identidade e/ou usar diferentes mtodos de envio} label create_new_role sv {Skapa ny roll} en {Create new role} de {Neue Rolle anlegen} fr {Crer un nouveau rle} pt {Criar nova identidade} label new_role sv {Ny roll} en {New role} de {Neue Rolle} fr {Nouveau rle} pt {Nova identidade} label set_as_default sv {Stt som standard} en {Set as default} de Voreinstellen fr {Utiliser par dfaut} pt {Usar por defeito} label role sv Roll en Role de Rolle fr Rle pt Identidade label cant_delete_role_used sv {Du kan inte radera denna rollen eftersom den refereras av fljande brevldor} en {You can not delete this role since it is referenced from the following folders} de {Sie knnen diese Rolle nicht lschen, weil diese von den folgenden Ordnern verwendet wird} fr {Vous ne pouvez pas dtruire ce rle ; les botes lettres suivantes y font rfrence} pt {No possvel destruir esta identidade porque referenciada nas pastas seguintes} label need_host_and_user sv {Du mste ange bde ett maskinnam och ett anvndarnamn} en {You must specify both a hostname and an username} de {Sie mssen sowohl einen Host als auch einen Benutzernamen angeben} fr {Vous devez entrer un nom d'hte et un nom d'utilisateur} pl {Musisz poda nazw hosta i uytkownika} pt {Deve especificar um nome de servidor e um nome de utilizador} label html_proxy_host en {Proxy server} de {Proxy Server} fr {Serveur mandataire} sv {Proxy server} label html_proxy_port en {Proxy port} de {Proxy Port} fr {Port mandataire} sv {Proxy port} label html_timeout en {HTTP Timeout} de {HTTP Wartezeit} fr {Dlai d'attente HTTP} sv {HTTP Timeout} label characters sv Tecken en Characters de Zeichen fr Caractres pt Caracteres label invalid_font sv {Ogiltigt typsnitt angivet} en {Invalid font name specified} de {Ungltiger Schriftname angegeben} fr {Nom de police incorrect} pt {Fonte invlida} label pgp_keyid sv PGP-nyckelid en {PGP keyid} de {PGP Schlssel ID} fr {Identifiant de clef PGP} pt {Identificador de chave PGP} label are_you_sure sv {r du sker p att du vill avsluta?} en {Are you sure you want to quit?} de {Sind Sie sicher, dass Sie TkRat beenden wollen?} fr {Voulez vous vraiment quitter ?} pt {Deseja de facto sair?} label folder_key_online sv {Toggla online} en {Toggle online} de {On-/Offline Modus wechseln} fr {Bascule En/Hors ligne} pt {Permutar Modo On-/Offline} label no_send_bad_host sv {TkRat knner inte till det kompletta namnet som din post skall skickas frn. Detta namnet kan anges p tv stllen i instllningsfnstret. Om du anger en domn i din "Frn"-adress s anvnds den. Annars anvnds vrdet "Domn" i Optioner->Avancerat->Ntverk. Om den r blank s anvnds maskinens hostnamn. Du kommer inte att kunna skicka ngra brev innan du tgrdar detta (s lnge du inte anvnder tvingafunktionen).} en {TkRat does not know the fully qualified name your mail should be sent from! This name can be provided in two places in the Preferences window. If you give a fully qualified "From" address in your role definition, then that will be used. Otherwise, it will use the value of the "Domain" option in Options->Advanced->Network, and if that one is blank, TkRat will use the current hostname. You will not be able to send any messages until you fix this (unless you enable the override).} de {TkRat kennt nicht den vollstndig qualifizierten Hostnamen fr Ihre Absenderangabe. Sie haben zwei Mglichkeiten, diesen Namen im Einstellungen- Dialog festzulegen: Falls Sie eine vollstndig qualifizierte "Von-Adresse" in den Einstellungen einer Rolle angeben, so wird diese verwendet. Andernfalls wird der Wert der Option "Domne" unter Optionen->Erweitert->Netzwerk verwendet. Ist letztere Einstellung leer, versucht TkRat den Namen Ihres Rechners zu ermitteln. Um Nachrichten versenden zu knnen, mssen Sie eine der genannten Einstellungen korrigieren.} fr {TkRat ignore le nom complet qu'il doit utiliser comme adresse d'expditeur ! Vous pouvez indiquer ce nom deux endroits dans la fentre prfrence. Si, dans la dfinition du rle, le champ De: contient un nom complet, TkRat l'utilisera. Sinon, la valeur sera celle de Domaine dans l'onglet Options/Avanc/Rseau. Si cette dernire valeur est vide, TkRat utilisera le nom de domaine de votre ordinateur. Vous ne pourrez pas expdier de courrier tant que ce problme 'est pas rgl.} pt {O TkRat no conhece o seu endereo completo! Este pode ser inserido em dois lugares nas janela de preferncias. Se fornecer um endereo completo De: na definio da sua 'identidade', ento ser esse o endereo usado. Caso contrrio, o valor da opo Domnio em Opes->Avanado->Rede ser usado. Se este espao for deixado em branco, ento o TkRat usar o nome do servidor. No ser possvel enviar mensagens enquanto no preencher estas opes.} label masq_from_conflict sv {I rollen "%s": Vrdet av optionen masquerade_as "%s" r inkompatibelt med domnen som anges i From: "%s". Masquerade_as anvnds inte lngre s From:-vrdet anvnds.} en {In role "%s": The value of option masquerade_as "%s" was incompatible with the domain specified in From: "%s". Masquerade_as is now obsolete so the latter will be used.} de {Rolle "%s": Der Wert der Option 'System ausgeben als' "%s" passt nicht zur Domne der Absenderadresse "%s". Die Option 'System ausgeben als' entfllt, es wird die Domne der Absenderadresse verwendet.} fr {La valeur de l'hte apparat comme dans le rle %s tait incompatible avec le domaine prcis dans le champ De: %s . l'hte apparat comme est maintenant obsolte, la valeur de De: sera utilise.} pt {Na identidade "%s": o valor da opo masquerade_as "%s" incompatvel com o domnio especificado em De: Masquerade_as tornou-se obsoleto e no vai ser utilizado.} label are_you_sure_delete_role sv {r du sker p att du vill radera denna rollen?} en {Are you sure you want to delete this role?} de {Sind Sie sicher, dass Sie diese Rolle lschen mchten?} fr {Voulez vous vraiment dtruire ce rle ?} pt {Deseja de facto destruir esta identidade?} label adr_syntax_error sv {Du kan inte skicka brevet s lnge som du har syntaxfel i fljande flt:} en {You cannot send while you have syntax errors in the following fields} de {Sie knnen diese Nachricht nicht versenden, solange die folgenden Felder Fehler enthalten:} fr {Vous ne pouvez pas expdier ce message tant qu'il y a des erreurs de syntaxe dans les champs suivants :} pt {No possvel enviar a mensagem enquanto no forem corrigidos os erros seguintes} label normally_ok sv {Skriv inget hr om du r osker} en {Do not enter anything here if you are unsure} de {Lassen Sie diese Felder frei, falls Sie sich unsicher sind.} fr {Ne remplissez ces champs que si vous tes sr de vous} pt {Em caso de dvida no altere a configurao} label unqual_adr_domain sv {Domn som lggs till korta adresser} en {Domain to add to unqualified addresses} de {Domne fr Adressen ohne Domnenangabe} fr {Domaine pour les adresses non qualifies} pt {Domnio a usar em endereos no qualificados} label smtp_from_long sv {Maskin som vi presenterar oss som i SMTP} en {Host to present us as in SMTP} de {Bei SMTP ausgeben als Host} fr {Nom d'hte pour les dialogues SMTP} pt {Servidor a apresentar no dilogo SMTP} label changes_title sv {ndringar sedan du sist anvnde TkRat} en {Changes since you last used TkRat} de {nderungen seit dem letztem Aufruf von TkRat} it {Cambiamenti dall'ultima volta che hai usato TkRat} fr {Changements depuis votre dernire utilisation de TkRat.} sr {Izmene otkada ste poslednji put koristili TkRat} pl {Zmiany od ostatniego uruchomienia TkRat} pt {Mudanas desde a sua ltima utilizao do TkRat.} label outgoing sv Utgende en Outgoing de Abgehende fr Envoys label specials sv Special en Specials de {Besondere Ordner} fr spcial label is_hold_folder en {This is a special folder used to keep messages which are held during composing} de {In diesem besonderen Ordner finden Sie die Nachrichten, welche fr eine sptere Weiterbearbeitung gehalten werden.} sv {Detta r en specialmapp som anvnds fr att lagra brev som skjuts upp medan de skrivs} fr {Ceci est une bote lettres spciales, utilise pour conserver les messages en suspens.} label is_outgoing_folder en {This is a special folder used to keep messages which are meant to be sent when possible} de {In diesem besonderen Ordner finden Sie die Nachrichten, welche verzgert, zu einen spteren Zeitpunkt verschickt werden.} sv {Detta r en specialmapp som anvnds fr att lagra brev som skall skickas vid tillflle} fr {Ceci est une bote lettres spciales, utilise pour conserver les messages en attente d'expdition.} label launching_send_cmd en "Launching send command..." de {Starte das Kommando fr den Versand...} sv "Startar sndkommandot..." fr {Dmarre la commande d'expdition} label prog_send_failed en "Send command failed" de {Der Versand ist fehlgeschlagen.} sv "Sndkommandot misslyckades" fr {chec de la commande d'expdition} label writing_message en "Writing message..." de {bertrage Nachricht...} sv "Skriver brev..." fr {criture du message...} label waiting_on_send_cmd en "Waiting on send command..." de {Warte auf den Abschluss des Versandes...} sv "Vntar p det sndkommandot..." fr {En attente de la fin de la commande...} label sent_ok en "Message send succeeded" de {Nachricht erfolgreich bertragen.} sv "Meddelandet skickat" fr {Message expdi} label opening_smtp_conn en "Connecting to SMTP server" de {Verbinde mit dem SMTP server...} sv "Kopplar upp till SMTP-servern" fr {Connexion au serveur SMTP} label send_failed en {Failed to send message. Reason:} de {Die Nachricht konnte nicht verschickt werden:} sv {Misslyckades att skicka brevet. Orsak:} fr {chec de l'expdition. Raison :} label increase_font_size en {Increase font size} de {Schrift vergrern} sv {ka textstorlek} fr {Augmenter la taille de la police} label decrease_font_size en {Decrease font size} de {Schrift verkleinern} sv {Minska textstorlek} fr {Diminuer la taille de la police} label show_url_in en {Show URL in} de {Zeige URL in} sv {Visa URL i} fr {Montrer l'URL dans} label reuse_old_window en {Existing window} de {bestehenden Browserfensters} sv {Existerande fnster} fr {Une fentre existante} label new_window en {New window} de {neuen Browserfensters} sv {Nytt fnster} fr {Une nouvelle fentre} label new_tab en {New tab} de {neuen Registers im Browser} sv {Ny tab} fr {Un nouvel onglet} label do_not_show_window_in_future en {Do not show this window in the future} de {Dieses Fenster in Zukunft nicht mehr anzeigen} sv {Visa inte detta fnster i framtiden} fr {Ne lus monter cette fentre l'avenir} label same_sending_prefs en {Use same send settings for all roles} de {Dieselben Einstellungen fr alle Rollen verwenden} sv {Anvnd samma skickinstllningar fr alla roller} fr {Utiliser les mme prfrences d'expdition pour tous les rles} label send_settings_differs en "Send settings differs" de {Die Einstellungen zum Versenden sind unterschiedlich} sv "Skickainstllningarna skiljer sig" fr {Les prfrences d'expditions diffrent} label roles_use_other_send_settings en "Some roles uses different send settings. They will be overwritten\ with the current settings if you continue." de {Bestimmte Rollen verwenden andere Einstellungen zum Versenden. Diese\ werden berschrieben, sollten Sie fortfahren.} sv "Vissa roller anvnder andra skickainstllningar. De kommer att\ skrivas ver om du fortstter." fr {Certain rles utilisent des prfrence d'expdition diffrentes.\ Elle seront remplaces par les prfrences actuelles si vous\ continuez.} label smtp_user en "SMTP user" de {SMTP Benutzer} sv "SMTP anvndare" fr {Utilisateur SMTP} label smtp_passwd sv Lsenord en Password de Passwort it Password fr {Mot de passe} sr ifra pl Haso pt {Palavra de entrada - password} label new_folder_wizard en "New Folder Wizard" de {Assistent fr neue Ordner} sv "'Ny mapp' hjlpreda" fr {Assistant de nouvelle bote lettre} label select_type_of_folder en "Select the type of folder to create" de {Whlen Sie, welchen Typ der neue Ordner haben soll:} sv "Vlj vilken typ av mapp du vill skapa" fr {Choisissez le type de bote crer} label next en Next de {Nchste} sv Nsta fr {Suivant} label fw_file en "File folder" de {Datei} sv "Filmapp" fr {Fichier} label fw_imap en IMAP de {IMAP} sv IMAP fr {IMAP} label fw_dbase en Database de {Datenbank} sv Databas fr {Base de donns} label fw_mh en MH de {MH} sv MH fr {MH} label fw_pop en "POP3 (limited support)" de {POP3 (eingeschrnkt untersttzt)} sv "POP3 (begrnsat std)" fr {POP3 (support limit)} label fw_dynamic en "A directory of file folders, one per sender" de {Ein Verzeichnis mit einem Datei-Ordner je Absender} sv "Ett bilbiotek med filmappar, en per avsndare" fr {Un rpertoire de bote lettres, un par expditeur} label define_file_folder en "Define file folder" de {Datei-Ordner anlegen} sv "Skapa filmapp" fr {Dfinir une bote lettre fichier} label why_filefolder en {Each file folder needs a name which is used in TkRat\ menus etc. You must also specify the file where the\ messages will be stored. If you specify a directory then TkRat\ will create folder entries for all files under that directory,\ it will also descend into subdirectories.} de {Jeder Datei-Ordner muss einen Namen haben, der in\ Mens und Auswahlen verwendet wird. Auerdem mssen Sie\ eine Datei angeben, in der die Nachrichten gespeichert werden.\ Wenn Sie statt einer Datei ein Verzeichnis angeben, wird TkRat\ fr jede Datei in diesem Verzeichnis einen Ordner anlegen. Dabei\ werden auch Unterverzeichnisse erfasst.} sv {Varje filmapp behver ett namn som anvnds i menyer etc.\ Man mste ocks ange filen dr breven sparas. Om du anger ett\ bibliotek s skapar TkRat mappar fr varje fil som finns i det, samt alla underbibliotek.} #fr {Chaque bote lettre fichier a besoin d'un nom, utilis\ #dans les menus de TkRat et ailleurs. Vous devez aussi indiquer\ #le nom du fichier qui contiendra les message.} label finish en Finish de {Fertigstellen} sv Klar fr {Terminer} label define_imap_folder en "Define IMAP folder(s)" de {IMAP-Ordner anlegen} sv "Definiera IMAP mapp(ar)" fr {Dfinir une/des bote(s) lettre IMAP} label imap_step1 en {Select if you want to reuse an already define IMAP server or\ if you want to define a new one. Reusing an old one will not affect\ folders already defined at it.} de {Whlen Sie hier aus, ob Sie einen bereits bekannten IMAP-Server\ aus der Liste verwenden wollen. Bereits angelegte Ordner werden dabei\ nicht verndert. Andernfalls knnen Sie auch einen\ weiteren Server registrieren. } sv {Vlj om du vill teranvnda en redan definierad IMAP-server eller\ om du vill definiera en ny. Att teranvnda en gammal kommer inte att\ pverka de mappar som redan r definierade p den.} fr {Choisissez si vos voulez utiliser un serveur IMAP dj dfini\ ou si vous voulez en dfinir un autre. Rutiliser un serveur dj\ dfini n'affectera aucune des botes lettre dj dfinies sur ce\ serveur.} label reuse_imap en {Reuse old IMAP server} de {Bekannten IMAP-Server verwenden} sv {teranvnd gammal IMAP-server} fr {Rutiliser un serveur IMAP} label define_new en {Define new} de {Einen neuen IMAP-Server anlegen} sv {Definiera ny} fr {En dfinir un nouveau} label imap_def en {Define how to reach the IMAP-server and which username to use.} de {Geben Sie hier an, wie sich TkRat mit dem IMAP-Server verbinden soll.} sv {Definiera hur man nr IMAP-servern och vilket anvndarnamn som\ skall anvndas.} fr {Dfinir comment contacter le serveur IMAP et quel nom d'utilisateur donner.} label why_imap_folder en {Each IMAP folder needs a name which is used in TkRat\ menus etc. You must also specify where on the IMAP-server\ the folder will exist, or where the import of folders will start.} de {Jeder Ordner muss einen Namen haben, der in\ Mens und Auswahlen verwendet wird. Auerdem mssen Sie\ angeben, unter welchem Pfad der Ordner auf dem IMAP-Server\ angelegt werden soll, bzw. wo der Import der Ordner beginnen soll.} sv {Varje IMAP-mapp behver ett namn som anvnds i menyer etc.\ Du mste ocks ange var p IMAP-servern som mappen finns, eller\ var importen skall starta.} #fr {Chaque bote lettre IMAP a besoin d'un nom, utilis\ #dans les menus de TkRat et ailleurs. Vous devez aussi indiquer\ # quel endroit crer la bote lettre sur le serveur.} label path en Path de {Pfad} sv Skvg fr {Chemin} label mailbox_does_not_exist en {The mailbox you specified does not exist.} de {Der angegebene Ordner existiert nicht.} sv "Mappen du specificerade finns inte." fr {La bote lettres que vous avez indique n'existe pas.} label maybe_create_mailbox en {The mailbox you specified does not exist. It may either just not\ be created yet or you have given the wrong path.} de {Der angegebene Ordner existiert nicht. Entweder wurde er noch\ nicht erzeugt, oder Sie haben eine falsche Pfadangabe gemacht.} sv {Mappen du specificerade finns inte. Antingen s har den helt\ enkelt inte skapats nnu eller s har du angett fel skvg.} fr {La bote lettres que vous avez indique n'existe pas. Soit elle\ n'existe pas encore, soit vous avez indiqu un mauvais chemin.} label go_back en "Go back" de {Zurck} sv "Tillbaka" fr Prcdent label imap_test_import en {If you specify a directory as 'Path' (or left it empty) then TkRat\ will recursively load all folders found under it. Sometimes it is\ tricky to figure out the correct value of 'Path'. The 'Test import'\ function lists what will be found on the top level when the import is\ done but does not descend into subdirectories.} sv {Om du angav ett bibliotek som 'Skvg' (eller lmnade det tomt) s\ kommer TkRat att rekursivt ladda alla mappar under det. Ibland r det\ svrt att lista ut vad man skall ange fr\ skvg. "Testimport"-funktionen listar vad som finns i bibliotek som\ angetts i skvg men undersker inte underbibliotek.} de {Wenn Sie als Pfad ein Verzeichnis angeben oder dieses Feld frei\ lassen, wird TkRat rekursiv alle Ordner unterhalb dieses Pfades\ importieren. Manchmal ist es schwierig, den korrekten Wert fr den\ Pfad zu bestimmen. Die Funktion "Probeimport" zeigt eine Liste der\ Elemente, die auf oberster Ebene beim Import gefunden werden.\ Elemente in Unterverzeichnissen werden dabei ignoriert.} label test_import en "Test import" de {Probeimport} sv "Testimport" fr {Tester l'importation} label define_imap_import en {Define IMAP import} de {IMAP-Ordner importieren} sv {Definiera IMAP import} fr {Dfinir une importation IMAP} label basic_op en "What do you want to do?" de {Neuen Ordner anlegen} sv "Vad vill du gra?" fr {Que voulez vous faire ?} label create_single_file_exists en {You specified that you wanted to create a new folder but the\ file you selected already exists} de {Sie wollten einen neuen Ordner anlegen, aber die Datei, die\ Sie angegeben haben, existiert bereits.} sv {Du angav att du ville skapa en mapp men filen du angav finns redan} fr {Vous voulez crer une nouvelle bote lettre, mais le fichier que\ vous avez indiqu existe dj.} label use_anyway en "Use anyway" de {Verwenden} sv "Anvnd nd" fr {Utiliser quand mme} label importing en Importing de {Importieren} sv Importerar fr Importation label currently_importing en {TkRat is currently importing your folders. This may take some time\ if there are lots of folders to import. Unfortunately it is not\ possible to abort this operation.} de {TkRat importiert nun Ihre Ordner. Dies kann einige Zeit dauern,\ falls viele Ordner importiert werden mssen. Leider ist es nicht\ mglich, diesen Vorgang abzubrechen.} sv {TkRat importerar just nu dina mappar. Detta kan ta lite tid om det\ r mnga mappar som skall importeras. Tyvrr gr det inte att avbryta.} fr {TkRat importe maintenant vos botes lettre. Cette opration peux\ prendre un certain temps s'il y a de nombreuses botes. Malheureusement,\ il est impossible d'interrompre cette opration.} label found en Found de {Gefunden} sv Hittade fr Trouv label last en Last de {Letzten} sv Sist fr Dernier label cant_close_while_wizards en {You cannot close the folder definition window while you have any\ Folder Wizards active.} de {Sie knnen dieses Fenster nicht schlieen, whrend ein Assistent\ arbeitet.} sv {Du kan inte stnga mappfnstret medans du har Mapphjlpredor aktiva} fr {Impossible de fermer la fentre de dfinition des botes lettre\ tant qu'il y a un assistant ouvert.} label define_mh_folder en "Define MH folder" de {MH-Ordner anlegen} sv {Definiera MH-mapp} fr {Dfinir d'une bote lettre MH} label why_mhfolder en {Each mh folder needs a name which is used in TkRat\ menus etc. You must also specify the path to the MH-folder. This\ path is from the root of your MH-directory, for example "inbox" is\ usually enough for your MH-inbox. If the path you give points to a\ directory then all folders in that directory will be imported.} de {Jeder MH-Ordner muss einen Namen haben, der in\ Mens und Auswahlen verwendet wird. Auerdem mssen Sie\ den vollstndigen Pfad des MH-Ordners angeben. Der Eintrag\ "inbox" gengt im Allgemeinen fr Ihren MH-Eingang.\ Wenn Sie ein Verzeichnis als Pfad angeben, werden alle Ordner\ in diesem Verzeichnis importiert.} sv {Varje MH-mapp behver ett namn som anvnds i menyer etc.\ Man mste ocks ange skvgen till mappen. Denna skvg skall anges\ frn roten av ditt MH-bibliotek, t.ex. "inbox" rcker vanligen fr din\ inkorg. Om skvgen pekar p ett bibliotek s kommer alla mappar i\ biblioteket att importeras.} #fr {Chaque bote lettre MH a besoin d'un nom,\ #utilis dans les menus de TkRat et ailleurs. Vous devez aussi indiquer\ #le chemin de votre bote MH. Ce chemin est relatif la base de votre\ #rpertoire MH. Par exemple inbox suffit en gnral pour votre\ #bote MH principale.} label mh_test_import en {The import will recursively load all folders found at the\ specified path. Sometimes it is tricky to figure out the correct value\ of 'Path'. The 'Test import' function lists what will be found on\ the-level when the import is done.} de {Ausgehend von der Pfadangabe werden alle Ordner rekursiv\ importiert. Manchmal ist es schwierig, die richtige Pfadangabe zu\ bestimmen. Die Schaltflche "Probeimport" zeigt eine Liste der\ gefundenen Ordner.} sv {Importen kommer att rekursivt ladda in alla mappar som finns under\ den angivna skvgen. Ibland r det svrt att lista ut vad man skall\ ange fr skvg. "Testimport"-funktionen listar vad som finns i\ bibliotek som angetts i skvg men undersker inte underbibliotek.} fr {L'importation va rcursivement charger toutes les botes lettres\ partir du chemin indiqu. Il est parfois difficile\ De dcouvrir la valeur exacte du chemin. La fonction Tester l' importation \ liste ce qui serait dcouvert par une vritable importation, mais ne descend\ pas dans les sous rpertoires.} label define_dbase_folder en "Define database folder" de {Datenbank-Ordner anlegen} sv "Definiera databasmapp" fr {Dfinir une bote lettre base de donnes} label why_dbase_folder en {The database is a collection of messages in which you can\ search. You can search on senders/recipients and subjects etc. Each\ message may also have one or more keywords associated with them as\ well as an expiration date and action. When you put a message in a\ database folder it gets the defined keywords and expiration data. When\ you open a database folder it does a search on the specified keywords.} de {Die Datenbank speichert Nachrichten, die Sie nach Absender,\ Empfnger, Betreff etc. durchsuchen knnen. Jeder Nachricht knnen\ auerdem Stichwrter und eine Aktion bei Verfall zugeordnet werden.\ Wenn Sie eine Nachricht in einen Datenbank-Ordner verschieben,\ werden dieser Nachricht die Stichwrter und die Aktion bei Verfall,\ die Sie hier fr den Order festlegen, zugeordnet.\ Wenn Sie einen Datenbank-Ordner ffnen,\ werden die zu den Stichwrtern passenden Nachrichten in der\ Datenbank angezeigt.} sv {Databasen r en samling brev som du kan ska i. Man kan ska p\ avsndare/mottagare rende etc. Varje brev kan ocks ha ett eller\ flera nyckelord associerade liksom ett expireringsdatum och\ hndelse. Nr man flyttar et brev till en databasmapp s fr brevet\ nyckelord och expireringsdata frn mappen. Nr man ppnar en mapp s\ utfrs en skning efter de angivna nyckelorden.} fr {La base de donne est une collection de messages dans laquelle vous\ pouvez effectuer des recherches par objet, expditeur, destinataire, etc...\ Vous pouvez aussi associer un ou plusieurs mots-cls chaque message\ ainsi qu'une date d'expiration, et une action effectuer lorsque le message\ arrive expiration. Lorsque vous placer un message dans une base de donnes\ vous pouvez indiquer des mots-cls, et les donnes relative l'expiration.\ Ouvrir une telle bote, effectue une recherche sur les critres de votre choix.} label why_pop en {Each folder needs a name which is used in TkRat\ menus etc. You must also define how to reach the POP3-server.} de {Jeder Ordner muss einen Namen haben, der in\ Mens und Auswahlen verwendet wird. Auerdem mssen Sie\ Ihre Zugangsdaten zum POP3-Server angeben.} sv {Varje mapp behver ett namn som anvnds i menyer etc.\ Man mste ocks ange hur man nr POP3-servern.} fr {Chaque bote lettre a besoin d'un nom, utilis\ dans les menus de TkRat et ailleurs. Vous devez aussi indiquer\ comment contacter le serveur POP3.} label define_dynamic_folder en {Define dynamic folder} de {Dynamischen Ordner anlegen} sv {Definiera dynamisk mapp} fr {Dfinir une bote lettre dynamique} label why_dynamic_folder en {A dynamic folder is a directory which contains a number of\ folders. When you insert a message it gets stored in a folder bearing\ the name of the primary recipient. You do not open a dynamic folder\ directly, instead you open one of the contained folders. All folders need a name which is used in TkRat and dynamic folders\ also need the path to the directory.} de {Ein dynamischer Ordner ist eigentlich ein Verzeichnis, das selbst\ eine beliebige Anzahl Ordner enthalten kann. Eine Nachricht wird in einem\ dynamischen Ordner gespeichert, indem sie in einer Datei innerhalb des\ hier angegebenen Verzeichnisses abgelegt wird. Der Dateinam wird dabei\ durch den ersten Empfngernamen in der Nachricht festgelegt.\ Diese automatisch erzeugten Ordner knnen dann jeweils einzeln zur\ Ansicht und zum Bearbeiten geffnet werden. Der dynamische Ordner kann\ jedoch nicht komplett geffnet werden. Jeder Ordner muss einen Namen haben, der in\ Mens und Auswahlen verwendet wird. Auerdem mssen Sie\ fr den dynamischen Ordner einen Verzeichnispfad angeben.} sv {En dynamisk mapp r ett bibliotek som innehller ett antal\ mappar. Nr du sparar ett brev i en dynamisk mapp s sparas det i en\ mapp som heter samma som den frste mottagaren. Man ppnar inte en\ dynamisk mapp direkt utan ppnar istllet undermapparna. Alla mappar behver ett namn som anger i menyer etc. En dynamisk mapp behver dessutom en skvg till sitt bibliotek.} fr {Une bote lettre dynamique est un rpertoire contenant d'autres botes\ lettre. Insrer un message dans une bote dynamique, l'insre en fait dans\ une bote du rpertoire dont le nom l'expditeur du\ message. On ouvre pas directement une bote dynamique, mais l'une des botes\ qu'il contient. Chaque bote lettre fichier a besoin d'un nom, utilis\ dans les menus de TkRat et ailleurs. Vous devez aussi indiquer\ le nom du rpertoire.} label create_but_not_empty en {You are creating a new folder but the directory you specified is\ not empty. Please select an empty or new directory.} de {Sie versuchen, einen neuen Ordner anzulegen, aber das Verzeichnis\ ist nicht leer. Bitte geben Sie ein leeres oder ein neues Verzeichnis an.} sv {Du skapar en ny mapp men bibliotek du har angett r inte\ tomt. Vnligen vlj ett tomt eller nytt bibliotek.} fr {Vous voulez crer une nouvelle bote dynamique, mais le rpertoire\ que vous avez indiqu n'est pas vide. Veuillez indiquer un rpertoire vide\ crer.} label first_use_wizard en "First Use Wizard" de {Assistent fr neue Nutzer} sv Starthjlpreda fr {Assistant de premire utilisation.} label identity en Identity de {Absenderdaten} sv Identitet fr {Identit} label firstuse_info en {This wizard will help you do the basic configurations which must\ be done before you can start to use TkRat.} de {Dieser Assistent hilft Ihnen dabei, die grundlegenden Einstellungen\ vorzunehmen, damit Sie TkRat verwenden knnen.} sv {Denna hjlpredan kommer att hjlpa dig gra den grundkonfiguration\ av TkRat som mste gras innan man kan brja anvnda programmet.} fr {Cet assistant va vous aider effectuer les rglages de base qui\ doivent tre fait avant de pouvoir utiliser TkRat.} label why_identity en {The first step is to specify your identity. This information will\ be included as the originator in messages you send.} de {Im ersten Schritt geben Sie Ihren Namen und Ihre Adresse an.\ Diese Daten dienen spter als Absender in den Nachrichten,\ die Sie verschicken.} sv {Det frsta steget r att mata in din identitet. Denna infromation\ kommer att inkluderas som avsndare i alla brev du skickar.} fr {La premire tape est d'indiquer votre identit. Cette information sera\ utilise dans les messages que vous enverrez.} label email_address en "Email address" de {E-Mail-Adresse} sv Epostadress fr {Adresse de couriel} label why_sending en {TkRat can send messages either by talking to an SMTP-server or by\ piping it to a command. Here you get to configure which.} de {TkRat kann Nachrichten zum Versand entweder per SMTP an einen Server\ bertragen oder an ein Programm ausgeben. Eine von beiden\ Mglichkeiten mssen Sie hier auswhlen.} sv {TkRat kan skicka brev genom att antingen prata direkt med en\ SMTP-server eller genom att skicka texten till ett program. Hr\ konfigurerar du vilket.} fr {TkRat peut expdier les messages soit en dialoguant avec un serveur\ SMTP, ou en utilisant un programme externe. Vous devez choisir l'une de\ ces mthodes.} label use_mail_server en "SMTP (Outgoing mail server)" de {SMTP (Server fr abgehende Nachrichten)} sv "SMTP (Server fr utgende post)" fr {SMTP (Serveur de courrier sortant)} label use_prog en "Pipe to local program (sendmail compatible)" de {Ausgabe an ein Programm (kompatibel zu sendmail)} sv "SKicka till lokalt program (kompatibelt med sendmail)" fr {Tube vers un programme (compatible avec sendmail)} label why_inbox en {You must now specify where you want to read your mail from.\ You can specify additional mailboxes and many more details in the\ "New/Edit folder" window.} de {Hier mssen Sie angeben, wo TkRat Ihre eingehenden Nachrichten\ lesen kann. Spter knnen Sie weitere Eingangsordner und zahlreiche\ Einstellungen im "Ordner anlegen/bearbeiten" Fenster vornehmen.} sv {Du mste nu ange var du hmtar din post ifrn. Du kan ange\ fler brevldor, samt mnga fler detaljer, i "Ny/ndra mapp"-fnstret} fr {Vous devez maintenant indiquer o se trouve votre bote lettre\ principale. Vous pourrez ajouter d'autre botes par la suite en suivant\ le menu Administration->Nouveau/dition des botes lettres .} label advanced_imap_conf en "Advanced IMAP configuration" de {Fortgeschrittene IMAP Konfiguration} sv "Avancerad IMAP-konfiguration" fr {Configuration IMAP avance} label advanced_pop_conf en "Advanced POP3 configuration" de {Fortgeschrittene POP3 Konfiguration} sv "Avancerad POP3-konfiguration" fr {Configuration POP3 avance} label done en Done de {Fertig} sv Klart fr Termin label import_info en "Import information" de {Hinweis zum Import} sv "Import information" fr {Information d'importation} label import_info_text en {TkRat is able to read your old folders. But before you can do that\ you must "import" them. This can be done by going to Admin->New/Edit\ folder... and then start the "New Folder Wizard".} de {TkRat kann Ihre bestehenden Ordner einlesen. Dazu ist es jedoch\ notwendig, diese zu "importieren", indem Sie aus dem Men Administration\ "Ordner anlegen/bearbeiten" whlen und dann den Assistenten fr neue\ Ordner starten (Auswahl aus dem Men).} sv {TkRat kan lsa dina ganla mappar. Men frst mste du "importera"\ dem. Detta kan man gra genom att g till Admin->Ny/ndra mapp... och\ sedan starta "'Ny mapp' hjlpreda".} fr {TkRat peut lire vos anciennes botes lettres. Mais avant d'y accder,\ vous devez les importer . Vous pouvez faire cela en suivant le menu\ Administration->Nouveau/dition des botes lettres->Assistant de bote\ lettre .} label inbox en Inbox de {Eingang} sv Inkorg fr Arrive label actions en Actions de {Bearbeiten} sv tgrder fr Actions label no_such_mailbox en "There is no such mailbox" de {Ein Ordner dieses Namens existiert nicht} sv "Det finns ingen sdan brevlda" fr {Aucune bote lettre de ce nom} label continue_composing en "Continue composing" de {Weiter bearbeiten} sv "Fortstt skriv" label folder_key_mvdb en "Move to database" de {In die Datenbank verschieben} sv "Flytta till databasen" label clear_selection en "Clear selection" sv "Avmarkera alla" de {Auswahl lschen} label using en Using sv Anvnder de Verwendet label wiz_next en Next de Weiter sv Nsta fr {Suivant} label wiz_previous sv Fregende en Previous de Zurck fr Prcdent sr Prethodni pl Poprzedni pt Anterior label wiz_cancel sv Avbryt en Cancel de Abbrechen it Annulla fr Annuler sr Poniti pl Anuluj pt Cancelar label wiz_finish en Finish de {Fertigstellen} sv Klar fr {Terminer} label wiz_import sv Importera en Import de Importieren it Importa fr Importer sr Uvezi pl Importuj pt Importar label save_outgoing_failed_title en "Save outgoing failed" sv "Misslyckades att spara utgende" de {Speichern der Ausgangskopie fehlgeschlagen} label recipients en Recipients sv Mottagare de {Empfnger} label save_outgoing_failed en "The message has been sent but there was an error when trying to\ save a local copy. You can try to save it to another folder or choose\ to not save a copy." sv "Brevet har skickats men ett fel uppstod nr en lokal kopia skulle\ sparas. Du kan frska spara det till en annan mapp eller vlja att\ inte spara ngon kopia" de {Die Nachricht wurde versandt. Beim Speichern einer Kopie der\ Nachricht ist jedoch ein Fehler aufgetreten. Sie knnen nun versuchen,\ die Kopie in einem anderen Ordner als dem vorgegebenen abzulegen, oder\ auf das Speichern verzichten.} label secring en "Secret keyring" sv "Hemlig nyckelring" de {Geheimer Schlsselbund} label pubring en "Public keyring" sv "Publik nyckelring" de {ffentlicher Schlsselbund} label sign_as en "Sign as" sv "Signera som" de {Unterschreibe als} label no_signing_key_selected en "No signing key selected" sv "Ingen signeringsnyckel vald" de {Kein Schlssel zum Unterschreiben ausgewhlt} label addressbook en "Address book" sv "Adresser" de {Adressbuch} label reread_addresses en "Reread all address sources" sv "Ls om alla adrressbcker" de {Alle Adressbcher neu einlesen} label import_addresses en "Import addresses" sv "Importera adresser" de {Adressbcher importieren} label pgp_actions en "PGP actions" sv "PGP tgrder" de {PGP Aktion} label sign_encrypt en "Sign & Encrypt" sv "Signera & kryptera" de {Unterschreiben und Verschlsseln} label check_on_finish en {TkRat will connect to the mail-server and verify the existence of\ the folder once you press finish.} sv {Nr du trycker p "Klar" s kommer TkRat att ansluta till servern\ och kontrollera om mappen finns.} fr {lorsque vous cliquerez sur Terminer , TkRat va se connecter au\ serveur de couriel et vrifier l'existence\ de la bote.} de {TkRat wird eine Verbindung zum Server aufbauen und prfen, ob\ der Ordner existiert, sobald Sie "Fertigstellen" auswhlen.} label failed_to_open_mailbox en "Failed to open mailbox" sv "Misslyckades ppna mappen" de {ffnen des Ordners fehlgeschlagen} label pgp_details en {PGP details} sv {PGP detaljer} de {PGP Einstellungen} label my_key en {My key} sv {Min nyckel} de {Mein Schlssel} label key en Key sv Nyckel de {Schlssel} label all_addresses en {All address fields} sv {Alla adressflt} de {Alle Adressfelder} label complete_msg_text en {Complete text} sv {Hela texten} de {Text der Nachricht} label operation en Operation sv Operation de {Verknpfung der Kriterien} label op_and en {All lines below must match} sv {Alla rader nedan mste matcha} de {Alle Kriterien mssen erfllt sein} label op_or en {One matching line is enough} sv {En matchande rad rcker} de {Ein Kriterium gengt} label line_expl en {It is enough that one word per line matches} sv {Det rcker om ett ord per rad matchar} de {Ein beliebiges Wort aus einer Zeile gengt} label time_interval en {Time interval} sv {Tidsintervall} de {Datum} label all_times en {All times} sv {Alla tider} de {Beliebig} label specified_times en {Specified interval} sv {Specificerat intervall} de {Bestimmter Datumsbereich} label all_addr_detail en {To, Cc & From} sv {Till, Kopia & Frn} de {An, Kopie, Von} label slow en {Slow} sv {Lngsamt} de {langsam} label illegal_from_date en {Illegal from-date} sv {Ogilitgt startdatum} de {Ungltiges Anfangsdatum} label illegal_to_date en {Illegal to-date} sv {Ogilitgt slutdatum} de {Ungltiges Enddatum} label forward_separately en {Forward separately} sv {Vidarebefodra separat} de {Einzeln weiterleiten} label forward_in_one en {Forward in one message} sv {Vidarebefodra i ett brev} de {In einer Nachricht weiterleiten} label bounce_messages en {Bounce messages} sv {Studsa meddelanden} de Umleiten label forwarded_messages en {Forwarded messages} sv {Vidarebefodrade brev} de {Weitergeleitete Nachrichten} label forwarded_msg_goes_here en {Forwarded message goes here} sv {Vidarebefodrat brev hamnar hr} de {Die weitergeleitete Nachricht erscheint hier} label bounce_name en {Bounce message} sv {Studsa brev} de {Nachricht umleiten} label sync_failed_disk_full en {Synchronization failed since disk is full} sv {Synkroniseringen misslyckades eftersom disken r full} de {Die Synchronisation ist fehlgeschlagen, da auf der Platte kein Speicher frei ist} label avoid_html en {Avoid html in body} sv {Undvik html i brdtext} de {HTML unterdrcken} label spell_checking en {Spell checking} sv {Stavningskontroll} de {Rechtschreibung} label dictionary en {Dictionary} sv {Ordlista} de {Wrterbuch} label auto_dicts en {Auto Dictionaries} sv {Auto-Ordlistor} de {Automatik-Wrterbcher} label gray85 sv Helgr en "Completely gray" de {Vollstndig grau} label duplicate_alias en {An alias with that name already exists} sv {Det finns redan ett alias med det namnet} de {Dieser Alias existiert bereits.} label mark_misspellings en "Mark misspellings" sv "Visa felstavningar" de {Rechtschreib-Fehler markieren} label filter en Filter sv Filter de {Filter} label failed_to_execute_spell en {Failed to launch spell checking command} sv {Misslyckades att starta programmet fr stavningskontroll} de {Das Programm fr die Rechtschreibprfung konnte nicht gestartet werden.} label no_dictionaries en {Did not find any dictionaries} sv {Hittade inga ordlistor} de {Keine Wrterbcher gefunden} label drafts en Drafts sv Utkast de {Entwrfe} label postpone en Postpone sv Uppskjut de {Als Entwurf speichern und schlieen} label compose_backup en "Backup interval" sv "Backupintervall" de {Intervall fr Sicherungskopien} label find_prev sv {Sk fregende} en {Find previous} de {Rckwrts suchen} label match_case en "Match case" sv "Matcha shiftlge" de {Gro-/Kleinschreibung beachten} label n_folder_windows sv {Du har %d mappfnster ppna} en {You have %d folder windows open} de {Es sind %d Ordnerfenster geffnet} label quit_anyway sv {Avsluta nd} en {Quit anyway} de {Trotzdem beenden} label store_snapshot sv {Spara kopia} en {Store snapshot} de {Einen Entwurf speichern} label mark_all en "Mark all" sv "Markera alla" de {Alle auswhlen} label clear_all en "Clear all" sv "Avmarkera alla" de {Auswahl lschen} label reread en Reread sv "Ls om" de {Neu einlesen} label failed_save_sent en {Failed to save a sent message: %s} sv {Misslyckades att spara ett skickat brev: %s} de {Eine Nachricht wurde verschickt, konnte jedoch nicht gespeichert werden: %s} label compose_last_chance en {Keep backup} sv {Spara backup} de {Sicherungszeit nach dem Abbrechen} label define_pop_folder en "Define POP folder" sv "Definiera POP mapp" de {POP3 Ordner anlegen} label show_addr_history en "Show address history" sv "Visa adresshistoria" de {Zuletzt verwendete Adressen anzeigen} label no_spell sv {Tyvrr, hittade inte Aspell eller Ispell 3.1.xx (eller\ senare). Stnger av automatisk stavningskontroll.} en {Sorry, did not find Aspell or Ispell 3.1.xx (or later). Turning\ off automatic spell-checking.} de {Tkrat konnte weder Aspell noch Ispell 3.1.xx (oder hher) finden.} #de {Tut mir leid, sie haben nicht Ispell 3.1.xx installiert.} #fr {Dsol, Ispell 3.1.xx n'est pas install.} #sr {alim, ali nemate Ispell 3.1.xx!} #pl {Sorry, nie masz zainstalowanego Ispella 3.1.xx} #pt {O programa ispell 3.1.xx no se encontra instalado.} label mark_as_read sv {Markera som lst} en {Mark as read} de {Als gelesen markieren} label mark_as_answered en "Mark as answered" sv "Markera som besvarat" de {Als beantwortet markieren} label mark_as_unanswered en "Mark as unanswered" sv "Markera som obesvarat" de {Als unbeantwortet markieren} label prop_font_family en "Proportional font" sv "Proportionellt typsnitt" de {Proportionalschrift} label fixed_font_family en "Fixed font" sv "Fastviddstypsnitt" de {Monospaceschrift} label html_show_images sv {Ladda externa bilder} en {Load external images} de {Bilder nachladen} label html_min_image_size en {Minimum image size} sv {Minsta bildstorlek} de {Mindestgre fr Bilder} label cmd en Cmd sv Kommando de {Befehlszeile} label html_messages en "HTML messages" sv "HTML-brev" de {Nachrichten im HTML Format} label composing en Composing sv "Skriva brev" de {Schreiben} label paths en Paths sv Skvgar de {Dateipfade} label new_messages en "New messages" sv "Nya brev" de {Wchter fr neue Nachrichten} label start_mode sv {Online mode vid start} en {Start online mode} de {Modus beim Programmstart} label list_format sv Listformat en {Message list format} de {Format der Nachrichtenliste} label open_in en {Open in} sv {ppna i} de {Anzeigen innerhalb eines} label always_use_external_editor sv {Anvnd alltid extern editor} en {Always use external editor} de {Immer den externen Editor verwenden} label watcher_intervals sv {Kontrollintervall} en {Check intervall} de {Prfintervall} label sending_mail_from en "Sending MAIL FROM <%s>" sv "Skickar MAIL FROM <%s>" de {Sende MAIL FROM <%s>} label sending_rcpt en "Sending RCPT TO <%s>" sv "Skickar RCPT TO <%s>" de {Sende RCPT TO <%s>} label sending_data en "Sending data" sv "Skickar data" de {Sende Daten} label ms en ms sv ms de {ms} label date_format en {Date format} sv Datumformat de {Datumsformat} label no_tnef_found en {This bodypart could be decoded if the 'tnef' program was installed.} sv {Denna bilaga skulle kunna avkodas om programmet 'tnef' var installerat.} de {Dieser Teil der Nachricht kann nur gelesen werden, wenn das Programm tnef zur Verfgung steht.} label tnef_path en {tnef command} sv {tnef-kommando} de {tnef Befehlszeile} label deleted_attachment en {This attachment has been deleted} sv {Denna bilaga har raderats} de {Der ursprnglich hier vorhande Anhang wurde gelscht.} label delete_attachments en {Delete attachments} sv {Radera bilagor} de {Anhnge lschen} label delete_attachments_expl en {Creates a copy of the current message. The\ copy will be placed in the same folder and\ will have the selected attachments deleted.} sv {Skapar en kopia av det valda brevet. Kopian\ placeras i samma mapp och de valda bilagorna\ kommer att vara raderade.} de {Es wird eine Kopie dieser Nachricht erzeugt. Diese Kopie wird\ in demselben Ordner wie das Original abgelegt. Die Kopie enthlt\ nicht die unten ausgewhlten Anhnge.} label attachments_to_delete en {Attachments to delete} sv {Bilagor att radera} de {Zu lschende Anhnge} label really_expire_title en {Really exipre database?} sv {Expirera databasen?} de {Datenbank nach verfallenen Nachrichten durchsuchen?} label really_expire en {It has been over a year since the database was expired. Continue\ with expiration?} sv {Det har gtt ver ett r sedan databasen expirerades. Skall\ databasen expireras nu?} de {Die Datenbank wurde seit mehr als einem Jahr nicht nach\ verfallenen Nachrichten durchsucht. Soll dies jetzt erfolgen?} label sync_folder en {Synchronize offline folder} sv {Synkonisera offline-mapp} de {Lokaler Abgleich dieses Ordners} label enable_offline en {Enable offline-mode} sv {Mjliggr offline-mod} de {Offline Modus aktivieren} label imap_offline en {TkRat can store a local copy of each IMAP folder. This makes it\ possible to access them also when there is no network connection to\ the IMAP server. This is called Offline' mode.} de {TkRat kann eine Kopie des IMAP-Ordners lokal speichern. Damit\ knnen Sie auch dann auf den Ordner zugreifen, wenn keine Verbindung\ zum Server besteht. Dieses Verhalten wird hier als Offline Modus bezeichnet.} sv {TkRat kan spara en lokal kopia av IMAP-mapparna. Dett gr det\ mjligt att komma t dem ven om man inte har ngot\ ntverksfrbindelse med IMAP-servern. Detta kallas offline-mod.} #fr {TkRat peut conserver une copie locale d'une bote lettres IMAP.\ #Cela permet d'accder la bote, mme sans connexion rseau. On appelle\ #cela le mode dconnect .} label edit_address en {Edit address} sv {Editera adress} de {Adressbucheintrag bearbeiten} label no_subject en {No subject} sv {Inget rende} de {Kein Betreff} label message_deleted en {The message had already been deleted} sv {Brevet hade redan raderats} de {Die Nachricht war bereits gelscht.} label messages_deleted en {Some messages had already been deleted} sv {Vissa brev hade redan raderats} de {Eine oder mehrere Nachrichten waren bereits gelscht.} label could_not_find_adr sv {Kunde inte hitta ngra nya adresser att extrahera} en {Could not find any new addresses to extract} de {Es wurden keine neuen Adressen gefunden.} #it {Non sono stati trovati indirizzi da estrarre} #fr {Pas d'adresse extraire} #sr {Nisam pronaao nijednu adresu za izdvajanje} #pl {Nie znaleziono adnych adresw} #pt {No foram encontrados quaisquer endereos} label dbase_same_subject en "Database: same subject" sv "Databas: samma rende" label dbase_to_from_sender en "Database: to/from sender" sv "Databas: till/frn avsndaren" label illegal_list_format en {Illegal character '%%%c' in list format} sv {Ogiltigt tecken '%%%c' i listformatet} label sync_offline_folders sv {Synkronisera frnkopplade ntverksmappar} en {Synchronize offline folders} label always_eeditor_but_none en {You have "Always use external editor" enabled but have\ not defined any external editors.} sv {Du har "Anvnd alltid extern editor" pslaget men har\ inte definierat ngon extertn editor} label pgp_err en Failed sv Fel label pgp_abort en Aborted sv Avbruten label bad_mailto_url en "Bad mailto url" sv "Ogiltig mailto" label info en Information sv Information label earliest_date en {Earliest date} sv {Tidigaste datum} label latest_date en {Latest date} sv {Senaste datum} label keyword en Keyword sv Nyckelord label usage_count en {Usage count} sv Anvnt label total_num_messages sv {Totalt antal brev} en {Total number of messages} #de {Gesamtanzahl der Nachrichten im Index} #it {Numero totale di messaggi nell'indice} #fr {Nombre de messages dans l'index} #sr {Ukupan broj pisama u indeksu} #pl {Cakowita liczba wiadomoci w indeksie} #pt {Nmero de mensagens no ndice} label dbinfo en {Database info} sv {databasinfo} label dbinfo_info_msg en {Message database info} sv {Brevets databasinfo} label dbinfo_info_folder en {Values comes from folder and not from selected messages} sv {Vrdena kommer frn mappen och inte frn de valda breven} label dbinfo_info_first_msg en {Values comes from the first message} sv {Vrdena kommer frn det frsta brevet} label flag_same_subject en {Flag messages with same subject} sv {Markera brev med samma rende} label exdate_expl en {+n means to expire 'n' days after insert/modify} sv {+n betyder att expirera 'n' dagar efter lagring/modifiering} label date_parsing_failed en "Failed to parse time" sv "Kunde inte frst tiden" tkrat_2.2cvs20100105-dfsg.orig/tkrat/addrlist.tcl000066400000000000000000000224471137544547100214540ustar00rootroot00000000000000# addrlist.tcl -- # # This file contains the code which handles the list of possible # address auto completions. # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Global state variables set addrlist_loaded 0 # The address list is stored as a simple list like this: # email1 # name1 # email2 # name2 # ... # Unknown names are expressed as empty strings. set addrlist {} # AddrListAdd -- # # Add addresses to the list of remembered addresses (if enabled) # # Arguments: # addrs - One or more addresses to add proc AddrListAdd {addrs} { global option addrlist_loaded addrlist if {0 == $option(num_autocomplete_addr)} { return } set alist [RatSplitAdr $addrs] if {0 == [llength $alist]} { return } if {0 == $addrlist_loaded} { LoadAddrList } set additions {} foreach al $alist { set a [RatCreateAddress -nodomain $al] set name [$a get name] set i [lsearch -exact $addrlist [$a get mail]] if {$i != -1} { if {"" == $name} { set name [lindex $addrlist [expr $i+1]] } set addrlist [lreplace $addrlist $i [expr $i+1]] } lappend additions [$a get mail] lappend additions $name } set addrlist [concat $additions [lrange $addrlist 0 [expr ($option(num_autocomplete_addr)-[llength $alist])*2-1]]] } # LoadAddrList -- # # Load the address list from disk # # Arguments: proc LoadAddrList {} { global addrlist option addrlist_loaded if {[file readable $option(ratatosk_dir)/addrlist]} { set fh [open $option(ratatosk_dir)/addrlist r] fconfigure $fh -encoding utf-8 set data [read $fh] close $fh eval $data } set addrlist_loaded 1 } # SaveAddrList -- # # Save the address list on disk # # Arguments: proc SaveAddrList {} { global addrlist option set fh [open $option(ratatosk_dir)/addrlist w] fconfigure $fh -encoding utf-8 puts $fh "set addrlist [list $addrlist]" close $fh } # GetMatchingAddrs -- # # Get any addresses whose first characters of the email or name portion # matches the given argument (non case sensitive match) # # Arguments: # match - String to match # max - Maximum number of entries to return proc GetMatchingAddrs {match max} { global addrlist option addrlist_loaded if {0 == $addrlist_loaded} { LoadAddrList } return [RatGetMatchingAddrsImpl $addrlist $match $max] } ############################################################################ # Routines for handling the popup of an autocomplete list # AddrListInit -- # # Initialize the popup list stuff # # Arguments: # w - text widget to use proc AddrListInit {w} { upvar \#0 _addrList$w hd set hd(start_pos) {} set hd(showing) 0 bind $w {AddrListHandleKeyRelease %W} bind $w {+;AddrListHandleDestroy %W} bind $w {break} foreach k {Up Down Return space} { bind $w {if [AddrListHandleListKey %W %K] break} } } # AddrListHandleDestroy -- # # Clean up when the parent is destroyed # # Arguments: # w - The text widget proc AddrListHandleDestroy {w} { upvar \#0 _addrList$w hd if {[info exists hd(w)]} { destroy $hd(w) } unset hd } # AddrListAlias -- # # Get matching aliases # # Arguments: # match - String to match # max - Maximum number of entries to return proc AddrListAlias {match max} { RatAlias list aliases nocase set result {} foreach n [lrange [array names aliases "$match*"] 0 [expr $max - 1]] { set content [lindex $aliases($n) 2] if {-1 != [string first "," $content]} { lappend result $content } else { lappend result "[lindex $aliases($n) 1] <$content>" } } return $result } # AddrListHandleKeyRelease -- # # Handle KeyRelease events # # Arguments: # w - The text widget proc AddrListHandleKeyRelease {w} { upvar \#0 _addrList$w hd global option if {!$option(show_autocomplete)} { return } # Find start of the current address set start [$w search -backwards "," insert "insert linestart"] if {"" != $start} { set start [$w index "$start +1c"] } else { set start [$w index "insert linestart"] } while {[$w compare $start < end+1c]} { set c [$w get $start] if {" " == $c || "\t" == $c} { set start [$w index $start+1c] } else { break } } set hd(end_pos) [$w search "," $start "$start lineend"] if {"" == $hd(end_pos)} { set hd(end_pos) [$w index "$start lineend"] } set addr [string trim [$w get $start $hd(end_pos)]] if {"" == $addr} { if {[info exists hd(w)]} { wm withdraw $hd(w) set hd(showing) 0 set hd(start_pos) {} } return } set matches [GetMatchingAddrs $addr \ $option(automplete_addr_num_suggestions)] set rem [expr $option(automplete_addr_num_suggestions) - [llength $matches]] if {$rem > 0} { foreach a [AddrListAlias $addr $rem] { lappend matches $a } } if {0 == [llength $matches]} { if {[info exists hd(w)]} { wm withdraw $hd(w) set hd(showing) 0 set hd(start_pos) {} } return } if {$start != $hd(start_pos)} { if {![info exists hd(w)]} { AddrListCreatePopup $w } set bbox [$w bbox $start] set x [expr [lindex $bbox 0] + [winfo rootx $w]] set y [expr [lindex $bbox 1] + [lindex $bbox 3] + [winfo rooty $w]] wm geometry $hd(w) +$x+$y wm deiconify $hd(w) set hd(showing) 1 } set hd(start_pos) $start $hd(list) delete 0 end foreach m $matches { $hd(list) insert end $m } if {[$hd(list) size] > 10} { $hd(list) configure -height 10 if {![winfo ismapped $hd(scroll)]} { pack $hd(scroll) -side right -fill y } } else { $hd(list) configure -height [llength $matches] if {[winfo ismapped $hd(scroll)]} { pack forget $hd(scroll) } } } # AddrListCreatePopup -- # # Create the popup window # # Arguments: # w - The text widget proc AddrListCreatePopup {w} { upvar \#0 _addrList$w hd global idCnt set hd(w) .addrlist[incr idCnt] toplevel $hd(w) -bd 1 -class TkRat wm overrideredirect $hd(w) 1 wm transient $hd(w) $w wm positionfrom $hd(w) program wm withdraw $hd(w) set hd(list) $hd(w).l set hd(scroll) $hd(w).s scrollbar $hd(scroll) \ -relief raised \ -bd 1 \ -highlightthickness 0 \ -command "$hd(list) yview" listbox $hd(list) \ -width 40 \ -relief raised \ -bd 1 \ -exportselection false \ -selectmode single \ -highlightthickness 0 \ -selectmode single \ -yscroll "$hd(scroll) set" pack $hd(list) -expand 1 -fill both -side left bind $hd(w) [list focus $w] bind $hd(list) <> [list AddrListSelect $w] } # AddrListSelect -- # # Handle list selection events # # Arguments: # w - The text widget proc AddrListSelect {w} { upvar \#0 _addrList$w hd set sel [$hd(list) curselection] if {1 != [llength $sel]} { return } $w mark set insert $hd(end_pos) $w delete $hd(start_pos) $hd(end_pos) $w insert $hd(start_pos) [$hd(list) get $sel] $w see insert wm withdraw $hd(w) set hd(showing) 0 set hd(start_pos) {} focus $w } # AddrListClose -- # # Close the popup window. Returns '1' if the close event can be ignored # # Arguments: # w - The text widget # force - Force closure of address list window proc AddrListClose {w force} { upvar \#0 _addrList$w hd set f [focus] if {!$force && [info exists hd(w)] && ($f == $hd(list) || $f == $hd(list) || $f == $hd(list))} { return 1 } if {$force || ([info exists hd(w)] && $f != "" && $f != $hd(list) && $f != $hd(w) && $f != $w)} { if {[info exists hd(w)]} { wm withdraw $hd(w) set hd(showing) 0 } set hd(start_pos) {} } return 0 } # AddrListHandleListUp -- # # Handle cursor keys in text widget. If listbox is active move selection. # Return 1 if the event was handled here # # Arguments: # w - The text widget # key - Key pressed (Up, Down, Return or space) proc AddrListHandleListKey {w key} { upvar \#0 _addrList$w hd # Do not do anything if popup is not shown if {!$hd(showing)} { return 0 } set sel [$hd(list) curselection] if {"space" == $key || "Return" == $key} { AddrListSelect $w } elseif {"Down" == $key} { if {"" == $sel} { set sel 0 } elseif {$sel < [expr [$hd(list) size]-1]} { $hd(list) selection clear $sel incr sel } $hd(list) selection set $sel } elseif {"" != $sel} { $hd(list) selection clear $sel if {0 != $sel} { incr sel -1 $hd(list) selection set $sel } } return 1 } tkrat_2.2cvs20100105-dfsg.orig/tkrat/alias.tcl000066400000000000000000001162441137544547100207360ustar00rootroot00000000000000# alias.tcl -- # # Code which handles aliases. # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn. # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # List of alias windows set aliasWindows {} # True if the address books have been modified set bookMod 0 # Aliases -- # # Display the aliases window # # Arguments: proc Aliases {} { global idCnt t b option aliasWindows # Create identifier set id al[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -class TkRat wm title $w $t(addressbook) set hd(w) $w set hd(old,default_book) $option(default_book) set hd(ignore_changes) 1 # The menus frame $w.mbar -relief raised -bd 1 FindAccelerators a {file edit show addrbooks} set m $w.mbar.file.m menubutton $w.mbar.file -text $t(file) -menu $m -underline $a(file) menu $m $m add command -label $t(new)... -command "AliasNew $id" set hd(new_menu) [list $m [$m index end]] $m add separator $m add command -label $t(reread_addresses) -command AliasRead set b($m,[$m index end]) reread_addresses set hd(read_menu) [list $m [$m index end]] $m add command -label $t(import_addresses) -command ScanAliases set hd(scan_menu) [list $m [$m index end]] set b($m,[$m index end]) import_aliases $m add separator $m add command -label $t(close) -command "AliasClose $id" set hd(close_menu) [list $m [$m index end]] set m $w.mbar.edit.m menubutton $w.mbar.edit -text $t(edit) -menu $m -underline $a(edit) menu $w.mbar.edit.m $m add command -label $t(edit) -command "AliasEdit $id" set hd(edit_menu) [list $m [$m index end]] $m add command -label $t(delete) -command "AliasDelete $id" set hd(delete_menu) [list $m [$m index end]] menubutton $w.mbar.show -text $t(show) -menu $w.mbar.show.m \ -underline $a(show) menu $w.mbar.show.m set hd(showmenu) $w.mbar.show.m set b($w.mbar.show) show_adrbook_menu menubutton $w.mbar.book -text $t(addrbooks) -menu $w.mbar.book.m \ -underline $a(addrbooks) set m $w.mbar.book.m menu $m $m add checkbutton -label $t(use_system_aliases) \ -variable option(use_system_aliases) set b($m,[$m index end]) use_system_aliases $m add separator $m add command -label $t(add)... -command AddrbookAdd set b($m,[$m index end]) add_addrbook $m add command -label $t(delete)... -command AddrbookDelete set b($m,[$m index end]) delete_addrbook $m add cascade -label $t(set_default)... -menu $m.sd set b($m,[$m index end]) set_default_addrbook menu $m.sd set hd(defaultmenu) $m.sd pack $w.mbar.file \ $w.mbar.edit \ $w.mbar.show \ $w.mbar.book -side left -padx 5 RatBindMenus $w $id alias {new_menu read_menu close_menu delete_menu edit_menu} # List of aliases frame $w.l scrollbar $w.l.scroll \ -relief raised \ -bd 1 \ -highlightthickness 0 \ -command "$w.l.list yview" listbox $w.l.list \ -yscroll "$w.l.scroll set" \ -relief raised \ -bd 1 \ -exportselection false \ -highlightthickness 0 \ -selectmode extended \ -setgrid true set hd(listbox) $w.l.list pack $w.l.scroll -side right -fill y pack $w.l.list -expand 1 -fill both set b($w.l.list) alias_list # Buttons frame $w.b button $w.b.new -text $t(new)... -command "AliasNew $id" button $w.b.edit -text $t(edit)... -command "AliasEdit $id" button $w.b.delete -text $t(delete) -command "AliasDelete $id" button $w.b.close -text $t(close) -command "destroy $w" pack $w.b.new $w.b.edit $w.b.delete $w.b.close -side left -padx 20 -pady 5 set hd(edit_button) $w.b.edit set hd(delete_button) $w.b.delete # Pack it all pack $w.mbar -side top -fill x pack $w.b -side bottom -anchor center pack $w.l -expand 1 -fill both # Create the booklist AliasesUpdateBooklist $id # Populate list AliasesPopulate $id # Track list slections bind $hd(listbox) <> "AliasUpdateState $id" bind $hd(listbox) "AliasEdit $id" bind $w "$w.b.close invoke" ::tkrat::winctl::SetGeometry alias $w $hd(listbox) lappend aliasWindows $id bind $hd(listbox) "AliasClose $id" # Set up traces trace variable option(use_system_aliases) w "AliasesUpdateBooklist {}" trace variable option(addrbooks) w "AliasesUpdateBooklist {}" } # AliasUpdateState -- # # Update the enabled/disabled state of edit and delete buttons # # Arguments: # handler - The handler of the alias window proc AliasUpdateState {handler} { upvar \#0 $handler hd global aliasBook # Edit is possible if exactly one element is selected set edit_state disabled if {[llength [$hd(listbox) curselection]] == 1} { set id [lindex $hd(aliasIds) [$hd(listbox) curselection]] set def [RatAlias get $id] if {$aliasBook(writable,[lindex $def 0])} { set edit_state normal } } $hd(edit_button) configure -state $edit_state [lindex $hd(edit_menu) 0] entryconfigure [lindex $hd(edit_menu) 1] \ -state $edit_state # Delete is possible if at least one element is selected if {[llength [$hd(listbox) curselection]] > 0} { set delete_state normal } else { set delete_state disabled } $hd(delete_button) configure -state $delete_state [lindex $hd(delete_menu) 0] entryconfigure [lindex $hd(delete_menu) 1] \ -state $delete_state } # AliasNew -- # # Create a new alias and select it for editing. # # Arguments: # handler - The handler identifying the window proc AliasNew {handler} { upvar \#0 $handler hd global t option aliasBook set result [AliasEditPanel $handler "" \ [list [lindex [lindex $option(addrbooks) 0] 0] \ "" "" {} {} {}]] if {[llength $result] != 7} { return } eval "RatAlias add $result" set aliasBook(changed,[lindex $result 0]) 1 # Find out where it should be inserted set id [lindex $result 1] set hd(aliasIds) [lsort [concat $hd(aliasIds) $id]] set i [lsearch -exact $hd(aliasIds) $id] # Update entry in list of addresses set ne [AliasFormat $id [list \ [lindex $result 0] \ [lindex $result 2] \ [lindex $result 3] \ [lindex $result 4] \ [lindex $result 5] \ [lindex $result 6]]] set old_top \ [expr int([lindex [$hd(listbox) yview] 0] * [llength $hd(aliasIds)])] $hd(listbox) insert $i $ne $hd(listbox) yview $old_top $hd(listbox) see $i } # AliasEdit -- # # Handle editing of aliases # # Arguments: # handler - The handler of the alias window proc AliasEdit {handler} { upvar \#0 $handler hd global aliasBook # Only one element may be selected if {[llength [$hd(listbox) curselection]] != 1} { return } set id [lindex $hd(aliasIds) [$hd(listbox) curselection]] set def [RatAlias get $id] if {!$aliasBook(writable,[lindex $def 0])} { return } set result [AliasEditPanel $handler $id $def] if {[llength $result] != 7} { return } RatAlias delete $id eval "RatAlias add $result" set book_old [lindex $def 0] set book_new [lindex $result 0] if {$book_old != $book_new} { set aliasBook(changed,$book_old) 1 } set aliasBook(changed,$book_new) 1 # Possibly resort list of addresses set id_new [lindex $result 1] set d [lsearch -exact $hd(aliasIds) $id] if {$id != $id_new} { set hd(aliasIds) [lsort [lreplace $hd(aliasIds) $d $d $id_new]] set i [lsearch -exact $hd(aliasIds) $id_new] } else { set i $d } # Update entry in list of addresses set ne [AliasFormat $id_new [list \ [lindex $result 0] \ [lindex $result 2] \ [lindex $result 3] \ [lindex $result 4] \ [lindex $result 5] \ [lindex $result 6]]] set old_top \ [expr int([lindex [$hd(listbox) yview] 0] * [llength $hd(aliasIds)])] $hd(listbox) delete $d $hd(listbox) insert $i $ne $hd(listbox) yview $old_top $hd(listbox) selection set $i $hd(listbox) see $i } # AliasEditPanel -- # # Show alias edit window # # Arguments: # handler - The handler of the alias window # name - Name of address book entry # def - Definition of address book entry proc AliasEditPanel {handler name def} { upvar \#0 $handler hd global t aliasBook idCnt tk_version # Create toplevel set w .al[incr idCnt] toplevel $w -class TkRat -bd 2 -relief flat wm title $w $t(edit_address) # The alias fields label $w.alias_lab -text $t(alias): -anchor e entry $w.alias -textvariable ${handler}(alias) set b($w.alias) alias_alias set hd(alias_focus) $w.alias label $w.fullname_lab -text $t(fullname): -anchor e entry $w.fullname -textvariable ${handler}(fullname) set b($w.fullname) alias_fullname label $w.email_lab -text $t(email_address): -anchor e text $w.email -height 3 set b($w.email) alias_content set hd(email_address_text) $w.email label $w.comment_lab -text $t(comment): -anchor e text $w.comment -height 3 set b($w.comment) alias_comment set hd(comment_text) $w.comment label $w.addrbook_lab -text $t(addressbook): -anchor e set m $w.addrbook.m menubutton $w.addrbook -textvariable ${handler}(addrbook) \ -indicatoron 1 -menu $m -bd 2 -relief raised -anchor w -justify left set b($w.addrbook) alias_adr_book menu $m -postcommand "PopulateAddrbookMenu $m ${handler}(addrbook)" label $w.pgp_actions_lab -text $t(pgp_actions): -anchor e set m $w.pgp_actions.m menubutton $w.pgp_actions -textvariable ${handler}(pgp_actions_t) \ -indicatoron 1 -menu $m -bd 2 -relief raised -anchor w -justify left set b($w.pgp_actions) alias_pgp_actions menu $m foreach v {none sign encrypt sign_encrypt} { $m add command -label $t($v) \ -command "[list set ${handler}(pgp_actions) $v]; \ UpdatePGPState $handler" } label $w.pgp_key_lab -text $t(pgp_key): -anchor e frame $w.pgp_key set m $w.pgp_key.mb.m menubutton $w.pgp_key.mb -textvariable ${handler}(pgp_key_t) \ -indicatoron 1 -menu $m -bd 2 -relief raised -anchor w -justify left set b($w.pgp_key.mb) alias_pgp_key menu $m -postcommand "PopulatePGPKeyMenu $m ${handler}" place $w.pgp_key.mb -relwidth 1.0 set hd(infowidgets) [list $w.alias $w.fullname $w.email \ $w.comment $w.pgp_actions $w.pgp_key.mb] set hd(addrbookwidget) $w.addrbook OkButtons $w $t(ok) $t(cancel) "set ${handler}(done)" set hd(ok_button) $w.buttons.ok grid $w.alias_lab $w.alias -sticky ew -padx 2 -pady 2 grid $w.fullname_lab $w.fullname -sticky ew -padx 2 -pady 2 grid $w.email_lab $w.email -sticky ewn -padx 2 -pady 2 grid rowconfigure $w [expr [lindex [grid size $w] 1] - 1] -weight 1 grid $w.comment_lab $w.comment -sticky ewn -padx 2 -pady 2 grid rowconfigure $w [expr [lindex [grid size $w] 1] - 1] -weight 1 grid $w.addrbook_lab $w.addrbook -sticky ew -padx 2 -pady 2 grid $w.pgp_actions_lab $w.pgp_actions -sticky ew -padx 2 -pady 2 grid $w.pgp_key_lab $w.pgp_key -sticky nsew -padx 2 -pady 2 grid $w.buttons - -sticky ew grid configure $w.email -sticky nsew grid configure $w.comment -sticky nsew grid columnconfigure $w 1 -weight 1 # Initialize values set hd(alias) $name set hd(addrbook) [lindex $def 0] set hd(fullname) [lindex $def 1] $hd(email_address_text) insert end [lindex $def 2] $hd(comment_text) insert end [lindex $def 3] set sign [expr [lsearch -exact [lindex $def 5] pgp_sign] != -1] set encrypt [expr [lsearch -exact [lindex $def 5] pgp_encrypt] != -1] if {$sign & $encrypt} { set hd(pgp_actions) sign_encrypt } elseif {$sign} { set hd(pgp_actions) sign } elseif {$encrypt} { set hd(pgp_actions) encrypt } else { set hd(pgp_actions) none } set hd(pgp_key) {} trace variable hd(pgp_key) w [list UpdatePGPState $handler] set hd(pgp_key) [lindex $def 4] # Handle ok button trace variable hd(alias) w [list AliasWinUpdateOk $handler] bind $hd(email_address_text) [list AliasWinUpdateOk $handler] bind $hd(email_address_text) <> [list AliasWinUpdateOk $handler] bind $hd(email_address_text) <> [list AliasWinUpdateOk $handler] AliasWinUpdateOk $handler # Show and wait for window wm protocol $w WM_DELETE_WINDOW "set ${handler}(done) 0" ::tkrat::winctl::SetGeometry aliasDetail $w after idle $hd(alias_focus) selection range 0 end focus $hd(alias_focus) tkwait variable ${handler}(done) trace vdelete hd(alias) w [list AliasWinUpdateOk $handler] trace vdelete hd(pgp_key) w [list UpdatePGPState $handler] if {$hd(done)} { set flags {} switch $hd(pgp_actions) { sign_encrypt { lappend flags pgp_sign lappend flags pgp_encrypt } sign { lappend flags pgp_sign } encrypt { lappend flags pgp_encrypt } } set content [string trim [$hd(email_address_text) get 1.0 end]] set comment [string trim [$hd(comment_text) get 1.0 end]] set r [list $hd(addrbook) $hd(alias) $hd(fullname) $content $comment \ $hd(pgp_key) $flags] } else { set r {} } destroy $w return $r } # AliasWinUpdateOk -- # # Update state of ok button # # Arguments: # handler - The handler of the alias window # args - Extra trace arguments proc AliasWinUpdateOk {handler args} { upvar \#0 $handler hd global tk_version set content [string trim [$hd(email_address_text) get 1.0 end]] if {"" == $hd(alias) || "" == $content} { $hd(ok_button) configure -state disabled } else { $hd(ok_button) configure -state normal } } # PopulateAddrbookMenu -- # # Populate the address book menu # # Arguments: # m - Menu to populate # var - Variable to store selections in proc PopulateAddrbookMenu {m var} { global aliasBook $m delete 0 end foreach book [array names aliasBook writable,*] { regsub writable, $book {} name $m add command -label $name -command [list set $var $name] if {!$aliasBook(writable,$name)} { $m entryconfigure end -state disabled } } } # PopulatePGPKeyMenu -- # # Populate the pgp key menu. # # Arguments: # m - Menu to populate # handler - The handler of the alias window proc PopulatePGPKeyMenu {m handler} { global t $m delete 0 end foreach k [lindex [RatPGP listkeys] 1] { if {[lindex $k 5]} { set desc "[join [lindex $k 3] {, }]; [lindex $k 2]" set id($desc) [lindex $k 0] } } $m add command -label "- $t(auto) -" \ -command [list set ${handler}(pgp_key) {}] foreach d [lsort [array names id]] { $m add command -label "$d" \ -command [list set ${handler}(pgp_key) [list $id($d) $d]] } FixMenu $m } # AliasesUpdateBooklist -- # # Update the list of known books for an alias window # Arguments: # handler - The handler of the alias window proc AliasesUpdateBooklist {handler args} { global aliasWindows option aliasBook if {"" == $handler} { set hds $aliasWindows } else { set hds $handler } foreach handler $hds { upvar \#0 $handler hd $hd(showmenu) delete 0 end $hd(defaultmenu) delete 0 end foreach a $option(addrbooks) { set book [lindex $a 0] if {![info exists hd(show,$book)]} { set hd(show,$book) 1 } } set sysbook [lindex $option(system_aliases) 0] if {$option(use_system_aliases)} { if {![info exists hd(show,$sysbook)]} { set hd(show,$sysbook) 1 } } else { catch {unset hd(show,$sysbook)} } foreach book [array names aliasBook writable,*] { regsub writable, $book {} name $hd(showmenu) add checkbutton -label $name \ -variable ${handler}(show,$name) \ -command "AliasesPopulate $handler" if {$aliasBook(writable,$name)} { $hd(defaultmenu) add radiobutton -label $name \ -variable option(default_book) -value $name \ -command {set bookMod 1} } } } } # AliasesFormat -- # # Format a single alias for the list # # Arguments: # a - Name of alias # ac - Alias content to format proc AliasFormat {a ac} { return [format "%-13s %-20s %s" $a [lindex $ac 1] [lindex $ac 2]] } # AliasesPopulate -- # # Populate the list of addresses # # Arguments: # handler - The handler of the alias window proc AliasesPopulate {{handler {}}} { global aliasWindows if {"" == $handler} { set hds $aliasWindows } else { set hds $handler } foreach handler $hds { upvar \#0 $handler hd set old [$hd(listbox) curselection] RatAlias list alias set top [lindex [$hd(listbox) yview] 0] $hd(listbox) delete 0 end set hd(aliasIds) {} foreach a [lsort [array names alias]] { set book [lindex $alias($a) 0] if {$hd(show,$book)} { lappend hd(aliasIds) $a $hd(listbox) insert end [AliasFormat $a $alias($a)] } } $hd(listbox) yview moveto $top if {"" != $old} { set i [lsearch -exact $hd(aliasIds) $old] if { -1 != $i } { $hd(listbox) selection set $i $hd(listbox) see $i } } AliasUpdateState $handler } } # UpdatePGPState -- # # Update the pgp state in address view # # Arguments: # handler - The handler of the alias window proc UpdatePGPState {handler args} { upvar \#0 $handler hd global t set hd(pgp_actions_t) $t($hd(pgp_actions)) if {0 < [llength $hd(pgp_key)]} { set hd(pgp_key_t) [lindex $hd(pgp_key) 1] } else { set hd(pgp_key_t) "- $t(auto) -" } } # AliasSave -- # # Save the aliases (if needed) # # Arguments: proc AliasSave {} { global option bookMod aliasBook set books $option(addrbooks) if {$option(use_system_aliases)} { lappend books $option(system_aliases) } foreach book $books { if {$aliasBook(changed,[lindex $book 0])} { RatAlias save [lindex $book 0] [lindex $book 2] } } if {$bookMod} { SaveOptions set bookMod 0 } } # AliasClose -- # # Close an aliases window. # # Arguments: # handler - The handler of the alias window proc AliasClose {handler} { global aliasWindows b upvar \#0 $handler hd bind $hd(listbox) {} set i [lsearch -exact $aliasWindows $handler] if {-1 != $i} { set aliasWindows [lreplace $aliasWindows $i $i] } ::tkrat::winctl::RecordGeometry alias $hd(w) $hd(listbox) foreach bn [array names b $hd(w)*] {unset b($bn)} destroy $hd(w) unset hd AliasSave } # AliasDelete -- # # Deletes aliases from the alias list. # # Arguments: # handler - The handler identifying the window proc AliasDelete {handler} { upvar \#0 $handler hd global aliasBook foreach a [$hd(listbox) curselection] { set alias [lindex $hd(aliasIds) $a] set aliasBook(changed,[lindex [RatAlias get $alias] 0]) 1 RatAlias delete $alias } AliasesPopulate } # AddrbookAdd -- # # Create a new address book # # Arguments: proc AddrbookAdd {} { global idCnt t b # Create identifier set id al[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -bd 5 -class TkRat wm title $w $t(new) set hd(w) $w label $w.n_label -text $t(name): -anchor e entry $w.n_entry -textvariable ${id}(name) set b($w.n_entry) name_of_adrbook grid $w.n_label $w.n_entry -sticky ew label $w.f_label -text $t(filename): -anchor e entry $w.f_entry -textvariable ${id}(file) -width 40 set b($w.f_entry) name_of_adrbook_file grid $w.f_label $w.f_entry -sticky ew button $w.browse -text $t(browse) -command "AddrbookBrowse $id" set b($w.browse) file_browse grid x $w.browse -sticky e grid columnconfigure $w 1 -weight 1 grid rowconfigure $w 1 -weight 1 # Buttons OkButtons $w $t(ok) $t(cancel) "AddrbookAddDone $id" bind $w "" bind $w.n_label "AddrbookAddDone $id 0" grid $w.buttons - -pady 5 -sticky ew ::tkrat::winctl::SetGeometry addrbookAdd $hd(w) update set hd(oldfocus) [focus] focus $w.n_entry } # AddrbookBrowse -- # # Browse for addrbook file # # Arguments: # handler - The handler of the add window proc AddrbookBrowse {handler} { upvar \#0 $handler hd global t option set hd(file) [rat_fbox::run \ -parent $hd(w) \ -title $t(new) \ -initialdir $option(initialdir) \ -ok $t(save) \ -mode save] if {"" != $hd(file) && $option(initialdir) != [file dirname $hd(file)]} { set option(initialdir) [file dirname $hd(file)] SaveOptions } } # AddrbookAddDone # # Called when an address book add window is done # # Arguments # handler - The handler of the add window # action - What to do proc AddrbookAddDone {handler action} { upvar \#0 $handler hd global option t b bookMod aliasBook env if {$action} { if {![string length $hd(name)]} { Popup $t(need_name) $hd(w) return } if {[info exists aliasBook(writable,$hd(name))]} { Popup $t(book_already_exists) $hd(w) return } set dir [file dirname $hd(file)] if {("/" != [string index $hd(file) 0] && "~" != [string index $hd(file) 0]) || ([file exists $hd(file)] && ![file readable $hd(file)]) || [file isdirectory $hd(file)] || (![file exists $hd(file)] && ![file writable $dir])} { Popup "$t(illegal_file_spec): $hd(file)" $hd(w) return } if {([file isfile $hd(file)] && [file writable $hd(file)]) || (![file exists $hd(file)] && [file isdirectory $dir] && [file writable $dir])} { set aliasBook(writable,$hd(name)) 1 } else { set aliasBook(writable,$hd(name)) 0 } set aliasBook(changed,$hd(name)) 0 set bookMod 1 lappend option(addrbooks) [list $hd(name) tkrat $hd(file)] if {[file exists $hd(file)]} { RatAlias read $hd(name) $hd(file) AliasesPopulate } } bind $hd(w).n_label {} ::tkrat::winctl::RecordGeometry addrbookAdd $hd(w) foreach bn [array names b $hd(w)*] {unset b($bn)} catch {focus $hd(oldfocus)} destroy $hd(w) unset hd } # AddrbookDelete -- # # Delete a address book # # Arguments: proc AddrbookDelete {} { global idCnt t b option # Create identifier set id al[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -class TkRat wm title $w $t(delete) set hd(w) $w # List of books frame $w.l scrollbar $w.l.scroll \ -bd 1 \ -highlightthickness 0 \ -command "$w.l.list yview" listbox $w.l.list \ -yscroll "$w.l.scroll set" \ -bd 1 \ -exportselection false \ -highlightthickness 0 \ -selectmode extended set hd(listbox) $w.l.list pack $w.l.scroll -side right -fill y pack $w.l.list -expand 1 -fill both set b($w.l.list) list_of_books_to_delete # Buttons OkButtons $w $t(delete) $t(cancel) "AddrbookDeleteDone $id" # Pack it pack $w.l \ $w.buttons -side top -padx 5 -pady 5 -expand 1 -fill both # Populate list foreach book [lsort $option(addrbooks)] { $hd(listbox) insert end [lindex $book 0] } bind $hd(listbox) "AddrbookDeleteDone $id 0" ::tkrat::winctl::SetGeometry addrBookDelete $w $w.l.list } # AddrbookDeleteDone # # Called when an address book delete window is done # # Arguments # handler - The handler of the delete window # action - What to do proc AddrbookDeleteDone {handler action} { upvar \#0 $handler hd global option aliasBook bookMod t b if {$action} { foreach s [$hd(listbox) curselection] { lappend del [$hd(listbox) get $s] } set keep {} set remove {} foreach book $option(addrbooks) { if {-1 == [lsearch -exact $del [lindex $book 0]]} { lappend keep $book } else { lappend remove [lindex $book 0] } } if {-1 != [lsearch -exact $remove $option(default_book)]} { set newDefault {} foreach book [array names aliasBook writable,*] { if {!$aliasBook($book)} { continue } regsub writable, $book {} name if {-1 == [lsearch -exact $remove $name]} { set newDefault $name break } } if {![string length $newDefault]} { Popup $t(need_writable_book) $hd(w) return } set option(default_book) $newDefault } foreach r $remove { unset aliasBook(writable,$r) unset aliasBook(changed,$r) } set option(addrbooks) $keep RatAlias list alias foreach a [array names alias] { if {-1 != [lsearch -exact $del [lindex $alias($a) 0]]} { RatAlias delete $a } } AliasesPopulate set bookMod 1 } bind $hd(listbox) {} ::tkrat::::winctl::RecordGeometry addrBookDelete $hd(w) $hd(listbox) foreach bn [array names b $hd(w)*] {unset b($bn)} destroy $hd(w) unset hd } # AliasExtract -- # # Extracts aliases from the current message # # Arguments: # handler - The handler of the folder window # msgs - Messages to extract from proc AliasExtract {handler msgs} { global idCnt t b aliasBook option upvar \#0 $handler fh # Get list of known addresses RatAlias list alias foreach a [array names alias] { foreach adr [split [lindex $alias($a) 2]] { set present([string tolower $adr]) 1 } } # Extract the addresses set adrlist {} foreach msg $msgs { foreach a [$msg get from reply_to sender cc bcc to] { if {[$a isMe]} { continue } set good 1 foreach a2 $adrlist { if {![$a compare $a2]} { set good 0 break } } if {[info exists present([string tolower [$a get mail]])]} { set good 0 } if {$good} { lappend adrlist $a } } } # Check that we found something if {![llength $adrlist]} { Popup $t(could_not_find_adr) $fh(toplevel) return } # Create identifier set id al[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -class TkRat wm title $w $t(extract_adr) set hd(w) $w # Create address book menu frame $w.book label $w.book.label -text $t(addrbook): set hd(book) $option(default_book) menubutton $w.book.menu -textvariable ${id}(book) -relief raised \ -menu $w.book.menu.m -width 20 -indicatoron 1 set b($w.book.menu) aliases_adr_book menu $w.book.menu.m foreach book [array names aliasBook writable,*] { if {!$aliasBook($book)} { continue } regsub writable, $book {} name $w.book.menu.m add radiobutton -label $name -value $name \ -variable ${id}(book) } pack $w.book.label \ $w.book.menu -side left pack $w.book -side top -pady 5 # Create frame with aliases to add frame $w.f label $w.f.use -text $t(use) label $w.f.name -text $t(alias) -width 8 label $w.f.fname -text $t(fullname) -width 20 label $w.f.content -text $t(content) -width 35 label $w.f.comment -text $t(comment) -width 30 grid $w.f.use $w.f.name $w.f.fname $w.f.content $w.f.comment -sticky w canvas $w.f.c frame $w.f.c.f set f $w.f.c.f set totlist "" foreach a $adrlist { if {[string length $totlist]} { set totlist "$totlist,\n[string tolower [$a get mail]]" } else { set totlist [string tolower [$a get mail]] } incr idCnt set hd($idCnt,use) 1 set name [string tolower [lindex [$a get name] 0]] if {![string length $name] || [info exists alias($name)]} { set name2 [string tolower [lindex [split [$a get mail] @.] 0]] if {![string length $name]} { set name $name2 } if {[info exist alias($name2)]} { for {set i 2} {[info exists alias($name2)]} {incr i} { set name2 $name$i } } set name $name2 } set alias($name) "" set hd($idCnt,name) $name set hd($idCnt,fname) [$a get name] set hd($idCnt,content) [string tolower [$a get mail]] checkbutton $f.c$idCnt -variable ${id}($idCnt,use) entry $f.en$idCnt -textvariable ${id}($idCnt,name) -width 8 bind $f.en$idCnt {bell; break} entry $f.ef$idCnt -textvariable ${id}($idCnt,fname) -width 20 entry $f.ec$idCnt -textvariable ${id}($idCnt,content) -width 35 entry $f.ek$idCnt -textvariable ${id}($idCnt,comment) -width 30 set b($f.c$idCnt) aliasadd_use set b($f.en$idCnt) alias_alias set b($f.ef$idCnt) alias_fullname set b($f.ec$idCnt) alias_content set b($f.ek$idCnt) alias_comment grid $f.c$idCnt \ $f.en$idCnt \ $f.ef$idCnt \ $f.ec$idCnt \ $f.ek$idCnt -sticky we set idw $idCnt } set num [llength $adrlist] if {$num > 1} { incr idCnt set hd($idCnt,use) 0 set hd($idCnt,content) $totlist checkbutton $f.c$idCnt -variable ${id}($idCnt,use) entry $f.en$idCnt -textvariable ${id}($idCnt,name) -width 8 entry $f.ef$idCnt -textvariable ${id}($idCnt,fname) -width 20 if {$num > 10} { frame $f.ec$idCnt scrollbar $f.ec$idCnt.scroll -relief sunken \ -command "$f.ec$idCnt.text yview" -highlightthickness 0 text $f.ec$idCnt.text -width 35 -height 10 -wrap none \ -yscroll "$f.ec$idCnt.scroll set" pack $f.ec$idCnt.scroll -side right -fill y pack $f.ec$idCnt.text -expand yes -fill both set hd(listcmd) $f.ec$idCnt.text } else { text $f.ec$idCnt -width 35 -height $num -wrap none set hd(listcmd) $f.ec$idCnt } $hd(listcmd) insert 1.0 $totlist set hd(listvar) $idCnt,content entry $f.ek$idCnt -textvariable ${id}($idCnt,comment) -width 30 set b($f.c$idCnt) aliasadd_use set b($f.en$idCnt) alias_alias set b($f.ef$idCnt) alias_fullname set b($f.ec$idCnt) alias_content set b($f.ek$idCnt) alias_comment grid $f.c$idCnt \ $f.en$idCnt \ $f.ef$idCnt \ $f.ec$idCnt \ $f.ek$idCnt -sticky wen } grid columnconfigure $f 1 -weight 1 grid columnconfigure $f 2 -weight 1 grid columnconfigure $f 3 -weight 1 grid columnconfigure $f 4 -weight 1 set wid [$w.f.c create window 0 0 -anchor nw -window $f] update idletasks set bbox [$w.f.c bbox $wid] $w.f.c configure -scrollregion $bbox set height [lindex $bbox 3] set width [lindex $bbox 2] set maxheight [expr {[winfo screenheight $w] - 200}] if {$height > $maxheight} { scrollbar $w.f.s -command "$w.f.c yview" \ -highlightthickness 0 -bd 1 $w.f.c configure -yscrollcommand "$w.f.s set" set height $maxheight set scroll $w.f.s } else { set scroll - } grid $w.f.c - - - - $scroll -sticky ns $w.f.c configure -width $width -height $height pack $w.f -side top -fill both # Create buttons frame $w.buttons button $w.buttons.add -text $t(add_aliases) \ -command "AliasExtractDone $id 1" -default active button $w.buttons.unmark -text $t(clear_selection) \ -command "AliasExtractClearSelection $id" button $w.buttons.cancel -text $t(cancel) \ -command "AliasExtractDone $id 0" pack $w.buttons.add \ $w.buttons.unmark \ $w.buttons.cancel -side left -expand 1 bind $w "AliasExtractDone $id 1" pack $w.buttons -side bottom -pady 5 -fill x bind $w.buttons.add "AliasExtractDone $id 0" ::tkrat::winctl::SetGeometry extractAlias $w } # AliasExtractDone -- # # The alias extract window is now done. # # Arguments: # handler - The handler of the extract window # action - Which action we should take proc AliasExtractDone {handler action} { upvar \#0 $handler hd global t b aliasBook # Find which entries we should use set ids {} foreach i [array names hd *,use] { if {$hd($i)} { lappend ids [lindex [split $i ,] 0] } } if { 1 == $action} { if {[info exists hd(listcmd)]} { set hd($hd(listvar)) [$hd(listcmd) get 1.0 end] } # Add the aliases foreach id $ids { if {![string length $hd($id,name)]} { Popup $t(missing_alias_name) $fh(toplevel) continue } RatAlias add $hd(book) $hd($id,name) $hd($id,fname) \ $hd($id,content) $hd($id,comment) {} set aliasBook(changed,$hd(book)) 1 } if {0 < [llength $ids]} { AliasesPopulate } AliasSave } bind $hd(w).buttons.add {} ::tkrat::winctl::RecordGeometry extractAlias $hd(w) foreach bn [array names b $hd(w)*] {unset b($bn)} destroy $hd(w) unset hd } # AliasExtractClearSelection -- # # Unmarks all aliases in the alias extract list # # Arguments: # handler - The handler of the extract window proc AliasExtractClearSelection {handler} { upvar \#0 $handler hd foreach i [array names hd *,use] { set hd($i) 0 } } # AliasChooser -- # # Pops up a window where the user may select an alias. This alias is # then returned. # # Arguments: # master - The text widget that is to be master for this window proc AliasChooser {master} { global idCnt t b # Create identifier set id al[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -class TkRat wm title $w $t(alias_chooser) wm transient $w [winfo toplevel $master] set hd(w) $w set hd(search) "" # Find coordinates for window set bbox [$master bbox insert] set x [expr {[winfo rootx $master]+[lindex $bbox 0]+5}] set y [expr {[winfo rooty $master]+[lindex $bbox 1]-20}] wm geom $w +$x+$y # Build the list scrollbar $w.scroll \ -relief raised \ -bd 1 \ -highlightthickness 0 \ -command "$w.list yview" listbox $w.list \ -yscroll "$w.scroll set" \ -relief raised \ -bd 1 \ -exportselection false \ -highlightthickness 0 \ -selectmode single set b($w.list) alias_chooser ::tkrat::winctl::Size aliasChooser $w.list pack $w.scroll -side right -fill y pack $w.list -expand 1 -fill both set hd(list) $w.list # Bind keys bind $w "set ${id}(done) 0" bind $w "set ${id}(done) 0" bind $w "set ${id}(done) 1" bind $w "set ${id}(done) 1" bind $w.list "set ${id}(done) 1" bind $w "AliasChooserMoveSel $id up" bind $w "AliasChooserMoveSel $id down" bind $w "AliasChooserSearch $id %A" wm protocol $w WM_DELETE_WINDOW "set ${id}(done) 0" # Populate list RatAlias list alias set hd(aliasIds) [lsort [array names alias]] foreach a $hd(aliasIds) { $hd(list) insert end [format "%-8s %-20s" $a [lindex $alias($a) 1]] } $hd(list) selection set 0 ::tkrat::winctl::ModalGrab $w # Wait for action tkwait variable ${id}(done) ::tkrat::winctl::RecordSize aliasChooser $w.list if {1 == $hd(done)} { set ret [lindex $hd(aliasIds) [$hd(list) curselection]] } else { set ret "" } unset b($w.list) destroy $w unset hd return $ret } # AliasChooserMoveSel -- # # Move the selection in the chooser. # # Arguments: # handler - The handler which defines this selection window # direction - Which direction we should move the selection. proc AliasChooserMoveSel {handler direction} { upvar \#0 $handler hd set cur [$hd(list) curselection] $hd(list) selection clear $cur if {[string compare up $direction]} { if {[incr cur] >= [$hd(list) size]} { incr cur -1 } if {[expr {$cur/double([$hd(list) size])}] >= [lindex [$hd(list) yview] 1]} { $hd(list) yview $cur } } else { if {$cur > 0} { incr cur -1 if {[expr {$cur/double([$hd(list) size])}] < \ [lindex [$hd(list) yview] 0]} { $hd(list) yview scroll -1 pages } } } $hd(list) selection set $cur set hd(search) "" } # AliasChooserSearch -- # # Searches the chooser list. # # Arguments: # handler - The handler which defines this selection window # key - The pressed key proc AliasChooserSearch {handler key} { upvar \#0 $handler hd if {1 != [scan $key "%s" key2]} { return } set hd(search) "$hd(search)$key2" set i [lsearch -glob $hd(aliasIds) "$hd(search)*"] if {-1 == $i} { bell set hd(search) "" } else { $hd(list) selection clear [$hd(list) curselection] $hd(list) selection set $i $hd(list) see $i } } # ElmGets -- # # Fix elm alias file reading to handle multiple line aliases # # Arguments: # fh - File handle # linevar - variable to store line in proc ElmGets {fh linevar} { upvar $linevar line set haveline 0 set line "" while {$haveline <= 0 && -1 != [gets $fh sline]} { set sline [string trim $sline] if {[string match {#*} $sline] || 0==[string length $sline]} { continue } set line "${line}${sline} " if {![string match {?*=?*=?* } $line] || [string match {*, } $line]} { set haveline 0 } else { set haveline 1 } } if {$haveline <= 0} { return $haveline } else { return [string length $line] } } # ReadElmAliases -- # # Read aliases.text files generated by elm # # Arguments: # file - Filename to read aliases from # book - Address book to insert them into proc ReadElmAliases {file book} { set n 0 set fh [open $file r] while { 0 < [ElmGets $fh line]} { if {[string match {*=*=*} $line] && [string length [lindex $line 0]]} { set a [split $line =] RatAlias add $book \ [string trim [lindex $a 0]] \ [string trim [lindex $a 1]] \ [string trim [lindex $a 2]] {} {} incr n } } close $fh return $n } # ReadMailAliases -- # # Get aliases out of mailrc files generated by mail and others # # Arguments: # file - Filename to read aliases from # book - Address book to insert them into proc ReadMailAliases {file book} { set n 0 set fh [open $file r] while { -1 != [gets $fh line]} { while { 1 == [regexp {\\$} $line]} { if {-1 == [gets $fh cont]} { break } set line [join [list [string trimright $line \\] $cont] ""] } if {[string match {alias *} $line]} { if {[regexp "^alias\[ \t\]+(\[a-zA-Z0-9_-\]+)\[ \t\]+(.+)$" $line \ {} name content]} { RatAlias add $book $name $name $content {} {} incr n } } } close $fh return $n } # ReadPineAliases -- # # Read the .addressbook files generated by pine # # Arguments: # file - Filename to read aliases from # book - Address book to insert them into proc ReadPineAliases {file book} { if {[catch {open $file r} fh]} { Popup $fh return 0 } set aliases {} while { -1 != [gets $fh line]} { if {[regsub {^ } $line "" cont]} { set aliases [lreplace $aliases end end \ "[lindex $aliases end] $cont"] } else { lappend aliases $line } } close $fh set n 0 foreach a $aliases { if {[regexp {^#DELETED} $a]} { continue } set sa [split $a "\t"] if {[string length [lindex $sa 0]]} { incr n set content [lindex $sa 2] regexp {^\((.+)\)$} $content notUsed content RatAlias add $book [lindex $sa 0] [lindex $sa 1] $content {} {} } } return $n } # AliasWeAreQuitting -- # # Save aliases if needed since tkrat is quitting # # Arguments: proc AliasWeAreQuitting {} { global aliasWindows if {[llength $aliasWindows]} { AliasSave } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/client.tcl000077500000000000000000000042021137544547100211140ustar00rootroot00000000000000# # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # TkRatClientUsage -- # # Shows the usage message # # Arguments: proc TkRatClientUsage {} { global argv0 puts "Usage: $argv0 \[-confdir dir\] \[-appname name\] \[-open ?name?\] \\" puts "\t\[-opennew ?name?\] \[-compose ?args?\] \[-netsync ?set?\] \\" puts "\t\[mailto-link\] \[-blank\]" exit 0 } # TkRatClientStart -- # # Parses command line arguments and sees if there is an existing tkrat # invocation to use # # Arguments: proc TkRatClientStart {} { global argv option catch {wm withdraw .} set started 0 # Parse arguments set appname tkrat for {set i 0} {$i < [llength $argv]} {incr i} { set in [expr {$i+1}] switch -regexp -- [lindex $argv $i] { -confdir { if {$in == [llength $argv]} { TkRatClientUsage } set option(ratatosk_dir) [lindex $argv $in] incr i } -appname { if {$in == [llength $argv]} { TkRatClientUsage } set appname [lindex $argv $in] incr i } -(open|opennew|compose|netsync|blank) { regexp -- -(open|opennew|compose|netsync|blank) \ [lindex $argv $i] unused c if {$in == [llength $argv] || [regexp ^- [lindex $argv $in]]} { lappend cmds $c } else { lappend cmds [list $c [lindex $argv $in]] incr i } } mailto:.* { lappend cmds [list mailto [lindex $argv $i]] } default { TkRatClientUsage } } } if {![info exists cmds]} { set cmds open } # Check if we have a tkrat running, start it if not set appname $appname-[info host] if {[catch {send -- $appname RatPing}]} { set started 1 tk appname $appname TkRatStart RatExec $cmds } else { # Send commands if {[catch {send -- $appname [list RatExec $cmds]} result]} { puts "Failed to send commands" global errorInfo puts $errorInfo exit puts $result exit } } if {!$started} { destroy . } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/compose.tcl000066400000000000000000003177671137544547100213270ustar00rootroot00000000000000# compose.tcl -- # # This file contains the code which handles the composing of messages # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # List of compsoe windows set composeWindowList {} # List of header names we know of set composeHeaderList {to subject cc bcc reply_to from} # List of headers which are not editable set composeAutoHeaderList {in_reply_to references date} # List of headers which contains addresses (OBS! must be lower case OBS!) set composeAdrHdrList {from to cc bcc reply_to} # Fields allowed in mailto links set mailtoAllowedHdrs {to cc bcc reply_to subject in_reply_to references body} # Address book icon set book_img [image create photo -data { R0lGODdhEgAMAKUAANDU0KisqGBkYHB0cPj4+ICEgIiIiHh8eNDQ0MjMyJCQkIiMiLi4uLC0 sPj8+GBgYOjs6KCkoDg4OHBwcJiYmJCUkMjIyMDEwICAgKioqHh4eFBQUEhMSODg4PD08Jic mKCgoLCwsNjY2FhcWDAwMP////////////////////////////////////////////////// /////////////////////////////////////////////////////////ywAAAAAEgAMAAAG f0CH0FHRjDQQh+bxAA2HA8kTQHqSpENBwUHqOjBcEuRgeA4CnqFnMewUMmaQp0vylLudwyee FtrbenEEaltCeXtDEwwBGRUBHSEfChUUByFPBQYRFwgIBxsJAAgRAgpPFFUeHR2oIggdDgpY QhwgIBkNFhEUCQkNFBSmT8PET0EAOw==}] # ComposeLoad -- # # Dummy proc used to force the loading of this file # # Arguments: proc ComposeLoad {} { } # Compose -- # # Initialize the composing of a new letter # # Arguments: # role - The role to do the composing as proc Compose {role} { global idCnt set handler composeM[incr idCnt] return [DoCompose $handler $role 0 1] } # ComposeReply -- # # Build a reply to the given message and let the user add his text # # Arguments: # msg- Message to reply to # to - Whom the reply should be sent to, 'sender' or 'all'. # role - The role to do the composing as # notify - Command to run when message has been sent proc ComposeReply {msg to role notify} { global t option set msg [ComposeChoose $msg $t(reply_to_which)] if {![string length $msg]} { return 0 } set handler [$msg reply $to $role] upvar \#0 $handler mh set mh(notify) $notify return [DoCompose $handler $role \ [expr {($option(reply_bottom)) ? "1" : "-1"}] \ [expr {($option(append_sig)) ? "1" : "0"}]] } # ComposeForwardInline -- # # Forward a message and keep the first part inline if it is a text part. # # Arguments: # msg - Message to forward # role - The role to do the composing as proc ComposeForwardInline {msg role} { global idCnt option t rat_tmp set handler composeM[incr idCnt] upvar \#0 $handler mh set msg [ComposeChoose $msg $t(forward_which)] if {![string length $msg]} { return 0 } foreach header [$msg headers] { set name [string tolower [lindex $header 0]] switch $name { subject { set mh(subject) "Fwd: [lindex $header 1]" } content_description { set hd(content_description) [lindex $header 1] } } if { -1 != [lsearch [string tolower $option(show_header_selection)] \ $name]} { set name [string map {- _} $name] set inline_header($name) [lindex $header 1] } } # Find if there is a bodypart which we can inline (a text part) set body [$msg body] set type [string tolower [$body type]] set inline {} set attach $body if { (![string compare text [lindex $type 0]] && ![string compare plain [lindex $type 1]]) || (![string compare message [lindex $type 0]] && ![string compare rfc822 [lindex $type 1]])} { set inline $body set attach {} } elseif {![string compare multipart [lindex $type 0]]} { set children [$body children] if {0 < [llength $children]} { set type [string tolower [[lindex $children 0] type]] if {![string compare text [lindex $type 0]] && ![string compare plain [lindex $type 1]]} { set inline [lindex $children 0] set attach [lrange $children 1 end] } else { set inline {} set attach $children } } } # Now we are ready to start constructing the new message set bhandler composeM[incr idCnt] upvar \#0 $bhandler bh set mh(body) $bhandler set bh(type) text set bh(subtype) plain if {[string length $inline]} { set bh(encoding) [$inline encoding] set bh(parameter) [$inline params] set bh(content_id) [$inline id] set bh(content_description) [$inline description] set preface "\n\n$option(forwarded_message)\n" set length 5 foreach f $option(show_header_selection) { if { $length < [string length $f]} { set length [string length $f] } } foreach field $option(show_header_selection) { set f [string map {- _} [string tolower $field]] if {[info exists inline_header($f)]} { if {[info exists t($f)]} { set name $t($f) } else { set name $field } set preface [format "%s%${length}s: %s\n" $preface $name \ $inline_header($f)] } } set mh(data) "${preface}\n[$inline data 0]" set mh(data_tags) "Cited noWrap no_spell" } foreach child $attach { set chandler composeC[incr idCnt] upvar \#0 $chandler ch lappend bh(children) $chandler set type [string tolower [$child type]] set ch(type) [lindex $type 0] set ch(subtype) [lindex $type 1] set ch(encoding) [$child encoding] set ch(parameter) [$child params] set ch(disp_type) [$child disp_type] set ch(disp_parm) [$child disp_parm] set ch(content_id) [$child id] set ch(content_description) [$child description] if {![info exists mh(data)] && ![string compare text $ch(type)]} { set mh(data) [$child data 0] set mh(data_tags) "Cited noWrap no_spell" } else { set ch(filename) $rat_tmp/rat.[RatGenId] set fh [open $ch(filename) w] $child saveData $fh 1 0 close $fh set ch(removeFile) 1 lappend mh(attachmentList) $chandler set ch(size) [file size $ch(filename)] } } return [DoCompose $handler $role 0 0] } # ComposeForwardAttachment -- # # Forward the given message. The given message is included as an # attachment of type message/rfc822 # # Arguments: # msg - Message to reply to # role - The role to do the composing as proc ComposeForwardAttachment {msg role} { global option t idCnt rat_tmp set msg [ComposeChoose $msg $t(forward_which)] if {![string length $msg]} { return 0 } set handler composeM[incr idCnt] upvar \#0 $handler mh # # Attach old message # set id compose[incr idCnt] upvar \#0 $id hd set hd(content_description) $t(forwarded_message) foreach header [$msg headers] { switch [string tolower [lindex $header 0]] { subject { set mh(subject) "Fwd: [lindex $header 1]" } content_description { set hd(content_description) [lindex $header 1] } } } set hd(filename) $rat_tmp/rat.[RatGenId] set fh [open $hd(filename) w] fconfigure $fh -translation binary set raw [string map {"\r\n" "\n"} [$msg rawText]] puts -nonewline $fh $raw close $fh set hd(type) message set hd(subtype) rfc822 set hd(removeFile) 1 set hd(size) [file size $hd(filename)] set mh(attachmentList) $id return [DoCompose $handler $role 0 1] } # ComposeBounce -- # # Bounce a message, i.e. remail it # # Arguments: # msg - Message to bounce # role - The role to do the composing as proc ComposeBounce {msg role} { global t idCnt set msg [ComposeChoose $msg $t(bounce_which)] if {"" == $msg} { return } set handler composeM[incr idCnt] upvar \#0 $handler mh set mh(msgs) $msg return [DoBounce $handler $role] } # ComposeContinue -- # # Continue to compose a message # # Arguments: # msg - Message to continue composing proc ComposeContinue {msg} { global idCnt option t rat_tmp set handler composeM[incr idCnt] upvar \#0 $handler mh set role $option(default_role) set mh(data_tags) "" set mh(other_tags) "" set replacements {} foreach header [$msg headers] { set name [string tolower [lindex $header 0]] if {[regexp -nocase {x-tkrat-original-([a-z]+)} $name unused f]} { lappend replacements [list $f [lindex $header 1]] continue } # Ignore address headers if {-1 != [lsearch -exact {to from cc bcc} $name]} { continue } switch $name { x-tkrat-internal-tags { set mh(other_tags) [lindex $header 1] } x-tkrat-internal-role { set role [lindex $header 1] } x-tkrat-internal-pgpactions { set a [lindex $header 1] set mh(pgp_sign) [lindex $a 0] set mh(pgp_encrypt) [lindex $a 1] set mh(pgp_sign_explicit) 1 } x-tkrat-internal-bcc { set mh(bcc) [lindex $header 1] } default { set mh($name) [lindex $header 1] } } } foreach r $replacements { set mh([lindex $r 0]) [lindex $r 1] } # Find if there is a bodypart which we can inline (a text part) set body [$msg body] set type [string tolower [$body type]] set inline {} set attach $body if { ("text" == [lindex $type 0] && "plain" == [lindex $type 1]) || ("message" == [lindex $type 0] && "rfc822" == [lindex $type 1])} { set inline $body set attach {} } elseif {"multipart" == [lindex $type 0]} { set children [$body children] if {0 < [llength $children]} { set type [string tolower [[lindex $children 0] type]] if {"text" == [lindex $type 0] && "plain" == [lindex $type 1]} { set inline [lindex $children 0] set attach [lrange $children 1 end] } else { set inline {} set attach $children } } } # Now we are ready to start constructing the new message set bhandler composeM[incr idCnt] upvar \#0 $bhandler bh set mh(body) $bhandler set body [$msg body] set type [string tolower [$body type]] set bh(type) [lindex $type 0] set bh(subtype) [lindex $type 1] if {"multipart" == $bh(type) && 0 < [llength [$body children]]} { set mh(data) [[lindex [$body children] 0] data 0] foreach child [lrange [$body children] 1 end] { set chandler composeC[incr idCnt] upvar \#0 $chandler ch lappend bh(children) $chandler set type [string tolower [$child type]] set ch(type) [lindex $type 0] set ch(subtype) [lindex $type 1] set ch(encoding) [$child encoding] set ch(parameter) [$child params] set ch(disp_type) [$child disp_type] set ch(disp_parm) [$child disp_parm] set ch(content_id) [$child id] set ch(content_description) [$child description] if {![info exists mh(data)] && ![string compare text $ch(type)]} { set mh(data) [$child data 0] } else { set ch(filename) $rat_tmp/rat.[RatGenId] set fh [open $ch(filename) w] $child saveData $fh 1 0 close $fh set ch(removeFile) 1 set ch(size) [file size $ch(filename)] lappend mh(attachmentList) $chandler } } } else { set mh(data) [$body data 0] } return [DoCompose $handler $role 0 0] } # ComposeClient -- # # Executes the compose command from the client # # Arguments: # hl - List of presupplied header values proc ComposeClient {hl} { global idCnt option set handler clientM[incr idCnt] upvar \#0 $handler mh if {[llength $hl]} { array set mh $hl } return [DoCompose $handler $option(default_role) 0 1] } # MailtoClient -- # # Executes the mailto command from the client # # Arguments: # m - mailto link proc MailtoClient {mailto} { global idCnt option t composeAdrHdrList mailtoAllowedHdrs regsub "^mailto:" $mailto "" mailto set handler clientM[incr idCnt] upvar \#0 $handler mh set s [split $mailto "?"] if {2 < [llength $s]} { Popup "$t(bad_mailto_url): $mailto" return } # The initial implied to field if {"" != [lindex $s 0]} { set mh(to) [RatDecodeUrlc [lindex $s 0] 1] } # The rest of the fields foreach f [split [lindex $s 1] "&"] { set l [split $f "="] if {2 != [llength $l]} { Popup "$t(bad_mailto_url): $mailto" return } regsub -all -- {-} [string tolower [lindex $l 0]] "_" field set addr [expr -1 == [lsearch -exact $composeAdrHdrList $field]] if {-1 == [lsearch -exact $mailtoAllowedHdrs $field]} { Popup "$t(bad_mailto_url): $mailto" return } if {"body" == $field} { set field data set mh(data_tags) {} } set mh($field) [RatDecodeUrlc [lindex $l 1] $addr] } return [DoCompose $handler $option(default_role) 0 1] } # ForwardGroupSeparately -- # # Forwards a group of messages as separate emails # # Arguments: # msgs - List of messages to forward # role - The role to do the composing as proc ForwardGroupSeparately {msgs role} { global t idCnt if {0 == [llength $msgs]} { return 0 } set handler composeM[incr idCnt] upvar \#0 $handler mh set mh(subject) $t(forwarded_message) set mh(special) forward_group set mh(msgs) $msgs return [DoCompose $handler $role 0 1] } # ForwardGroupInOne -- # # Forwards a group of messages as attachments in one email # # Arguments: # msgs - List of messages to forward # role - The role to do the composing as proc ForwardGroupInOne {msgs role} { global t idCnt rat_tmp if {0 == [llength $msgs]} { return 0 } set handler composeM[incr idCnt] upvar \#0 $handler mh set mh(subject) $t(forwarded_messages) # # Attach messages # foreach msg $msgs { set id compose[incr idCnt] upvar \#0 $id hd set hd(content_description) $t(forwarded_message) foreach header [$msg headers] { switch [string tolower [lindex $header 0]] { content_description { set hd(content_description) [lindex $header 1] } } } set hd(filename) $rat_tmp/rat.[RatGenId] set fh [open $hd(filename) w] fconfigure $fh -translation binary set raw [string map {"\r\n" "\n"} [$msg rawText]] puts -nonewline $fh $raw close $fh set hd(type) message set hd(subtype) rfc822 set hd(removeFile) 1 set hd(size) [file size $hd(filename)] lappend mh(attachmentList) $id } return [DoCompose $handler $role 0 1] } # BounceMessages -- # # Bounces all the specified messages # # Arguments: # msgs - List of messages to forward # role - The role to do the composing as proc BounceMessages {msgs role} { global t idCnt if {0 == [llength $msgs]} { return 0 } set handler composeM[incr idCnt] upvar \#0 $handler mh set mh(special) bounce_group set mh(msgs) $msgs return [DoBounce $handler $role] } # ComposeInit -- # # Initialize a number of compose variables # # Arguments: # handler - The handler for the active compose session # role - The role to do the composing as proc ComposeInit {handler role} { global composeHeaderList option upvar \#0 $handler mh foreach i $composeHeaderList { set mh(O_$i) 0 } foreach adr {from reply_to to cc bcc} { if {![info exists mh($adr)]} { set mh($adr) {} } } foreach f {from reply_to bcc role} { set mh(orig,$f) "" } set mh(role) $role set mh(save_to) "" set mh(closing) 0 set mh(role_sig) 0 if {![info exists mh(pgp_sign)]} { set mh(pgp_sign) $option($role,sign_outgoing) set mh(pgp_sign_explicit) 0 set mh(pgp_encrypt) $option(pgp_encrypt) } set mh(pgp_signer) $option($mh(role),sign_as) set mh(final_backup_done) 0 if {![info exists mh(special)]} { set mh(special) none } if {![info exists mh(charset)]} { set mh(charset) auto } } # DoCompose -- # # Actually do the composition. This involves building a window in which # the user may do a lot of things. # # Arguments: # handler - The handler for the active compose session # role - The role to do the composing as # edit_text - '1' if we should place the cursor in the text field. # '-1' if we should place the cursor at the top of the text field # add_sig - '1' if we should add the signature proc DoCompose {handler role edit_text add_sig} { global option t b composeHeaderList composeWindowList defaultFontWidth \ tk_strictMotif env charsetName editors fixedItalicFont upvar \#0 $handler mh # Initialize variables ComposeInit $handler $role if {![info exists editors]} { EditorsRead } set vars [string map {- _} [string tolower $option(compose_headers)]] foreach i $vars { set mh(O_$i) 1 } set mh(send_handler) ComposeSend set mh(window_id) compose set mh(redo) 0 set mh(do_wrap) $option(do_wrap) set mh(eeditor) $option(eeditor) set mh(mark_nowrap) $option(mark_nowrap) set mh(autospell) $option(autospell) set mh(dict) $option(def_spell_dict) # Create window set w .$handler set mh(toplevel) $w set mh(title) $t(compose_name) toplevel $w -class TkRat wm iconname $w $t(compose_name) # Menus FindAccelerators a {file edit role headers extra admin} frame $w.menu -relief raised -bd 1 set m $w.menu.file.m menubutton $w.menu.file -menu $m -text $t(file) -underline $a(file) menu $m -tearoff 1 $m add command -label $t(insert_file)... \ -command "ComposeInsertFile $handler" set b($m,[$m index end]) compose_insert_file $m add separator $m add command -label $t(store_snapshot) \ -command "ComposeStoreSnapshot $handler" $m add separator $m add command -label $t(abort) \ -command "DoComposeCleanup $w $handler backup" set b($m,[$m index end]) abort_compose set mh(abort_menu) [list $m [$m index end]] lappend mh(eEditBlock) $w.menu.file set m $w.menu.edit.m menubutton $w.menu.edit -menu $m -text $t(edit) -underline $a(edit) menu $m -postcommand "ComposePostEdit $handler $m" -tearoff 1 $m add command -label $t(undo) \ -command "event generate $w.body.text <>" set b($m,[$m index end]) undo set mh(undo_menu) [list $m [$m index end]] $m add command -label $t(redo) \ -command "event generate $w.body.text <>" set b($m,[$m index end]) redo set mh(redo_menu) [list $m [$m index end]] $m add separator $m add command -label $t(cut) \ -command "event generate $w.body.text <>" set b($m,[$m index end]) cut set mh(cut_menu) [list $m [$m index end]] $m add command -label $t(copy) \ -command "event generate $w.body.text <>" set b($m,[$m index end]) copy set mh(copy_menu) [list $m [$m index end]] $m add command -label $t(paste) \ -command "event generate $w.body.text <>" set b($m,[$m index end]) paste set mh(paste_menu) [list $m [$m index end]] $m add command -label $t(cut_all) \ -command "event generate $w.body.text <>" set b($m,[$m index end]) cut_all set mh(cut_all_menu) [list $m [$m index end]] $m add separator $m add checkbutton -label $t(show_addr_history) \ -variable option(show_autocomplete) -command SaveOptions set b($m,[$m index end]) show_addr_history $m add separator $m add checkbutton -label $t(automatic_wrap) \ -variable ${handler}(do_wrap) \ -command [list ComposeSetWrap $handler] set b($m,[$m index end]) automatic_wrap $m add command -label $t(wrap_paragraph) \ -command "event generate $w.body.text <>" set b($m,[$m index end]) wrap_paragraph set mh(wrap_menu) [list $m [$m index end]] $m add command -label $t(do_wrap_cited) \ -command "ComposeWrapCited $handler" set b($m,[$m index end]) do_wrap_cited $m add checkbutton -label $t(underline_nonwrap) \ -variable ${handler}(mark_nowrap) \ -command "ComposeSetMarkWrap $handler" set b($m,[$m index end]) mark_nowrap $m add separator $m add command -label $t(check_spelling)... \ -command "rat_ispell::CheckTextWidget $w.body.text" set b($m,[$m index end]) do_check_spelling $m add checkbutton -label $t(mark_misspellings) \ -variable ${handler}(autospell) -onvalue 1 -offvalue 0 \ -command "MarkMisspellings $handler" set mh(autospell_menu) [list $m [$m index end]] $m add cascade -label $t(language) -menu $m.lang menu $m.lang -postcommand "BuildComposeLang $handler $m.lang" set mh(language_menu) [list $m [$m index end]] $m add separator $m add command -label $t(run_through_command)... \ -command "ComposeSpecifyCmd $handler" set b($m,[$m index end]) run_through_command set mh(edit_end) [$m index end] set m $w.menu.role.m menubutton $w.menu.role -menu $m -text $t(role) -underline $a(role) menu $m -postcommand \ [list PostRoles $handler $m [list UpdateComposeRole $handler]] menubutton $w.menu.headers -menu $w.menu.headers.m -text $t(headers) \ -underline $a(headers) set b($w.menu.headers) headers_menu menu $w.menu.headers.m -tearoff 1 foreach header $composeHeaderList { $w.menu.headers.m add checkbutton -label $t($header) \ -variable ${handler}(O_$header) \ -onvalue 1 -offvalue 0 \ -command "ComposeBuildHeaderEntries $handler" } set m $w.menu.extra.m menubutton $w.menu.extra -menu $m -text $t(extra) -underline $a(extra) menu $m -tearoff 1 if {0 < $option(pgp_version)} { $m add checkbutton \ -label $t(sign) \ -variable ${handler}(pgp_sign) \ -onvalue 1 -offvalue 0 set b($m,[$m index end]) pgp_sign $m add checkbutton \ -label $t(encrypt) \ -variable ${handler}(pgp_encrypt) \ -onvalue 1 -offvalue 0 set b($m,[$m index end]) pgp_encrypt $m add command -label "$t(pgp_details)..." \ -command "PGPDetails $handler" } $m add cascade -label $t(charset) -menu $m.cm set b($m,[$m index end]) use_charset menu $m.cm $m.cm add radiobutton -label $t(auto) \ -variable ${handler}(charset) -value auto foreach c $option(charsets) { if {0 < [string length $charsetName($c)]} { set name "$c ($charsetName($c))" } else { set name $c } $m.cm add radiobutton -label $name -variable ${handler}(charset) \ -value $c } set m $w.menu.admin.m menubutton $w.menu.admin -menu $m -text $t(admin) \ -underline $a(admin) menu $m -tearoff 1 $m add command -label $t(define_keys)... -command {KeyDef compose} set b($m,[$m index end]) define_keys $m add command -label $t(editors)... -command EditorsList set b($m,[$m index end]) editors $m add command -label $t(command_list)... -command CmdList set b($m,[$m index end]) command_list $m add command -label $t(show_generated)... \ -command "ShowGeneratedHeaders $handler" set b($m,[$m index end]) show_generated_headers pack $w.menu.file \ $w.menu.edit \ $w.menu.role \ $w.menu.headers \ $w.menu.extra \ $w.menu.admin -side left -padx 5 # Header fields set mh(headerFrame) $w.h frame $mh(headerFrame) # Message body frame $w.body scrollbar $w.body.scroll -relief sunken -bd 1 -takefocus 0 \ -command "$w.body.text yview" -highlightthickness 0 text $w.body.text -relief sunken -bd 1 -setgrid true \ -yscrollcommand "$w.body.scroll set" -wrap none pack $w.body.scroll -side right -fill y pack $w.body.text -side left -expand yes -fill both set mh(composeBody) $w.body.text if {[info exists mh(data)]} { $w.body.text insert end $mh(data) $mh(data_tags) if {[info exists mh(other_tags)]} { foreach ot $mh(other_tags) { if {0 < [llength [lindex $ot 1]]} { eval "$mh(composeBody) tag add [lindex $ot 0] \ [lindex $ot 1]" } } } if { 1 == $edit_text } { $w.body.text mark set insert end } elseif { -1 == $edit_text } { $w.body.text insert 1.0 "\n\n" noWrap $w.body.text mark set insert 1.0 } else { $w.body.text mark set insert 1.0 } } else { $w.body.text mark set insert 1.0 } if { 1 == $add_sig} { set pos [$w.body.text index insert] if {1 == [llength [info commands RatUP_Signature]]} { if {[catch {RatUP_Signature $handler} sigtext]} { Popup "$t(sig_cmd_failed): $sigtext" $w unset sigtext } } elseif {![file isdirectory $option($role,signature)] && [file readable $option($role,signature)]} { set fh [open $option($role,signature) r] set sigtext [read -nonewline $fh] close $fh set mh(role_sig) 1 } if {[info exists sigtext]} { if {$option(sigdelimit)} { $w.body.text insert end "\n" {} "-- " {noWrap no_spell} } $w.body.text insert end "\n$sigtext" {noWrap no_spell sig} } $w.body.text mark set insert $pos } $w.body.text see insert rat_edit::create $w.body.text # Calculate font width if {![info exists defaultFontWidth]} { CalculateFontWidth $w.body.text } # Attachments window frame $w.attach frame $w.attach.b label $w.attach.b.label -text $t(attachments) button $w.attach.b.attachf -text $t(attach_file) -command "Attach $handler" set b($w.attach.b.attachf) attach_file menubutton $w.attach.b.attachs -text $t(attach_special) -indicatoron 1 \ -menu $w.attach.b.attachs.m -relief raised set b($w.attach.b.attachs) attach_special set m $w.attach.b.attachs.m menu $m $m add command -label $t(attach_pgp_keys)... -command "AttachKeys $handler" set b($m,[$m index end]) attach_keys if { 0 == $option(pgp_version)} { $m entryconfigure [$m index end] -state disabled } button $w.attach.b.detach -text $t(detach) -state disabled \ -command "Detach $handler $w.attach.b.detach" set b($w.attach.b.detach) detach pack $w.attach.b.label -side left -padx 10 pack $w.attach.b.attachf \ $w.attach.b.attachs \ $w.attach.b.detach -side left -padx 5 frame $w.attach.list scrollbar $w.attach.list.scroll -relief sunken -takefocus 0 \ -command "$w.attach.list.list yview" -highlightthickness 0 listbox $w.attach.list.list -yscroll "$w.attach.list.scroll set" \ -height 3 -relief sunken -bd 1 \ -exportselection false -highlightthickness 0 -selectmode extended set b($w.attach.list.list) attachments bind $w.attach.list.list \ "CheckDeatchStatus $handler $w.attach.b.detach" bind $w.attach.list.list \ "CheckDeatchStatus $handler $w.attach.b.detach" pack $w.attach.list.scroll -side right -fill y pack $w.attach.list.list -side left -expand 1 -fill both pack $w.attach.b \ $w.attach.list -side top -fill x set mh(attachmentListWindow) $w.attach.list.list if {"forward_group" == $mh(special)} { $mh(attachmentListWindow) insert end \ "-- $t(forwarded_msg_goes_here) --" } if {![info exists mh(attachmentList)]} { set mh(attachmentList) {} } else { foreach attachment $mh(attachmentList) { upvar \#0 $attachment bp if { [info exists bp(content_description)] && "" != $bp(content_description) } { set desc $bp(content_description) } else { set p(NAME) {} set p(FILENAME) {} foreach pp [concat $bp(parameter) $bp(disp_parm)] { array set p $pp } if {[string length $p(NAME)]} { set desc $p(NAME) } elseif {[string length $p(FILENAME)]} { set desc $p(FILENAME) } else { set desc "$bp(filename)" } } if {[info exists bp(size)]} { set size " ([RatMangleNumber $bp(size)])" } else { set size "" } $mh(attachmentListWindow) insert end \ "$bp(type)/$bp(subtype) : $desc$size" } } # Buttons frame $w.buttons button $w.buttons.send -text $t(send) -command "ComposeSend $w $handler" set b($w.buttons.send) send lappend mh(eEditBlock) $w.buttons.send menubutton $w.buttons.sendsave -text $t(send_save) -indicatoron 1 \ -menu $w.buttons.sendsave.m -relief raised -underline 0 set b($w.buttons.sendsave) sendsave menu $w.buttons.sendsave.m -tearoff 0 -postcommand \ "RatSendSavePostMenu $w $w.buttons.sendsave.m $handler" lappend mh(eEditBlock) $w.buttons.sendsave button $w.buttons.hold -text $t(postpone) \ -command "ComposeHold $w $handler" set b($w.buttons.hold) postpone lappend mh(eEditBlock) $w.buttons.hold menubutton $w.buttons.edit -indicatoron 1 -menu $w.buttons.edit.m \ -relief raised -direction flush -textvariable ${handler}(eeditor) set mh(eeditb) $w.buttons.edit menu $w.buttons.edit.m -tearoff 0 set mh(eeditm) $w.buttons.edit.m ComposeEEditorPopulate $handler trace variable editors w "ComposeEEditorPopulate $handler" set b($w.buttons.edit) eedit lappend mh(eEditBlock) $w.buttons.abort button $w.buttons.abort -text $t(abort) -command \ "DoComposeCleanup $w $handler backup" set b($w.buttons.abort) abort_compose lappend mh(eEditBlock) $w.buttons.edit pack $w.buttons.send \ $w.buttons.sendsave \ $w.buttons.hold -side left -padx 5 pack $w.buttons.abort -side right -padx 5 pack $w.buttons.edit -expand 1 -padx 5 lappend mh(sendButtons) $w.buttons.send lappend mh(sendButtons) $w.buttons.sendsave # Populate headerlist and pack everything set first [ComposeBuildHeaderEntries $handler] pack $w.menu -side top -fill x pack $mh(headerFrame) -side top -fill x -padx 5 -pady 5 pack $w.buttons -side bottom -fill x pack $w.attach -side bottom -fill x -padx 5 -pady 5 pack $w.body -expand yes -fill both set mh(oldfocus) [focus] if { 1 == $edit_text || -1 == $edit_text } { focus $w.body.text } elseif {[string length $first]} { focus $first } ::tkrat::winctl::SetGeometry compose $w $w.body.text lappend composeWindowList $handler ComposeBind $handler wm protocol $w WM_DELETE_WINDOW "DoComposeCleanup $w $handler backup" if { 1 == $option(always_editor)} { if {0 == [llength $editors]} { Popup $t(always_eeditor_but_none) $mh(toplevel) } else { ComposeEEdit $handler [lindex $editors 0] } } UpdateComposeRole $handler MarkMisspellings $handler if {$option(compose_backup) > 0} { set mh(next_backup) [after [expr $option(compose_backup)*1000] \ [list ComposeStoreBackup $handler 1]] } return $handler } proc DoComposeCleanup {w handler backup} { global composeWindowList b editors folderWindowList upvar \#0 $handler mh # Are we already doing this? if {0 == [info exists mh(closing)] || 1 == $mh(closing)} { return } set mh(closing) 1 if {$backup != "noback"} { ComposeDoFinalBackup $handler } if {[info exists mh(body)]} { ComposeFreeBody $mh(body) } foreach a $mh(attachmentList) { upvar \#0 $a ah if {[info exists ah]} { if {[info exists ah(removeFile)] && $ah(removeFile)} { catch {file delete -- $ah(filename)} } unset ah } } set index [lsearch $composeWindowList $handler] set composeWindowList [lreplace $composeWindowList $index $index] if {[winfo exists $w]} { if {$mh(window_id) == "compose"} { if {[info exists mh(composeBody)]} { ::tkrat::winctl::RecordGeometry compose $w $mh(composeBody) } else { ::tkrat::winctl::RecordGeometry compose $w } } elseif {$mh(window_id) == "bounce"} { ::tkrat::winctl::RecordGeometry bounce $w } foreach bn [array names b $w.*] {unset b($bn)} destroy $w if {![array size folderWindowList]} { destroy . } catch "focus -force $mh(oldfocus)" } trace vdelete editors w "ComposeEEditorPopulate $handler" unset mh } # MarkMisspellings -- # # Enable/disable marking of misspelled words # # Aruments: # handler - The handler which identifies the context proc MarkMisspellings {handler} { upvar \#0 $handler mh global option set option(autospell) $mh(autospell) SaveOptions if {$mh(autospell)} { rat_textspell::init $mh(composeBody) $mh(dict) set lang_state normal } else { rat_textspell::uninit $mh(composeBody) set lang_state disabled } [lindex $mh(language_menu) 0] entryconfigure \ [lindex $mh(language_menu) 1] -state $lang_state } # BuildComposeLang -- # # Build the compose language menu # # Arguments: # handler - The handler which identifies the context # m - Nam eof the menu to build proc BuildComposeLang {handler m} { upvar \#0 $handler mh global t $m configure -postcommand "" foreach l [concat auto [rat_textspell::get_dicts]] { if {"auto" == $l} { set label $t(auto) } else { set label [string totitle $l] } $m add radiobutton -label $label -variable ${handler}(dict) -value $l \ -command [list rat_textspell::set_dict $mh(composeBody) $l] } FixMenu $m } # CheckDetachStatus -- # # Update status of detach button # # Arguments: # handler - The handler which identifies the context # detach - The detach button proc CheckDeatchStatus {handler detach} { upvar \#0 $handler mh set state disabled if {0 < [llength [$mh(attachmentListWindow) curselection]]} { set state normal } if {"forward_group" == $mh(special) && 1 == [$mh(attachmentListWindow) selection includes 0]} { set state disabled } $detach configure -state $state } # ComposeBind -- # # Bind keyboard shortcuts for the compose window # # Arguments: # handler - The handler which identifies the context proc ComposeBind {handler} { upvar \#0 $handler mh set wins $mh(toplevel) if {[info exists mh(composeBody)]} { lappend wins $mh(composeBody) RatBindMenu $mh(composeBody) compose_key_cut $mh(cut_menu) RatBindMenu $mh(composeBody) compose_key_copy $mh(copy_menu) RatBindMenu $mh(composeBody) compose_key_wrap $mh(wrap_menu) RatBindMenu $mh(composeBody) compose_key_cut_all $mh(cut_all_menu) RatBindMenu $mh(composeBody) compose_key_paste $mh(paste_menu) RatBindMenu $mh(composeBody) compose_key_undo $mh(undo_menu) RatBindMenu $mh(composeBody) compose_key_redo $mh(redo_menu) } foreach w $wins { RatBindMenu $w compose_key_abort $mh(abort_menu) RatBind $w compose_key_send \ "ComposeSend $mh(toplevel) $handler; break" RatBind $w compose_key_editor \ "ComposeEEdit $handler \[lindex \$editors 0\]" } } # DoBounce -- # # Actually do the bouncing. This involves building a window in which # the user may specify recipients # # Arguments: # handler - The handler for the active compose session # role - The role to do the composing as proc DoBounce {handler role} { global option t b composeHeaderList composeWindowList defaultFontWidth \ tk_strictMotif env charsetName editors fixedItalicFont upvar \#0 $handler mh # Initialize variables ComposeInit $handler $role foreach h {{to 1} {cc 1} {bcc 0}} { lappend headerList [lindex $h 0] set mh(O_[lindex $h 0]) [lindex $h 1] } set mh(send_handler) ComposeBounceSend set mh(window_id) bounce set mh(attachmentList) {} # Create window set w .$handler set mh(toplevel) $w set mh(title) $t(bounce_name) toplevel $w -class TkRat wm iconname $w $t(bounce_name) # Menus FindAccelerators a {file role headers} frame $w.menu -relief raised -bd 1 set m $w.menu.file.m menubutton $w.menu.file -menu $m -text $t(file) -underline $a(file) menu $m -tearoff 1 $m add command -label $t(abort) \ -command "DoComposeCleanup $w $handler noback" set b($m,[$m index end]) abort_compose set mh(abort_menu) [list $m [$m index end]] set m $w.menu.role.m menubutton $w.menu.role -menu $m -text $t(role) -underline $a(role) menu $m -postcommand \ [list PostRoles $handler $m [list UpdateComposeRole $handler]] menubutton $w.menu.headers -menu $w.menu.headers.m -text $t(headers) \ -underline $a(headers) set b($w.menu.headers) headers_menu menu $w.menu.headers.m -tearoff 1 foreach header $headerList { $w.menu.headers.m add checkbutton -label $t($header) \ -variable ${handler}(O_$header) \ -onvalue 1 -offvalue 0 \ -command "ComposeBuildHeaderEntries $handler" } pack $w.menu.file \ $w.menu.role \ $w.menu.headers -side left -padx 5 # Header fields set mh(headerFrame) $w.h frame $mh(headerFrame) # Buttons frame $w.buttons button $w.buttons.send -text $t(send) \ -command "ComposeBounceSend $w $handler" set b($w.buttons.send) send menubutton $w.buttons.sendsave -text $t(send_save) -indicatoron 1 \ -menu $w.buttons.sendsave.m -relief raised -underline 0 set b($w.buttons.sendsave) sendsave menu $w.buttons.sendsave.m -tearoff 0 -postcommand \ "RatSendSavePostMenu $w $w.buttons.sendsave.m $handler" button $w.buttons.abort -text $t(abort) -command \ "DoComposeCleanup $w $handler noback" set b($w.buttons.abort) abort_compose pack $w.buttons.send \ $w.buttons.sendsave -side left -padx 5 pack $w.buttons.abort -side right -padx 5 lappend mh(sendButtons) $w.buttons.send lappend mh(sendButtons) $w.buttons.sendsave bind $w "$w.buttons.abort invoke" # Populate headerlist and pack everything set first [ComposeBuildHeaderEntries $handler] pack $w.menu -side top -fill x pack $mh(headerFrame) -side top -fill x -padx 5 -pady 5 pack $w.buttons -side bottom -fill x -padx 5 -pady 5 set mh(oldfocus) [focus] if {[string length $first]} { focus $first } ::tkrat::winctl::SetGeometry bounce $w lappend composeWindowList $handler ComposeBind $handler UpdateComposeRole $handler return $handler } # RatSendSavePostMenu -- # # Create the want to save to menu # # Arguments: # w - Name of window # m - Name of menu # handler - Handler of save window proc RatSendSavePostMenu {w m handler} { global t $m delete 0 end VFolderBuildMenu $m 0 "RatSendSaveDo $w $handler" 1 $m add separator $m add command -label $t(to_file)... \ -command "RatSendSaveDo $w $handler \ \[InsertIntoFile [winfo toplevel $w]\]" $m add command -label $t(to_dbase)... \ -command "RatSendSaveDo $w $handler \ \[InsertIntoDBase [winfo toplevel $w]\]" FixMenu $m } proc RatSendSaveDo {w handler save_to} { upvar \#0 $handler hd if {"" == $save_to} { return } if {1 == [llength $save_to]} { global vFolderDef set hd(save_to) $vFolderDef($save_to) } else { set hd(save_to) $save_to } $hd(send_handler) $w $handler } # ComposeBuildHeaderEntries -- # # Builds a list of header entries and packs them into the appropriate frame # # Arguments: # handler - The handler for the active compose session proc ComposeBuildHeaderEntries {handler} { global composeHeaderList composeAdrHdrList t b upvar \#0 $handler mh set oldfocus [focus] foreach slave [grid slaves $mh(headerFrame)] { destroy $slave } set mh(headerHandles) {} set first {} set row 0 grid columnconfigure $mh(headerFrame) 1 -weight 1 foreach header $composeHeaderList { if {0 == $mh(O_$header)} { continue } label $mh(headerFrame).${header}_label -text $t($header): grid $mh(headerFrame).${header}_label -row $row -column 0 -sticky en set w $mh(headerFrame).${header}_entry if {-1 != [lsearch $composeAdrHdrList $header]} { lappend mh(headerHandles) \ [ComposeBuildHE $w $handler ${handler}($header)] } else { entry $w -textvariable ${handler}($header) } set b($w) compose_$header set b($w.t) compose_$header grid $w -row $row -column 1 -sticky we incr row if {0 == [string length $first]} { set first $mh(headerFrame).${header}_entry } } catch {focus $oldfocus} return $first } # ComposeUpdateHeaderEntries -- # # Updates all the header-entries from the variables # # Arguments: # handler - The handler for the active compose session proc ComposeUpdateHeaderEntries {handler} { upvar \#0 $handler mh foreach hh $mh(headerHandles) { set w [lindex $hh 0] set hd [lindex $hh 1] upvar \#0 $hd hdr upvar \#0 $hdr(varname) var $w delete 1.0 end $w insert end $var ComposeHandleHE $w $hd } } # ComposeSend -- # # Actually send a message # # Arguments: # mainW - The main compose window # handler - The handler for the active compose session proc ComposeSend {mainW handler} { global t composeAdrHdrList vFolderDef vFolderOutgoing option \ folderWindowList idCnt rat_tmp upvar \#0 $handler mh # Update all header entries foreach hh $mh(headerHandles) { set w [lindex $hh 0] set hhd [lindex $hh 1] ComposeHandleHE $w $hhd } # Check that we have at least one recipient if { 0 == [string length "$mh(to)$mh(cc)$mh(bcc)"]} { Popup $t(need_to) $mh(toplevel) return } # Extract potential pgp keys to use foreach e {to cc} { if {![catch {RatAlias expand pgp $mh($e) $mh(role)} out]} { set mh_pgp($e) $out } else { set mh_pgp($e) "" } } # Alias expansion and syntax error checking set err {} foreach e $composeAdrHdrList { if {[info exists mh($e)] && [string length $mh($e)]} { if {![catch {RatAlias expand sending $mh($e) $mh(role)} out]} { AddrListAdd $mh($e) set mh($e) $out } else { lappend err $t($e) } } } SaveAddrList if {0 < [llength $err]} { Popup "$t(adr_syntax_error): $err" $mh(toplevel) return } # Check if a save folder is defined if {![string length $mh(save_to)] && "" != $option($mh(role),save_outgoing) && [info exists vFolderDef($option($mh(role),save_outgoing))]} { set mh(save_to) $vFolderDef($option($mh(role),save_outgoing)) } # Prepare pgp recipients if {$mh(pgp_signer) == ""} { set mh(pgp_signer) [RatExtractAddresses $mh(role) $mh(from)] } set mh(pgp_rcpts) {} foreach p [concat $mh_pgp(to) $mh_pgp(cc)] { lappend mh(pgp_rcpts) [lindex $p 0] } # Generate date header set mh(date) [RatGenerateDate] set fh [RatOpenFolder $vFolderDef($vFolderOutgoing)] if {"forward_group" == $mh(special)} { foreach a $mh(msgs) { set id compose[incr idCnt] upvar \#0 $id hd set hd(content_description) $t(forwarded_message) foreach header [$a headers] { switch -- [string tolower [lindex $header 0]] { content_description { set hd(content_description) [lindex $header 1] } } } set hd(filename) $rat_tmp/rat.[RatGenId] set fileh [open $hd(filename) w] fconfigure $fileh -translation binary puts -nonewline $fileh [string map {"\r\n" "\n"} [$a rawText]] close $fileh set hd(type) message set hd(subtype) rfc822 set hd(removeFile) 1 set oldAttachments $mh(attachmentList) lappend mh(attachmentList) $id set msg [ComposeCreateMsg $handler] if {$mh(pgp_sign) || $mh(pgp_encrypt)} { if {[catch {$msg pgp $mh(pgp_sign) $mh(pgp_encrypt) $mh(role) \ $mh(pgp_signer) $mh(pgp_rcpts)}]} { $fh close return } } $fh insert $msg rename $msg "" file delete $hd(filename) unset hd set mh(attachmentList) $oldAttachments } } else { # Create message and insert into outgoing queue set msg [ComposeCreateMsg $handler] if {$mh(pgp_sign) || $mh(pgp_encrypt)} { if {[catch {$msg pgp $mh(pgp_sign) $mh(pgp_encrypt) $mh(role) \ $mh(pgp_signer) $mh(pgp_rcpts)}]} { $fh close return } } $fh insert $msg rename $msg "" } foreach i [$fh flagged seen 0] { $fh setFlag $i seen 1 } foreach h [array names folderWindowList] { if {$folderWindowList($h) == $fh} { Sync $h update } } $fh close # Inform sender (if online) if {$option(online)} { RatNudgeSender } # Possibly inform folder window if {[info exists mh(notify)]} { eval $mh(notify) } # Get compose window to clean up DoComposeCleanup $mainW $handler backup } # ComposeBounceSend -- # # Actually bounce a message # # Arguments: # mainW - The main bounce window # handler - The handler for the active bounce session proc ComposeBounceSend {mainW handler} { global t idCnt composeAdrHdrList option vFolderDef vFolderOutgoing upvar \#0 $handler mh # Update all header entries foreach hh $mh(headerHandles) { set w [lindex $hh 0] set hhd [lindex $hh 1] ComposeHandleHE $w $hhd } # Check that we have at least one recipient if { 0 == [string length "$mh(to)$mh(cc)$mh(bcc)"]} { Popup $t(need_to) $mh(toplevel) return } # Alias expansion and syntax error checking set err {} foreach e $composeAdrHdrList { if {[info exists mh($e)]} { if {![catch {RatAlias expand sending $mh($e) $mh(role)} out]} { AddrListAdd $mh($e) set mh($e) $out } else { lappend err $t($e) } } } SaveAddrList if {0 < [llength $err]} { Popup "$t(adr_syntax_error): $err" $mh(toplevel) return } # Check if a save folder is defined if {![string length $mh(save_to)] \ && "" != $option($mh(role),save_outgoing)} { set mh(save_to) $vFolderDef($option($mh(role),save_outgoing)) } set fh [RatOpenFolder $vFolderDef($vFolderOutgoing)] set good 0 set fail 0 foreach msg $mh(msgs) { if {0 == [llength [info commands $msg]]} { incr fail continue } incr good set envelope {} set mh(files) {} foreach h {to cc bcc} { if {"" != $mh($h)} { lappend envelope [list $h $mh($h)] regsub -all "\n" $mh($h) "\n " value lappend envelope [list X-TkRat-Original-$h $value] } } foreach header [$msg headers] { set name [string map {- _} [string tolower [lindex $header 0]]] if {"subject" == $name || "from" == $name || "reply_to" == $name} { lappend envelope [list $name [lindex $header 1]] } } lappend envelope [list message_id [RatGenerateMsgId $mh(role)]] lappend envelope [list X-TkRat-Internal-Role $mh(role)] lappend envelope [list X-TkRat-Internal-PGPActions \ [list $mh(pgp_sign) $mh(pgp_encrypt)]] if {[string length $mh(save_to)]} { lappend envelope [list X-TkRat-Internal-Save-To $mh(save_to)] } set bmsg [RatCreateMessage $mh(role) \ [list $envelope \ [ComposeCreateBody ${handler}(files) [$msg body]]]] $fh insert $bmsg rename $bmsg "" foreach f $mh(files) { file delete -force $f } } if {$fail > 0} { if {$fail == 1 && $good == 0} { Popup $t(message_deleted) } else { Popup $t(messages_deleted) } } foreach i [$fh flagged seen 0] { $fh setFlag $i seen 1 } foreach h [array names folderWindowList] { if {$folderWindowList($h) == $fh} { Sync $h update } } $fh close # Inform sender (if online) if {$option(online)} { RatNudgeSender } # Possibly inform folder window if {[info exists mh(notify)]} { eval $mh(notify) } # Get compose window to clean up DoComposeCleanup $mainW $handler backup } # ComposeCreateBody -- # # Create a body for for sending to ratCreateMessage # # Arguments: # flist - Name of list which will contain names of used files # body - Body command proc ComposeCreateBody {flist body} { global rat_tmp upvar \#0 $flist filelist set type [$body type] set desc [$body description] set header {} if {"" != $desc} { lappend header [list content_description $desc] } set ltype [string tolower [lindex $type 0]] if {"multipart" == $ltype} { set bodydata {} foreach c [$body children] { lappend bodydata [ComposeCreateBody $flist $c] } } elseif {"text" == $ltype} { set bodydata [list utfblob [$body data 0 utf-8]] } else { set filename $rat_tmp/rat.[RatGenId] set fh [open $filename w] $body saveData $fh 1 0 close $fh set bodydata [list file $filename] lappend filelist $filename } return [list \ [lindex $type 0] \ [lindex $type 1] \ [$body params] \ [$body encoding] \ [$body disp_type] \ [$body disp_parm] \ $header \ $bodydata] } # CompseEEdit -- # # Run an external editor on the bodypart # # Arguments: # handler - The handler for the active compose session # e - Id of external editor to use proc ComposeEEdit {handler e} { upvar \#0 $handler mh global t idCnt editor charsetMapping rat_tmp if {[info exists mh(eedit_running)]} { return } set ehandler compose_E[incr idCnt] upvar \#0 $ehandler eh # Write data, change text visible and edit set ecmd [lindex $editor($e) 0] set charset [lindex $editor($e) 1] if {[info exists charsetMapping($charset)]} { set charset $charsetMapping($charset) } set fname $rat_tmp/rat.[RatGenId] set fh [open $fname w] if {"system" != $charset} { if {[catch {fconfigure $fh -encoding $charset} error]} { Popup $error $mh(toplevel) return } } puts -nonewline $fh [$mh(composeBody) get 0.0 end-1c] close $fh foreach block $mh(eEditBlock) { $block configure -state disabled } set mh(eedit_running) 1 $mh(composeBody) delete 0.0 end $mh(composeBody) insert end "\n\n\n $t(running_ext_editor)" if { 0 == [regsub "%s" $ecmd $fname cmd]} { set cmd "$ecmd $fname" } set pos "+[winfo rootx $mh(toplevel)]+[winfo rooty $mh(toplevel)]" regsub "%x" $cmd $pos cmd trace variable eh(status) w "ComposeEEdit2 $handler $fname $charset" RatBgExec ${ehandler}(status) $cmd } proc ComposeEEdit2 {handler fname charset name1 name2 op} { upvar \#0 $handler mh upvar \#1 $name1 eh # Check if still active, if then insert data if {[info exists mh]} { $mh(composeBody) delete 0.0 end set fh [open $fname r] if {"system" != $charset} { catch {fconfigure $fh -encoding $charset} } while { -1 != [gets $fh line]} { $mh(composeBody) insert end "$line\n" noWrap } close $fh catch "file delete -force -- $fname" foreach block $mh(eEditBlock) { $block configure -state normal } } # Remove the trace trace vdelete ${name1}($name2) w "ComposeEEdit2 $handler $fname" unset mh(eedit_running) } # Attach -- # # Attach a file to the message currently being composed # # Arguments: # handler - The handler for the active compose session proc Attach {handler} { global idCnt t b option fixedNormFont upvar \#0 $handler mh # Create identifier set id attach[incr idCnt] set w .$id upvar \#0 $id hd set hd(done) 0 set hd(filename) [rat_fbox::run \ -title $t(attach_file) \ -ok $t(open) \ -initialdir $option(initialdir) \ -mode open \ -parent $mh(toplevel)] if {"" == $hd(filename)} { unset hd return } if {$option(initialdir) != [file dirname $hd(filename)]} { set option(initialdir) [file dirname $hd(filename)] SaveOptions } toplevel $w -class TkRat wm title $w $t(attach_file) wm transient $w $mh(toplevel) # Get default type $hd(filename) set type [RatType $hd(filename)] set hd(typestring) [lindex $type 0] set hd(encoding) [lindex $type 1] set hd(disp_fname) [file tail $hd(filename)] set hd(size) [file size $hd(filename)] # Build specification window frame $w.file label $w.file.label -width 14 -anchor e -text $t(filename): label $w.file.name -textvariable ${id}(filename) \ -anchor w -font $fixedNormFont -width 65 pack $w.file.label \ $w.file.name -side left frame $w.type frame $w.type.r label $w.type.typelabel -width 14 -anchor e -text $t(type): menubutton $w.type.type -menu $w.type.type.m -anchor w -width 23 \ -textvariable ${id}(typestring) -relief raised -indicatoron 1 set b($w.type.type) type_menu menu $w.type.type.m -tearoff 0 foreach type { {text {plain enriched}} {image {jpeg gif tiff bmp xpm png ppm pgm}} {audio {basic}} {video {mpeg}} {message {rfc822 partial external-body}} {application {octet-stream pdf postscript msword}}} { set typename [lindex $type 0] set submenu $w.type.type.m.$typename $w.type.type.m add cascade -menu $submenu -label $typename menu $submenu -tearoff 0 foreach s [lindex $type 1] { $submenu add command -label $s \ -command "set ${id}(typestring) $typename/$s" } $submenu add command -label $t(other)... \ -command "SubtypeSpec ${id}(typestring) $typename $w" } label $w.type.r.enclabel -anchor e -text $t(current_encoding): menubutton $w.type.r.enc -menu $w.type.r.enc.m -anchor w -width 14 \ -textvariable ${id}(encoding) -relief raised -indicatoron 1 set b($w.type.r.enc) encoding_menu menu $w.type.r.enc.m -tearoff 0 # Make sure the user can only select the apropriate entries switch $hd(encoding) { 8bit { set bit7 disabled set bit8 normal set binary normal } binary { set bit7 disabled set bit8 disabled set binary normal } default { set bit7 normal set bit8 normal set binary normal } } $w.type.r.enc.m add command -label 7bit \ -command "set ${id}(encoding) 7bit" -state $bit7 $w.type.r.enc.m add command -label 8bit \ -command "set ${id}(encoding) 8bit" -state $bit8 $w.type.r.enc.m add command -label binary \ -command "set ${id}(encoding) binary" -state $binary $w.type.r.enc.m add command -label quoted-printable \ -command "set ${id}(encoding) quoted-printable" -state $bit7 $w.type.r.enc.m add command -label base64 \ -command "set ${id}(encoding) base64" -state $bit7 pack $w.type.typelabel \ $w.type.type -side left pack $w.type.r.enclabel \ $w.type.r.enc -side left pack $w.type.r -side right -padx 5 frame $w.fname label $w.fname.label -width 14 -anchor e -text $t(filename): entry $w.fname.entry -width 65 -textvariable ${id}(disp_fname) pack $w.fname.label \ $w.fname.entry -side left set b($w.fname.entry) attach_fname frame $w.desc label $w.desc.label -width 14 -anchor e -text $t(description): entry $w.desc.entry -width 65 -textvariable ${id}(content_description) pack $w.desc.label \ $w.desc.entry -side left set b($w.desc.entry) attach_description frame $w.id label $w.id.label -width 14 -anchor e -text $t(id): entry $w.id.entry -width 65 -textvariable ${id}(content_id) set b($w.id.entry) attach_id pack $w.id.label \ $w.id.entry -side left OkButtons $w $t(ok) $t(cancel) "set ${id}(done)" pack $w.file \ $w.type \ $w.fname \ $w.desc \ $w.id \ $w.buttons -side top -fill both -pady 2 ::tkrat::winctl::SetGeometry attach2 $w ::tkrat::winctl::ModalGrab $w $w.desc.entry $w.desc.entry icursor 0 tkwait variable ${id}(done) if { 1 == $hd(done) } { set type [split $hd(typestring) /] set hd(type) [lindex $type 0] set hd(subtype) [lindex $type 1] set hd(removeFile) 0 if {"text" == $hd(type)} { set f [open $hd(filename) r] set bd [read $f 32768] close $f set charset [RatCheckEncodings bd $option(charset_candidates)] if {"" == $charset} { set charset $option(charset) } set hd(parameter) \{[list charset $charset]\} } if {[string length $hd(disp_fname)]} { set hd(disp_parm) \{[list filename $hd(disp_fname)]\} if {[info exists hd(parameter)]} { lappend hd(parameter) [list name $hd(disp_fname)] } else { set hd(parameter) \{[list name $hd(disp_fname)]\} } } set hd(disp_type) attachment lappend mh(attachmentList) $id if { "" != $hd(content_description) } { set desc $hd(content_description) } else { set desc "$hd(typestring): $hd(disp_fname) ([RatMangleNumber $hd(size)])" } $mh(attachmentListWindow) insert end $desc } else { unset hd } ::tkrat::winctl::RecordGeometry attach2 $w foreach bn [array names b $w.*] {unset b($bn)} destroy $w } # SubtypeSpec -- # # Let the user specify an subtype # # Arguments: # variable - The name of a global variable in which the result is to be # left. # type - The primary type of the object. # topwin - The window we should be transient for proc SubtypeSpec {variable type topwin} { upvar \#0 $variable var global t idCnt fixedNormFont set id subtype[incr idCnt] set w .$id upvar \#0 $id hd set hd(done) 0 toplevel $w -class TkRat wm title $w $t(custom_type) wm transient $w $topwin frame $w.type label $w.type.label -width 10 -anchor e -text $t(type): label $w.type.name -text $type -anchor w -font $fixedNormFont pack $w.type.label \ $w.type.name -side left frame $w.subtype label $w.subtype.label -width 10 -anchor e -text $t(subtype): entry $w.subtype.entry -width 20 -textvariable ${id}(spec) pack $w.subtype.label \ $w.subtype.entry -side left OkButtons $w $t(ok) $t(cancel) "set ${id}(done)" pack $w.type \ $w.subtype -side top -anchor w -padx 5 pack $w.buttons -side top -pady 10 -fill x bind $w "set ${id}(done) 1" bind $w.subtype.entry "set ${id}(done) 1" ::tkrat::winctl::SetGeometry subtypeSpec $w ::tkrat::winctl::ModalGrab $w $w.subtype.entry tkwait variable ${id}(done) if {1 == $hd(done)} { set var $type/$hd(spec) } ::tkrat::winctl::RecordGeometry subtypeSpec $w destroy $w unset hd } # AttachKeys -- # # Attach keys to the message currently being composed # # Arguments: # handler - The handler for the active compose session proc AttachKeys {handler} { RatPGPGetIds AttachKeysDo $handler } proc AttachKeysDo {handler ids} { global idCnt t option rat_tmp upvar \#0 $handler mh foreach keyid $ids { # Create identifier set id attach[incr idCnt] upvar \#0 $id hd set hd(type) application set hd(subtype) pgp-keys set hd(encoding) 7bit set hd(content_description) \ "$t(pgp_key) [lindex $keyid 0] $t(for) [lindex $keyid 1]" set hd(filename) $rat_tmp/[RatGenId] set hd(removeFile) 1 set f [open $hd(filename) w] puts $f [RatPGP extract [lindex $keyid 0]] close $f lappend mh(attachmentList) $id $mh(attachmentListWindow) insert end $hd(content_description) } } # Detach -- # # Detach a previously attached attachment to a message # # Arguments: # handler - The handler for the active compose session # button - The button which shall be disabled proc Detach {handler button} { upvar \#0 $handler mh $button configure -state disabled foreach element [lsort -integer -decreasing \ [$mh(attachmentListWindow) curselection]] { $mh(attachmentListWindow) delete $element if {"forward_group" == $mh(special)} { incr element -1 } ComposeFreeBody [lindex $mh(attachmentList) $element] set mh(attachmentList) [lreplace $mh(attachmentList) $element $element] } } # ComposeFreeBody -- # # Free a bodypart from memory and remove any temporary files associated with it # # Arguments: # handler - The handler for the active compose session proc ComposeFreeBody {handler} { upvar \#0 $handler bh if {![info exists bh(type)]} { return } if { "multipart" == $bh(type)} { if {[info exists bh(children)]} { foreach body $bh(children) { ComposeFreeBody $body } } } if {[info exists bh(removeFile)] && $bh(removeFile)} { catch {file delete -- $bh(filename)} } unset bh } # ComposeHold -- # # Insert the message being composed into the hold. # # Arguments: # mainW - The main compose window # handler - The handler for the active compose session proc ComposeHold {mainW handler} { upvar \#0 $handler mh global t vFolderDef vFolderHold # Update all header entries foreach hh $mh(headerHandles) { set w [lindex $hh 0] set hhd [lindex $hh 1] ComposeHandleHE $w $hhd } # Cancel any pending backups if {[info exists mh(next_backup)]} { catch {after cancel $mh(next_backup)} } # Create message and insert into hold set msg [ComposeCreateMsg $handler] set fh [RatOpenFolder $vFolderDef($vFolderHold)] $fh insert $msg rename $msg "" # Mark all messages in hold as read foreach i [$fh flagged seen 0] { $fh setFlag $i seen 1 } $fh close # Get compose window to clean up DoComposeCleanup $mainW $handler noback } # ComposeChoose -- # # This routine gets a message handler and scans it for embedded messages. # If none are found the message handler is returned. If any are found the # user may choose which message handler is to be returned. # # Arguments: # msg - Message handler of message to reply to # info - An informative text which is to be displayed at the top # of the window proc ComposeChoose {msg info} { global idCnt t b option fixedBoldFont set msgs [ComposeChooseDig [$msg body] $msg] while { -1 != [set i [lsearch -exact $msgs {}]]} { set msgs [lreplace $msgs $i $i] } if { 1 == [llength $msgs] } { return $msgs } # Create identifier set id cc[incr idCnt] set w .$id upvar \#0 $id hd set hd(done) 0 # Create toplevel toplevel $w -class TkRat wm title $w $t(reply_to)? # Populate window set mnum 0 set hd(choosed) $msg label $w.label -text $info pack $w.label -side top -fill x frame $w.l -relief sunken -bd 1 canvas $w.l.canvas \ -yscrollcommand "$w.l.scroll set" \ -highlightthickness 0 scrollbar $w.l.scroll \ -relief sunken \ -bd 1 \ -command "$w.l.canvas yview" \ -highlightthickness 0 pack $w.l.scroll -side right -fill y pack $w.l.canvas -expand 1 -fill both frame $w.l.canvas.f set elemId [$w.l.canvas create window 0 0 -anchor nw -window $w.l.canvas.f] foreach m $msgs { set f $w.l.canvas.f.f$mnum incr mnum frame $f radiobutton $f.r -value $m -variable ${id}(choosed) text $f.t -relief flat set width 0 set height 0 $f.t tag configure HeaderName -font $fixedBoldFont foreach h [$m headers] { set header([string tolower [lindex $h 0]]) [lindex $h 1] } foreach field [string tolower $option(show_header_selection)] { if {[info exists header($field)]} { set n [string map {- _} $field] if {[info exists t($n)]} { set name $t($n) } else { set name $field } $f.t insert end "$name: " HeaderName "$header($field)" set length [lindex [split [$f.t index insert] .] 1] if {$length >$width} { set width $length } $f.t insert end "\n" incr height } } $f.t configure -width $width -height $height -state disabled pack $f.r -side left -anchor n pack $f.t pack $f -side top -anchor w set b($f.r) choose_msg set b($f.t) choose_msg } pack $w.l -side top -expand 1 -fill both OkButtons $w $t(ok) $t(cancel) "set ${id}(done)" pack $w.buttons -fill both -pady 5 ::tkrat::winctl::SetGeometry composeChoose $w $w.l.canvas update idletasks set bbox [$w.l.canvas bbox $elemId] eval {$w.l.canvas configure -scrollregion $bbox} ::tkrat::winctl::ModalGrab $w tkwait variable ${id}(done) ::tkrat::winctl::RecordGeometry composeChoose $w $w.l.canvas destroy $w if {1 == $hd(done)} { set r $hd(choosed) } else { set r {} } unset hd return $r } # ComposeChooseDig -- # # Gets a bodypart handler and checks for embedded messages in it. The # list of found messages is returned # # Arguments: # body - The bodypart to look in # msgs - The list of messages found so far proc ComposeChooseDig {body msgs} { set type [$body type] if {![string compare message/rfc822 [lindex $type 0]/[lindex $type 1]]} { return [concat $msgs [$body message]] } foreach child [$body children] { set type [$child type] switch -glob [string tolower [lindex $type 0]/[lindex $type 1]] { message/rfc822 { set msg [$child message] set msgs [concat $msgs $msg] set msgs [ComposeChooseDig [$msg body] $msgs] } multipart/* { set msgs [ComposeChooseDig $child $msgs] } } } return $msgs } # ComposeBuildHE -- # # Build the header entry widget. The interface looks somewhat like the # entry-widget, but we use a text-widget and have some special bindings. # # Arguments: # w - The window to build # handler - The handler for the active compose session # textvariable - The variable to keep and leave the result in proc ComposeBuildHE {w mhandler textvariable} { global idCnt defaultFontWidth ISO_Left_Tab book_img upvar \#0 $textvariable textvar upvar \#0 $mhandler mh set handler compHE[incr idCnt] upvar \#0 $handler hd # Build windows frame $w text $w.t -relief sunken -yscroll "$w.s set" -width 40 -height 1 -wrap none scrollbar $w.s -relief sunken -command "$w.t yview" -highlightthickness 0 button $w.b -command "ComposeHandleHEAlias $w.t $handler" -bd 1 \ -image $book_img -padx 0 -pady 0 -takefocus 0 pack $w.t -side left -expand yes -fill x pack $w.b -side right -anchor n # Initialize variables set hd(scrollbar) $w.s set hd(lines) 1 set hd(varname) $textvariable set hd(scroll) 0 set hd(width) 0 set hd(mhandler) $mhandler set hd(autocomplete_list) {} set hd(autocomplete_start) {} # Do bindings bind $w "focus $w.t" bind $w.t {focus [tk_focusNext %W]; break} bind $w.t {focus [tk_focusNext %W]; break} bind $w.t {focus [tk_focusPrev %W]; break} bind $w.t <$ISO_Left_Tab> {focus [tk_focusPrev %W]; break} bind $w.t { } bind $w.t "ComposeHandleHEComma %W $handler" bind $w.t "after 1 ComposeHandleHEFocusOut %W $handler" bind $w.t "unset $handler" bind $w.t <> "ComposeHandleHEPaste %W $handler; break" bind $w.t <> "ComposeHandleHEPaste %W $handler; break" bind $w.t "ComposeHandleHEAlias %W $handler" bind $w.t "ComposeHandleHEConfigure %W $handler %w" AddrListInit $w.t # Create error tag if {[winfo cells $w.t] > 2} { $w.t tag configure error -foreground red } else { $w.t tag configure error -underline 1 } # Initialize if {![info exists textvar]} { set textvar {} } else { $w.t insert end $textvar } return [list $w.t $handler] } # ComposeHandleHEConfigure -- # # Handle configure events in an address entry # # Arguments: # w - The text widget # handler - The handler which identifies this address widget # pixwidth- The width of the text widget (in pixels) proc ComposeHandleHEConfigure {w handler pixwidth} { global defaultFontWidth upvar \#0 $handler hd if {![info exists hd(borders)]} { set hd(borders) [expr {2*([$w cget -borderwidth] \ +[$w cget -highlightthickness])}] } set width [expr {($pixwidth-$hd(borders))/$defaultFontWidth}] if {$width == $hd(width) || $width < 1} { return } set hd(width) $width $w configure -tabs [expr {$defaultFontWidth*$hd(width)/2}] ComposeHandleHE $w $handler } # ComposeHandleHEFocusOut -- # # Handle FocusOut events for the eheader entry. This routine checks if # the actually still has focus and in that case does nothing. # # Arguments: # w - The text widget # handler - The handler which identifies this address widget proc ComposeHandleHEFocusOut {w handler} { if {[winfo exists $w] && [focus] != $w && 0 == [AddrListClose $w 0]} { ComposeHandleHE $w $handler } } # ComposeHandleHEComma -- # # Handle address separator events # # Arguments: # w - The text widget # handler - The handler which identifies this address widget proc ComposeHandleHEComma {w handler} { AddrListClose $w 1 ComposeHandleHE $w $handler } # ComposeHandleHE -- # # Handle events in an address entry # # Arguments: # w - The text widget # handler - The handler which identifies this address widget proc ComposeHandleHE {w handler} { upvar \#0 $handler hd upvar \#0 $hd(varname) var upvar \#0 $hd(mhandler) mh set sr [$w tag nextrange sel 1.0] if {[llength $sr]} { set sel [$w get [lindex $sr 0] [lindex $sr 1]] } set old [string trim [$w get 1.0 end]] $w delete 1.0 end set tempalist [RatSplitAdr $old] set alist {} set max 0 set tot 0 foreach adr $tempalist { if {[catch {RatAlias expand display $adr $mh(role)} adr2]} { set tag($adr) error lappend alist $adr } else { set alist [concat $alist [RatSplitAdr $adr2]] } } # PGP actions foreach adr $tempalist { if {![catch {RatAlias expand pgpactions $adr $mh(role)} pgpa]} { if {[lindex $pgpa 0]} { set mh(pgp_sign) 1 set mh(pgp_sign_explicit) 1 } if {[lindex $pgpa 1]} { set mh(pgp_encrypt) 1 } } } foreach adr $alist { if {![info exists tag($adr)]} { set tag($adr) {} } set len [expr [string length $adr]+2] incr tot $len if {$len > $max} { set max $len } } if {$tot <= $hd(width)} { foreach adr $alist { $w insert end $adr $tag($adr) ", " } } elseif {$max <= [expr {$hd(width)/2}]} { set c 1 foreach adr $alist { if {1 == $c} { $w insert end $adr $tag($adr) ",\t" set c 2 } else { $w insert end $adr $tag($adr) ",\n" set c 1 } } } else { foreach adr $alist { $w insert end $adr $tag($adr) ",\n" } } if {[string length $old] && ![regexp {,$} $old]} { set s [$w search -backwards , end] if {$s != ""} { $w delete $s end } } set hd(lines) [expr {int([$w index end])-1}] if {$hd(lines) > 4 && !$hd(scroll)} { pack $hd(scrollbar) -side right -fill y $w configure -height 4 set hd(scroll) 1 } elseif {$hd(lines) <= 4} { pack forget $hd(scrollbar) $w configure -height $hd(lines) set hd(scroll) 0 } $w see insert if {[llength $sr]} { set r [$w search -- $sel [lindex $sr 0]] if {"" == $r} { set r [$w search -- $sel 1.0] } if {"" != $r} { $w tag add sel $r $r+[string length $sel]c } } set var [string trim [$w get 1.0 end]] } # ComposeHandleHEPaste -- # # Handle PasteSelection events in an address entry # # Arguments: # w - The text widget # handler - The handler which identifies this address widget proc ComposeHandleHEPaste {w handler} { catch { set var [string map {mailto: {}} [selection get -displayof $w]] $w insert insert $var } ComposeHandleHE $w $handler } # ComposeHandleHEAlias -- # # Handle the alias popup window # w - The text widget # handler - The handler which identifies this address widget proc ComposeHandleHEAlias {w handler} { set alias [AliasChooser $w] if {[string length $alias]} { if {[string length [string trim [$w get 1.0 end]]]} { $w insert end , } $w insert end $alias ComposeHandleHE $w $handler } } # ComposeInsertFile -- # # Insert a file into the message currently being composed # # Arguments: # handler - The handler for the active compose session proc ComposeInsertFile {handler} { global t option upvar \#0 $handler mh set filename [rat_fbox::run \ -ok $t(open) \ -title $t(insert_file) \ -initialdir $option(initialdir) \ -parent [winfo toplevel $mh(composeBody)] \ -mode open] if {$filename != ""} { if {$option(initialdir) != [file dirname $filename]} { set option(initialdir) [file dirname $filename] SaveOptions } if {[catch {open $filename} fh]} { Popup [format $t(failed_to_open_file) $fh] $mh(toplevel) } else { set mh(undoText) {} $mh(composeBody) mark set undoStart insert $mh(composeBody) mark set undoEnd insert $mh(composeBody) insert insert [read $fh] noWrap close $fh } } } # ComposePostEdit -- # # Post the edit menu. This routine may disable/enable apropriate entries # in the menu # # Arguments: # handler - The handler for the active compose session proc ComposePostEdit {handler m} { global cmdList cmdName cmdCmd t upvar \#0 $handler hd rat_edit::state $hd(composeBody) state $m entryconfigure [lindex $hd(undo_menu) 1] -state $state(undo) $m entryconfigure [lindex $hd(redo_menu) 1] -state $state(redo) $m entryconfigure [lindex $hd(cut_menu) 1] -state $state(selection) $m entryconfigure [lindex $hd(copy_menu) 1] -state $state(selection) $m entryconfigure [lindex $hd(paste_menu) 1] -state $state(paste) if {![info exist cmdList]} { CmdRead } elseif { $hd(edit_end) < [$m index end]} { $m delete [expr {$hd(edit_end)+1}] end } foreach i $cmdList { $m add command -label $cmdName($i) \ -command "ComposeRunCmd $handler [list $cmdCmd($i)]" } } # ComposeSpecifyCmd -- # # Let the user specify a program to run part of the text through # # Arguments: # handler - The handler for the active compose session proc ComposeSpecifyCmd {handler} { global idCnt t b cmdArrayId cmdList cmdName cmdCmd upvar \#0 $handler mh # Create identifier set id insert[incr idCnt] set w .$id upvar \#0 $id hd set hd(done) 0 # Create toplevel toplevel $w -class TkRat wm title $w $t(run_through_command) wm transient $w $mh(toplevel) # The save as line frame $w.s checkbutton $w.s.but -text $t(save_as) -variable ${id}(doSave) entry $w.s.entry -width 20 -textvariable ${id}(saveAs) bind $w.s.entry \ "if {0 < \[string length ${id}(saveAs)\]} { \ set ${id}(doSave) 1 \ } else { \ set ${id}(doSave) 0 \ }" label $w.s.label -text $t(command) pack $w.s.label -side left -padx 5 -anchor s pack $w.s.entry \ $w.s.but -side right -pady 5 set b($w.s.but) save_cmd_as set b($w.s.entry) save_cmd_as # The text widget and the buttons text $w.t -relief sunken -bd 1 -wrap word -setgrid 1 set b($w.t) command_to_run_through OkButtons $w $t(ok) $t(cancel) "set ${id}(done)" pack $w.s -side top -anchor w -fill x pack $w.t -side top -expand 1 -fill both -padx 5 pack $w.buttons -fill both -pady 5 ::tkrat::winctl::SetGeometry giveCmd $w $w.t ::tkrat::winctl::ModalGrab $w $w.s.entry tkwait variable ${id}(done) ::tkrat::winctl::RecordGeometry giveCmd $w $w.t if {1 == $hd(done)} { if {$hd(doSave)} { if {[string length $hd(saveAs)]} { lappend cmdList $cmdArrayId set cmdName($cmdArrayId) $hd(saveAs) set cmdCmd($cmdArrayId) [string trim [$w.t get 1.0 end]] incr cmdArrayId CmdWrite } else { Popup $t(need_name) $w } } ComposeRunCmd $handler [string trim [$w.t get 1.0 end]] } destroy $w unset hd } # ComposeRunCmd -- # # Runs a command on a specified part of the text # # Arguments: # handler - The handler for the active compose session # cmd - The command to run proc ComposeRunCmd {handler cmd} { upvar \#0 $handler hd global t rat_tmp # Find area to work on if { 0 != [llength [$hd(composeBody) tag ranges sel]]} { set start sel.first set end sel.last } else { set start 1.0 set end end-1c } # Remember things for undo set hd(undoText) [$hd(composeBody) get $start $end] set hd(undoTags) [$hd(composeBody) tag ranges noWrap] set hd(undoInsert) [$hd(composeBody) index insert] set hd(cmdStart) [$hd(composeBody) index $start] set hd(cmdEnd) [$hd(composeBody) index $end] set hd(text) [$hd(composeBody) get 1.0 end-1c] # Run command set name $rat_tmp/rat.[RatGenId] set fh [open $name.in w] puts $fh [$hd(composeBody) get $start $end] close $fh if {[regexp {%s} $cmd]} { set cmd [string map [list %s $name.in] $cmd] } else { set cmd "cat $name.in | $cmd >$name.out" } # Replace the text with the message $hd(composeBody) delete 1.0 end $hd(composeBody) insert end "\n\n\n\t$t(command_is_running)..." # Disable compose window foreach block $hd(eEditBlock) { $block configure -state disabled } $hd(composeBody) configure -state disabled trace variable hd(status) w "ComposeRunCmdDone $handler $name" if {[catch {RatBgExec ${handler}(status) $cmd} result]} { Popup "$t(command_failed): $result" $hd(toplevel) } } proc ComposeRunCmdDone {handler name name1 name2 op} { upvar \#0 $handler hd if {[info exists hd]} { foreach block $hd(eEditBlock) { $block configure -state normal } $hd(composeBody) configure -state normal $hd(composeBody) delete 1.0 end $hd(composeBody) insert 1.0 $hd(text) $hd(composeBody) mark set undoStart $hd(cmdStart) $hd(composeBody) mark set undoEnd $hd(cmdEnd) if {0 == $hd(status)} { $hd(composeBody) delete undoStart undoEnd if {[file readable $name.out]} { set outfile $name.out } else { set outfile $name.in } set fh [open $outfile r] $hd(composeBody) insert undoStart [read -nonewline $fh] close $fh } } catch {file delete -force -- $name.in $name.out} trace vdelete $name1($name2) w "ComposeRunCmdDone $handler $name" } # CmdWrite -- # # Write the saved expressions to disk # # Arguments: proc CmdWrite {} { global option cmdArrayId cmdList cmdName cmdCmd set f [open $option(ratatosk_dir)/commands w] puts $f "set cmdArrayId $cmdArrayId" puts $f "set cmdList [list $cmdList]" foreach c $cmdList { puts $f "set cmdName($c) [list $cmdName($c)]" puts $f "set cmdCmd($c) [list $cmdCmd($c)]" } close $f } # CmdRead -- # # Read the saved expressions # # Arguments: proc CmdRead {} { global option cmdArrayId cmdList cmdName cmdCmd if {[file readable $option(ratatosk_dir)/commands]} { source $option(ratatosk_dir)/commands } else { set cmdArrayId 0 set cmdList {} } } # CmdList -- # # Lets the user view/modify the list of commands # # Arguments: proc CmdList {} { global cmdList cmdName idCnt t b # Create identifier set id ccmd[incr idCnt] upvar \#0 $id hd set w .$id set hd(changed) 0 set hd(list) $w.l.list set hd(text) $w.t set hd(apply) $w.b.apply set hd(delete) $w.b.delete # Create toplevel toplevel $w -class TkRat wm title $w $t(command_list) # The list frame $w.l listbox $hd(list) \ -yscroll "$w.l.scroll set" \ -exportselection false \ -highlightthickness 0 \ -selectmode single \ -setgrid 1 scrollbar $w.l.scroll \ -command "$hd(list) yscroll" \ -highlightthickness 0 pack $w.l.scroll -side right -fill y pack $hd(list) -expand 1 -fill both set b($hd(list)) saved_commands # The buttons frame $w.b button $hd(apply) -text $t(apply) -command "CmdApply $w $id" \ -state disabled button $hd(delete) -text $t(delete) -command "CmdDelete $w $id" \ -state disabled button $w.b.close -text $t(close) -command "destroy $w" pack $hd(apply) \ $w.b.delete \ $w.b.close -side top -pady 5 -padx 5 set b($hd(apply)) apply_changes_to_cmd set b($hd(delete)) delete_command set b($w.b.close) dismiss # The command content text $hd(text) \ -relief sunken \ -bd 1 \ -wrap word \ -width 40 \ -height 4 \ -state disabled set b($hd(text)) command_content # Pack them all pack $hd(text) -side bottom -expand 1 -fill x pack $w.b -side right -padx 5 -pady 5 pack $w.l -fill both -expand 1 -padx 5 -pady 5 # Make sure we have the list in memory if {![info exist cmdList]} { CmdRead } # no commands defined so no list if {[llength $cmdList] == 0} { return } # Populate the list foreach c $cmdList { $hd(list) insert end $cmdName($c) } # Bind the listbox and text bind $hd(list) "\ $hd(apply) configure -state disabled; \ $hd(delete) configure -state normal; \ $hd(text) configure -state normal; \ $hd(text) delete 1.0 end; \ $hd(text) insert 1.0 \ \$cmdCmd(\[lindex \$cmdList \[%W index @%x,%y\]\])" bind $hd(text) "CmdTextCheck $w $id" wm protocol $w WM_DELETE_WINDOW "destroy $w" bind $hd(list) "CmdClose $w $id" bind $w "$w.b.close invoke" ::tkrat::winctl::SetGeometry cmdList $w $hd(list) } # CmdDelete -- # # Delete a command # # Arguments: # w - The command list window # handler - The changes variable proc CmdDelete {w handler} { global cmdList cmdName upvar \#0 $handler hd set index [$hd(list) curselection] unset cmdName([lindex $cmdList $index]) set cmdList [lreplace $cmdList $index $index] set hd(changed) 1 # Populate the list $hd(list) delete 0 end foreach c $cmdList { $hd(list) insert end $cmdName($c) } # Clear the text $hd(text) delete 1.0 end $hd(text) configure -state disabled # Disable the buttons $hd(apply) configure -state disabled $hd(delete) configure -state disabled } # CmdTextCheck -- # # Check if the command text has been changed # # Arguments: # w - The command list window # handler - The changes variable proc CmdTextCheck {w handler} { global cmdList cmdCmd upvar \#0 $handler hd if {[string compare [$hd(text) get 1.0 end-1c] \ $cmdCmd([lindex $cmdList [$hd(list) curselection]])]} { $hd(apply) configure -state normal } else { $hd(apply) configure -state disabled } } # CmdApply -- # # Apply the current change # # Arguments: # w - The command list window # handler - The changes variable proc CmdApply {w handler} { global cmdList cmdCmd upvar \#0 $handler hd set cmdCmd([lindex $cmdList [$hd(list) curselection]]) \ [$hd(text) get 1.0 end-1c] $hd(apply) configure -state disabled set hd(changed) 1 } # CmdClose -- # # Closes the command window # # Arguments: # w - The command list window # handler - The changes variable proc CmdClose {w handler} { upvar \#0 $handler hd ::tkrat::winctl::RecordGeometry cmdList $w $hd(list) if { 1 == $hd(changed)} { CmdWrite } catch {focus $hd(oldfocus)} unset hd } # ShowGeneratedHeaders -- # # Show the generated headers window # # Arguments: # handler - Handler identifying the folder window proc ShowGeneratedHeaders {handler} { global idCnt t $handler fixedNormFont fixedBoldFont # Create identifier set id iw[incr idCnt] set w .$id # Create toplevel toplevel $w -class TkRat wm title $w $t(generated_header) # Message part frame $w.b button $w.b.update -text $t(update) -command "UpdateGH $handler $w.text" button $w.b.dismiss -text $t(dismiss) -command "destroy $w" pack $w.b.update $w.b.dismiss -side left -padx 5 -expand 1 text $w.text -yscroll "$w.scroll set" -relief sunken -bd 1 -wrap none scrollbar $w.scroll -relief raised -bd 1 \ -command "$w.text yview" pack $w.b -side bottom -pady 5 -fill x pack $w.scroll -side right -fill y pack $w.text -expand 1 -fill both $w.text tag configure name -font $fixedBoldFont $w.text tag configure value -font $fixedNormFont -lmargin2 20 UpdateGH $handler $w.text bind $w.text "::tkrat::winctl::RecordGeometry showGH $w $w.text" bind $w "$w.b.dismiss invoke" ::tkrat::winctl::SetGeometry showGH $w $w.text } # FixAddress -- # # Mangles the given addresses into the formats used when speaking SMTP. # Returns a string containing the mangled addresses # # Arguments: # role - Role to do work under # al - String containing addresses # mode - Way of printing address (rfc822 or mail) proc FixAddress {role al mode} { set result {} foreach adr [RatSplitAdr $al] { set a [RatCreateAddress $adr $role] lappend result [$a get $mode] rename $a "" } return [join $result ", "] } # UpdateGH -- # # Update a show generated headers window # # Arguments: # handler - Handler identifying the folder window # w - Name of the text widget # args - Ignored trace variables proc UpdateGH {handler w args} { global option upvar \#0 $handler hd $w configure -state normal $w delete 1.0 end set helo [RatGetCurrent smtp_helo $hd(role)] set host [RatGetCurrent host $hd(role)] set gen [RatGenerateAddresses $handler] # EHLO $w insert end "(SMTP) EHLO: " name $helo value "\n" # Envelope from set from [RatCreateAddress [lindex $gen 0] $hd(role)] $w insert end "(SMTP) MAIL FROM: " name [$from get mail] value "\n" rename $from {} # Envelope rcpt to $w insert end "(SMTP) RCPT TO: " name set al "" foreach f {to cc bcc} { if {[string length $hd($f)]} { if {[string length $al]} { set al "$al, $hd($f)" } else { set al $hd($f) } } } set first 1 set adr_smtp [RatAlias expand sending $al $hd(role)] set addresses [FixAddress $hd(role) $adr_smtp mail] foreach a [RatSplitAdr $addresses] { if {$first} { set first 0 } else { $w insert end " " name } $w insert end "<[string trim $a]>\n" value } $w insert end "\n" # From: # Possible Sender: $w insert end " From: " name [lindex $gen 0] value "\n" if {"" != [lindex $gen 1]} { $w insert end " Sender: " name [lindex $gen 1] value "\n" } # Reply-to if { "" != $hd(reply_to)} { $w insert end "Reply-To: " name \ "[FixAddress $hd(role) [RatAlias expand sending $hd(reply_to) $hd(role)] rfc822]\n" \ value } # To if { "" != $hd(to)} { $w insert end " To: " name \ "[FixAddress $hd(role) [RatAlias expand sending $hd(to) $hd(role)] rfc822]\n" \ value } # CC if { "" != $hd(cc)} { $w insert end " cc: " name \ "[FixAddress $hd(role) [RatAlias expand sending $hd(cc) $hd(role)] rfc822]\n" \ value } $w configure -state disabled } # EditorsRead -- # # Read the editors file # # Arguments: proc EditorsRead {} { global option editors editor ratCurrent t charsetMapping editorsChanged \ charsetReverseMapping if {[file readable $option(ratatosk_dir)/editors]} { source $option(ratatosk_dir)/editors foreach e [array names editor] { if {[info exists charsetMapping([lindex $editor($e) 1])]} { set editor($e) [list [lindex $editor($e) 0] \ $charsetMapping([lindex $editor($e) 1])] } } } else { set editors [list $t(external_editor)] if {[info exists charsetReverseMapping($ratCurrent(charset))]} { set charset $charsetReverseMapping($ratCurrent(charset)) } else { set charset $ratCurrent(charset) } set editor($t(external_editor)) \ [list $option(editor) $charset] } if {![info exists option(eeditor)]} { set option(eeditor) [lindex $editors 0] } set editorsChanged 0 } # EditorsWrite -- # # Write the editors file # # Arguments: proc EditorsWrite {} { global option editors editor editorsChanged if {0 == $editorsChanged} { return } set f [open $option(ratatosk_dir)/editors w] puts $f "set editors [list $editors]" foreach e $editors { puts $f [list set editor($e) $editor($e)] } close $f set editorsChanged 0 } # EditorsList -- # # Show the editors list window # # Arguments: proc EditorsList {} { global t rat_list::create editors editorList "EditorsEdit add" "EditorsEdit edit" \ EditorsDelete EditorsWrite \ $t(editors) $t(add) $t(edit) $t(delete) $t(dismiss) } # EditorsDelete -- # # Delete an editor # # Arguments: # name - Name of editor to delete proc EditorsDelete {name} { global editor editorsChanged unset editor($name) incr editorsChanged } # EditorsEdit -- # # Edit an editor definition # # Arguments: # mode - determines what to do. Valud values are: "add", "edit" # arg1,2 - Argument depending on mode proc EditorsEdit {mode arg1 {arg2 {}}} { global editors editor idCnt t b option charsetName charsetReverseMapping # Create identifier set id eedit[incr idCnt] set w .$id upvar \#0 $id hd set hd(w) $w set hd(mode) $mode set hd(oldfocus) [focus] # Create toplevel toplevel $w -class TkRat wm title $w $t(editors) label $w.lname -text $t(name): -anchor e entry $w.ename -textvariable ${id}(name) set b($w.ename) name_of_editor label $w.lcmd -text $t(command): -anchor e entry $w.ecmd -textvariable ${id}(cmd) -width 40 set b($w.ecmd) editor_command label $w.lcharset -text $t(charset): -anchor e menubutton $w.mcharset \ -textvariable ${id}(charset_name) \ -indicatoron 1 \ -relief raised \ -menu $w.mcharset.m set b($w.mcharset) editor_charset menu $w.mcharset.m -tearoff 0 set width 4 foreach c [concat system [lsort [encoding names]]] { if {[info exists charsetReverseMapping($c)] && 0 < [string length $charsetReverseMapping($c)]} { set name $charsetReverseMapping($c) } else { set name $c } set hd(chname,$c) $name $w.mcharset.m add command -label $name -command \ "set ${id}(charset_name) [list $hd(chname,$c)]; \ set ${id}(charset) $c" if {[string length $hd(chname,$c)] > $width} { set width [string length $hd(chname,$c)] } } $w.mcharset configure -width $width FixMenu $w.mcharset.m OkButtons $w $t(ok) $t(cancel) "EditorsEditDone $id" grid $w.lname $w.ename -sticky we grid $w.lcmd $w.ecmd -sticky we grid $w.lcharset -sticky we grid $w.mcharset -column 1 -row 2 -sticky w grid $w.buttons - -sticky we -pady 5 if {"edit" == $mode} { set hd(name) $arg1 set hd(cmd) [lindex $editor($arg1) 0] set hd(charset) [lindex $editor($arg1) 1] if {![info exists hd(chname,$hd(charset))]} { set hd(charset) system } set hd(changeproc) $arg2 set hd(oldname) $arg1 focus $w.ecmd } else { focus $w.ename set hd(charset) system set hd(addproc) $arg1 } set hd(charset_name) $hd(chname,$hd(charset)) bind $w.lname "EditorsEditClosed $id" ::tkrat::winctl::SetGeometry editorsEdit $w } # EditorsEditDone -- # # Called when EditorsEdit window is done # # Arguments: # handler - Handler describing the windo # ok - Boolean indicating if ok was pressed proc EditorsEditDone {handler ok} { upvar \#0 $handler hd global editor editorsChanged b t if {$ok} { if {"" == $hd(name)} { Popup $t(need_name) $hd(w) return } if {"edit" != $hd(mode) && [info exists editor($hd(name))]} { Popup $t(name_occupied) $hd(w) return } if {![IsExecutable [lindex $hd(cmd) 0]]} { Popup $t(illegal_file_spec) $hd(w) return } set editor($hd(name)) [list $hd(cmd) $hd(charset)] if {"edit" == $hd(mode)} { if {$hd(name) != $hd(oldname)} { eval $hd(changeproc) [list $hd(name)] unset editor($hd(oldname)) } } else { eval $hd(addproc) [list $hd(name)] } incr editorsChanged } destroy $hd(w) } # EditorsEditClosed -- # # Destroy handler # # Arguments: # handler - Handler describing the windo proc EditorsEditClosed {handler} { upvar \#0 $handler hd global b ::tkrat::winctl::RecordGeometry editorsEdit $hd(w) foreach bn [array names b $hd(w).*] {unset b($bn)} catch {focus $hd(oldfocus)} unset hd } # ComposeEEditorPopulate -- # # Populate the external editor menu # # Arguments: # handler - Handler identifying the compose window # args - Arguments provided by trace proc ComposeEEditorPopulate {handler args} { upvar \#0 $handler hd global editors t $hd(eeditm) delete 0 end foreach e $editors { $hd(eeditm) add command -label $e \ -command "ComposeEEdit $handler [list $e] ; \ set ${handler}(eeditor)(eeditor) [list $e]" } if {[llength $editors] > 0} { set hd(eeditor) [lindex $editors 0] $hd(eeditb) configure -state normal } else { set hd(eeditor) $t(external_editor) $hd(eeditb) configure -state disabled } } # ComposeWrapCited -- # # Wraps the cited message # # Arguments: # handler - Handler identifying the compose window proc ComposeWrapCited {handler} { upvar \#0 $handler hd set s 1.0 rat_edit::storeSnapshot $hd(composeBody) while {[llength [set r [$hd(composeBody) tag nextrange Cited $s]]]} { set start [lindex $r 0] set end [lindex $r 1] set n [RatWrapCited [$hd(composeBody) get $start $end]] $hd(composeBody) delete $start $end $hd(composeBody) insert $start $n {noWrap Cited no_spell} set s [lindex $r 1] } } # CompareAddresses -- # # Compares two list of addresses and returns 0 if they are equal # # Arguments: # role - Role to operate under # adr1, adr2 - List of addresses proc CompareAddresses {role adr1 adr2} { set l1 [lsort [RatExtractAddresses $role $adr1]] set l2 [lsort [RatExtractAddresses $role $adr2]] return [string compare $l1 $l2] } # UpdateComposeRole # # Updates variables depeneding on role # # Arguments: # handler - Handler identifying the compose window proc UpdateComposeRole {handler} { global t option upvar \#0 $handler mh set role $mh(role) foreach v {from reply_to bcc} { if {![CompareAddresses $mh(orig,role) $mh($v) $mh(orig,$v)]} { set mh($v) $option($role,$v) } set mh(orig,$v) $option($role,$v) } set mh(orig,role) $role set gen [RatGenerateAddresses $handler] set mh(from) [lindex $gen 0] set mh(sender) [lindex $gen 1] if {0 == $mh(pgp_sign) || 0 == $mh(pgp_sign_explicit)} { set mh(pgp_sign) $option($role,sign_outgoing) } set mh(pgp_signer) $option($mh(role),sign_as) ComposeUpdateHeaderEntries $handler wm title $mh(toplevel) "$mh(title) ($option($mh(role),name))" if {1 >= [llength [split $mh(from) .]] && 0 == $option(force_send)} { set state disabled Popup $t(no_send_bad_host) $mh(toplevel) } else { set state normal } foreach sb $mh(sendButtons) { $sb configure -state $state } if {$mh(role_sig) && ![file isdirectory $option($role,signature)] && [file readable $option($role,signature)]} { set fh [open $option($role,signature) r] set sigtext [read -nonewline $fh] close $fh set insert [$mh(composeBody) index insert] if {{} != [$mh(composeBody) tag ranges sig]} { $mh(composeBody) delete sig.first sig.last } elseif {$option(sigdelimit)} { $mh(composeBody) insert end "\n" {} "-- " {noWrap no_spell} } $mh(composeBody) insert end "\n$sigtext" {noWrap no_spell sig} $mh(composeBody) mark set insert $insert } } # ComposeCreateMsg -- # # Creates a message from a compose window # # Arguments: # handler - Handler identifying the compose window proc ComposeCreateMsg {handler {extra_envelope {}}} { global composeHeaderList composeAutoHeaderList option t charsetMapping \ composeAdrHdrList rat_tmp upvar \#0 $handler mh # Envelope set envelope {} foreach h [concat $composeHeaderList $composeAutoHeaderList] { if {[info exists mh($h)] && [string length $mh($h)]} { lappend envelope [list $h $mh($h)] if {-1 != [lsearch $composeAdrHdrList $h]} { regsub -all "\n" $mh($h) "\n " value lappend envelope [list X-TkRat-Original-$h $value] } } } # Text body if {[info exists mh(composeBody)]} { if [catch {$mh(composeBody) get 1.0 end-1c} bodydata] { set bodydata {} } set tags {} foreach tag {Cited noWrap no_spell sig} { lappend tags [list $tag [$mh(composeBody) tag ranges $tag]] } lappend envelope [list X-TkRat-Internal-Tags $tags] # Determine suitable charset set p(charset) $mh(charset) if {[info exists bh(parameter)]} { set params $bh(parameter) lappend params {a a} array get p $params } if {"auto" == $p(charset)} { set fallback $option(charset) set p(charset) [RatCheckEncodings bodydata \ $option(charset_candidates)] } else { set fallback $p(charset) set p(charset) [RatCheckEncodings bodydata $mh(charset)] } if {"" == $p(charset)} { if {0 != [RatDialog $mh(toplevel) $t(warning) $t(bad_charset) {} \ 0 $t(continue) $t(abort)]} { return {} } set p(charset) $fallback } # Find encoding set fn [RatTildeSubst $rat_tmp/rat.[RatGenId]] set fh [open $fn w] if {[info exists charsetMapping($p(charset))]} { fconfigure $fh -encoding $charsetMapping($p(charset)) } else { fconfigure $fh -encoding $p(charset) } puts -nonewline $fh $bodydata close $fh set encoding [RatGetCTE $fn] file delete $fn } else { set bodydata {} set encoding "" } lappend envelope [list message_id [RatGenerateMsgId $mh(role)]] lappend envelope [list X-TkRat-Internal-Role $mh(role)] lappend envelope [list X-TkRat-Internal-PGPActions \ [list $mh(pgp_sign) $mh(pgp_encrypt)]] if {[string length $mh(save_to)]} { lappend envelope [list X-TkRat-Internal-Save-To $mh(save_to)] } foreach e $extra_envelope { lappend envelope $e } # Prepare parameters array set params {} foreach name [array names p] { lappend params [list $name $p($name)] } # Collect into body entity set body [list text plain $params $encoding inline {} {} \ [list utfblob $bodydata]] # Handle attachments if { 0 < [llength $mh(attachmentList)]} { set attachments [list $body] foreach a $mh(attachmentList) { upvar \#0 $a bh set body_header {} foreach h {content_description content_id} { if {[info exists bh($h)] && [string length $bh($h)]} { lappend body_header [list $h $bh($h)] } } foreach v {parameter disp_parm} { if {![info exists bh($v)]} { set bh($v) {} } } if {![info exists bh(encoding)]} { set bh(encoding) 7bit } set bp [list $bh(type) $bh(subtype) $bh(parameter) $bh(encoding) \ attachment $bh(disp_parm) $body_header \ [list file $bh(filename)]] lappend attachments $bp } set body [list multipart mixed {} 7bit {} {} {} $attachments] } return [RatCreateMessage $mh(role) [list $envelope $body]] } # RatSendFailed -- # # Handles a failed send. Does this bu first poping up a error dialog and # then restart composing of the message. # # Arguments: # name - Handler of message # reason - Text string desribing failure proc RatSendFailed {name reason} { global t catch { Popup "$t(send_failed)\n$reason" ComposeContinue $name } } # RatSaveOutgoing -- # # Save a copy of a sent message to the given folder # # Arguments: # msg - Handler of message # folder - Folder to save message in proc RatSaveOutgoing {msg folder} { global t set msg2 [$msg duplicate] $msg2 remove_internal if {![catch {$msg2 copy $folder} err]} { rename $msg2 "" return } # The save failed, present dialog which allows them to select a new folder set handler save_outgoing_failed set w .$handler toplevel $w -class TkRat set subject "" set recipients "" foreach h [$msg2 headers] { set hn [string tolower [lindex $h 0]] if {"subject" == $hn} { set subject [lindex $h 1] } elseif {-1 != [lsearch -exact {to cc bcc} $hn]} { if {"" != $recipients} { set recipients "$recipients, [lindex $h 1]" } else { set recipients [lindex $h 1] } } } if {[string length $subject] > 40} { set subject "[string range $subject 0 36]..." } if {[string length $recipients] > 40} { set recipients "[string range $recipients 0 36]..." } message $w.msg -justify left -text $t(save_outgoing_failed) -aspect 600 label $w.subject_l -text "$t(subject):" -anchor e label $w.subject_v -text $subject -anchor w label $w.recipients_l -text "$t(recipients):" -anchor e label $w.recipients_v -text $recipients -anchor w frame $w.b -bd 5 button $w.b.cancel -text " $t(cancel) " \ -command "destroy $w; rename $msg2 {}" menubutton $w.b.save -text $t(save_to) -indicatoron 1 \ -menu $w.b.save.m -relief raised -underline 0 \ -padx 5 -pady 2 menu $w.b.save.m -tearoff 0 -postcommand \ "RatSaveOutgoingPostMenu $w $w.b.save.m $msg2" pack $w.b.save $w.b.cancel -side left -expand 1 grid $w.msg - grid $w.subject_l $w.subject_v -sticky ew grid $w.recipients_l $w.recipients_v -sticky ew grid $w.b - -sticky ew bind $w "$w.b.cancel invoke" bind $w.msg "::tkrat::winctl::RecordGeometry saveOutgoing $w" wm title $w $t(save_outgoing_failed_title) ::tkrat::winctl::SetGeometry saveOutgoing $w } # RatSaveOutgoingPostMenu -- # # Create the want to save to menu # # Arguments: # w - Name of window # m - Name of menu # msg - Message to save proc RatSaveOutgoingPostMenu {w m msg} { global t $m delete 0 end VFolderBuildMenu $m 0 "RatSaveOutgoingDo $w $msg" 1 $m add separator $m add command -label $t(to_file)... \ -command "RatSaveOutgoingDo $w $msg \ \[InsertIntoFile [winfo toplevel $w]\]" $m add command -label $t(to_dbase)... \ -command "RatSaveOutgoingDo $w $msg \ \[InsertIntoDBase [winfo toplevel $w]\]" FixMenu $m } proc RatSaveOutgoingDo {w msg save_to} { if {"" == $save_to} { return } if {1 == [llength $save_to]} { global vFolderDef set def $vFolderDef($save_to) } else { set def $save_to } if {![catch {$msg copy $def} err]} { rename $msg "" destroy $w } } # ComposeStoreBackup -- # # Store a backup of the current message # # Arguments: # handler - The handler for the active compose session # schedule - True if another backup should be scheduled proc ComposeStoreBackup {handler schedule} { upvar \#0 $handler mh global option vFolderDef vFolderHold if {![info exists mh(hold_fh)]} { set mh(hold_fh) [RatOpenFolder $vFolderDef($vFolderHold)] } set msg [ComposeCreateMsg $handler \ [list [list X-TkRat-Internal-AutoBackup [clock seconds]]]] foreach u [$mh(hold_fh) list "%u"] { set uids($u) 1 } $mh(hold_fh) insert $msg rename $msg "" foreach u [$mh(hold_fh) list "%u"] { if {![info exists uids($u)]} { break } } if {[info exists mh(old_backup)]} { ComposeRemoveOldBackup $mh(hold_fh) $mh(old_backup) } set mh(old_backup) $u if {$option(compose_backup) > 0 && $schedule} { set mh(next_backup) [after [expr $option(compose_backup)*1000] \ [list ComposeStoreBackup $handler 1]] } } proc ComposeRemoveOldBackup {hold_fh old_backup} { set uids [$hold_fh list "%u"] set index [lsearch -exact $uids $old_backup] if {-1 != $index} { $hold_fh setFlag $index deleted 1 $hold_fh update sync } } # ComposeDoFinalBackup -- # # Remove the backup of the current message # # Arguments: # handler - The handler for the active compose session proc ComposeDoFinalBackup {handler} { upvar \#0 $handler mh global option if {$mh(final_backup_done)} { return } set mh(final_backup_done) 1 if {[info exists mh(next_backup)]} { catch {after cancel $mh(next_backup)} } ComposeStoreBackup $handler 0 after [expr $option(compose_last_chance)*1000] \ "ComposeRemoveOldBackup $mh(hold_fh) $mh(old_backup); $mh(hold_fh) close" } # ComposeStoreSnapshot -- # # Store a snopshot of the message being composed # # Arguments: # handler - The handler for the active compose session proc ComposeStoreSnapshot {handler} { upvar \#0 $handler mh global option vFolderDef vFolderHold if {![info exists mh(hold_fh)]} { set mh(hold_fh) [RatOpenFolder $vFolderDef($vFolderHold)] } set msg [ComposeCreateMsg $handler] $mh(hold_fh) insert $msg rename $msg "" } # ComposeSetWrap -- # # Set the wrap lines mode # # Arguments: # handler - The handler for the active compose session proc ComposeSetWrap {handler} { upvar \#0 $handler mh global option set option(do_wrap) $mh(do_wrap) SaveOptions rat_edit::setWrap $mh(composeBody) $mh(do_wrap) } # ComposeSetMarkWrap -- # # Set the mark non-wrappable mode # # Arguments: # handler - The handler for the active compose session proc ComposeSetMarkWrap {handler} { upvar \#0 $handler mh global option set option(mark_nowrap) $mh(mark_nowrap) SaveOptions $mh(composeBody) tag configure noWrap -underline $mh(mark_nowrap) } tkrat_2.2cvs20100105-dfsg.orig/tkrat/convert.tcl000066400000000000000000001040561137544547100213230ustar00rootroot00000000000000# convert.tcl -- # # This file contains code which converts old version of the database and # vfolders to the latest version. # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # FixDbase4 -- # # Convert the database from version 4 to 5. # # Arguments: proc FixDbase4 {} { global fix_scale option t # Check with user if { 0 != [RatDialog "" $t(upgrade_dbase) $t(old_dbase) {} \ 0 $t(continue) $t(abort)]} { exit 1 } wm withdraw . set dir [RatTildeSubst $option(dbase_dir)] # Tell user what we are doing set w .upgdbase toplevel $w -class TkRat wm title $w "Upgrade dbase to version 5" scale $w.scale -length 6c -showvalue 0 -sliderlength 5 \ -variable fix_scale -orient horiz pack $w.scale -side top -padx 5 -pady 5 ::tkrat::winctl::SetGeometry fixDbase $w # Find how many entries we must fix set fh [open $dir/index.info r] gets $fh l close $fh set entries [lindex $l 1] # Fix index.changes-file if {[file readable $dir/index.changes]} { set newIndex [open $dir/index.changes.new w] set oldIndex [open $dir/index.changes r] while {0 < [gets $oldIndex l]} { if {"a" == [string index $l 0]} { incr entries } else { puts $newIndex $l } } close $oldIndex close $newIndex } # Fix index set fix_scale 0 $w.scale configure -to $entries set lock [open $dir/lock w] puts $lock "Updating" close $lock set newIndex [open $dir/index.new w] set oldIndex [open $dir/index r] fconfigure $newIndex -encoding utf-8 for {set fix_scale 0} {$fix_scale < $entries} {incr fix_scale} { update idletasks # Read old index entry for {set i 0} {$i < 11} {incr i} { gets $oldIndex line($i) } # Get Message-Id and references from stored message set msgid {} set ref {} set m [open $dir/dbase/$line(10) r] gets $m joined while {0 < [gets $m l]} { if {[regexp "^( |\t)" $l]} { set joined "$joined$l]" continue } if {[regexp -nocase {^message-id:[^<]*(<[^>]+>)} $joined {} r]} { set msgid $r } if {[regexp -nocase {^in-reply-to:[^<]*(<[^>]+>)} $joined {} r]} { set ref $r } if {"" == $ref && [regexp -nocase \ {^references:[^<]*(<[^>]+>)} $joined {} r]} { set ref $r } set joined $l } close $m # Remember offset of this entry set offset($i) [tell $newIndex] # Write entry for {set i 0} {$i < 3} {incr i} { puts $newIndex $line($i) } puts $newIndex $msgid puts $newIndex $ref for {set i 3} {$i < 11} {incr i} { puts $newIndex $line($i) } } close $newIndex close $oldIndex # Generate new files if {[file readable $dir/index.changes.new]} { file rename $dir/index.changes $dir/index.changes.4 file rename -force -- $dir/index.changes.new $dir/index.changes } file rename -force -- $dir/index $dir/index.4 file rename -force -- $dir/index.new $dir/index file rename -force -- $dir/index.info $dir/index.info.4 set f [open $dir/index.info w] puts $f "5 $entries" close $f file delete -force -- $dir/lock ::tkrat::winctl::RecordGeometry fixDbase $w destroy $w } # FixDbase3 -- # # Convert the database from version 3 to 4. # # Arguments: proc FixDbase3 {} { global fix_scale option t # Check with user if { 0 != [RatDialog "" $t(upgrade_dbase) $t(old_dbase) {} \ 0 $t(continue) $t(abort)]} { exit 1 } wm withdraw . set dir [RatTildeSubst $option(dbase_dir)] FixOldDbase dir # Tell user what we are doing set w .upgdbase toplevel $w -class TkRat wm title $w "Upgrade dbase to version 4" scale $w.scale -length 6c -showvalue 0 -sliderlength 5 \ -variable fix_scale -orient horiz pack $w.scale -side top -padx 5 -pady 5 ::tkrat::winctl::SetGeometry fixDbase $w # Find how many entries we must fix set fh [open $dir/index.ver r] gets $fh version gets $fh entries close $fh set fix_scale 0 $w.scale configure -to $entries # Do actual fixing set lock [open $dir/lock w] puts $lock "Updating" close $lock set newIndex [open $dir/index.new w] set oldIndex [open $dir/index r] for {set fix_scale 0} {$fix_scale < $entries} {incr fix_scale} { update idletasks for {set i 0} {$i < 14} {incr i} { gets $oldIndex line($i) } # To set result $line(0) regsub {@.+$} $result {} name while {[regexp {[a-zA-Z][ ]+[a-zA-Z]} $result match]} { regsub {[ ]+} $match {,} subst regsub $match $result $subst result } puts -nonewline $newIndex $result regsub {(, )+} $line(1) {} result if {[string length $result]} { puts -nonewline $newIndex " ($result)" } puts $newIndex "" # From set result $line(2) while {[regexp {[a-zA-Z][ ]+[a-zA-Z]} $result match]} { regsub {[ ]+} $match {,} subst regsub $match $result $subst result } puts -nonewline $newIndex $result regsub {(, )+} $line(3) {} result if {[string length $result]} { puts -nonewline $newIndex " ($result)" } puts $newIndex "" # Cc puts $newIndex $line(4) # Subject puts $newIndex $line(5) # Date (UNIX time_t as a string) puts $newIndex $line(6) # Keywords (SPACE separated list) puts $newIndex $line(7) # Size puts $newIndex [file size $dir/dbase/$line(13)] # Status set status "" set msgFh [open $dir/dbase/$line(13) r] while {[string length [gets $msgFh hline]]} { if { 0 == [string length $hline]} { break } if {![string compare status: [string tolower [lindex $hline 0]]]} { set status [lindex $hline 1] break } } close $msgFh puts $newIndex $status # Expiration time (UNIX time_t as a string) if {[string length $line(11)]} { puts $newIndex [RatTime +100] } else { puts $newIndex "" } # Expiration event (none, remove, incoming, backup or custom *) puts $newIndex $line(12) # Filename regsub {[%,].+} $name {} fdir if {[file exists $dir/dbase/$fdir/.seq]} { set seqFh [open $dir/dbase/$fdir/.seq r+] set sequence [expr {1+[gets $seqFh]}] seek $seqFh 0 puts $seqFh $sequence close $seqFh } else { set sequence 0 if {![file isdirectory $dir/dbase/$fdir]} { exec mkdir $dir/dbase/$fdir } set seqFh [open $dir/dbase/$fdir/.seq w] puts $seqFh $sequence close $seqFh } set modSequence "" for {set i [expr {[string length $sequence]-1}]} {$i>=0} {incr i -1} { set modSequence $modSequence[string index $sequence $i] } set filename $fdir/$modSequence puts $newIndex $filename exec mv $dir/dbase/$line(13) $dir/dbase/$filename } close $newIndex close $oldIndex set infoFH [open $dir/index.info w] puts $infoFH "4 $entries" close $infoFH file delete -force -- $dir/index.ver file delete -force -- $dir/index.changes file delete -force -- $dir/index.read exec mv $dir/index.new $dir/index file delete -force -- $dir/lock # Find unlinked entries pack forget $w.scale label $w.message -text "Looking for unlinked entries" pack $w.message update set unlinkedList [exec find $dir/dbase -name *@* -print] ::tkrat::winctl::RecordGeometry fixDbase $w if {[llength $unlinkedList]} { global vFolderDef foreach file $unlinkedList { exec cat $file >>[RatTildeSubst ~/UnlinkedMessages] file delete -force -- $file } destroy $w RatDialog "" $t(unlinked_messages) \ "$t(unl_m1) [llength $unlinkedList] $t(unl_m2)" {} 0 \ $t(continue) set id [expr {[lindex \ [lsort -integer -decreasing [array names vFolderDef]] 0] + 1}] set vFolderDef($id) [list UnlinkedMessages file {} \ [RatTildeSubst ~/UnlinkedMessages]] set vFolderDef(0) [replace $vFolderDef(0) 3 3 \ [concat [lindex $vFolderDef(0) 3] $id]] VFolderWrite } else { destroy $w } wm withdraw . } # FixOldDbase -- # # This repairs any inconstencies in the database that are created by # a fault in the logic in the old version. # # Arguments: # dir - Directory in which to find dbase proc FixOldDbase {dir} { global option # Check for existance if { 0 == [file exists $dir/index]} { return } # The database is good so far set good 1 # First check for locks if { 1 == [file exists $dir/index.read]} { if { 0 < [file size $dir/index.read]} { set result [RatDialog "" "Dbase in use?" \ "I find a lock on the database.\ Are you running another copy of tkrat somewhere?" {} 1 Yes No ] if { $result == 0} { # Another copy is runing don't touch the database return } else { # Possibly corrupt database set good 0 catch "file delete -force -- $dir/index.read" } } } # Now do a quick consistency check of the database if { 1 == [file exists $dir/index.lock]} { set good 0 catch "file delete -force -- $dir/index.lock" } if { 1 == [file exists $dir/index.changes] } { set good 0 catch "file delete -force -- $dir/index.changes" } if { 0 == [file exists $dir/index.ver] } { set good 0 } else { set fh [open $dir/index.ver r] gets $fh version gets $fh orig_entries close $fh } if { 1 == $good } { scan [exec wc -l $dir/index] "%d" lines if { [expr {($lines/14)*14}] != $lines } { # Not even divisible by 14 set good 0 } else { if { [expr {$lines/14}] != $orig_entries} { # Mismatch with info in index.ver set good 0 } } } if { 1 == $good } { # Dbase seems to be OK return } # Tell the user set w .dbc toplevel $w -class TkRat wm title $w Dbase wm iconname $w Dbase message $w.msg -text "Database corrupt. Fixing it..." -aspect 800 pack $w.msg -padx 10 -pady 10 ::tkrat::winctl::SetGeometry fixDbase2 $w update DoFixOldDbase $dir # Final cleanup ::tkrat::winctl::RecordGeometry fixDbase2 $w destroy $w } # DoFixOldDbase -- # # This routine does the acutual fixing # # Arguments: # dir - Directory of the dbase proc DoFixOldDbase {dir} { # Initialize set entries 0 set in [open $dir/index r] set out [open $dir/nindex w] while { 0 < [gets $in line(0)] && 0 == [eof $in]} { # Read 13 lines for {set i 1} {$i < 14} {incr i} { gets $in line($i) } # Check that the last line contains a /< sequence while { 0 == [regexp /< $line(13)] } { # Nope, corrupt entry... fix it for {set i 1} {$i < 14} {incr i} { if { 1 == [regexp {^ |^ } $line($i)] } { set p [expr {$i-1}] set line($p) "$line($p)$line($i)" for {set j $i} {$j < 13} {incr j} { set line($j) $line([expr {$j+1}]) } gets $in line(13) } } if { 1 == [eof $in]} { tk_Dialog Error "Can't fix database, giving up" {} 0 Ok exit } } # Write this entry for {set i 0} {$i < 14} {incr i} { puts $out $line($i) } incr entries # Consistency check if { 1 == [eof $in]} { tk_Dialog Error "Can't fix database, giving up" {} 0 Ok exit } } close $in close $out exec mv $dir/nindex $dir/index set fh [open $dir/index.ver w] puts $fh 2 puts $fh $entries close $fh } # FixVFolderList -- # # Upgrade the vfolderlist if needed. # # Arguments: proc FixVFolderList {} { global vfolder_list vfolder_def vFolderStructIdent vFolderStruct \ vFolderDef vFolderDefIdent vFolderVersion option set vFolderStructIdent 0 set vFolderStruct(0) {} if {![info exists vfolder_list]} { return } FixVFolderStruct $vfolder_list unset vfolder_list set vFolderDefIdent 1 set vFolderDef(0) $option(default_folder) set vFolderStruct(0) [linsert $vFolderStruct(0) 0 {vfolder 0 INBOX}] foreach vf [array names vfolder_def] { if {![info exists vFolderDef($vf)]} { continue } if {$vf > $vFolderDefIdent} { set vFolderDefIdent $vf } set l $vfolder_def($vf) set n $vFolderDef($vf) if {![string compare [lindex $l 0] file]} { set vFolderDef($vf) [list $n file {} [lindex $l 1]] } else { set l2 [lindex $l 2] set vFolderDef($vf) [list $n dbase {} \ [lindex $l2 0] [lindex $l2 1] \ [string trimleft [lindex $l2 3] +]] } } incr vFolderDefIdent set vFolderVersion 4 VFolderWrite } # FixVFolderStruct -- # # Fixes one menu in the vFolderStruct # # Arguments: # content - The menu to fix (in the old format) proc FixVFolderStruct {content} { global vFolderStructIdent vFolderStruct vFolderDef set ident $vFolderStructIdent incr vFolderStructIdent foreach elem $content { if {![string compare [lindex $elem 1] dir]} { lappend vFolderStruct($ident) [list struct \ [FixVFolderStruct [lindex $elem 2]] [lindex $elem 0]] } else { set vFolderDef([lindex $elem 2]) [lindex $elem 0] lappend vFolderStruct($ident) [list vfolder [lindex $elem 2] \ [lindex $elem 0]] } } return $ident } # UpgradeVFolderList4to5 -- # # Upgrade the vfolderlist from version 4 to version 5 # This upgrade removes the pair of extra braces around the folder specs # # Arguments: proc UpgradeVFolderList4to5 {} { global vFolderDef vFolderVersion foreach n [array names vFolderDef] { set p [lindex $vFolderDef($n) 1] if {"pop3" != $p && "imap" != $p} { continue } set d $vFolderDef($n) set vFolderDef($n) [lreplace $d 3 3 [lindex [lindex $d 3] 0]] } set vFolderVersion 5 } # UpgradeVFolderList5to6 -- # # Upgrade the vfolderlist from version 5 to version 6 # This upgrade Adds monitor and watch to the inbox and fixes so that the # features list always has an even number of elements # # Arguments: proc UpgradeVFolderList5to6 {} { global vFolderDef vFolderVersion vFolderInbox foreach id [array names vFolderDef] { set f [lindex $vFolderDef($id) 2] if {-1 != [set i [lsearch -exact trace $f]]} { set f [linsert $f [expr {$i+1}] 1] } if {-1 != [set i [lsearch -exact subscribed $f]]} { set f [linsert $f [expr {$i+1}] 1] } if {$vFolderInbox == $id} { set f [concat $f {monitor 1 watch 1}] } set vFolderDef($id) [lreplace $vFolderDef($id) 2 2 $f] } set vFolderVersion 6 } # GetHid -- # # Create a host definition, or reuse an old one if a match is found # # Arguments: # def - Host definition {host port flags user} proc GetHid {def} { global mailServer foreach m [array names mailServer] { if {![string compare $mailServer($m) $def]} { return $m } } set i 0 set hid [lindex $def 0] while {[info exists mailServer($hid)]} { set hid [lindex $def 0]-[incr i] } set mailServer($hid) $def return $hid } # UpgradeVFolderDef6to7 -- # # Upgrade a folder definition from version 6 to version 7 # # Arguments: # d - Folder definition proc UpgradeVFolderDef6to7 {d} { global option # Make a backup file copy -force $option(ratatosk_dir)/vfolderlist \ $option(ratatosk_dir)/vfolderlist.backup switch -regexp [lindex $d 1] { imap|dis { regexp {\{([^:\}]+)(:([0-9]+))?\}(.*)} [lindex $d 3] \ unused host u2 port path set md [list $host $port {} [lindex $d 4]] set d [concat [lrange $d 0 2] [GetHid $md] [list $path]] } pop3 { regexp {\{([^/:\}]+)(:([0-9]+))?/pop3\}} [lindex $d 3] \ unused host u2 port if {"" == $port} { set port 110 } set md [list $host $port {pop3} [lindex $d 4]] set d [concat [lrange $d 0 2] [GetHid $md]] } dir { set d [lreplace $d 1 1 file] } dbase { set d [list \ [lindex $d 0] \ [lindex $d 1] \ [lindex $d 2] \ [lindex $d 4] \ [lindex $d 5] \ [list and keywords [lindex $d 3]]] } file|mh|dynamic { # Nothing special needed } } return $d } # UpgradeVFolderList6to7 -- # # Upgrade the vfolderlist from version 6 to version 7 # # Arguments: proc UpgradeVFolderList6to7 {} { global vFolderDef vFolderVersion vFolderInbox vFolderDefIdent \ vFolderStruct vFolderStructIdent vFolderSave unset vFolderDefIdent vFolderStructIdent # Create mapping to new ids set nextId 0 foreach id [array names vFolderStruct] { set tmps($id) $vFolderStruct($id) if {0 == $id} { set structmap($id) $id } else { set structmap($id) [incr nextId] } unset vFolderStruct($id) } foreach id [array names vFolderDef] { set tmpd($id) $vFolderDef($id) set defmap($id) [incr nextId] unset vFolderDef($id) } # Find names of menus set structname(0) {} foreach id [array names tmps] { foreach e $tmps($id) { if {"struct" == [lindex $e 0]} { set structname([lindex $e 1]) [lindex $e 2] } } } # Convert vFolderStruct entries foreach id [array names structname] { set s {} foreach e $tmps($id) { switch [lindex $e 0] { "struct" { set n $structmap([lindex $e 1]) } "vfolder" { set n $defmap([lindex $e 1]) } "import" { set n $defmap([lindex $e 1]) set imported([lindex $e 1]) 1 } } lappend s $n } set vFolderDef($structmap($id)) [list $structname($id) struct {} $s] } # Convert vFolderDef entries foreach id [array names tmpd] { if {[info exists imported($id)]} { set pat [lindex $tmpd($id) 4] set fl(trace) 0 array set fl [lindex $tmpd($id) 2] if {0 != $fl(trace)} { set flags [list reimport session] } else { set flags [list reimport manually] } if {"imap" == [lindex $tmpd($id) 1] || "dis" == [lindex $tmpd($id) 1]} { if {"" == [lindex $tmpd($id) 8]} { set host \{[lindex $tmpd($id) 5]\}[lindex $tmpd($id) 7] } else { set host \{[lindex $tmpd($id) 5]\:[lindex $tmpd($id) 8]\}[lindex $tmpd($id) 7] } set d [concat [lrange $tmpd($id) 0 1] \ [list [lindex $tmpd($id) 3]] \ [list $host] [lindex $tmpd($id) 6]] } else { set d [concat [lrange $tmpd($id) 0 1] \ [list [lindex $tmpd($id) 3]] \ [lrange $tmpd($id) 5 end]] } } else { set d $tmpd($id) } set d [UpgradeVFolderDef6to7 $d] if {[info exists imported($id)]} { set d [list [lindex $tmpd($id) 0] import $flags $d $pat {}] } set vFolderDef($defmap($id)) $d } set vFolderVersion 7 if {"" != $vFolderInbox} { set vFolderInbox $defmap($vFolderInbox) } if {"" != $vFolderSave} { set vFolderSave $defmap($vFolderSave) } } # UpgradeVFolderList7to8 -- # # Upgrade the vfolderlist from version 7 to version 8 # # Arguments: proc UpgradeVFolderList7to8 {} { global vFolderDef vFolderVersion foreach id [array names vFolderDef] { switch -regexp [lindex $vFolderDef($id) 1] { imap|dis { set enc [RatEncodeQP system [lindex $vFolderDef($id) 4]] set vFolderDef($id) [lreplace $vFolderDef($id) 4 4 $enc] } file|mh|dynamic { set enc [RatEncodeQP system [lindex $vFolderDef($id) 3]] set vFolderDef($id) [lreplace $vFolderDef($id) 3 3 $enc] } } } set vFolderVersion 8 } # FixOldOptions -- # # Read old options files and try to adapt to modern options # # Arguments: proc FixOldOptions {} { upvar \#0 option newOption source $newOption(ratatosk_dir)/ratatoskrc.gen set changed 0 if {[info exists option(show_header)]} { set newOption(show_header_selection) $option(show_header) set changed 1 } if {[info exists option(reply_lead)]} { set newOption(reply_lead) $option(reply_lead) set changed 1 } if {[info exists option(signature)]} { set newOption(signature) $option(signature) set changed 1 } if {[info exists option(xeditor)]} { set newOption(editor) $option(xeditor) set changed 1 } if {[info exists option(watcher_geom)]} { set newOption(watcher_geometry) $option(watcher_geom) set changed 1 } if {[info exists option(printcmd)]} { set newOption(print_command) $option(printcmd) set changed 1 } if {$changed} { SaveOptions } file delete -force [RatTildeSubst $newOption(ratatosk_dir)/ratatoskrc.gen] } # ScanAliases -- # # See if the user has any old alias files, and if then scan them. # # Arguments: proc ScanAliases {} { global option t set n 0 if {[file readable ~/.mailrc]} { incr n [ReadMailAliases ~/.mailrc $option(default_book)] } if {[file readable ~/.elm/aliases.text]} { incr n [ReadElmAliases ~/.elm/aliases.text $option(default_book)] } if {[file readable ~/.addressbook]} { incr n [ReadPineAliases ~/.addressbook $option(default_book)] } if {$n} { foreach book $option(addrbooks) { if {$option(default_book) == [lindex $book 0]} { set file [lindex $book 2] break } } RatAlias save $option(default_book) $file } set option(scan_aliases) 3 SaveOptions AliasesPopulate } # AddImapPorts -- # # Add port spexification to all imap folders (except those that already # have it. # # Arguments: proc AddImapPorts {} { global option vFolderDef VFolderRead foreach id [array names vFolderDef] { if {[string compare imap [lindex $vFolderDef($id) 1]]} { continue } set spec [lindex $vFolderDef($id) 2] regsub {(\{[^\{\}:]*)\}} $spec "\\1:$option(imap_port)\}" spec set vFolderDef($id) [lreplace $vFolderDef($id) 2 2 $spec] } VFolderWrite } # ConvertHold -- # # Convert the old-style message hold to a new folder # # Arguments: # dir - Hold to convert # var - Name of vfolderdef which contains the new folder def proc ConvertHold {dir var} { global vFolderDef upvar \#0 $var v # Load old hold functions package require ratatosk_old 2.2 # Get def of new folder VFolderRead set fh [RatOpenFolder $vFolderDef($v)] ComposeLoad # Loop over messages in hold while {0 < [llength [RatHold $dir list]]} { set hd [RatHold $dir extract 0] set msg [ConvertHoldMsg $hd] $fh insert $msg rename $msg "" } # Mark all messages in hold as read foreach i [$fh flagged seen 0] { $fh setFlag $i seen 1 } $fh close } # ConvertHoldMsg -- # # Prepares a message extracted from the hold to be made into a real message # # Arguments: # mgh - handler of the extracted message proc ConvertHoldMsg {mgh} { global charsetMapping option t composeHeaderList rat_tmp upvar \#0 $mgh mh if {[info exists mh(body)]} { upvar \#0 $mh(body) bh if {![string compare "$bh(type)/$bh(subtype)" text/plain]} { set edit $mh(body) set children {} } elseif {![string compare "$bh(type)" multipart]} { set children $bh(children) upvar \#0 [lindex $children 0] ch1 if {![string compare "$ch1(type)/$ch1(subtype)" text/plain]} { set edit [lindex $children 0] set children [lreplace $children 0 0] } else { set edit {} } } else { set edit {} set children $mh(body) } if {[info exists bh(pgp_sign)]} { set mh(pgp_sign) $bh(pgp_sign) set mh(pgp_encrypt) $bh(pgp_encrypt) } if {[string length $edit]} { upvar \#0 $edit bp set fh [open $bp(filename) r] if {[info exists bp(parameter)]} { foreach lp $bp(parameter) { set p([lindex $lp 0]) [lindex $lp 1] } } if {[info exists p(charset)]} { set charset $p(charset) } else { if {[info exists mh(charset)]} { set charset $mh(charset) } else { set charset auto } } if {"auto" == $charset} { set charset utf-8 } set mh(charset) $charset fconfigure $fh -encoding $charsetMapping($charset) set mh(data) [read $fh] set mh(data_tags) {} close $fh if {$bp(removeFile)} { catch "file delete -force -- $bp(filename)" } } set mh(attachmentList) $children } ####################################################################### # Create message # Envelope set envelope {} foreach h $composeHeaderList { if {[string length $mh($h)]} { lappend envelope [list $h $mh($h)] } } lappend envelope [list X-TkRat-Internal-Role $mh(role)] # Determine suitable charset catch {unset p} set p(charset) $mh(charset) if {[info exists bh(parameter)]} { foreach lp $bp(parameter) { set p([lindex $lp 0]) [lindex $lp 1] } } if {"auto" == $p(charset)} { set fallback $option(charset) set p(charset) [RatCheckEncodings mh(data) \ $option(charset_candidates)] } else { set fallback $p(charset) set p(charset) [RatCheckEncodings mh(data) $mh(charset)] } if {"" == $p(charset)} { if {0 != [RatDialog $mh(toplevel) $t(warning) $t(bad_charset) {} \ 0 $t(continue) $t(abort)]} { return {} } set p(charset) $fallback } # Prepare parameters array set params {} foreach name [array names p] { lappend params [list $name $p($name)] } # Find encoding set fn [RatTildeSubst $rat_tmp/rat.[RatGenId]] set fh [open $fn w] if {[info exists charsetMapping($p(charset))]} { fconfigure $fh -encoding $charsetMapping($p(charset)) } else { fconfigure $fh -encoding $p(charset) } puts -nonewline $fh $mh(data) close $fh set encoding [RatGetCTE $fn] file delete $fn # Collect into body entity set body [list text plain $params $encoding inline {} {} \ [list utfblob $mh(data)]] # Handle attachments if { 0 < [llength $mh(attachmentList)]} { set attachments [list $body] foreach a $mh(attachmentList) { upvar \#0 $a bh set body_header {} foreach h {description id} { if {[info exists bh($h)] && [string length $bh($h)]} { lappend body_header [list $h $bh($h)] } } foreach v {parameter disp_parm} { if {![info exists bh($v)]} { set bh($v) {} } } if {![info exists bh(encoding)]} { set bh(encoding) 7bit } set bodypart [list $bh(type) $bh(subtype) $bh(parameter) \ $bh(encoding) attachment $bh(disp_parm) \ $body_header \ [list file $bh(filename)]] lappend attachments $bodypart } set body [list multipart mixed {} 7bit {} {} {} $attachments] } set msg [RatCreateMessage $mh(role) [list $envelope $body]] # pgp stuff set mh(pgp_signer) [RatExtractAddresses $mh(role) $mh(from)] set mh(pgp_rcpts) [RatExtractAddresses $mh(role) $mh(to) $mh(cc)] if {$mh(pgp_sign) || $mh(pgp_encrypt)} { if {[catch {$msg pgp $mh(pgp_sign) $mh(pgp_encrypt) $mh(role) \ $mh(pgp_signer) $mh(pgp_rcpts)}]} { return } } return $msg } # NewVersionUpdate -- # # Does updates that needs to be done when a new version is started for the # first time. # # Arguments: proc NewVersionUpdate {} { global option globalOption env t if {$option(last_version_date) < 19960908 && $option(smtp_verbose) == 2} { set option(smtp_verbose) 3 } if {$option(last_version_date) < 19970112} { global ratPlace ratSize ratPlaceModified ::tkrat::winctl::ReadPos catch {unset ratPlace(aliasList)} catch {unset ratPlace(aliasEdit)} catch {unset ratPlace(aliasCreate)} catch {unset ratSize(aliasList)} set ratPlaceModified 1 ::tkrat::winctl::SavePos } # Add port number to imap folders if {$option(last_version_date) < 19970209} { AddImapPorts } # Convert log timeout to seconds if {$option(last_version_date) < 19970601} { if {$option(log_timeout) > 100} { set option(log_timeout) [expr {$option(log_timeout)/1000}] } } # Convert to new address book specification if {$option(last_version_date) < 19970731 && [info exists option(aliases_file)]} { set option(addrbooks) \ [list [list Personal tkrat $option(aliases_file)]] unset option(aliases_file) } # Convert to new cache options if {$option(last_version_date) < 19970827} { if {[info exists option(pgp_pwkeep)]} { if {0 != $option(pgp_pwkeep)} { set option(cache_pgp) 1 } else { set option(cache_pgp) 0 } set option(cache_pgp_timeout) $option(pgp_pwkeep) } if {[info exists option(keep_conn)]} { if {0 != $option(keep_conn)} { set option(cache_conn) 1 } else { set option(cache_conn) 0 } set option(cache_conn_timeout) $option(keep_conn) } } # Check dbase if {[file readable $option(dbase_dir)/index.ver]} { # Upgrade to version 4 FixDbase3 } if {[file readable $option(dbase_dir)/index.info]} { set f [open $option(dbase_dir)/index.info r] gets $f line close $f if {3 == [lindex $line 0]} { FixDbase4 } if {4 == [lindex $line 0]} { FixDbase4 } } # Convert old options if {[file readable $option(ratatosk_dir)/ratatoskrc.gen]} { FixOldOptions } # Convert alias files to utf-8 if {$option(last_version_date) < 19980214} { set as $option(addrbooks) lappend as $option(system_aliases) foreach a $as { if {"tkrat" == [lindex $a 1] && [file writable [lindex $a 2]]} { set f [lindex $a 2] set fh [open $f r] while { 0 < [gets $fh l] && 0 == [eof $fh]} { lappend lines $l } close $fh set fh [open $f w] fconfigure $fh -encoding utf-8 foreach l $lines { puts $fh "$l {}" } close $fh } } } # Convert expression file if {[file readable $option(ratatosk_dir)/expressions]} { source $option(ratatosk_dir)/expressions } if {[info exists expArrayId]} { set f [open $option(ratatosk_dir)/expressions w] set newExpList {} foreach e $expList { lappend newExpList $expName($e) puts $f [list set expExp($expName($e)) $expExp($e)] } puts $f "set expList [list $newExpList]" close $f } # Convert fontsize option if {$option(last_version_date) < 19991219 && [info exists option(fontsize)]} { if {$option(fontsize) != 12} { foreach o {prop_norm fixed_norm} { set option($o) [lreplace $option($o) 2 2 $option(fontsize)] } } unset option(fontsize) unset globalOption(fontsize) } if {$option(last_version_date) < 19991219 && 1 == [llength $option(watcher_font)]} { set option(watcher_font) [list name $option(watcher_font)] } if {1 != [llength $option(watcher_time)]} { set new 30 # Get value from std folders foreach v $option(watcher_time) { if {"std" == [lindex $v 0] && 2 == [llength $v]} { set new [lindex $v 1] break } } set option(watcher_time) $new } if {$option(last_version_date) < 20010809} { foreach v {bcc from masquerade_as name reply_to sendprog sendprog_8bit sendprot signature smtp_hosts save_outgoing} { if {[info exists option($v)]} { set option(r0,$v) $option($v) unset option($v) catch {unset globalOption($v)} } } } if {$option(last_version_date) < 20011206} { foreach r $option(roles) { if {![info exists option($r,masquerade_as)]} { continue } if {"" != $option($r,masquerade_as)} { set from [string trim $option($r,from)] if {"" == $option($r,from)} { set option($r,from) "$env(USER)@$option($r,masquerade_as)" } elseif {-1 == [string first "@" $option($r,from)]} { set option($r,from) \ "$option($r,from)@$option($r,masquerade_as)" } elseif {![regexp "@$option($r,masquerade_as)(>|$)" \ $option($r,from)]} { Popup [format $t(masq_from_conflict) \ $option($r,name) $option($r,masquerade_as) \ $option($r,from)] } } unset option($r,masquerade_as) if {[info exists globalOption($r,masquerade_as)]} { unset globalOption($r,masquerade_as) } } } # Check if we have held messages in old format if {$option(last_version_date) < 20020808} { if {[info exists option(hold_dir)]} { set hold_dir $option(hold_dir) } else { set hold_dir $option(ratatosk_dir)/hold } if {[file isdirectory $hold_dir] && 0 < [llength [glob -nocomplain -directory $hold_dir *.desc]]} { ConvertHold $hold_dir vFolderHold } if {[info exists option(send_cache)]} { set send_dir $option(send_cache) } else { set send_dir $option(ratatosk_dir)/send } if {[file isdirectory $send_dir] && 0 < [llength [glob -nocomplain -directory $send_dir *.desc]]} { ConvertHold $send_dir vFolderOutgoing } file delete -force $send_dir file delete -force $hold_dir } # Add new fields to roles if {$option(last_version_date) < 20020830} { foreach r $option(roles) { foreach vt {{uqa_domain {}} {smtp_helo {}} {validate_cert 0} \ {same_sending_prefs 0} {smtp_user {}} \ {smtp_passwd {}}} { if {![info exists option($r,[lindex $vt 0])]} { set option($r,[lindex $vt 0]) [lindex $vt 1] } } } } # Adjust size of norm italic font if {$option(last_version_date) < 20030213 && [info exists option(fixed_italic)]} { set option(fixed_italic) \ [lreplace $option(fixed_italic) 2 2 [lindex $option(fixed_norm) 2]] } # Add pgp fields to roles if {$option(last_version_date) < 20031123} { foreach r $option(roles) { if {[info exists option(pgp_sign)]} { set option($r,sign_outgoing) $option(pgp_sign) } else { set option($r,sign_outgoing) 0 } set option($r,sign_as) {} } } # Remove old color settings (they are incompatible) if {$option(last_version_date) < 20040710} { if {4 != [llength $option(color_set)]} { set option(color_set) {\#dde3eb black white black} } } # Convert saved address history encoding to utf-8 if {$option(last_version_date) < 20050701 && [file readable $option(ratatosk_dir)/addrlist]} { set fh [open $option(ratatosk_dir)/addrlist r] set data [read $fh] close $fh set fh [open $option(ratatosk_dir)/addrlist w] fconfigure $fh -encoding utf-8 puts -nonewline $fh $data close $fh } # Convert font specifications if {$option(last_version_date) < 20050706} { if {[info exists option(prop_norm)] && "components" == [lindex $option(prop_norm) 0]} { set option(font_family_prop) \ [string tolower [lindex $option(prop_norm) 1]] set option(font_size) [lindex $option(prop_norm) 2] } if {[info exists option(fixed_norm)] && "components" == [lindex $option(fixed_norm) 0]} { set option(font_family_fixed) \ [string tolower [lindex $option(fixed_norm) 1]] if {$option(font_size) != [lindex $option(fixed_norm) 2]} { set option(font_size) [lindex $option(fixed_norm) 2] } } } if {$option(last_version_date) < 20050707 && [info exists option($option(url_viewer))]} { set option(browser_cmd) $option($option(url_viewer)) if {"firefox" == $option(browser_cmd)} { set option(url_viewer) firefox } } if {$option(last_version_date) < 20050707 && [file exists "$option(ratatosk_dir)/log"]} { file delete "$option(ratatosk_dir)/log" } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/dbase.tcl000066400000000000000000000307041137544547100207170ustar00rootroot00000000000000# dbase.tcl -- # # This file contains code which handles dbase checks # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # Expire -- # # Run the database expiration # # Arguments: proc Expire {} { global option t inbox vFolderDef expAfter idCnt fixedNormFont vFolderInbox # Sanity check # If it has been over a year and there are more than 100 messages in # the database then we ask the user for confirmation. if {[RatDaysSinceExpire] > 365 && [lindex [RatDbaseInfo] 0] > 100} { set action [RbatDialog "" $t(really_expire_title) \ $t(really_expire) question 1 $t(expire) $t(cancel)] if {$action != 0} { return } } # Prepare for next expiration if {1 > $option(expire_interval)} { set int 1 } else { set int $option(expire_interval) } set expAfter [after [expr {$int*24*60*60*1000}] Expire] if { 0 == [string length $inbox] } { set vfolder $vFolderDef($vFolderInbox) set inb [RatOpenFolder $vFolderDef($vFolderInbox)] } else { set inb $inbox } set id [RatLog 2 $t(db_expire) explicit] if {[catch {RatExpire $inb [RatTildeSubst $option(dbase_backup)]} \ result]} { RatClearLog $id Popup [format $t(dbase_error) $result] return } RatClearLog $id if { 0 == [string length $inbox] } { $inb close } set scanned [lindex $result 0] set deleted [lindex $result 1] set backup [lindex $result 2] set inbox [lindex $result 3] set custom [lindex $result 4] if { 0 != $deleted || 0 != $backup || 0 != $inbox} { set w .exp[incr idCnt] toplevel $w -class TkRat wm title $w $t(expire) label $w.lab -text $t(expire_result): grid $w.lab -columnspan 2 label $w.lab_scan -text $t(scanned): -anchor e label $w.val_scan -text [format %5d $scanned] \ -width 10 -font $fixedNormFont -anchor w grid $w.lab_scan -column 0 -row 1 -sticky e grid $w.val_scan -column 1 -row 1 -sticky w label $w.lab_delete -text $t(deleted): -anchor e label $w.val_delete -text [format %5d $deleted] -font $fixedNormFont grid $w.lab_delete -column 0 -row 2 -sticky e grid $w.val_delete -column 1 -row 2 -sticky w label $w.lab_backup -text $t(backedup): -anchor e label $w.val_backup -text [format %5d $backup] -font $fixedNormFont grid $w.lab_backup -column 0 -row 3 -sticky e grid $w.val_backup -column 1 -row 3 -sticky w label $w.lab_inbox -text $t(moved_to_inbox): -anchor e label $w.val_inbox -text [format %5d $inbox] -font $fixedNormFont grid $w.lab_inbox -column 0 -row 4 -sticky e grid $w.val_inbox -column 1 -row 4 -sticky w button $w.but -text $t(dismiss) -command "destroy $w" grid $w.but -column 0 -columnspan 2 -row 5 bind $w "$w.but invoke" } } # DbaseCheck # # Checks the database and show the result # # Arguments: # fix - True if we should try to fix problems as well proc DbaseCheck {fix} { global idCnt t fixedNormFont # Create identifier set id dbaseWin[incr idCnt] set w .$id # Do checking set mid [RatLog 2 $t(checking_dbase)... explicit] set result [RatDbaseCheck $fix] RatClearLog $mid # Create toplevel toplevel $w -class TkRat wm title $w $t(dbase_check) # Top part of window frame $w.top frame $w.top.l label $w.top.l.totm -text $t(total_num_messages): label $w.top.l.totmv -text [lindex $result 0] -font $fixedNormFont -width 5 grid $w.top.l.totm -row 0 -column 0 -sticky e grid $w.top.l.totmv -row 0 -column 1 -sticky w label $w.top.l.tots -text $t(total_size): label $w.top.l.totsv -text [RatMangleNumber [lindex $result 4]] \ -font $fixedNormFont -width 5 grid $w.top.l.tots -row 1 -column 0 -sticky e grid $w.top.l.totsv -row 1 -column 1 -sticky w frame $w.top.r label $w.top.r.numm -text $t(num_malformed): label $w.top.r.nummv -text [lindex $result 1] -font $fixedNormFont -width 5 grid $w.top.r.numm -row 0 -column 0 -sticky e grid $w.top.r.nummv -row 0 -column 1 -sticky w label $w.top.r.numn -text $t(num_nomessages): label $w.top.r.numnv -text [lindex $result 2] -font $fixedNormFont -width 5 grid $w.top.r.numn -row 1 -column 0 -sticky e grid $w.top.r.numnv -row 1 -column 1 -sticky w label $w.top.r.numu -text $t(num_unlinked): label $w.top.r.numuv -text [lindex $result 3] -font $fixedNormFont -width 5 grid $w.top.r.numu -row 2 -column 0 -sticky e grid $w.top.r.numuv -row 2 -column 1 -sticky w pack $w.top.l \ $w.top.r -side left -pady 5 -padx 10 -anchor n # Messages frame $w.mess scrollbar $w.mess.scroll \ -relief sunken \ -command "$w.mess.text yview" text $w.mess.text \ -yscroll "$w.mess.scroll set" \ -setgrid true pack $w.mess.scroll -side right -fill y pack $w.mess.text -expand 1 -fill both foreach m [lindex $result 5] { $w.mess.text insert end "$m\n" } # Button button $w.dismiss -text $t(dismiss) -command "destroy $w" bind $w "$w.dismiss invoke" # Pack it pack $w.top -side top pack $w.mess -side top -expand 1 -fill both pack $w.dismiss -pady 5 # handle geometry ::tkrat::winctl::SetGeometry dbCheckW $w $w.mess.text bind $w.mess.text "::tkrat::winctl::RecordGeometry dbCheckW $w $w.mess.text" } # DbaseInfo -- # # Show information about dbase # # Arguments: proc DbaseInfo {} { global idCnt t fixedNormFont # Create identifier set id dbaseWin[incr idCnt] upvar \#0 $id hd set w .$id # Collect data set dinfo [RatDbaseInfo] set keywords [RatDbaseKeywords] set hd(from) [lindex $dinfo 1] set hd(to) [lindex $dinfo 2] # Create toplevel toplevel $w -class TkRat wm title $w $t(dbase_check) # Top information label $w.num_lab -text $t(total_num_messages): label $w.num -text [lindex $dinfo 0] \ -font $fixedNormFont -width 7 -anchor e grid $w.num_lab $w.num -sticky w label $w.total_lab -text $t(total_size): label $w.total -text [RatMangleNumber [lindex $dinfo 3]] \ -font $fixedNormFont -width 7 -anchor e grid $w.total_lab $w.total -sticky w label $w.start_lab -text $t(earliest_date): label $w.start -text [clock format [lindex $dinfo 1]] -font $fixedNormFont grid $w.start_lab $w.start -sticky w label $w.end_lab -text $t(latest_date): label $w.end -text [clock format [lindex $dinfo 2]] -font $fixedNormFont grid $w.end_lab $w.end -sticky w # Table rat_table::create $w.table [list \ [list $t(keyword) string] \ [list $t(usage_count) int]] \ $keywords -bd 1 -relief sunken set hd(table) $w.table bind $hd(table) <> [list DbaseInfoSelect $id] grid $w.table - # Buttons frame $w.b button $w.b.show -text $t(show_messages) -state disabled\ -command [list DbaseInfoShow $id] set hd(show) $w.b.show button $w.b.dismiss -text $t(dismiss) -command "destroy $w" -default active bind $w "$w.b.dismiss invoke" bind $w "$w.b.dismiss invoke" pack $w.b.show $w.b.dismiss -side left -expand 1 -padx 5 -pady 5 grid $w.b - bind $hd(table) <> [list $hd(show) invoke] # Handle geometry ::tkrat::winctl::SetGeometry dbInfoW $w bind $w.num "::tkrat::winctl::RecordGeometry dbInfoW $w" } # DbaseInfoSelect -- # # Called when the selection in the table changes # # Arguments: # id - identifies the info window proc DbaseInfoSelect {id} { upvar \#0 $id hd set hd(selected) [lindex [rat_table::get_selection $hd(table)] 0] if {"" != $hd(selected)} { $hd(show) configure -state normal } else { $hd(show) configure -state disabled } } # DbaseInfoShow -- # # Show messages with the selected keyword # # Arguments: # id - identifies the info window proc DbaseInfoShow {id} { upvar \#0 $id hd set exp [list "int" $hd(from) $hd(to) "and" "keywords" $hd(selected)] set vf [list def [list "Dbase search" dbase {} {} {} $exp]] NewFolderWin $vf } # MsgDbInfo -- # # Show the message dbinfo dialog # # Arguments: # src - source of info ("folder" or "msg") # info - dbase info # folder - folder handler of the folder containing the message # indexes - list of message indexes proc MsgDbInfo {src info folder indexes} { global idCnt t b fixedNormFont # Create identifier set id dbaseWin[incr idCnt] upvar \#0 $id hd set w .$id set hd(toplevel) $w set hd(folder) $folder set hd(indexes) $indexes # Do we have valid data? if {"" == [lindex $info 0]} { set msg [$folder get [lindex $indexes 0]] set info [$msg dbinfo_get] set src first_msg } # Collect data set hd(keywords) [lindex $info 0] set hd(keywords_orig) $hd(keywords) set time [lindex $info 1] if {$time < 833839200} { # 19960604 set hd(ex_date) "+$time" } else { set hd(ex_date) [clock format $time -format "%Y-%m-%d %T"] } set hd(ex_date_orig) $hd(ex_date) set hd(ex_type) [lindex $info 2] set hd(ex_type_orig) $hd(ex_type) # Create toplevel toplevel $w -class TkRat -bd 5 wm title $w $t(dbinfo) # Top label label $w.info -text $t(dbinfo_info_$src) grid $w.info - # Information label $w.keywords_lab -text $t(keywords): -pady 5 entry $w.keywords -textvariable ${id}(keywords) -width 35 grid $w.keywords_lab $w.keywords -sticky w set b($w.keywords) keywords label $w.exdate_lab -text $t(exdate): -pady 5 frame $w.exdate -pady 5 entry $w.exdate.entry -textvariable ${id}(ex_date) -width 35 label $w.exdate.expl -text $t(exdate_expl) pack $w.exdate.entry $w.exdate.expl -side top grid $w.exdate_lab $w.exdate -sticky wn set b($w.exdate) exp_date label $w.extype_lab -text $t(extype): radiobutton $w.extype_none -text $t(none) \ -variable ${id}(ex_type) -value none set b($w.extype_none) exp_none grid $w.extype_lab $w.extype_none -sticky w foreach e {remove incoming backup} { radiobutton $w.extype_$e -text $t($e) \ -variable ${id}(ex_type) -value $e grid x $w.extype_$e -sticky w set b($w.extype_$e) exp_$e } # Buttons frame $w.b button $w.b.apply -text $t(apply) -command [list MsgDbInfoApply $id] set b($w.b.apply) dbinfo_apply_$src set hd(apply) $w.b.apply button $w.b.reset -text $t(reset) -command [list MsgDbInfoReset $id] set b($w.b.reset) reset set hd(reset) $w.b.reset button $w.b.dismiss -text $t(dismiss) -command "destroy $w" set b($w.b.dismiss) dismiss set hd(dismiss) $w.b.dismiss bind $w "$w.b.dismiss invoke" pack $w.b.apply $w.b.reset $w.b.dismiss \ -side left -expand 1 -padx 5 -pady 5 grid $w.b - # Handle geometry ::tkrat::winctl::SetGeometry msgDbInfoW $w ::tkrat::winctl::ModalGrab $w $w.keywords bind $w.keywords "::tkrat::winctl::RecordGeometry msgDbInfoW $w" if {"msg" == $src} { trace variable hd w [list MsgDbInfoTrace $id] # Trigger the trace set hd(keywords) $hd(keywords) } } # MsgDbInfoTrace -- # # Trace function for MsgDbInfo, enables/disables the apply button # # Arguments: # id - identifies the window # args - standard trace arguments proc MsgDbInfoTrace {id name1 name2 op} { upvar \#0 $id hd set state disabled if {$hd(keywords) != $hd(keywords_orig) || $hd(ex_type) != $hd(ex_type_orig) || $hd(ex_date) != $hd(ex_date_orig)} { set state normal } $hd(apply) configure -state $state $hd(reset) configure -state $state } # MsgDbInfoReset -- # # Reset to original values # # Arguments: # id - identifies the window proc MsgDbInfoReset {id} { upvar \#0 $id hd foreach v {keywords ex_type ex_date} { set hd($v) $hd(${v}_orig) } } # MsgDbInfoApply -- # # Apply new values # # Arguments: # id - identifies the window proc MsgDbInfoApply {id} { upvar \#0 $id hd global t if {"+" == [string index $hd(ex_date) 0]} { set add [expr [string range $hd(ex_date) 1 end]*24*60*60] set ex_date [expr [clock seconds] + $add] } else { if {[catch {clock scan $hd(ex_date)} ex_date]} { Popup $t(date_parsing_failed) $hd(toplevel) return } } $hd(folder) dbinfo_set $hd(indexes) $hd(keywords) $ex_date $hd(ex_type) $hd(dismiss) invoke } tkrat_2.2cvs20100105-dfsg.orig/tkrat/delattachments.tcl000066400000000000000000000115541137544547100226430ustar00rootroot00000000000000# delattachments.tcl -- # # Contains code for deleting attachments # # TkRat software and its included text is Copyright 1996-2005 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. namespace eval ::tkrat::delattachments { namespace export delete } # ::tkrat::delattachments::delete -- # # Show the delet attachments window # # Arguments: # msg - Message to perform delete attachments from # handler - Handler of the folder window we are called from proc ::tkrat::delattachments::delete {msg handler} { global idCnt t upvar \#0 $handler fh set id ::tkrat::delattachments::state[incr idCnt] upvar \#0 $id hd # Create toplevel set w .customcopy_[incr idCnt] toplevel $w -class TkRat -bd 5 wm title $w $t(delete_attachments) wm transient $w $fh(w) # Initialize defaults set hd(msg) $msg set hd(folder_handler) $handler set hd(w) $w # Populate window message $w.msg -text $t(delete_attachments_expl) -aspect 350 # Attachments labelframe $w.f -text $t(attachments_to_delete) add_attachments $w.f $id [$msg body] {} "" # Buttons frame $w.buttons button $w.buttons.delete -text $t(delete) -default active -state disabled \ -command "::tkrat::delattachments::do $id" button $w.buttons.cancel -text $t(cancel) -command "destroy $w" pack $w.buttons.delete \ $w.buttons.cancel -side left -expand 1 bind $w "$w.buttons.delete invoke" bind $w "$w.buttons.cancel invoke" set hd(delete_button) $w.buttons.delete pack $w.msg $w.f $w.buttons -side top -fill x -expand 1 # Place it ::tkrat::winctl::SetGeometry DeleteAttachments $w bind $w.buttons.delete \ "::tkrat::delattachments::cleanup $id" } # ::tkrat::delattachments::cleanup -- # # Cleans things up when the window is destroyed # # Arguments: # handler - Hander of the delete attachments window proc ::tkrat::delattachments::cleanup {handler} { upvar \#0 $handler hd ::tkrat::winctl::RecordGeometry DeleteAttachments $hd(w) unset hd } # ::tkrat::delattachments::add_attachments -- # # Adds the attachments buttons # # Arguments: # w - Frame to add to # handler - Hander of the delete attachments window # body - Body to add children of # parent - Parent specification # leader - Leading string proc ::tkrat::delattachments::add_attachments {w handler body parent leader} { upvar \#0 $handler hd global idCnt set i 0 foreach c [$body children] { set id [incr idCnt] lappend hd(buttons) $id set hd(id_$id) [concat $parent $i] set hd(state_$id) 0 # Description of bodypart set type [join [string tolower [$c type]] /] set size [RatMangleNumber [$c size]] set desc " $type ($size)" if {[$c description] != ""} { set desc "$desc\n [$c description]" } if {[$c filename] != ""} { set desc "$desc\n [$c filename]" } # Widgets set f $w.a$id frame $f label $f.l -text $leader checkbutton $f.c -text $desc -justify left -anchor nw \ -variable ${handler}(state_$id) \ -command "::tkrat::delattachments::update_state $handler" pack $f.l $f.c -side left pack $f -side top -anchor w # Does this have children? if {"MULTIPART" != [lindex [$c type] 0]} { add_attachments $w $handler $c $hd(id_$id) " $leader" } incr i } } # ::tkrat::delattachments::update_state -- # # Update the state of the delete button # # Arguments: # handler - Hander of the delete attachments window proc ::tkrat::delattachments::update_state {handler} { upvar \#0 $handler hd set state disabled foreach b $hd(buttons) { if {$hd(state_$b)} { set state normal break } } $hd(delete_button) configure -state $state } # ::tkrat::delattachments::do -- # # Actually do the deletion # # Arguments: # handler - Hander of the delete attachments window proc ::tkrat::delattachments::do {handler} { upvar \#0 $handler hd upvar \#0 $hd(folder_handler) fh global t # Create list of attachments to delete set attachments {} foreach b $hd(buttons) { if {$hd(state_$b)} { lappend attachments $hd(id_$b) } } # Perform deletion and insert new message if {[catch {$hd(msg) delete_attachments $attachments} nmsg]} { Popup $t(message_deleted) } else { $fh(folder_handler) insert $nmsg # Mark original message for deletion set index [$fh(folder_handler) find $hd(msg)] if {-1 != $index} { $fh(folder_handler) setFlag $index deleted 1 } Sync $hd(folder_handler) update } # Destroy window destroy $hd(w) } tkrat_2.2cvs20100105-dfsg.orig/tkrat/dialog.tcl000066400000000000000000000203171137544547100210770ustar00rootroot00000000000000# # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # RatLogin # See ../doc/interface proc RatLogin {host trial user prot port} { global t idCnt m set id login[incr idCnt] set w .$id upvar \#0 $id hd set hd(user) $user set hd(store) 0 # Create toplevel toplevel $w -class TkRat wm title $w $t(login) # Populate window label $w.label -text "$t(opening) $prot $t(mailbox_on) $host:$port" frame $w.user label $w.user.label -text $t(user): -width 10 -anchor e entry $w.user.entry -textvariable ${id}(user) -width 20 if {[string length $hd(user)]} { $w.user.entry configure -state disabled } pack $w.user.label $w.user.entry -side left frame $w.passwd label $w.passwd.label -text $t(passwd): -width 10 -anchor e entry $w.passwd.entry -textvariable ${id}(passwd) -width 20 -show {-} pack $w.passwd.label $w.passwd.entry -side left checkbutton $w.store -text $t(store_passwd) -variable ${id}(store) set m($w.store) store_passwd OkButtons $w $t(ok) $t(cancel) "set ${id}(done)" pack $w.label -side top -padx 5 -pady 5 pack $w.user \ $w.passwd \ $w.store \ $w.buttons -side top -fill both -pady 2 ::tkrat::winctl::SetGeometry ratLogin $w ::tkrat::winctl::ModalGrab $w $w.passwd.entry tkwait variable ${id}(done) ::tkrat::winctl::RecordGeometry ratLogin $w destroy $w unset m($w.store) update idletasks if { 1 == $hd(done) } { set r [list $hd(user) $hd(passwd) $hd(store)] } else { set r {{} {} 0} } unset hd return $r } # Popup -- # # Show a message which the user has to acknowledge # # Arguments: # message - The message to show # parent - Parent window proc Popup {message {parent {}}} { global t RatDialog $parent ! $message {} 0 $t(continue) update idletasks } # RatDialog -- # # This looks almost like the tk dialog, except that it uses a message # instead of a label and it doesn't set the font. # # This procedure displays a dialog box, waits for a button in the dialog # to be invoked, then returns the index of the selected button. # # Arguments: # parent - Parent window # title - Title to display in dialog's decorative frame. # text - Message to display in dialog. # bitmap - Bitmap to display in dialog (empty string means none). # default - Index of button that is to display the default ring # (-1 means none). # args - One or more strings to display in buttons across the # bottom of the dialog box. proc RatDialog {parent title text bitmap default args} { global tkPriv idCnt # 1. Create the top-level window and divide it into top # and bottom parts. set w .dialog[incr idCnt] catch {destroy $w} toplevel $w -class TkRat wm title $w $title wm iconname $w Dialog wm protocol $w WM_DELETE_WINDOW { } wm transient $w $parent frame $w.bot -relief raised -bd 1 pack $w.bot -side bottom -fill both frame $w.top -relief raised -bd 1 pack $w.top -side top -fill both -expand 1 if {80 > [string length $text] && -1 == [string first $text "\n"]} { set aspect 3000 } else { set aspect 600 } # 2. Fill the top part with bitmap and message (use the option # database for -wraplength so that it can be overridden by # the caller). option add *Dialog.msg.wrapLength 3i widgetDefault message $w.msg -justify left -text $text -aspect $aspect pack $w.msg -in $w.top -side right -expand 1 -fill both -padx 3m -pady 3m if {$bitmap != ""} { label $w.bitmap -bitmap $bitmap pack $w.bitmap -in $w.top -side left -padx 3m -pady 3m } # 3. Create a row of buttons at the bottom of the dialog. set i 0 foreach but $args { button $w.button$i -text $but -command "set tkPriv(button) $i" if {$i == $default} { $w.button$i configure -default active } pack $w.button$i -in $w.bot -side left -expand 1 \ -padx 3m -pady 2m bind $w.button$i " $w.button$i configure -state active -relief sunken update idletasks after 100 set tkPriv(button) $i break " incr i } # 4. Create a binding for on the dialog if there is a # default button. if {$default >= 0} { bind $w " $w.button$default configure -state active -relief sunken update idletasks after 100 set tkPriv(button) $default " } # 5. Withdraw the window, then update all the geometry information # so we know how big it wants to be, then center the window in the # display and de-iconify it. wm withdraw $w update idletasks set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \ - [winfo vrootx [winfo parent $w]]}] set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 \ - [winfo vrooty [winfo parent $w]]}] wm geom $w +$x+$y wm deiconify $w # 6. Set a grab and claim the focus too. if {$default >= 0} { set f $w.button$default } else { set f $w } ::tkrat::winctl::ModalGrab $w $f # 7. Wait for the user to respond, then restore the focus and # return the index of the selected button. Restore the focus # before deleting the window, since otherwise the window manager # may take the focus away so we can't redirect it. Finally, # restore any grab that was in effect. tkwait variable tkPriv(button) destroy $w return $tkPriv(button) } # RatText -- # # Display a text to the user # # Arguments: # title - Title to display in text's decorative frame. # text - Message to display in text. proc RatText {title text} { global idCnt t set text [string map [list "\a" ""] $text] # Create identifier set id rattext[incr idCnt] set w .$id # Create toplevel toplevel $w -class TkRat wm title $w $title # Message part button $w.button -text $t(close) -command "destroy $w" text $w.text -yscroll "$w.scroll set" -relief sunken -bd 1 scrollbar $w.scroll -relief sunken -bd 1 \ -command "$w.text yview" pack $w.button -side bottom -padx 5 -pady 5 pack $w.scroll -side right -fill y pack $w.text -expand 1 -fill both $w.text insert end $text\n $w.text configure -state disabled bind $w "$w.button invoke" bind $w.text "::tkrat::winctl::RecordGeometry ratText $w $w.text" ::tkrat::winctl::SetGeometry ratText $w $w.text } # bgerror -- # # This is a modified version of bgerror. It allows one to include the # stack trace in a bug report message. # # Arguments: # err - The error message. proc bgerror {err} { global errorInfo t set info $errorInfo set button [tk_dialog .bgerrorDialog "Error in Tcl Script" \ "Error: $err" error 0 OK $t(send_bug) "Skip Messages" \ "Stack Trace"] if {$button == 0} { return -code ok } elseif {$button == 1} { SendBugReport [list [list "Stack Trace: $err" "$info"]] return -code ok } elseif {$button == 2} { return -code break } set w .bgerrorTrace catch {destroy $w} toplevel $w -class TkRat wm minsize $w 1 1 wm title $w "Stack Trace for Error" wm iconname $w "Stack Trace" button $w.ok -text OK -command "destroy $w" text $w.text -relief sunken -bd 2 -yscrollcommand "$w.scroll set" \ -setgrid true -width 60 -height 20 scrollbar $w.scroll -relief sunken -command "$w.text yview" pack $w.ok -side bottom -padx 3m -pady 2m pack $w.scroll -side right -fill y pack $w.text -side left -expand yes -fill both $w.text insert 0.0 $info $w.text mark set insert 0.0 bind $w "$w.ok invoke" # Center the window on the screen. wm withdraw $w update idletasks set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \ - [winfo vrootx [winfo parent $w]]}] set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 \ - [winfo vrooty [winfo parent $w]]}] wm geom $w +$x+$y wm deiconify $w # Be sure to release any grabs that might be present on the # screen, since they could make it impossible for the user # to interact with the stack trace. if {[grab current .] != ""} { grab release [grab current .] } return -code ok } tkrat_2.2cvs20100105-dfsg.orig/tkrat/exp.tcl000066400000000000000000000274611137544547100204430ustar00rootroot00000000000000# exp.tcl -- # # This file contains code for handling message selection expressions # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of my legal notices is contained in the file called # COPYRIGHT, included with this distribution. # ExpCreate -- # # Create the expression creation window # # Arguments: # handler - The handler which identifies the folder window that # the selection should be done in. proc ExpCreate {handler {addproc {}}} { global t b option idCnt # Create identifier set id expWin[incr idCnt] set w .$id upvar \#0 $id hd set hd(doSave) 0 set hd(w) $w set hd(handler) $handler set hd(op) and set hd(exp) {} set hd(oldfocus) [focus] set hd(addproc) $addproc # Create toplevel toplevel $w -class TkRat wm title $w $t(create_exp) # Populate window frame $w.top menubutton $w.top.mode -indicatoron 1 -menu $w.top.mode.m \ -textvariable ${id}(modeName) -bd 1 -relief raised -width 18 menu $w.top.mode.m -tearoff 0 $w.top.mode.m add command -label $t(basic_mode) -command "ExpModeBasic $id" $w.top.mode.m add command -label $t(advanced_mode) -command "ExpModeAdv $id" frame $w.top.c if {"" != $handler} { checkbutton $w.top.c.but -text $t(save_as): -variable ${id}(doSave) } else { label $w.top.c.but -text $t(save_as): set hd(doSave) 1 } entry $w.top.c.entry -width 20 -textvariable ${id}(saveAs) bind $w.top.c.entry \ "if {0 < \[string length ${id}(saveAs)\]} { \ set ${id}(doSave) 1 \ } else { \ set ${id}(doSave) 0 \ }" pack $w.top.c.but \ $w.top.c.entry -side left pack $w.top.mode \ $w.top.c -side left -padx 20 set b($w.top.mode) switch_expression set b($w.top.c.but) save_expr_as set b($w.top.c.entry) save_expr_as frame $w.f -bd 2 -relief ridge set hd(frame) $w.f frame $w.buttons button $w.buttons.ok -text $t(ok) -default active -command "ExpDone $id 1" button $w.buttons.clear -text $t(clear) -command "ExpClear $id" button $w.buttons.cancel -text $t(cancel) -command "destroy $w" pack $w.buttons.ok \ $w.buttons.clear \ $w.buttons.cancel -side left -expand 1 bind $w "ExpDone $id 1" pack $w.top -side top -fill x -pady 5 pack $w.f -side top -fill both -pady 5 -expand 1 pack $w.buttons -side top -fill x -pady 5 set b($w.buttons.ok) exp_ok set b($w.buttons.clear) clear set b($w.buttons.cancel) dismiss set hd(mode) {} set hd(action) create if {[string compare advanced $option(expression_mode)]} { ExpModeBasic $id } else { ExpModeAdv $id } bind $w "$w.buttons.cancel invoke" bind $w.buttons.ok "ExpClose $id" ::tkrat::winctl::SetGeometry ratExpression $w } # ExpEdit -- # # Edit an expression # # Arguments: # name - Name of expression to edit proc ExpEdit {name namechange} { global t b idCnt expExp # Create identifier set id expWin[incr idCnt] set w .$id upvar \#0 $id hd set hd(w) $w set hd(handler) $id set hd(mode) {} set hd(frame) $w.f set hd(action) edit set hd(exp) "$expExp($name) " set hd(saveAs) $name set hd(oldName) $name # Create toplevel toplevel $w -class TkRat wm title $w $t(edit_exp) frame $w.f ExpModeAdv $id OkButtons $w $t(ok) $t(cancel) "ExpDone $id" pack $w.buttons -side bottom -pady 5 -fill x pack $w.f bind $w.buttons.ok "ExpClose $id" ::tkrat::winctl::SetGeometry ratExpEdit $w } # ExpModeBasic -- # # Configure window for basic mode # # Arguments: # handler - The handler which identifies the expression window proc ExpModeBasic {handler} { upvar \#0 $handler hd global t b # Check that this is really neccessary if {![string compare $hd(mode) basic]} { return } # Setup variables set hd(modeName) $t(basic_mode) set hd(mode) basic set w $hd(frame) # Clear frame foreach s [grid slaves $w] { destroy $s } frame $w.f radiobutton $w.f.and -text $t(and) -variable ${handler}(op) -value and radiobutton $w.f.or -text $t(or) -variable ${handler}(op) -value or pack $w.f.and \ $w.f.or -side left -padx 5 grid $w.f -columnspan 2 set b($w.f.and) exp_basic_and set b($w.f.or) exp_basic_or foreach f {subject from reply-to sender to cc} { label $w.l$f -text $t($f): entry $w.e$f -textvariable ${handler}($f) -width 50 grid $w.l$f $w.e$f -sticky e set b($w.l$f) exp_basic_field set b($w.e$f) exp_basic_field } focus $w.esubject } # ExpModeAdv -- # # Configure window for advanced mode # # Arguments: # handler - The handler which identifies the expression window proc ExpModeAdv {handler} { upvar \#0 $handler hd global t b # Check that this is really neccessary if {![string compare $hd(mode) advanced]} { return } # Setup variables set hd(modeName) $t(advanced_mode) set hd(mode) advanced set w $hd(frame) # Clear frame foreach s [grid slaves $w] { destroy $s } # Pack windows frame $w.f text $w.f.text -relief sunken -bd 1 -width 80 -height 4 -wrap word \ -yscroll "$w.f.scroll set" -setgrid 1 $w.f.text tag configure error -underline 1 $w.f.text tag bind error "$w.f.text tag remove error 1.0 end" scrollbar $w.f.scroll -relief sunken -bd 1 -command "$w.f.text yview" pack $w.f.scroll -side right -fill y pack $w.f.text -expand 1 -fill both grid $w.f -columnspan 4 -sticky nsew set b($w.f.text) exp_adv_exp frame $w.f1 label $w.f1.l -text $t(fields) grid $w.f1.l -sticky we -columnspan 2 set i 1 foreach n {to from subject sender cc reply-to size} { button $w.f1.f$i -text $t($n) -width 10 \ -command "$w.f.text insert insert \"$n \"" set b($w.f1.f$i) exp_adv_fields incr i } grid $w.f1.f1 $w.f1.f5 -row 1 grid $w.f1.f2 $w.f1.f6 -row 2 grid $w.f1.f3 $w.f1.f7 -row 3 grid $w.f1.f4 -row 4 frame $w.o1 label $w.o1.l -text $t(operators) grid $w.o1.l -sticky we set i 1 foreach n [list "has $t(has) has" "is $t(is) is" "> > gt" "< < lt"] { button $w.o1.o$i -text [lindex $n 1] -width 10 \ -command "$w.f.text insert insert \"[lindex $n 0] \"" grid $w.o1.o$i -row $i set b($w.o1.o$i) exp_adv_[lindex $n 2] incr i } frame $w.b1 label $w.b1.l -text $t(booleans) grid $w.b1.l -sticky we set i 1 foreach n {not and or} { button $w.b1.b$i -text $t($n) -width 10 \ -command "$w.f.text insert insert \"$n \"" grid $w.b1.b$i -row $i set b($w.b1.b$i) exp_adv_bool incr i } frame $w.g1 label $w.g1.l -text $t(grouping) grid $w.g1.l -sticky we set i 1 foreach n {( )} { button $w.g1.g$i -text $n -width 10 \ -command "$w.f.text insert insert \"$n \"" grid $w.g1.g$i -row $i set b($w.g1.g$i) exp_adv_p incr i } grid $w.f1 \ $w.o1 \ $w.b1 \ $w.g1 -sticky n focus $w.f.text $w.f.text insert end $hd(exp) bind $w.f.text "ExpDone $handler 1; break" set hd(text) $w.f.text } # ExpClear -- # # Clears the current expression window # # Arguments: # handler - the handler which identifies the expression window proc ExpClear {handler} { upvar \#0 $handler hd if {![string compare basic $hd(mode)]} { foreach f {subject from reply-to sender to cc} { set hd($f) "" } } else { $hd(text) delete 1.0 end } } # ExpDone -- # # Called when we are done with the expression window # # Arguments: # handler - the handler which identifies the expression window # action - what we should do proc ExpDone {handler action} { upvar \#0 $handler hd global t b option expList expExp if {"" != $hd(handler)} { upvar \#0 $hd(handler) fHd } if {![info exist expList]} { set expList {} } if {1 == $action} { # Build expression if {![string compare basic $hd(mode)]} { set exp "" foreach f {subject from reply-to sender to cc} { if {[string length $hd($f)]} { if {[string length $exp]} { set exp "$exp $hd(op) " } regsub -all {\\} $hd($f) {\\\\} m set exp "${exp}$f has [list $m]" } } if {[catch {RatParseExp $exp} expId]} { Popup $t(syntax_error_exp) $hd(w) return } } else { set exp [string trim [$hd(text) get 1.0 end]] if {[catch {RatParseExp $exp} expId]} { set i [lindex $expId 0] $hd(text) tag add error "1.$i wordstart" "1.$i wordend" Popup "$t(error_underlined): [lindex $expId 1]" $hd(w) return } } if {"create" == $hd(action)} { if {$hd(doSave)} { if {[string length $hd(saveAs)]} { if {-1 != [lsearch -exact $expList $hd(saveAs)]} { Popup $t(name_occupied) $hd(w) return } if {"" != $hd(addproc)} { eval "$hd(addproc) [list $hd(saveAs)]" } else { lappend expList $hd(saveAs) } set expExp($hd(saveAs)) $exp ExpWrite } else { Popup $t(need_name) $hd(w) return } } # Apply expression if {[info exists fHd]} { set ids [$fHd(folder_handler) match $expId] if {[string length $ids]} { SetFlag $hd(handler) flagged 1 $ids } } } else { if {$hd(saveAs) != $hd(oldName)} { if {-1 != [lsearch -exact $expList $hd(saveAs)]} { Popup $t(name_occupied) $hd(w) return } ExpDelete $hd(oldName) lappend expList $hd(saveAs) } set expExp($hd(saveAs)) $exp ExpWrite } RatFreeExp $expId } destroy $hd(w) } proc ExpClose {handler} { upvar \#0 $handler hd global b option if {"create" == $hd(action)} { ::tkrat::winctl::RecordGeometry ratExpression $hd(w) } else { ::tkrat::winctl::RecordGeometry ratExpEdit $hd(w) } catch {focus $hd(oldfocus)} foreach bn [array names b $hd(w).*] {unset b($bn)} if {[string compare $hd(mode) $option(expression_mode)]} { set option(expression_mode) $hd(mode) SaveOptions } unset hd } # ExpWrite -- # # Write the saved expressions to disk # # Arguments: proc ExpWrite {} { global option expList expExp set f [open $option(ratatosk_dir)/expressions w] puts $f "set expList [list $expList]" foreach e $expList { puts $f [list set expExp($e) $expExp($e)] } close $f } # ExpRead -- # # Read the saved expressions # # Arguments: proc ExpRead {} { global option expList expExp if {[file readable $option(ratatosk_dir)/expressions]} { source $option(ratatosk_dir)/expressions } } # ExpBuildMenu # # Build a menu of saved expressions # # Arguments: # m - The menu to populate # handler - The handler which identifies the folder window that # the selection should be done in. proc ExpBuildMenu {m handler} { global expList expExp $m delete 0 end if {![info exists expList]} { return } foreach i $expList { $m add command -label $i -command [list ExpMenuApply $i $handler] } } # ExpMenuApply # # Apply an expression from the menu # # Arguments: # id - The array index of the selected expression # handler - The handler which identifies the folder window that # the selection should be done in. proc ExpMenuApply {id handler} { upvar \#0 $handler hd global expExp set expId [RatParseExp $expExp($id)] set ids [$hd(folder_handler) match $expId] if {[string length $ids]} { SetFlag $handler flagged 1 $ids } RatFreeExp $expId } # ExpHandleSaved # # Handle saved expressions # # Arguments: proc ExpHandleSaved {handler} { global t rat_list::create expList expList {ExpCreate {}} ExpEdit ExpDelete \ ExpWrite $t(saved_expr) $t(create) $t(edit) $t(delete) $t(dismiss) } # ExpDelete -- # # Delete the selected expression # # Arguments: # name - Name of expression to delete proc ExpDelete {name} { global expExp unset expExp($name) } tkrat_2.2cvs20100105-dfsg.orig/tkrat/firstwizard.tcl000066400000000000000000000314271137544547100222140ustar00rootroot00000000000000# firstwizard.tcl - # # TkRat software and its included text is Copyright 1996-2010 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # # # Functions which implements the first use wizard proc FirstUseWizard {} { global t propBigFont idCnt env set id firstusewizard[incr idCnt] set w .$id upvar \#0 $id hd set hd(history) {} set hd(top) $w # Create toplevel and insert infrastructure toplevel $w -class TkRat wm title $w $t(first_use_wizard) label $w.title -textvariable ${id}(title) -font $propBigFont set hd(scrollbody) $w.body set hd(body) [rat_scrollframe::create $w.body -bd 10] frame $w.buttons button $w.buttons.prev -text "< $t(wiz_previous)" -state disabled \ -command "set cmd \[lindex \$${id}(history) end-1\]; \ set ${id}(history) \[lreplace \$${id}(history) end-1 end\]; \ \$cmd $id" button $w.buttons.next -text "$t(wiz_next) >" -state disabled button $w.buttons.cancel -text $t(cancel) -command "destroy $w" pack $w.buttons.cancel -side right -padx 20 -pady 5 pack $w.buttons.next $w.buttons.prev -side right pack $w.title -side top -fill x pack $w.buttons -side bottom -fill x pack $w.body -fill both -expand 1 set hd(next) $w.buttons.next set hd(prev) $w.buttons.prev set hd(cancel) $w.buttons.cancel set hd(bodym) $w.body bind $w "$hd(next) invoke" bind $w "$w.buttons.cancel invoke" grid propagate $w.body 0 grid columnconfigure $w.body 0 -weight 1 grid rowconfigure $hd(body) 20 -weight 1 bind $w.body "if {\"%W\" == \"$w.body\"} {FirstUseWizardClose $id}" ::tkrat::winctl::SetGeometry firstUseWizard $w $w.body # Initialize values set hd(fullname) "$env(GECOS)" if {[string match *.* [info hostname]]} { set guess [join [lrange [split [info hostname] .] 1 end] .] } else { set guess [info hostname] } set hd(email_address) "$env(USER)@$guess" set hd(sendprot) smtp set hd(smtp_hosts) localhost set hd(imap_host) localhost set hd(imap_user) $env(USER) set hd(pop3_host) localhost set hd(pop3_user) $env(USER) if {[catch {exec which sendmail} hd(sendprog)]} { if {[file executable /usr/lib/sendmail]} { set hd(sendprog) /usr/lib/sendmail } } set hd(inproto) file set hd(filename) $env(MAIL) FirstUseWizardIdent $id tkwait window $hd(top) } proc FirstUseWizardClose {id} { upvar \#0 $id hd ::tkrat::winctl::RecordGeometry $hd(top) $hd(top).body unset hd } proc FirstUseWizardReset {id} { upvar \#0 $id hd global t eval destroy [winfo children $hd(body)] grid columnconfigure $hd(body) 0 -weight 0 grid columnconfigure $hd(body) 1 -weight 0 $hd(next) configure -text "$t(wiz_next) >" # Work around bug in grid (reported and fixed 200210??) grid size $hd(body) } proc FirstUseWizardIdent {id} { upvar \#0 $id hd global t # Setup the first step FirstUseWizardReset $id lappend hd(history) FirstUseWizardIdent set hd(title) $t(identity) rat_flowmsg::create $hd(body).message1 -text "$t(firstuse_info)" grid $hd(body).message1 - -sticky w -pady 10 rat_flowmsg::create $hd(body).message2 -text "$t(why_identity)" grid $hd(body).message2 - -sticky w frame $hd(body).space1 -height 20 grid $hd(body).space1 label $hd(body).name_l -text $t(fullname): -anchor e entry $hd(body).name_e -textvariable ${id}(fullname) grid $hd(body).name_l $hd(body).name_e -sticky we frame $hd(body).space2 -height 20 grid $hd(body).space2 label $hd(body).email_l -text $t(email_address): -anchor e entry $hd(body).email_e -textvariable ${id}(email_address) grid $hd(body).email_l $hd(body).email_e -sticky we $hd(prev) configure -state disabled $hd(next) configure -command "FirstUseWizardSend $id" -state normal grid columnconfigure $hd(body) 1 -weight 1 SetupShortcuts [list $hd(next) $hd(prev)] focus $hd(body).email_e $hd(body).email_e selection range 0 end rat_scrollframe::recalc $hd(scrollbody) } proc FirstUseWizardSend {id} { upvar \#0 $id hd global t # Setup the first step FirstUseWizardReset $id lappend hd(history) FirstUseWizardSend set hd(title) $t(sending) set a [list $hd(next) $hd(prev)] rat_flowmsg::create $hd(body).message -text "$t(why_sending)" grid $hd(body).message - -sticky w frame $hd(body).space1 -height 20 grid $hd(body).space1 radiobutton $hd(body).smtp -text $t(use_mail_server) \ -variable ${id}(sendprot) -value smtp \ -command "FirstUseWizardSendSetup $id" grid $hd(body).smtp - -sticky w lappend a $hd(body).smtp label $hd(body).smtpserver_l -text $t(smtp_hosts): -anchor e entry $hd(body).smtpserver_e -textvariable ${id}(smtp_hosts) grid $hd(body).smtpserver_l $hd(body).smtpserver_e -sticky we frame $hd(body).space2 -height 20 grid $hd(body).space2 radiobutton $hd(body).prog -text $t(use_prog) \ -variable ${id}(sendprot) -value prog \ -command "FirstUseWizardSendSetup $id" grid $hd(body).prog - -sticky w lappend a $hd(body).prog label $hd(body).prog_l -text $t(sendprog): -anchor e entry $hd(body).prog_e -textvariable ${id}(sendprog) grid $hd(body).prog_l $hd(body).prog_e -sticky we button $hd(body).file_browse -text $t(browse)... \ -command "Browse $hd(top) ${id}(sendprog) any" grid x $hd(body).file_browse -sticky e $hd(prev) configure -state normal $hd(next) configure -command "FirstUseWizardInbox $id" -state normal grid columnconfigure $hd(body) 1 -weight 1 SetupShortcuts $a FirstUseWizardSendSetup $id rat_scrollframe::recalc $hd(scrollbody) } proc FirstUseWizardSendSetup {id} { upvar \#0 $id hd switch $hd(sendprot) { "smtp" {set s 1; set p 0; set fw $hd(body).smtpserver_e} "prog" {set s 0; set p 1; set fw $hd(body).prog_e} } rat_ed::enabledisable $s [list $hd(body).smtpserver_l \ $hd(body).smtpserver_e] rat_ed::enabledisable $p [list $hd(body).prog_l $hd(body).prog_e] if {[focus] != $fw} { focus $fw $fw selection range 0 end } } proc FirstUseWizardInbox {id} { upvar \#0 $id hd global t # Setup the first step FirstUseWizardReset $id lappend hd(history) FirstUseWizardInbox set hd(title) $t(incom_mbox) set a [list $hd(next) $hd(prev)] rat_flowmsg::create $hd(body).message -text "$t(why_inbox)" grid $hd(body).message - -sticky w frame $hd(body).space1 -height 10 grid $hd(body).space1 radiobutton $hd(body).file -text $t(file) \ -variable ${id}(inproto) -value file \ -command "FirstUseWizardInboxSetup $id" grid $hd(body).file - -sticky w lappend a $hd(body).file label $hd(body).file_l -text $t(pathname): -anchor e entry $hd(body).file_e -textvariable ${id}(filename) grid $hd(body).file_l $hd(body).file_e -sticky we button $hd(body).file_browse -text $t(browse)... \ -command "Browse $hd(top) ${id}(filename) any" grid x $hd(body).file_browse -sticky e radiobutton $hd(body).imap -text $t(imap) \ -variable ${id}(inproto) -value imap \ -command "FirstUseWizardInboxSetup $id" grid $hd(body).imap - -sticky w lappend a $hd(body).imap label $hd(body).imap_l -text $t(host): -anchor e entry $hd(body).imap_e -textvariable ${id}(imap_host) grid $hd(body).imap_l $hd(body).imap_e -sticky we label $hd(body).iuser_l -text $t(user): -anchor e entry $hd(body).iuser_e -textvariable ${id}(imap_user) grid $hd(body).iuser_l $hd(body).iuser_e -sticky we button $hd(body).imap_adv -text $t(advanced_imap_conf)... \ -command "FirstUseWizardInboxAdv $id imap" grid x $hd(body).imap_adv -sticky e radiobutton $hd(body).pop -text $t(pop3) \ -variable ${id}(inproto) -value pop3 \ -command "FirstUseWizardInboxSetup $id" grid $hd(body).pop - -sticky w lappend a $hd(body).pop label $hd(body).pop_l -text $t(host): -anchor e entry $hd(body).pop_e -textvariable ${id}(pop3_host) grid $hd(body).pop_l $hd(body).pop_e -sticky we label $hd(body).puser_l -text $t(user): -anchor e entry $hd(body).puser_e -textvariable ${id}(pop3_user) grid $hd(body).puser_l $hd(body).puser_e -sticky we button $hd(body).pop_adv -text $t(advanced_pop_conf)... \ -command "FirstUseWizardInboxAdv $id pop3" grid x $hd(body).pop_adv -sticky e $hd(prev) configure -state normal $hd(next) configure -command "FirstUseWizardImport $id" -state normal grid columnconfigure $hd(body) 1 -weight 1 SetupShortcuts $a FirstUseWizardInboxSetup $id rat_scrollframe::recalc $hd(scrollbody) } proc FirstUseWizardInboxSetup {id} { upvar \#0 $id hd switch $hd(inproto) { "imap" {set i 1; set p 0; set f 0; set fw $hd(body).imap_e} "pop3" {set i 0; set p 1; set f 0; set fw $hd(body).pop_e} "file" {set i 0; set p 0; set f 1; set fw $hd(body).file_e} } rat_ed::enabledisable $i [list $hd(body).imap_l $hd(body).imap_e \ $hd(body).iuser_l $hd(body).iuser_e \ $hd(body).imap_adv] rat_ed::enabledisable $p [list $hd(body).pop_l $hd(body).pop_e \ $hd(body).puser_l $hd(body).puser_e \ $hd(body).pop_adv] rat_ed::enabledisable $f [list $hd(body).file_l $hd(body).file_e \ $hd(body).file_browse] if {[focus] != $fw} { focus $fw $fw selection range 0 end } } proc FirstUseWizardInboxAdv {id prot} { upvar \#0 $id hd global idCnt t propBigFont set id2 imapadv[incr idCnt] set w .$id2 upvar \#0 $id2 hd2 set hd2(host) $hd(${prot}_host) set hd2(name) Dummy toplevel $w -class TkRat wm title $w $t(first_use_wizard) label $w.title -textvariable ${id}(title) -font $propBigFont set hd2(body) [frame $w.body -bd 10] frame $w.buttons button $w.buttons.done -text "$t(done)" -state disabled \ -command "set ${id}(action) done; destroy $w" button $w.buttons.cancel -text "$t(cancel)" -state disabled \ -command "set ${id}(action) cancel; destroy $w" set hd2(next) $w.buttons.done pack $w.buttons.done -pady 10 pack $w.title -side top -fill x pack $w.buttons -side bottom -fill x pack $w.body -fill both -expand 1 bind $w "destroy $w" wm protocol $w WM_DELETE_WINDOW "destroy $w" if {"imap" == $prot} { set hd2(user) $hd(imap_user) VFolderWizardIMAPServer $id2 standalone } else { set hd2(user) $hd(pop3_user) VFolderWizardPOP $id2 standalone } ::tkrat::winctl::SetGeometry firstUseAdv $w $w tkwait window $w if {"done" == $hd(action)} { set hd(mailServer) [VFolderWizardBuildNewServer $id2 $prot] set hd(${prot}_host) $hd2(host) set hd(${prot}_user) $hd2(user) } unset hd2 } proc FirstUseWizardImport {id} { upvar \#0 $id hd global t FirstUseWizardReset $id lappend hd(history) FirstUseWizardImport set hd(title) $t(import_info) rat_flowmsg::create $hd(body).message -text "$t(import_info_text)" grid $hd(body).message - -sticky w -pady 10 $hd(prev) configure -state normal $hd(next) configure -command "FirstUseWizardDone $id" -text $t(finish) \ -state normal grid columnconfigure $hd(body) 1 -weight 1 SetupShortcuts [list $hd(next) $hd(prev)] rat_scrollframe::recalc $hd(scrollbody) } proc FirstUseWizardDone {id} { upvar \#0 $id hd global option mailServer vFolderDef vFolderInbox t set r $option(default_role) set option($r,from) "$hd(fullname) <$hd(email_address)>" set option($r,sendprot) $hd(sendprot) set option($r,sendprog) $hd(sendprog) set option($r,smtp_hosts) $hd(smtp_hosts) # Setup incoming mailbox if {"pop3" == $hd(inproto) || "imap" == $hd(inproto)} { if {"pop3" == [lindex $vFolderDef($vFolderInbox) 1] || "imap" == [lindex $vFolderDef($vFolderInbox) 1]} { set si [lindex $vFolderDef($vFolderInbox) 3] } else { if {"imap" == $hd(inproto)} { set si $hd(imap_host) set i 1 while {[info exists mailServer($si)]} { set si "$hd(imap_host)-[incr i]" } } else { set si 0 foreach ms [array names mailServer] { if { -1 != [lsearch -exact \ [lindex $mailServer($ms) 2] pop3] && [string is integer $ms] && $ms > $si} { set si $ms } } incr si } } if {![info exists hd(mailServer)]} { if {"pop3" == $hd(inproto)} { set hd(mailServer) \ [list $hd(pop3_host) 110 {pop3} $hd(pop3_user)] } else { set hd(mailServer) [list $hd(imap_host) 143 {} $hd(imap_user)] } } set mailServer($si) $hd(mailServer) } switch $hd(inproto) { "pop3" { set def [list $t(inbox) pop3 {} $si] } "imap" { set def [list $t(inbox) imap {} $si INBOX] } "file" { set def [list $t(inbox) file {} $hd(filename)] } } set vFolderDef($vFolderInbox) $def SaveOptions VFolderWrite destroy $hd(top) } tkrat_2.2cvs20100105-dfsg.orig/tkrat/folder.tcl000066400000000000000000002274161137544547100211240ustar00rootroot00000000000000# folder.tcl -- # # This file contains code which handles a folder window. # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Variables used to update online/offline settings of tkrat menus set tkrat_menus {} set tkrat_online_index 0 set tkrat_online_imgs {} # Images set online_img [image create photo -data { R0lGODlhIAAQAKUAAAAAAA8PDV1ZT2JeVGRgVmVgVmplW2xoXnBsYnJtY3h1a314bn56cIB9 dIF9c4qFe4uHfYuHfoyJgY2JgZOOhJeTipiTiZyXjZ+ck6CdlKaim6eimKeknqqnoLGtpLKw q7Oxqrezqry5sr67tcTCvcXDvcbCusjFv87Kw9HQzOHf2+Lh3ePh3ejn5Oro4+vq5uzr6O7s 6vDu6/Dv7PLw7fPy8Pn5+P///////////////////////////////////yH5BAEAAD8ALAAA AAAgABAAAAZ1wJ9wSCwaj8ikcslsOp9Qo6XxM1GjRkeI5KJ2L6IJVthYzTrdn6zxsZWuSZd8 Pqe9HjBqrIGptVh0c0QNhIUNCgojIAsqCj8pDRQnG4aGTAoSGgkojhwNDBkOB2M/BREIHqMV DQMGAqREFgQ/EHCwt7i5urhBADs=}] set offline_img [image create photo -data { R0lGODlhIAAQAKUAAAAAAA8PDV1ZT2JeVGRgVmVgVmplW2xoXnBsYnJtY3h1a314bn56cIB9 dIF9c4qFe4uHfYuHfoyJgY2JgZOOhJeTipiTiZyXjZ+ck6CdlKaim6eimKeknqqnoLGtpLKw q7Oxqrezqry5sr67tcTCvcXDvcbCusjFv87Kw9HQzN9CHuHf2+Lh3ePh3ejn5Oro4+vq5uzr 6O7s6vDu6/Dv7PLw7fPy8Pn5+P///////////////////////////////yH5BAEAAD8ALAAA AAAgABAAAAaIwJ9wSCwaj8ikcslsOokqlTD6PFoaU+mP+jNhnY4Q6fXdRrU/8kU0WTZYtA55 eEbPGp9bqUx8+V81MA8xfFxCMg0YNi4tf0QNDQoKIyALKwpQaD8pDRQnG5CQRwoSGgkomFmG HA0MGQ4HSwURCB6xqmgVDQMGAk8WBFnCPxB8VcOGx8rLzM1LQQA7}] set icon_img [image create photo -data { R0lGODlhQABAAIQeAAAAAD8DZksEek0EfVQEgFYFjHUGgHsGgIAIgIAJgIAKgK1vIsqBKM+F KdSIKs+/keDPnezapfHfqf/3Tf3qsf//Wf//aP//e///gv//hP//kf//ov//sv//u/////// /yH5BAEKAB8ALAAAAABAAEAAAAX+4CeOZGmeaKqubOu+cCzPdG3feE4DIq//H4BQqBkGgTch R8PcXCYaixBZA3A2GYd262hMGD7qS9jJcs+ORVjcApTR6Iaa3X7D0fPemt7jmO9neVZSJHtA ABppCw2AW3NWGQ1gPRuGOgATC2oMjI2PiV2PG3lIAGo8mI1pqKCaqBekhz5CFp13jwyapwCV sT97ABWes0MXGk4NU3xBfsN9x39ehHwAtYCuzH9nkpaHwrezG4qatqHdOW7agajNu5xcvr/N 16xau8Hw5zZC4s68WtzS5StlBYunU/O6TDA48FDCW7tAwdGF7ZBEiAAugLkI756+Gf+uqcmQ Qc2Fcvb+1BAwIGDAgY8wIIkEQLJLsgYoVSYoMIQnDlT9Jo40o8sUOXsAFAwQEAAAgqY/BQql SdSjUVcJAmgF4PPnw45UU2ZkwuTCEFMAmqaF6QKR0KFia27h5sHDWWXogjo6JXfVBXVdHAB4 QEEC2zFS92ZkiJTjXMGDD7dJHBewGsdzwdw1suOrq775TgLyQrb0NBluwYLmghMATlXjKOId w9EkYNY3UXa0OrvtRVd677SmSNwVsCMxUiP9+5b45rsmwkgOErIxHuNn1dTdHuGc9JgJge8W YoysWQASHkigQOGBoedn2/zO+Mf4BcYKMQiRsD79GiWlaTDBBeexoNwq82XugB9rE+jHX3v/ hedKBfFEN59E4qkiBwAeFObeLInIRkSFJRx4mSP0wbaKB+t92EOIaC1QAS8kFnIhipjBsWGH 7s0YBIxnkVTjCCZm1MmJKq4IoWBB1KLGEtBUpMKBG05gXZLaLYnKVcX1dkI15SCJpIraPeCe IPB5iUIqKIo2JmxlomURa2pY+eZBHJ45nYHWtLmhaKq44oGeVBjVSZVVBqrdoAAwWSgA7yDo l26K1WWma3vGlIsrm9aJkaVnDvkTb2DGVhQE7oWaaXLdXJWmZs8t86UQC5yRgTCaIIWPqGzA Z5Ueq9KhpqzEFltsCAA7}] # FolderWindowInit -- # # Initializes a folder window, that is it populates it with widgets and # installs all callbacks. It reaturns the handler that should be used for # this folder window. # # Arguments: # w - Window which the folder window should be packed into # toopen - Folder which the caller is about to open so do not # open for monitoring proc FolderWindowInit {w toopen} { global b t idCnt statusText option folderWindowList defaultFontWidth\ fixedNormFont tkrat_menus tkrat_online_index \ online_img tkrat_online_imgs numDeferred icon_img accelerator # Create the handler set handler f[incr idCnt] upvar \#0 $handler fh # Initialize variables set fh(toplevel) [winfo toplevel $w] set fh(w) $w set fh(folder_name) {} set fh(folder_status) {} set fh(num_messages) 0 set fh(groupMessageLists) {} set fh(uids) {} set fh(message_scroll) $w.t.messlist.scroll set fh(message_list) $w.t.messlist.list set fh(group) {} set fh(text) $w.b.text.text set fh(find_match_case) 0 set fh(find_match) exact set fh(find_loc) body set fh(browse) 0 set fh(setflag) "" set fh(syncing) 0 set fh(menu_nokeep) {} set fh(role) $option(default_role) set fh(special_folder) none set fh(context_menu) $w.t.messlist.list.contextmenu set fh(struct_menu) $w.t.mbar.message.m.structmenu set fh(wrap_mode) $option(wrap_mode) set fh(last_filter) "" upvar \#0 $fh(text) texth set texth(show_header) $option(show_header) set texth(struct_menu) $fh(struct_menu) set texth(width_adjust) {} ::tkrat::winctl::Size folderWindow $w frame $w.t frame $w.b # Icon set i .icon[incr idCnt] toplevel $i -class TkRat pack [label $i.l -image $icon_img] wm iconwindow $fh(toplevel) $i # The menu and information line frame $w.t.mbar -relief raised -bd 1 FindAccelerators a {tkrat folders message group show admin help filter} # Tkrat menu menubutton $w.t.mbar.tkrat -menu $w.t.mbar.tkrat.m -text $t(tkrat) \ -underline $a(tkrat) set m $w.t.mbar.tkrat.m menu $m -tearoff 1 -tearoffcommand "lappend tkrat_menus" \ -postcommand "PostTkRatMenu $handler" $m add cascade -label $t(role) -menu $m.role set b($m,[$m index end]) folder_select_role menu $m.role -postcommand \ [list PostRoles $handler $m.role [list UpdateFolderTitle $handler]] $m add cascade -label $t(new_folder) -menu $m.new_folder set b($m,[$m index end]) new_folder menu $m.new_folder -postcommand "NewFolderMenu $handler $m.new_folder" $m add separator $m add command -label $t(find)... -command "FolderFind $handler" set b($m,[$m index end]) find set fh(find_menu) [list $m [$m index end]] $m add command -label $t(compose)... \ -command "Compose \$${handler}(role)" set fh(compose_menu) [list $m [$m index end]] set b($m,[$m index end]) compose $m add separator $m add checkbutton -label $t(watcher) \ -variable option(watcher_enable) -onvalue 1 -offvalue 0 \ -command SaveOptions set b($m,[$m index end]) watcher_enable $m add checkbutton -label $t(browse_mode) -variable ${handler}(browse) set b($m,[$m index end]) browse_mode $m add command -label $t(update_folder) \ -command "RatBusy \"Sync $handler update\"" set fh(update_menu) [list $m [$m index end]] set b($m,[$m index end]) update $m add command -label $t(sync_folder) \ -command "RatBusy \"Sync $handler sync\"" set fh(sync_menu) [list $m [$m index end]] set b($m,[$m index end]) sync $m add command -label $t(netsync_folder) -state disabled \ -command "RatBusy {\$${handler}(folder_handler) netsync; \ Sync $handler update}" set b($m,[$m index end]) netsync_folder set fh(netsync_folder_menu) [list $m [$m index end]] $m add command -label $t(netsync) -command "RatBusy NetworkSync" set b($m,[$m index end]) netsync set fh(netsync_all_menu) [list $m [$m index end]] $m add command lappend tkrat_menus $m set fh(online_menu) [list $m [$m index end]] set tkrat_online_index [$m index end] $m add separator $m add command -label $t(see_log)... -command SeeLog set b($m,[$m index end]) see_log $m add command -label $t(close) -command "DestroyFolderWin $handler 0" set fh(close_menu) [list $m [$m index end]] set b($m,[$m index end]) close_folder $m add command -label $t(quit) -command "Quit $handler" set fh(quit_menu) [list $m [$m index end]] set b($m,[$m index end]) quit # Folder menu menubutton $w.t.mbar.folder -menu $w.t.mbar.folder.m -text $t(folders) \ -underline $a(folders) menu $w.t.mbar.folder.m \ -postcommand "PostFolder $handler $w.t.mbar.folder.m" -tearoff 1 set b($w.t.mbar.folder) folder_menu # Message menu menubutton $w.t.mbar.message -menu $w.t.mbar.message.m -text $t(message) \ -underline $a(message) set b($w.t.mbar.message) message_menu set m $w.t.mbar.message.m menu $m -tearoff 0 -postcommand "PostMessageMenu $handler $m" # Show menu menubutton $w.t.mbar.show -menu $w.t.mbar.show.m -text $t(show) \ -underline $a(show) set b($w.t.mbar.show) show_menu set m $w.t.mbar.show.m menu $m -tearoff 1 $m add radiobutton -label $t(no_wrap) \ -variable ${handler}(wrap_mode) -value none \ -command [list SetWrapMode $handler] set b($m,[$m index end]) show_no_wrap $m add radiobutton -label $t(wrap_char) \ -variable ${handler}(wrap_mode) -value char \ -command [list SetWrapMode $handler] set b($m,[$m index end]) show_wrap_char $m add radiobutton -label $t(wrap_word) \ -variable ${handler}(wrap_mode) -value word \ -command [list SetWrapMode $handler] set b($m,[$m index end]) show_wrap_word $m add separator $m add radiobutton -label $t(show_all_headers) \ -variable $fh(text)(show_header) -value all \ -command [list SetShowHeaders $handler] set b($m,[$m index end]) show_all_headers $m add radiobutton -label $t(show_selected_headers) \ -variable $fh(text)(show_header) -value selected \ -command [list SetShowHeaders $handler] set b($m,[$m index end]) show_selected_headers $m add radiobutton -label $t(show_no_headers) \ -variable $fh(text)(show_header) -value no \ -command [list SetShowHeaders $handler] set b($m,[$m index end]) show_no_headers $m add separator $m add cascade -label $t(sort_order) -menu $m.sort set b($m,[$m index end]) sort_order_folder lappend fh(menu_nokeep) [list $m [$m index end]] menu $m.sort foreach o {threaded subject subjectonly senderonly sender folder reverseFolder date reverseDate size reverseSize} { $m.sort add radiobutton -label $t(sort_$o) \ -variable ${handler}(folder_sort) -value $o \ -command [list SetSortOrder $handler] set b($m.sort,[$m.sort index end]) sort_$o } # Group menu menubutton $w.t.mbar.group -menu $w.t.mbar.group.m -text $t(group) \ -underline $a(group) set b($w.t.mbar.group) group_menu set m $w.t.mbar.group.m menu $m -postcommand "SetupGroupMenu $m $handler" -tearoff 1 $m add command -label $t(create_in_win)... \ -command "GroupMessageList $handler" set b($m,[$m index end]) create_in_win $m add command -label $t(create_by_expr)... -command "ExpCreate $handler" set b($m,[$m index end]) create_by_expr $m add cascade -label $t(use_saved_expr) -menu $m.saved set b($m,[$m index end]) use_saved_expr menu $m.saved -postcommand "ExpBuildMenu $m.saved $handler" $m add command -label $t(clear_group) -command "GroupClear $handler" set b($m,[$m index end]) clear_group $m add separator $m add command -label $t(forward_separately) \ -command "ForwardGroupSeparately \[GetMsgSet $handler group\] \ \$${handler}(role)" set b($m,[$m index end]) forward_separately $m add command -label $t(forward_in_one) \ -command "ForwardGroupInOne \[GetMsgSet $handler group\] \ \$${handler}(role)" set b($m,[$m index end]) forward_in_one $m add command -label $t(bounce_messages) \ -command "BounceMessages \[GetMsgSet $handler group\] \ \$${handler}(role)" set b($m,[$m index end]) bounce_messages $m add command -label $t(extract_adr)... \ -command "AliasExtract $handler \[GetMsgSet $handler group\]" set b($m,[$m index end]) extract_adr $m add separator $m add command -label $t(print) -command "Print $handler group" set b($m,[$m index end]) print_group $m add cascade -label $t(move) -menu $m.move set b($m,[$m index end]) move_group menu $m.move -postcommand "PostMove $handler 1 group $m.move" $m add cascade -label $t(copy) -menu $m.copy set b($m,[$m index end]) copy_group menu $m.copy -postcommand "PostMove $handler 0 group $m.copy" $m add separator $m add command -label $t(delete) -command \ "SetFlag $handler deleted 1 \ \[\$${handler}(folder_handler) flagged flagged 1\]" set b($m,[$m index end]) delete_group $m add command -label $t(undelete) -command \ "SetFlag $handler deleted 0 \ \[\$${handler}(folder_handler) flagged flagged 1\]" set b($m,[$m index end]) undelete_group # Disable in drafts... $m add command -label $t(mark_as_unread) -command \ "SetFlag $handler seem 0 \ \[\$${handler}(folder_handler) flagged flagged 1\]" set b($m,[$m index end]) mark_as_unread $m add command -label $t(mark_as_read) -command \ "SetFlag $handler seen 1 \ \[\$${handler}(folder_handler) flagged flagged 1\]" set b($m,[$m index end]) mark_as_read $m add command -label $t(mark_as_answered) -command \ "SetFlag $handler answered 0 \ \[\$${handler}(folder_handler) flagged flagged 1\]" set b($m,[$m index end]) mark_as_answered $m add command -label $t(mark_as_unanswered) -command \ "SetFlag $handler answered 1 \ \[\$${handler}(folder_handler) flagged flagged 1\]" set b($m,[$m index end]) mark_as_unanswered $m add separator $m add command -label $t(dbinfo)... \ -command "MsgDbInfo folder \ \[\$${handler}(folder_handler) dbinfo_get\] \ \$${handler}(folder_handler) \ \[\$${handler}(folder_handler) flagged flagged 1\]" set b($m,[$m index end]) dbinfo # Admin menu menubutton $w.t.mbar.admin -menu $w.t.mbar.admin.m -text $t(admin) \ -underline $a(admin) set b($w.t.mbar.admin) admin_menu set m $w.t.mbar.admin.m menu $m -tearoff 1 $m add command -label $t(newedit_folder)... -command VFolderDef set b($m,[$m index end]) newedit_folder $m add command -label $t(addressbook)... -command Aliases set b($m,[$m index end]) addressbook $m add command -label $t(preferences)... -command Preferences set b($m,[$m index end]) preferences $m add command -label $t(define_keys)... -command "KeyDef folder" set b($m,[$m index end]) define_keys $m add command -label $t(saved_expr)... -command "ExpHandleSaved $handler" set b($m,[$m index end]) saved_expr $m add command -label $t(setup_netsync)... -command SetupNetworkSync set b($m,[$m index end]) setup_netsync $m add command -label $t(purge_pwcache) -command RatPurgePwChache set b($m,[$m index end]) purge_pwcache $m add cascade -label $t(reread) -menu $m.reread $m add cascade -label $t(dbase) -menu $m.dbase set rm $m.reread menu $rm $rm add command -label $t(reimport_all) -command VFolderReimportAll set b($rm,[$rm index end]) reimport_all $rm add command -label $t(reread_userproc) -command ReadUserproc set b($rm,[$rm index end]) reread_userproc $rm add command -label $t(reread_mailcap) -command RatMailcapReload set b($rm,[$rm index end]) reread_mailcap menu $m.dbase $m.dbase add command -label $t(info)... \ -command "DbaseInfo" $m.dbase add command -label $t(check_dbase)... \ -command "RatBusy \"DbaseCheck 0\"" set b($m.dbase,[$m.dbase index end]) check_dbase $m.dbase add command -label $t(check_fix_dbase)... \ -command "RatBusy \"DbaseCheck 1\"" set b($m.dbase,[$m.dbase index end]) check_fix_dbase # Help menu menubutton $w.t.mbar.help -menu $w.t.mbar.help.m -text $t(help) \ -underline $a(help) set b($w.t.mbar.help) help_menu set m $w.t.mbar.help.m menu $m -tearoff 1 $m add checkbutton -label $t(balloon_help) \ -variable option(show_balhelp) \ -command {SaveOptions} set b($m,[$m index end]) balloon_help $m add separator $m add command -label $t(version)... -command Version set b($m,[$m index end]) show_version $m add command -label Ratatosk... -command Ratatosk set b($m,[$m index end]) explain_ratatosk $m add command -label $t(help_window)... -command Help set b($m,[$m index end]) help_window $m add command -label $t(send_bug)... -command SendBugReport set b($m,[$m index end]) send_bug # The structure menu (populated by the Show routine) menu $texth(struct_menu) -tearoff 0 # Information button $w.t.mbar.netstatus -image $online_img -bd 0 -width 32 lappend tkrat_online_imgs $w.t.mbar.netstatus label $w.t.mbar.status -textvariable ${handler}(folder_status) set b($w.t.mbar.status) folder_status # Pack the menus into the menu bar pack $w.t.mbar.tkrat \ $w.t.mbar.folder \ $w.t.mbar.message \ $w.t.mbar.show \ $w.t.mbar.group \ $w.t.mbar.admin -side left -padx 5 pack $w.t.mbar.help -side right -padx 5 pack $w.t.mbar.netstatus $w.t.mbar.status -side right # The information part frame $w.t.info -relief raised -bd 1 label $w.t.info.flabel -text $t(name): label $w.t.info.fname -textvariable ${handler}(folder_name) \ -anchor w -font $fixedNormFont set fh(filter_clear) $w.t.info.clear set fh(filter_apply) $w.t.info.apply button $fh(filter_clear) -text $t(clear) -bd 1 -highlightthickness 0 \ -command [list FilterClear $handler] -state disabled button $fh(filter_apply) -text $t(apply) -bd 1 -highlightthickness 0 \ -command [list FilterApply $handler] -state disabled entry $w.t.info.filter -width 25 -bd 1 -textvariable ${handler}(filter) set fh(filter_entry) $w.t.info.filter label $w.t.info.filterl -text $t(filter): ::tkrat::winctl::InstallMnemonic $w.t.info.filterl $a(filter) \ $fh(filter_entry) # Override keyboard bindings... bind KbdBlock {break} bind KbdBlock {break} bindtags $fh(filter_entry) [list $fh(filter_entry) Entry KbdBlock $w all] pack $w.t.info.flabel -side left pack $w.t.info.fname -side left -fill x -expand 1 pack $fh(filter_clear) \ $fh(filter_apply) \ $fh(filter_entry) \ $w.t.info.filterl -side right set b($w.t.info.fname) current_folder_name bind $fh(filter_entry) "$fh(filter_apply) invoke; break" # The message list frame $w.t.messlist scrollbar $fh(message_scroll) \ -relief sunken \ -command "$w.t.messlist.list yview" \ -highlightthickness 0 text $fh(message_list) \ -yscroll "$fh(message_scroll) set" \ -bd 0 \ -highlightthickness 0 \ -wrap none \ -spacing1 1 \ -spacing3 2 \ -cursor {} $fh(message_list) tag configure Active \ -relief raised \ -borderwidth [$fh(message_list) cget -selectborderwidth] \ -foreground [$fh(message_list) cget -selectforeground] \ -background [$fh(message_list) cget -selectbackground] if { 4 < [winfo cells $fh(message_list)]} { $fh(message_list) tag configure sel -background #ffff80 $fh(message_list) tag configure Found -background #ffff00 } else { $fh(message_list) tag configure sel -underline 1 $fh(message_list) tag configure Found -borderwidth 2 -relief raised } $fh(message_list) tag raise sel pack $fh(message_scroll) -side right -fill y pack $fh(message_list) -side left -expand 1 -fill both # The context menu (populated by FolderContextMenu) menu $fh(context_menu) -tearoff 0 # The status line label $w.statustext -textvariable statusText -relief raised \ -bd 1 -width 80 set b($w.statustext) status_text # The command buttons frame $w.b.buttons menubutton $w.b.buttons.move -text $t(move) -bd 1 -relief raised \ -menu $w.b.buttons.move.m -indicatoron 1 menu $w.b.buttons.move.m \ -postcommand "PostMove $handler 1 current $w.b.buttons.move.m" button $w.b.buttons.delete -text $t(delete) -bd 1 -highlightthickness 0 \ -command "SetFlag $handler deleted 1; FolderNext $handler" button $w.b.buttons.compose -text $t(compose)... -highlightthickness 0 \ -command "Compose \$${handler}(role)" -bd 1 button $w.b.buttons.reply_sender -text $t(reply_sender)... -bd 1 \ -highlightthickness 0 -command "FolderReply $handler sender" button $w.b.buttons.reply_all -text $t(reply_all)... -bd 1 \ -highlightthickness 0 -command "FolderReply $handler all" pack $w.b.buttons.move \ $w.b.buttons.delete \ $w.b.buttons.compose \ $w.b.buttons.reply_sender \ $w.b.buttons.reply_all -side left -expand 1 -fill x if { 0 < $option(pgp_version)} { set fh(sigbut) $w.b.buttons.signature button $fh(sigbut) -text $t(sig): -bd 1 -anchor w -state disabled \ -highlightthickness 0 -width [expr {[string length $t(sig)]+5}] pack $fh(sigbut) -side right -expand 1 -fill x set b($fh(sigbut)) pgp_none } set b($w.b.buttons.move) move_msg set b($w.b.buttons.delete) delete_msg set b($w.b.buttons.compose) compose_msg set b($w.b.buttons.reply_sender) reply_sender set b($w.b.buttons.reply_all) reply_all # The actual text frame $w.b.text -relief raised -bd 1 scrollbar $w.b.text.yscroll \ -relief sunken \ -command "$w.b.text.text yview" \ -highlightthickness 0 scrollbar $w.b.text.xscroll \ -relief sunken \ -command "$w.b.text.text xview" \ -highlightthickness 0 \ -orient horiz text $fh(text) \ -yscroll "RatScrollShow $w.b.text.text $w.b.text.yscroll" \ -xscroll "$w.b.text.xscroll set" \ -relief raised \ -wrap $fh(wrap_mode) \ -bd 0 \ -highlightthickness 0 \ -setgrid 1 $fh(text) mark set searched 1.0 set texth(text_frame) $w.b.text set texth(text_xscroll) $w.b.text.xscroll set texth(text_yscroll) $w.b.text.yscroll pack $w.b.text.yscroll -side right -fill y pack $w.b.text.text -side top -expand yes -fill both # Pack all the parts into the window pack $w.t.mbar -side top -fill x pack $w.t.info -side top -fill x pack $w.t.messlist -fill both -expand 1 pack $w.b.buttons -side top -fill x pack $w.b.text -side top -expand yes -fill both frame $w.handle -width 10 -height 10 \ -relief raised -borderwidth 2 \ -cursor sb_v_double_arrow set b($w.handle) pane_button # Do special packing bind $w \ "set ${handler}(H) \[winfo height $w\]; \ set ${handler}(Y0) \[winfo rooty $w\]; \ set ${handler}(Y1) \[expr \[winfo width $w\] - 5\]; \ place configure $w.handle -x \$${handler}(Y1)" bind $w.handle \ "FolderPane $handler \[expr (%Y-\$${handler}(Y0))/\$${handler}(H).0\]" FolderPane $handler [::tkrat::winctl::GetPane folderWindow] place $w.t -relwidth 1 place $w.b -relwidth 1 -rely 1 -anchor sw place $w.statustext -relwidth 1 -anchor w place $w.handle -anchor e after idle "set d \[expr \[winfo height $w.statustext\] / 2\]; \ place configure $w.t -height -\$d; \ place configure $w.b -height -\$d" # Do bindings focus $w bind $w <1> "if {\"%W\" != \"$fh(filter_entry)\"} {focus $w}" bind $fh(message_list) <1> \ "FolderSelect $handler \[expr int(\[%W index @%x,%y\])-1\]" bind $fh(message_list) "FolderDouble $handler; break" bind $fh(message_list) break bind $fh(message_list) break bind $fh(message_list) break bind $fh(message_list) break bind $fh(message_list) break bind $fh(message_list) \ "FolderFlagEvent $handler \[%W index @%x,%y\]; break" bind $fh(message_list) \ "FolderFlagMotion $handler \[%W index @%x,%y\]; break" bind $fh(message_list) \ "FolderFlagEvent $handler \[%W index @%x,%y\]; break" bind $fh(message_list) \ "FolderFlagMotion $handler \[%W index @%x,%y\]; break" bind $fh(message_list) <2> "FolderFlagEvent $handler \[%W index @%x,%y\]" bind $fh(message_list) \ "FolderFlagRange $handler \[%W index @%x,%y\]" bind $fh(message_list) \ "FolderFlagMotion $handler \[%W index @%x,%y\]" bind $fh(message_list) <3> "FolderContextMenu $handler %x %y %X %Y" bind $fh(text) "FolderMap $handler" bind $fh(text) "FolderUnmap $handler" bind $fh(text) "DestroyFolderWin $handler 1" FolderBind $handler wm protocol $fh(toplevel) WM_DELETE_WINDOW "DestroyFolderWin $handler 1" # Calculate font width if {![info exists defaultFontWidth]} { CalculateFontWidth $fh(text) } trace variable fh(filter) w [list FilterChanged $handler] # Do things which are done just when the first folder window is opened if {![info exists folderWindowList]} { RatLibSetOnlineMode $option(online) update idletasks RatBusy { # Reimport imported folders with the session setting global vFolderDef foreach id [array names vFolderDef] { if {"import" != [lindex $vFolderDef($id) 1]} { continue } set a(reimport) unset array set a [lindex $vFolderDef($id) 2] if {"session" == $a(reimport)} { RatImport $id } } # Open monitored folders FolderStartMonitor $toopen } } SetOnlineStatus $option(online) set folderWindowList($handler) "" return $handler } # UpdateFolderStatus -- # # Updates the show status of this folder window # # Arguments: # handler - The handler which identifies the folder window # new - Number of new messages # messages - Number of messages # size - Size of mailbox (-1 for unknown) proc UpdateFolderStatus {handler new messages size} { upvar \#0 $handler fh set fh(old_messages) $messages set fh(old_size) $size if {-1 == $size} { set s "?" } else { set s [RatMangleNumber $size] } set fh(folder_status) "$new / $messages / $s" } # UpdateFolderStatusNew -- # # Updates the new messages count in the show status of this folder window # # Arguments: # handler - The handler which identifies the folder window # new - Number of new messages proc UpdateFolderStatusNew {handler new} { upvar \#0 $handler fh UpdateFolderStatus $handler $new $fh(old_messages) $fh(old_size) } # ResizeBodyText -- # # Handle resizing of the body text widget # # Arguments: # w - The handler which identifies the body text widget # width - The new width proc ResizeBodyText {w width} { upvar \#0 $w texth foreach c $texth(width_adjust) { $c configure -width $width } } # PostTkRat -- # # Post-command for Tkrat menu # # Arguments: # handler - The handler which identifies the folder window proc PostTkRatMenu {handler} { upvar \#0 $handler fh global folderWindowList if {1 == [array size folderWindowList]} { set state disabled } else { set state normal } [lindex $fh(close_menu) 0] entryconfigure [lindex $fh(close_menu) 1] \ -state $state } # PostMessageMenu -- # # Post-command for Message menu # # Arguments: # handler - The handler which identifies the folder window # m - Menu to populat proc PostMessageMenu {handler m} { upvar \#0 $handler fh if {![info exists fh(folder_handler)]} { return } if {"" == $fh(list_index)} { set msg "" } else { set msg $fh(current_msg) } BuildMessageMenu $handler $m $msg } # BuildMessageMenu -- # # Populate the message menu # # Arguments: # handler - The handler which identifies the folder window # m - Menu to populate # msg - The current message proc BuildMessageMenu {handler m msg} { upvar \#0 $handler fh global t b accelerator set msgsel_state normal set delete_state normal set undelete_state disabled set markunread_state disabled set markread_state normal set answered_state normal set unanswered_state disabled set dela_state disabled set fi [$fh(folder_handler) find $msg] if {-1 == $fi} { set delete_state disabled set msgsel_state disabled set markread_state disabled set answered_state disabled } else { if {1 == [$fh(folder_handler) getFlag $fi deleted]} { set delete_state disabled set undelete_state normal } if {1 == [$fh(folder_handler) getFlag $fi seen]} { set markunread_state normal set markread_state disabled } if {1 == [$fh(folder_handler) getFlag $fi answered]} { set unanswered_state normal set answered_state disabled } set body [$msg body] if {"MULTIPART" == [lindex [$body type] 0]} { set dela_state normal } } $m delete 0 end if {"drafts" == $fh(special_folder)} { $m add command -label $t(continue_composing)... -state $msgsel_state \ -command "\ ComposeContinue $msg; \ SetFlag $handler deleted 1 $fi" } else { $m add command -label $t(reply_sender)... -state $msgsel_state \ -accelerator $accelerator(folder_key_replys) \ -command "ComposeReply $msg sender $fh(role) \ \"FolderReplySent $handler $msg\"" set b($m,[$m index end]) reply_sender $m add command -label $t(reply_all)... -state $msgsel_state \ -accelerator $accelerator(folder_key_replya) \ -command "ComposeReply $msg all $fh(role) \ \"FolderReplySent $handler $msg\"" set b($m,[$m index end]) reply_all } $m add command -label $t(forward_inline)... -state $msgsel_state \ -accelerator $accelerator(folder_key_forward_i) \ -command "ComposeForwardInline $msg $fh(role)" set b($m,[$m index end]) forward_inline $m add command -label $t(forward_as_attachment)... \ -state $msgsel_state \ -accelerator $accelerator(folder_key_forward_a) \ -command "ComposeForwardAttachment $msg $fh(role)" set b($m,[$m index end]) forward_attached if {"drafts" != $fh(special_folder)} { $m add command -label $t(bounce)... -state $msgsel_state \ -accelerator $accelerator(folder_key_bounce) \ -command "ComposeBounce $msg $fh(role)" set b($m,[$m index end]) bounce $m add command -label $t(extract_adr)... -state $msgsel_state \ -command "AliasExtract $handler $msg" set b($m,[$m index end]) extract_adr } $m add command -label $t(delete_attachments)... -state $dela_state \ -command "::tkrat::delattachments::delete $msg $handler" $m add separator $m add command -label $t(print)... -command "Print $handler $msg" \ -state $msgsel_state -accelerator $accelerator(folder_key_print) set b($m,[$m index end]) print if {"drafts" != $fh(special_folder)} { $m add cascade -label $t(move) -menu $m.move -state $msgsel_state set b($m,[$m index end]) move if {![winfo exists $m.move]} { menu $m.move } $m.move configure -postcommand "PostMove $handler 1 $msg $m.move" } $m add cascade -label $t(copy) -menu $m.copy -state $msgsel_state set b($m,[$m index end]) copy_msg if {![winfo exists $m.copy]} { menu $m.copy } $m.copy configure -postcommand "PostMove $handler 0 $msg $m.copy" $m add separator $m add command -label $t(delete) -state $delete_state \ -command "SetFlag $handler deleted 1 $fi" \ -accelerator $accelerator(folder_key_delete) set b($m,[$m index end]) delete $m add command -label $t(undelete) -state $undelete_state \ -command "SetFlag $handler deleted 0 $fi" \ -accelerator $accelerator(folder_key_undelete) set b($m,[$m index end]) undelete if {"drafts" != $fh(special_folder)} { $m add command -label $t(mark_as_unread) -state $markunread_state \ -command "SetFlag $handler seen 0 $fi" \ -accelerator $accelerator(folder_key_markunread) set b($m,[$m index end]) mark_as_unread $m add command -label $t(mark_as_read) -state $markread_state \ -command "SetFlag $handler seen 1 $fi" set b($m,[$m index end]) mark_as_read $m add command -label $t(mark_as_answered) -state $answered_state \ -command "SetFlag $handler answered 1 $fi" set b($m,[$m index end]) mark_as_answered $m add command -label $t(mark_as_unanswered) -state $unanswered_state \ -command "SetFlag $handler answered 0 $fi" set b($m,[$m index end]) mark_as_unanswered } $m add command -label $t(flag_same_subject) -state $msgsel_state \ -command [list GroupSameSubject $handler $msg] if {"dbase" == [$fh(folder_handler) type]} { $m add separator if {"" != $msg} { set info [$msg dbinfo_get] } else { set info {} } $m add command -label $t(dbinfo)... -state $msgsel_state \ -command [list MsgDbInfo msg $info \ $fh(folder_handler) $fi] set b($m,[$m index end]) dbinfo } } # FolderStartMonitor -- # # Start monitoring of folders # # Arguments: # toopen - Folder which will be opened anyway so do not bother to open here proc FolderStartMonitor {toopen} { global vFolderDef vFolderMonitorFH vFolderMonitorID ratNetOpenFailures set netopenFailures 0 foreach id [array names vFolderDef] { catch {unset f} array set f [lindex $vFolderDef($id) 2] if {[info exists f(monitor)] && $f(monitor) && ![FolderFailedOpen check $vFolderDef($id)]} { if {![catch {VFolderDoOpen $id $vFolderDef($id)} hd]} { WatcherInit $hd } else { FolderFailedOpen set $vFolderDef($id) } } } if {0 < $ratNetOpenFailures} { SetOnlineStatus 0 } } # FolderFailedOpen -- # # Check if we previously failed to open a folder on the same host/prot/port # Returns true if we previously failed to open. # # Arguments: # op - Operation to perform (check, set or clearall) # def - Folder definition to check proc FolderFailedOpen {op def} { global failedConnections if {-1 == [lsearch -exact {imap pop3} [lindex $def 1]]} { return 0 } set spec [lindex $def 3] if {"check" == $op} { return [info exists failedConnections($spec)] } elseif {"set" == $op} { set failedConnections($spec) 1 } else { unset failedConnections } return 0 } # FolderPane -- # # Pane the folder window # # Arguments: # handler - The handler which identifies the folder window # y - Y position of dividing line proc FolderPane {handler y} { upvar \#0 $handler fh if {$y < 0.01 || 0.99 < $y} return # Prevents placing into inaccessibility (off the window). set w $fh(w) set fh(pane) $y place $w.t -relheight $y place $w.b -relheight [expr {1.0 - $y}] place $w.statustext -rely $y place $w.handle -rely $y } # FolderBind -- # # Bind the key definitions for a folder window # # Arguments: # handler - The handler which identifies the fodler window proc FolderBind {handler} { upvar \#0 $handler fh RatBindMenu $fh(w) folder_key_close $fh(close_menu) RatBindMenu $fh(w) folder_key_online $fh(online_menu) RatBindMenu $fh(w) folder_key_quit $fh(quit_menu) RatBindMenu $fh(w) folder_key_sync $fh(sync_menu) RatBindMenu $fh(w) folder_key_netsync $fh(netsync_all_menu) RatBindMenu $fh(w) folder_key_update $fh(update_menu) RatBindMenu $fh(w) folder_key_compose $fh(compose_menu) RatBindMenu $fh(w) folder_key_find $fh(find_menu) RatBind $fh(w) folder_key_delete \ "SetFlag $handler deleted 1; FolderNext $handler" RatBind $fh(w) folder_key_undelete \ "SetFlag $handler deleted 0; FolderNext $handler" RatBind $fh(w) folder_key_replya "FolderReply $handler all" RatBind $fh(w) folder_key_replys "FolderReply $handler sender" RatBind $fh(w) folder_key_forward_i \ "FolderSomeCompose $handler ComposeForwardInline" RatBind $fh(w) folder_key_forward_a \ "FolderSomeCompose $handler ComposeForwardAttachment" RatBind $fh(w) folder_key_bounce "FolderSomeCompose $handler ComposeBounce" RatBind $fh(w) folder_key_markunread \ "SetFlag $handler seen 0; FolderNext $handler" RatBind $fh(w) folder_key_print "Print $handler current" RatBind $fh(w) folder_key_openfile \ "PostFolderOpen $handler \[SelectFileFolder $fh(toplevel)\]" RatBind $fh(w) folder_key_nextu "FolderSelectUnread $handler; break" RatBind $fh(w) folder_key_flag \ "SetFlag $handler flagged toggle; FolderNext $handler" RatBind $fh(w) folder_key_next "FolderNext $handler" RatBind $fh(w) folder_key_prev "FolderPrev $handler" RatBind $fh(w) folder_key_home "ShowHome $fh(text)" RatBind $fh(w) folder_key_bottom "ShowBottom $fh(text)" RatBind $fh(w) folder_key_pagedown "ShowPageDown $fh(text)" RatBind $fh(w) folder_key_pageup "ShowPageUp $fh(text)" RatBind $fh(w) folder_key_linedown "ShowLineDown $fh(text)" RatBind $fh(w) folder_key_lineup "ShowLineUp $fh(text)" RatBind $fh(w) folder_key_cycle_header "CycleShowHeader $handler" RatBind $fh(w) folder_key_mvdb \ "PostFolderOpen $handler \[SelectDbaseFolder $fh(toplevel)\]" } # FolderWindowClear -- # # Clears a folder window. This is the state we get into when no folder is # currently open. # # Arguments: # handler - The handler which identifies the folder window proc FolderWindowClear {handler} { global folderWindowList option upvar \#0 $handler fh set fh(folder_name) "" UpdateFolderTitle $handler UpdateFolderStatus $handler 0 0 0 ShowNothing $fh(text) catch {unset fh(current_msg); unset fh(folder_handler)} $fh(message_list) configure -state normal $fh(message_list) delete 1.0 end $fh(message_list) configure -state disabled set fh(list_index) "" FolderButtons $handler 0 set folderWindowList($handler) "" } # FolderRead -- # # Reads and the given folder and calls draw_folder_list to show the content. # If there already is an active folder it is closed. As arguments it expects # a folderwindow handler and a command which when run opens the folder. # # Arguments: # handler - The handler which identifies the folder window # foldercmd - Command which creates the new folder # name - Human readable name of folder (proposed) proc FolderRead {handler foldercmd name} { return [RatBusy [list FolderReadDo $handler $foldercmd $name]] } proc FolderReadDo {handler foldercmd name} { global option inbox t folderWindowList folderExists folderUnseen upvar \#0 $handler fh # First we expunge the old folder if {[info exists fh(folder_handler)]} { if {![string compare $inbox $fh(folder_handler)]} { set inbox "" } set folderWindowList($handler) "" if {[info exists fh(folder_handler)]} { CloseFolder $fh(folder_handler) unset fh(folder_handler) } } # Open the new folder set id [RatLog 2 $t(opening_folder)... explicit] if {[catch $foldercmd folder_handler]} { RatClearLog $id FolderWindowClear $handler return {} } RatClearLog $id # Set name (if needed) if {[string length $name]} { $folder_handler setName $name } # Update our information set fh(folder_handler) $folder_handler set folderWindowList($handler) $folder_handler set fh(folder_sort) [$folder_handler getSortOrder] set i [$fh(folder_handler) info] set fh(folder_name) [lindex $i 0] if {"" != [$fh(folder_handler) role]} { set fh(role) [$fh(folder_handler) role] } UpdateFolderTitle $handler FolderDrawList $handler set i [$fh(folder_handler) info] UpdateFolderStatus $handler $folderUnseen($fh(folder_handler)) \ $folderExists($fh(folder_handler)) [lindex $i 2] switch $option(folder_sort) { reverseFolder { set dir -1 set start 0 } reverseDate { set dir -1 set start 0 } reverseSize { set dir -1 set start 0 } default { set dir 1 set start [expr {$fh(num_messages)-1}] } } if { 0 != $fh(num_messages)} { switch $option(start_selection) { last { set index [expr {$fh(num_messages)-1}] } first_new { set index [FolderGetNextUnread $handler $start $dir] } before_new { set index [FolderGetNextUnread $handler $start $dir] set fi $fh(mapping,$index) if { 0 == [$fh(folder_handler) getFlag $fi seen]} { incr index [expr -1 * $dir] if {$index < 0 || $index >= $fh(num_messages)} { incr index $dir } } } default { # And first set index 0 } } } else { set index "" } FolderSelect $handler $index set type [$folder_handler type] if {"dis" == $type} { set state normal } else { set state disabled } [lindex $fh(netsync_folder_menu) 0] entryconfigure \ [lindex $fh(netsync_folder_menu) 1] -state $state # Initialize watcher set fh(exists) $folderExists($folder_handler) WatcherInit $folder_handler return $folder_handler } # FolderDrawList -- # # Constructs the list of messages in the folder and shows this. # # Arguments: # handler - The handler which identifies the folder window proc FolderDrawList {handler} { upvar \#0 $handler fh global option set old_num_messages $fh(num_messages) set fh(num_messages) 0 set folder_index 0 $fh(message_list) configure -state normal $fh(message_list) delete 1.0 end set entries [$fh(folder_handler) list "%u $option(list_format)"] set fh(uids) {} array unset fh mapping,* array unset fh rmapping,* foreach e $entries { regexp {^([^ ]*) (.*)} $e unused uid l if {"" == $fh(filter) || [string match -nocase "*$fh(filter)*" $l]} { $fh(message_list) insert end "$l\n" set fh(mapping,$fh(num_messages)) $folder_index set fh(rmapping,$folder_index) $fh(num_messages) lappend fh(uids) $uid incr fh(num_messages) } incr folder_index } $fh(message_list) delete end-1c foreach w $fh(groupMessageLists) { GroupMessageListUpdate $w $handler } $fh(message_list) xview moveto 0 $fh(message_list) configure -state disabled } # FolderListRefreshEntry -- # # Refresh an entry in the message list # # Arguments: # handler - The handler which identifies the folder window # index - Index of the entry to refresh (list index) proc FolderListRefreshEntry {handler index} { upvar \#0 $handler fh global option set fi $fh(mapping,$index) set line [expr {$index+1}] $fh(message_list) configure -state normal set tags [$fh(message_list) tag names $line.0] $fh(message_list) delete $line.0 "$line.0 lineend" $fh(message_list) insert $line.0 [format %-256s \ [[$fh(folder_handler) get $fi] list $option(list_format)]] $tags $fh(message_list) configure -state disabled } # FolderSelect -- # # Handle the selection of a message # # Arguments: # handler - The handler which identifies the folder window # index - Index to select proc FolderSelect {handler index} { upvar \#0 $handler fh global option t b folderUnseen if {![info exists fh(folder_handler)]} { return } set fh(list_index) $index if {0 == [string length $index] || $index >= $fh(num_messages)} { catch {unset fh(current_msg)} FolderButtons $handler 0 ShowNothing $fh(text) set fh(list_index) "" return } set fh(folder_index) $fh(mapping,$index) if {![info exists fh(current_msg)]} { FolderButtons $handler 1 } set line [expr {$index+1}] $fh(message_list) tag remove Active 1.0 end $fh(message_list) tag add Active $line.0 "$line.0 lineend+1c" $fh(message_list) see $line.0 update idletasks if {![info exists fh(message_list)]} return set fh(current_msg) [$fh(folder_handler) get $fh(folder_index)] set seen [$fh(folder_handler) getFlag $fh(folder_index) seen] $fh(folder_handler) setFlag $fh(folder_index) seen 1 set result [Show $fh(text) $fh(current_msg) $fh(browse)] set sigstatus [lindex $result 0] set pgpOutput [lindex $result 1] if { 0 == $seen } { FolderListRefreshEntry $handler $index set fh(folder_new) [RatMangleNumber $folderUnseen($fh(folder_handler))] UpdateFolderStatusNew $handler $folderUnseen($fh(folder_handler)) } if {0 < $option(pgp_version) && [info exists fh(sigbut)]} { set state normal set command {} set b($fh(sigbut)) $sigstatus switch $sigstatus { pgp_none { set state disabled } pgp_unchecked { set command "FolderCheckSignature $handler" } pgp_good { set command [list RatText $t(pgp_output) $pgpOutput] } pgp_bad { set command [list RatText $t(pgp_output) $pgpOutput] } pgp_err { set command [list RatText $t(pgp_output) $pgpOutput] } pgp_abort { set command "$fh(current_msg) rerunPGP \; FolderSelect $handler $index" } } $fh(sigbut) configure -state $state \ -text "$t(sig): $t($sigstatus)" \ -command $command } } # FolderDouble -- # # Handle doubleclicks on messages # # Arguments: # handler - The handler which identifies the folder window proc FolderDouble {handler} { upvar \#0 $handler fh if {![info exists fh(current_msg)]} { return } if {"drafts" == $fh(special_folder)} { ComposeContinue $fh(current_msg) $fh(folder_handler) setFlag $fh(folder_index) deleted 1 FolderListRefreshEntry $handler $fh(list_index) } else { FolderReply $handler all } } # FolderContextMenu -- # # Popup the context menu on the indicated message # # Arguments: # handler - The handler which identifies the folder window # x, y - Location in listbox # X, Y - Location on screen proc FolderContextMenu {handler x y X Y} { upvar \#0 $handler fh global t if {![info exists fh(folder_handler)]} { return } set index [expr int([$fh(message_list) index @$x,$y])-1] if {![info exists fh(mapping,$index)]} { return } set fi $fh(mapping,$index) if {[catch {$fh(folder_handler) get $fi} msg]} { return } BuildMessageMenu $handler $fh(context_menu) $msg tk_popup $fh(context_menu) $X $Y } # FolderNext -- # # Advance to the next message in the folder # # Arguments: # handler - The handler which identifies the folder window proc FolderNext {handler} { upvar \#0 $handler fh set ml $fh(message_list) if {![string length $fh(list_index)]} { return } set index [expr {1+$fh(list_index)}] if { $index >= $fh(num_messages) } { return } if {$index >= [expr {round([lindex [$ml yview] 1]*$fh(num_messages))}]} { $ml yview $index } FolderSelect $handler $index } # FolderPrev -- # # Retreat to the previous message in the folder # # Arguments: # handler - The handler which identifies the folder window proc FolderPrev {handler} { upvar \#0 $handler fh set ml $fh(message_list) if {![string length $fh(list_index)]} { return } set index [expr {$fh(list_index)-1}] if {$index < 0} { return } if {$index < [expr {round([lindex [$ml yview] 0]*$fh(num_messages))}]} { $ml yview scroll -1 pages } FolderSelect $handler $index } # SetFlag -- # # Set a flag to a specified value for the current message or a set of # messages given as argument. # # Arguments: # handler - The handler which identifies the folder window # flag - The flag to set # value - The new value of the deletion flag ('1' or '0') # messages - An optional list of messages to do this to # The list is of folder indexes. proc SetFlag {handler flag value {messages {}}} { upvar \#0 $handler fh global option folderUnseen if {![string length $fh(list_index)]} { return } if {$fh(list_index) >= $fh(num_messages)} { return } if {![string length $messages]} { set messages $fh(mapping,$fh(list_index)) } set onlist {} set offlist {} foreach i $messages { if {[string compare toggle $value]} { if {$value} { lappend onlist $i } else { lappend offlist $i } } else { if {[$fh(folder_handler) getFlag $i $flag]} { lappend offlist $i } else { lappend onlist $i } } } if {0 < [llength $onlist]} { $fh(folder_handler) setFlag $onlist $flag 1 } if {0 < [llength $offlist]} { $fh(folder_handler) setFlag $offlist $flag 0 } foreach i $messages { FolderListRefreshEntry $handler $fh(rmapping,$i) } UpdateFolderStatusNew $handler $folderUnseen($fh(folder_handler)) } # Quit -- # # Closes the given folder window and quits tkrat # # Arguments: # handler - The handler which identifies the folder window proc Quit {handler} { global folderWindowList upvar \#0 $handler fh if {0 == [PrepareQuit]} { return } foreach fw [array names folderWindowList] { CloseFolderWin $fw } RatCleanup destroy . } # Quit -- # # Closes the given folder window and quits tkrat # # Arguments: # handler - The handler which identifies the folder window proc PrepareQuit {} { global expAfter logAfter ratSenderSending t composeWindowList \ alreadyQuitting folderWindowList if {[info exists alreadyQuitting]} { return 0 } set alreadyQuitting 1 if {[info exists composeWindowList]} { if {0 < [llength $composeWindowList]} { if {1 == [RatDialog "" $t(really_quit) $t(compose_sessions) \ {} 1 $t(quit_anyway) $t(dont_quit)]} { unset alreadyQuitting return 0 } } } if {1 < [array size folderWindowList]} { set msg [format $t(n_folder_windows) [array size folderWindowList]] if {1 == [RatDialog "" $t(really_quit) $msg \ {} 1 $t(quit_anyway) $t(dont_quit)]} { unset alreadyQuitting return 0 } } AliasWeAreQuitting if {$ratSenderSending} { RatLog 2 $t(waiting_on_sender) explicit while {1 == $ratSenderSending} { tkwait variable ratSenderSending } RatLog 2 "" explicit } if {[string length $expAfter]} { after cancel $expAfter } if {[string length $logAfter]} { after cancel $logAfter } return 1 } # FindAdjacent -- # # Find elemnts in new list which were adjacent to an element in an old list # # Arguments: # handler - The handler which identifies the folder window # old_index - Index of old message # old_list - Old list of messages proc FindAdjacent {handler old_index old_list} { upvar \#0 $handler fh global option set listAfterFull [$fh(folder_handler) list %u] set listAfter {} for {set i 0} {$i <$fh(num_messages)} {incr i} { lappend listAfter [lindex $listAfterFull $fh(mapping,$i)] } set list [lrange $old_list [expr {$old_index+1}] end] for {set i $old_index} {$i >= 0} {incr i -1} { lappend list [lindex $old_list $i] } foreach element $list { if {-1 != [set index [lsearch -exact $listAfter $element]]} { return $index } } return 0 } # Sync -- # # Does an update on the current folder. # # Arguments: # handler - The handler which identifies the folder window # mode - Mode of sync proc Sync {handler mode} { upvar \#0 $handler fh global option folderExists folderUnseen if {![info exists fh(folder_handler)]} {return} if {$fh(syncing)} { return } set fh(syncing) 1 set oldActive $fh(list_index) set listBefore $fh(uids) if {[string length $oldActive]} { set subject [lindex $listBefore $oldActive] set msg $fh(current_msg) } if {[llength $listBefore]} { # Get data about what is visible at the moment. We want... # ...name & index of the top message # ...to know if the active message is visible # ...to know if the last message is visible if {[catch { set oldTopIndex [lindex [split [$fh(message_list) index @0,0] .] 0] set fi $fh(mapping,[expr $oldTopIndex-1]) set oldTopMsg [$fh(folder_handler) get $fi] if {"" != $oldActive} { set sawActive [$fh(message_list) bbox $oldActive.0+1l] } else { set sawActive 0 } set sawEnd [$fh(message_list) bbox end-1c] } err]} { unset oldTopIndex } } if {[catch {$fh(folder_handler) update $mode}]} { FolderWindowClear $handler set fh(syncing) 0 return } set xview [lindex [$fh(message_list) xview] 0] FolderDrawList $handler # Update information set i [$fh(folder_handler) info] UpdateFolderStatus $handler $folderUnseen($fh(folder_handler)) \ $folderExists($fh(folder_handler)) [lindex $i 2] if { 0 == [lindex $i 1] } { FolderSelect $handler "" set fh(syncing) 0 return } # Check if our element is still in there set fh(list_index) "" if {[string length $oldActive]} { set findex [$fh(folder_handler) find $msg] if { -1 != $findex && [info exists fh(rmapping,$findex)]} { set fh(folder_index) $findex set fh(list_index) $fh(rmapping,$findex) set line [expr {$fh(list_index)+1}] $fh(message_list) tag add Active $line.0 "$line.0 lineend+1c" $fh(message_list) see $line.0 } else { set index [FindAdjacent $handler $oldActive $listBefore] FolderSelect $handler $index } if {![string length $fh(list_index)]} { FolderSelect $handler 0 } } # Fix scroll position in text widget if {[info exists oldTopIndex]} { # Set topmost visible message set findex [$fh(folder_handler) find $oldTopMsg] if { -1 == $findex || ![info exists fh(mapping,$findex)]} { set index [FindAdjacent $handler $oldTopIndex $listBefore] } else { set index $fh(mapping,$findex) } $fh(message_list) yview $index # If the last message used to be visible, make sure it still is if {4 == [llength $sawEnd]} { $fh(message_list) see {end linestart-1l} } # If the active message was visible make it visible again # this will override the last-message visibility if {4 == [llength $sawActive]} { $fh(message_list) see [expr $fh(list_index)+1].0 } } $fh(message_list) xview moveto $xview set fh(syncing) 0 } # FolderReply -- # # Construct a reply to a message and update the messages status if the # reply was sent. # # Arguments: # handler - The handler which identifies the folder window # recipient - Who the reply should be sent to 'sender' or 'all' proc FolderReply {handler recipient} { upvar \#0 $handler fh if {![string length $fh(list_index)]} { return } set hd [ComposeReply $fh(current_msg) $recipient $fh(role) \ "FolderReplySent $handler $fh(current_msg)"] } proc FolderReplySent {handler current} { upvar \#0 $handler fh if {[info exists fh]} { set findex [$fh(folder_handler) find $current] if { -1 != $findex } { if {![$fh(folder_handler) getFlag $findex answered]} { if {[info exists fh(mapping,$findex)]} { SetFlag $handler answered 1 $fh(mapping,$findex) } else { $fh(folder_handler) setFlag $findex answered 1 } } } } } # FolderSomeCompose -- # # Run a compose function on a message and update the message status if the # message actually was sent. # # Arguments: # handler - The handler which identifies the folder window proc FolderSomeCompose {handler composeFunc} { upvar \#0 $handler fh global option if {![string length $fh(list_index)]} { return } $composeFunc $fh(current_msg) $fh(role) } # PostFolder -- # # Populate the folder menu # # Arguments: # handler - The handler which identifies the folder window # m - The menu which we should populate proc PostFolder {handler m} { global vFolderLastUsedList vFolderDef upvar \#0 $handler fh global t option vFolderSpecials idmap$m if {[info exists idmap$m]} { unset idmap$m } $m delete 1 end VFolderBuildMenu $m 0 "VFolderOpen $handler" 0 $m add separator foreach id $vFolderLastUsedList { if {[info exists vFolderDef($id)]} { VFolderAddItem $m $id $id "VFolderOpen $handler" 0 } } $m add separator VFolderBuildMenu $m $vFolderSpecials "VFolderOpen $handler" 0 $m add separator $m add command -label $t(open_file)... \ -command "PostFolderOpen $handler \[SelectFileFolder $fh(toplevel)\]" $m add command -label $t(open_dbase)... \ -command "PostFolderOpen $handler \ \[SelectDbaseFolder $fh(toplevel)\]" FixMenu $m } # PostFolderOpen -- # # Opens a folder choosen interactively from the PostFolder menu # # Arguments: # handler - The handler which identifies the folder window # cmd - The command which opens the new folder (if any) proc PostFolderOpen {handler cmd} { if {"" != $cmd} { FolderRead $handler $cmd "" } } # PostMove -- # # Populate the move menu # # Arguments: # handler - The handler which identifies the folder window # move - 1 for move mode (original is deleted) # which - Which set of messages we should move (current or group) # m - The menu which we should populate proc PostMove {handler move which m} { upvar \#0 $handler fh global t vFolderLastUsedList vFolderDef if {"current" == $which && 1 == $move} { set a 1 } else { set a 0 } $m delete 0 end VFolderBuildMenu $m 0 \ "VFolderInsert $handler $a $move \[GetMsgSet $handler $which\]" 1 $m add separator foreach id $vFolderLastUsedList { if {[info exists vFolderDef($id)]} { VFolderAddItem $m $id $id \ "VFolderInsert $handler $a $move \[GetMsgSet $handler $which\]" 1 } } $m add separator $m add command -label $t(to_file)... -command \ "VFolderInsert $handler $a $move \[GetMsgSet $handler $which\] \ \[InsertIntoFile $fh(toplevel)]" $m add command -label $t(to_dbase)... -command \ "VFolderInsert $handler $a $move \[GetMsgSet $handler $which\] \ \[InsertIntoDBase $fh(toplevel)\]" FixMenu $m } # GetMsgSet -- # # Get the messages that the current operation should be performed on # # Arguments: # handler - The handler which identifies the folder window # which - Which set of messages we should move (current or group) proc GetMsgSet {handler which} { upvar \#0 $handler fh switch $which { current { if {[info exists fh(current_msg)]} { return $fh(current_msg) } else { return {} } } group { set msgs {} foreach i [$fh(folder_handler) flagged flagged 1] { lappend msgs [$fh(folder_handler) get $i] } return $msgs } default { return $which } } } # FolderButtons -- # # Enable or disable the buttons in the folder window which depends on # an active message. # # Arguments: # handler - The handler which identifies the folder window # onoff - The new state of the buttons proc FolderButtons {handler onoff} { upvar \#0 $handler fh global option t b set w $fh(w) if {$onoff} { set state normal } else { set state disabled if {0 < $option(pgp_version) && [info exists fh(sigbut)]} { $fh(sigbut) configure -state disabled -text "$t(sig): $t(none)" set b($fh(sigbut)) pgp_none } } foreach but [list $w.b.buttons.move \ $w.b.buttons.delete \ $w.b.buttons.reply_sender \ $w.b.buttons.reply_all] { $but configure -state $state } foreach m $fh(menu_nokeep) { [lindex $m 0] entryconfigure [lindex $m 1] -state $state } } # FolderGetNextUnread -- # # Return the index of the next unread message in folder after index, # or index if none is found. # # Arguments: # handler - The handler which identifies the folder window # index - Where in the list to start looking proc FolderGetNextUnread {handler index dir} { upvar \#0 $handler fh if {0 == $fh(num_messages)} { return 0 } for {set i [expr {$index+$dir}]} {$i != $index} {incr i $dir} { if {$i >= $fh(num_messages)} { set i 0 } elseif {$i < 0} { set i [expr {$fh(num_messages)-1}] } if {$i == $index} { return 0 } if { 0 == [$fh(folder_handler) getFlag $fh(mapping,$i) seen]} { return $i } } return $index } # FolderSelectUnread -- # # Selects the next unread message in the folder. # # Arguments: # handler - The handler which identifies the folder window proc FolderSelectUnread {handler} { upvar \#0 $handler fh set index $fh(list_index) if {0 == [llength $index]} { return } set i [FolderGetNextUnread $handler $index 1] FolderSelect $handler $i } # CycleShowHeader -- # # Cycle through the values of the show_header option # # Arguments: # handler - The handler which identifies the folder window proc CycleShowHeader {handler} { global option upvar \#0 $handler fh upvar \#0 $fh(text) texth switch $texth(show_header) { all { set texth(show_header) selected } selected { set texth(show_header) no } no { set texth(show_header) all } } FolderSelect $handler $fh(list_index) SaveOptions } # FolderCheckSignature -- # # Check the signature(s) of the current message # # Arguments: # handler - The handler which identifies the folder window proc FolderCheckSignature {handler} { upvar \#0 $handler fh upvar \#0 msgInfo_$fh(current_msg) msgInfo global t b set tot pgp_none set b($fh(sigbut)) pgp_none set first 1 set result {} foreach bodypart $msgInfo(pgp,signed_parts) { set part [string map [list "\a" ""] [$bodypart checksig]] if {[string length $result]} { set result "$totresult\n\n$part" } else { set result $part } set status [$bodypart sigstatus] set tot $status set b($fh(sigbut)) $status if {![string compare pgp_good $status] && $first} { set first 0 if {[string compare $bodypart [$fh(current_msg) body]]} { set tot pgp_part set b($fh(sigbut)) part_sig } } } if {[string length $result] && $tot != "pgp_abort"} { RatText $t(pgp_output) $result } $fh(sigbut) configure -state normal \ -text "$t(sig): $t($tot)" \ -command [list RatText $t(pgp_output) $result] } # FolderFind -- # # Find text in a message or the message list # # Arguments: # handler - The handler which identifies the folder window proc FolderFind {handler} { global t b idCnt upvar \#0 $handler fh # Create identifier set id f[incr idCnt] upvar \#0 $id hd set w .$id # Initialize variables set hd(match_case) $fh(find_match_case) set hd(match) $fh(find_match) if {"list" == $fh(find_loc)} { set hd(loc) $fh(message_list) } else { set hd(loc) $fh(text) } set hd(w) $w set hd(handler) $handler set hd(def_start) 0 set hd(oldfocus) [focus] set hd(text) "" # Create toplevel toplevel $w -class TkRat -bd 5 wm title $w $t(find) # Create window frame $w.l label $w.l.label -text $t(find_in): radiobutton $w.l.list -text $t(message_list) -variable ${id}(loc) \ -value $fh(message_list) radiobutton $w.l.body -text $t(message_body) -variable ${id}(loc) \ -value $fh(text) pack $w.l.label \ $w.l.list \ $w.l.body -side left -anchor w set b($w.l.list) find_in_mlist set b($w.l.body) find_in_body entry $w.e -textvariable ${id}(text) set b($w.e) enter_search_text_here checkbutton $w.c -text $t(match_case) -variable ${id}(match_case) set b($w.c) toggle_ignore_case frame $w.b frame $w.b.find_next -relief sunken -bd 2 button $w.b.find_next.b -text $t(find_next) -state disabled pack $w.b.find_next.b -padx 1 -pady 1 frame $w.b.find_prev -relief flat -bd 2 button $w.b.find_prev.b -text $t(find_prev) -state disabled pack $w.b.find_prev.b -padx 1 -pady 1 button $w.b.dismiss -text $t(dismiss) -command "destroy $w" pack $w.b.find_next \ $w.b.find_prev \ $w.b.dismiss -side left -expand 1 set b($w.b.find.b) find_first set b($w.b.find_next.b) find_next set b($w.b.find_prev.b) find_prev set b($w.b.dismiss) dismiss pack $w.l \ $w.e \ $w.c \ $w.b -side top -fill x -expand 1 ::tkrat::winctl::SetGeometry find $w focus $w.e bind $w.e [list $w.b.find_next invoke] bind $w.e "FolderFindDone $id" bind $w "$w.b.dismiss invoke" set hd(find_args) \ [list $w.e $w.c $w.b.find_next.b $w.b.find_prev.b] trace variable hd(loc) w "FinderFindTrace $id" FinderFindTrace $id } # FinderFindTrace -- # # Trace the find text variable and change the button state accordingly # # Arguments: # id - The handler which identifies the find window proc FinderFindTrace {id args} { upvar \#0 $id hd if {[info exists hd(find)]} { rat_find::uninit $hd(find) } set hd(find) [eval rat_find::init $hd(loc) $hd(find_args)] } # FolderFindDone -- # # Close find window # # Arguments: # id - The handler which identifies the find window proc FolderFindDone {id} { upvar \#0 $id hd global b if {[info exists hd(find)]} { rat_find::uninit $hd(find) } ::tkrat::winctl::RecordGeometry find $hd(w) catch {focus $hd(oldfocus)} destroy $hd(w) foreach a [array names b $hd(w)*] { unset b($a) } unset hd } # FolderMap -- # # Called when the folder window is mapped # # Arguments: # handler - The handler which identifies the folder window proc FolderMap {handler} { upvar \#0 $handler fh global option if {[info exists fh(folder_handler)]} { WatcherSleepFH $fh(folder_handler) } if {0 != $option(checkpoint_interval)} { set fh(checkpointaid) \ [after [expr {$option(checkpoint_interval)*1000}] \ "FolderCheckpoint $handler"] } } # FolderUnmap -- # # Called when the folder window is unmapped # # Arguments: # handler - The handler which identifies the folder window proc FolderUnmap {handler} { upvar \#0 $handler fh global option if {$option(checkpoint_on_unmap)} { #RatBusy {Sync $handler checkpoint} Sync $handler checkpoint } if {[info exists fh(checkpointaid)]} { after cancel $fh(checkpointaid) unset fh(checkpointaid) } } # FolderCheckpoint -- # # Called when the folder window should be periodically checkpointed # # Arguments: # handler - The handler which identifies the folder window proc FolderCheckpoint {handler} { upvar \#0 $handler fh global option RatBusy {Sync $handler checkpoint} if {0 != $option(checkpoint_interval)} { set fh(checkpointaid) \ [after [expr {$option(checkpoint_interval)*1000}] \ "FolderCheckpoint $handler"] } } # NewFolderMenu -- # # Populate the new folder menu # # Arguments: # handler - The handler which identifies the folder window # m - The menu which we should populate proc NewFolderMenu {handler m} { upvar \#0 $handler fh global t vFolderSpecials vFolderLastUsedList vFolderDef $m delete 0 end VFolderBuildMenu $m 0 NewFolderWin 0 $m add separator foreach id $vFolderLastUsedList { if {[info exists vFolderDef($id)]} { VFolderAddItem $m $id $id NewFolderWin 0 } } $m add separator VFolderBuildMenu $m $vFolderSpecials NewFolderWin 0 $m add separator $m add command -label $t(open_file)... -command "NewFolderWin openfile" $m add command -label $t(open_dbase)... -command "NewFolderWin opendbase" $m add command -label $t(dbase_same_subject) set sub_idx [$m index end] $m add command -label $t(dbase_to_from_sender) set send_idx [$m index end] $m add command -label $t(empty) -command "NewFolderWin empty" if {![info exists fh(current_msg)]} { $m entryconfigure $sub_idx -state disabled $m entryconfigure $send_idx -state disabled } else { set subject [$fh(current_msg) list "%c"] set exp [list and subject \"$subject\"] set vf_subject [list $t(dbase_same_subject) dbase {} {} {} $exp] $m entryconfigure $sub_idx -state normal \ -command [list NewFolderWin $vf_subject] set from [$fh(current_msg) get from] if {"" == $from} { set from [$fh(current_msg) get sender] } if {"" == $from} { set from [$fh(current_msg) get reply_to] } if {"" == $from} { $m entryconfigure $send_idx -state disabled } else { if {[llength $from] > 1} { set from [lindex $from 0] } set exp [list and all_addresses [$from get mail]] set vf_sender [list $t(dbase_to_from_sender) dbase {} {} {} $exp] $m entryconfigure $send_idx -state normal \ -command [list NewFolderWin $vf_sender] } } FixMenu $m } # NewFolderWin -- # # Create a new folder window and populate it # # Arguments: # vf - VFolder definition of folder to open proc NewFolderWin {vf} { global idCnt option folderWindowList # Complement folder information (if needed) set manual 0 if {"openfile" == $vf} { upvar \#0 [lindex [array names folderWindowList] 0] fh set vf [SelectFileFolder $fh(toplevel)] if {"" == $vf} return set manual 1 } elseif {"opendbase" == $vf} { upvar \#0 [lindex [array names folderWindowList] 0] fh set vf [SelectDbaseFolder $fh(toplevel)] if {"" == $vf} return set manual 1 } elseif {"def" == [lindex $vf 0]} { set vf [list RatOpenFolder [lindex $vf 1]] set manual 1 } # Create folder window set w .f[incr idCnt] toplevel $w -class TkRat SetIcon $w $option(icon) set handler [FolderWindowInit $w ""] UpdateFolderTitle $handler ::tkrat::winctl::Place folderWindow $w if {"empty" != $vf} { if {$manual} { FolderRead $handler $vf "" } else { VFolderOpen $handler $vf } } } # CloseFolder -- # # Closes the given folder # # Arguments: # handler - The handler which identifies the folder proc CloseFolder {handler} { catch {$handler close} } # CloseFolderWin -- # # Closes the given folder window # # Arguments: # handler - The handler which identifies the folder window proc CloseFolderWin {handler} { global folderWindowList upvar \#0 $handler fh if {[catch {unset folderWindowList($handler)}]} { return } if {[info exists fh(folder_handler)]} { CloseFolder $fh(folder_handler) } if {[winfo exists $fh(w)]} { ::tkrat::winctl::RecordGeometry folderWindow \ [winfo toplevel $fh(w)] [winfo toplevel $fh(w)] $fh(pane) bind $fh(text) { } destroy $fh(w) } unset fh } # DestroyFolderWin -- # # Destroys the given folder window, if it was the last then we also # quit tkrat. # # Arguments: # handler - The handler which identifies the folder window # force - True if the window really will be closed proc DestroyFolderWin {handler force} { global folderWindowList if {1 < [array size folderWindowList] || ($force && 1 == [PrepareQuit])} { CloseFolderWin $handler if {0 == [array size folderWindowList]} { RatCleanup destroy . } } } # FolderFlagEvent -- # # Toggle flag status of current message # # Arguments: # handler - The handler which identifies the folder window # index - Message under pointer proc FolderFlagEvent {handler index} { upvar \#0 $handler fh set fh(setflag) [expr {int($index-1)}] set fi $fh(mapping,$fh(setflag)) SetFlag $handler flagged toggle $fi set fh(lastFlagResult) [$fh(folder_handler) getFlag $fi flagged] } # FolderFlagMotion -- # # Toggle flag status of current message # # Arguments: # handler - The handler which identifies the folder window # index - Message under pointer proc FolderFlagMotion {handler index} { upvar \#0 $handler fh set i [expr {int($index-1)}] if {$i != $fh(setflag)} { if {$i > $fh(setflag)} { for {set ui $i} {$ui > $fh(setflag)} {incr ui -1} { SetFlag $handler flagged $fh(lastFlagResult) $fh(mapping,$ui) } } else { for {set ui $i} {$ui < $fh(setflag)} {incr ui} { SetFlag $handler flagged $fh(lastFlagResult) $fh(mapping,$ui) } } set fh(setflag) $i } } # FolderFlagRange -- # # Toggle flag status of messages between current and last # # Arguments: # handler - The handler which identifies the folder window # index - Message under pointer proc FolderFlagRange {handler index} { upvar \#0 $handler fh if {"" != $fh(setflag)} { FolderFlagMotion $handler $index } } # NetworkSyncs -- # # Do network synchronization # # Arguments: proc NetworkSync {} { global option t numDeferred folderWindowList if {[lindex $option(network_sync) 2]} { RatLog 2 $t(running_cmd) explicit if {[catch "exec [lindex $option(network_sync) 3]" error]} { Popup $error } RatLog 2 "" explicit } if {[lindex $option(network_sync) 0]} { RatNudgeSender } if {[lindex $option(network_sync) 1]} { RatSyncDisconnected foreach f [array names folderWindowList] { upvar \#0 $f fh if {[info exists fh(folder_handler)] && "" != $fh(folder_handler) && "dis" == [$fh(folder_handler) type]} { Sync $f update } } } } # SetOnlineStatus -- # # Update all instanses of tkrat to reflect the current online status # # Arguments: # online - true if new mode is online proc SetOnlineStatus {online} { global tkrat_menus tkrat_online_index option t b \ tkrat_online_imgs online_img offline_img if {$option(online) != $online} { catch {RatLibSetOnlineMode $online} } if {$option(online)} { set label $t(go_offline) set bal go_offline set ibal icon_online set cmd "RatBusy {SetOnlineStatus 0}; SaveOptions" set img $online_img } else { set label $t(go_online) set bal go_online set ibal icon_offline set cmd "RatBusy {SetOnlineStatus 1}; SaveOptions" set img $offline_img } set imgs {} foreach i $tkrat_online_imgs { if {[winfo exists $i]} { $i configure -image $img -command $cmd lappend imgs $i set b($i) $ibal } } set tkrat_online_imgs $imgs foreach m $tkrat_menus { if {[winfo exists $m]} { $m entryconfigure $tkrat_online_index -label $label -command $cmd set b($m,$tkrat_online_index) $bal } } } # PostRoles -- # # Post command for the roles menu. Populates the menu with the list # of roles. # # Arguments: # handler - The handler which identifies the folder window # m - Menu to populate proc PostRoles {handler m cmd} { global option $m delete 0 end foreach r $option(roles) { $m add radiobutton -variable ${handler}(role) -value $r \ -label $option($r,name) -command $cmd } } # UpdateFolderTitle -- # # Updates the title of a folder window # # Arguments: # handler - The handler which identifies the folder window proc UpdateFolderTitle {handler} { global option upvar \#0 $handler fh set title [string map [list %f $fh(folder_name) \ %r $option($fh(role),name)] \ $option(main_window_name)] wm title $fh(toplevel) $title set ititle [string map [list %f $fh(folder_name)] $option(icon_name)] wm iconname $fh(toplevel) $ititle } # FilterClear -- # # Clears the filter # # Arguments: # handler - The handler which identifies the folder window proc FilterClear {handler} { upvar \#0 $handler fh set fh(filter) "" set fh(last_filter) $fh(filter) Sync $handler update if {![info exists fh(current_msg)]} { FolderSelect $handler 0 } FilterChanged $handler if {[focus] == $fh(filter_entry)} { focus $fh(w) } } # FilterApply -- # # Applies the filter # # Arguments: # handler - The handler which identifies the folder window proc FilterApply {handler} { upvar \#0 $handler fh set fh(last_filter) $fh(filter) Sync $handler update $fh(filter_apply) configure -state disabled focus $fh(w) } # FilterChanged -- # # Callback which is called when the filter definition changes # # Arguments: # handler - The handler which identifies the folder window # args - Standard trace callback arguments proc FilterChanged {handler args} { upvar \#0 $handler fh if {"" == $fh(filter)} { $fh(filter_clear) configure -state disabled } else { $fh(filter_clear) configure -state normal } if {$fh(last_filter) == $fh(filter)} { $fh(filter_apply) configure -state disabled } else { $fh(filter_apply) configure -state normal } } # SetSortOrder -- # # Callback when the user has selected a new sort order in the menu. # Should update the current sort order and set the default sort order # to the selected one. # # Arguments: # handler - The handler which identifies the folder window proc SetSortOrder {handler} { upvar \#0 $handler fh global option set option(folder_sort) $fh(folder_sort) $fh(folder_handler) setSortOrder $fh(folder_sort) RatBusy {Sync $handler update} SaveOptions } # SetShowHeaders -- # # Updates the show_header setting. Both for this folder and the # default. # # Arguments: # handler - The handler which identifies the folder window proc SetShowHeaders {handler} { upvar \#0 $handler fh upvar \#0 $fh(text) texth global option set option(show_header) $texth(show_header) FolderSelect $handler $fh(list_index) SaveOptions } # SetWrapMode -- # # Updates the wrap mode settings. Both for this folder and the # default. # # Arguments: # handler - The handler which identifies the folder window proc SetWrapMode {handler} { upvar \#0 $handler fh global option set option(wrap_mode) $fh(wrap_mode) $fh(text) configure -wrap $fh(wrap_mode) SaveOptions } tkrat_2.2cvs20100105-dfsg.orig/tkrat/font.tcl000066400000000000000000000014531137544547100206060ustar00rootroot00000000000000# font.tcl -- # # Handles cataloguing and selection of fonts # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # AddFont -- # # Add a font to the list of known fonts. # # Arguments: # encoding - The encoding of this font # size - The size difference from "normal" # attributes - The attributes # name - The name of the font proc AddFont {encoding size attributes name} { return } # RemoveFonts -- # # Remove slected fonts from the list of known fonts. # We currently ignore the problem that some encodings may become unsupported # # Arguments: # name - Name of fonts to remove (may be regexp) proc RemoveFonts {name} { return } tkrat_2.2cvs20100105-dfsg.orig/tkrat/fontedit.tcl000066400000000000000000000162161137544547100214570ustar00rootroot00000000000000# fontedit.tcl -- # # Contains code for the edit font window # # TkRat software and its included text is Copyright 1996-2005 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. namespace eval ::tkrat::fontedit { namespace export edit } # ::tkrat::fontedit::edit -- # # Show the edit font window # # Arguments: # font - Font setting to edit # l - Label to update afterwards # parent - Parent window proc ::tkrat::fontedit::edit {font l parent} { global t idCnt pref set id doEditFont[incr idCnt] upvar \#0 $id hd # Initialization set hd(done) 0 set hd(new_spec) $pref(opt,$font) set hd(old_spec) "" set hd(font_name) "" if {"components" == [lindex $hd(new_spec) 0]} { set hd(family) [lindex $hd(new_spec) 1] set hd(size) [lindex $hd(new_spec) 2] set hd(weight) [lindex $hd(new_spec) 3] set hd(slant) [lindex $hd(new_spec) 4] set hd(underline) [lindex $hd(new_spec) 5] set hd(overstrike) [lindex $hd(new_spec) 6] set hd(method) components } else { set hd(name) [lindex $hd(new_spec) 1] set hd(method) name set hd(family) Helvetica set hd(size) 12 } # Create toplevel set w .fontedit toplevel $w -class TkRat wm title $w $t(edit_font) wm transient $w $parent # Top label label $w.topl -text $t(use_one_method) # Specification method frame frame $w.s -bd 1 -relief raised radiobutton $w.s.select -variable ${id}(method) -value components \ -command "::tkrat::fontedit::update_font_spec $id components" label $w.s.fl -text $t(family): set m $w.s.family.m menubutton $w.s.family -bd 1 -relief raised -indicatoron 1 -menu $m \ -textvariable ${id}(family) -width 15 menu $m -tearoff 0 set families [lsort -dictionary [font families]] foreach f $families { $m add command -label $f -command \ "set ${id}(family) [list $f]; \ ::tkrat::fontedit::update_font_spec $id components" } FixMenu $m label $w.s.sl -text " $t(size):" set m $w.s.size.m menubutton $w.s.size -bd 1 -relief raised -indicatoron 1 -menu $m \ -textvariable ${id}(size) -width 3 menu $m -tearoff 0 foreach s {4 5 6 7 8 9 10 11 12 13 14 15 16 18 20 22 24 26 30 36} { $m add command -label $s -command \ "set ${id}(size) $s; \ ::tkrat::fontedit::update_font_spec $id components" } checkbutton $w.s.weight -text "$t(bold) " -onvalue bold -offvalue normal \ -variable ${id}(weight) \ -command "::tkrat::fontedit::update_font_spec $id components" checkbutton $w.s.italic -text "$t(italic) " -onvalue italic \ -offvalue roman -variable ${id}(slant) \ -command "::tkrat::fontedit::update_font_spec $id components" checkbutton $w.s.underline -text "$t(underline) " \ -variable ${id}(underline) \ -command "::tkrat::fontedit::update_font_spec $id components" checkbutton $w.s.overstrike -text $t(overstrike) \ -variable ${id}(overstrike) \ -command "::tkrat::fontedit::update_font_spec $id components" pack $w.s.select \ $w.s.fl $w.s.family \ $w.s.sl $w.s.size \ $w.s.weight \ $w.s.italic \ $w.s.underline \ $w.s.overstrike -side left -pady 2 # Name method frame frame $w.n -bd 1 -relief raised radiobutton $w.n.select -variable ${id}(method) -value name \ -command "::tkrat::fontedit::update_font_spec $id name" label $w.n.l -text $t(name): entry $w.n.e -width 20 -textvariable ${id}(name) set hd(updateButton) $w.n.set button $w.n.set -text $t(update) \ -command "::tkrat::fontedit::update_font_spec $id name" -bd 1 pack $w.n.select \ $w.n.l \ $w.n.e \ $w.n.set -side left -pady 2 trace variable hd(name) w "::tkrat::fontedit::fix_update_btn $id" trace variable hd(method) w "::tkrat::fontedit::fix_update_btn $id" fix_update_btn $id # Sample text message $w.sample -text $t(ratatosk) -aspect 200 -justify left set hd(sample) $w.sample # Buttons OkButtons $w $t(ok) $t(cancel) "set ${id}(done)" set hd(okbutton) $w.buttons.ok # Pack things pack $w.topl \ $w.s \ $w.n -side top -fill x -pady 2 -padx 2 pack $w.buttons -side bottom -fill x -pady 2 -padx 2 pack $w.sample -fill x -pady 2 -padx 2 # Bindings bind $w.n.e "::tkrat::fontedit::update_font_spec $id name" bind $w.n.e "::tkrat::fontedit::update_font_spec $id name; break" # Update sample font update_font $id # Show window and wait for completion ::tkrat::winctl::SetGeometry editFont $w ::tkrat::winctl::ModalGrab $w pack propagate $w 0 tkwait variable ${id}(done) # Finalization ::tkrat::winctl::RecordGeometry editFont $w destroy $w set pref(opt,$font) $hd(old_spec) if {"" != $hd(font_name)} { font delete $hd(font_name) } unset hd $l configure -text [ConvertFontToText $pref(opt,$font)] \ -font [RatCreateFont $pref(opt,$font)] } # ::tkrat::fontedit::fix_update_btn -- # # Set state of the update button # # Arguments: # handler - Handler of font window # args - Possibly standard trace args proc ::tkrat::fontedit::fix_update_btn {handler args} { upvar \#0 $handler hd if {"" != $hd(name) && "name" == $hd(method)} { set state normal } else { set state disabled } $hd(updateButton) configure -state $state } # ::tkrat::fontedit::update_font_spec -- # # Update the shown font # # Arguments: # handler - Handler of font window # method - which method to use proc ::tkrat::fontedit::update_font_spec {handler method} { upvar \#0 $handler hd if {"components" == $method} { set hd(new_spec) [list components $hd(family) $hd(size) $hd(weight) \ $hd(slant) $hd(underline) $hd(overstrike)] set hd(method) components } else { set hd(new_spec) [list name $hd(name)] set hd(method) name } update_font $handler } # ::tkrat::fontedit::update_font -- # # Update the sample text # # Arguments: # handler - Handler of font window proc ::tkrat::fontedit::update_font {handler} { upvar \#0 $handler hd global t if {"$hd(new_spec)" == "$hd(old_spec)"} { return } if {[lindex $hd(new_spec) 0] == "components"} { if {"" == $hd(font_name)} { set op create set hd(font_name) fontedit } else { set op configure } font $op $hd(font_name) \ -family [lindex $hd(new_spec) 1] \ -size -[lindex $hd(new_spec) 2] \ -weight [lindex $hd(new_spec) 3] \ -slant [lindex $hd(new_spec) 4] \ -underline [lindex $hd(new_spec) 5] \ -overstrike [lindex $hd(new_spec) 6] set fn $hd(font_name) } else { set fn [lindex $hd(new_spec) 1] } set hd(old_spec) $hd(new_spec) if {[catch {$hd(sample) configure -font $fn} err]} { set okstatus disabled set msg $t(invalid_font) set aspect 1000 $hd(sample) configure -font fixed } else { set okstatus normal set msg $t(ratatosk) set aspect 200 } $hd(sample) configure -text $msg -aspect $aspect $hd(okbutton) configure -state $okstatus } tkrat_2.2cvs20100105-dfsg.orig/tkrat/group.tcl000066400000000000000000000223511137544547100207740ustar00rootroot00000000000000# group.tcl -- # # This file contains code which handles group operations # # # TkRat software and its included text is Copyright 1996-2006 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # GroupMessageList -- # # Pops a message list that lets the user select messages for a group # # Arguments: # handler - The handler which identifies the folder window proc GroupMessageList {handler} { global b idCnt t option upvar \#0 $handler fh # Create identifier set id f[incr idCnt] set w .$id # Create toplevel toplevel $w -class TkRat wm title $w $t(edit_group) frame $w.f scrollbar $w.f.scroll \ -relief sunken \ -command "$w.f.list yview" \ -highlightthickness 0 listbox $w.f.list \ -yscroll "$w.f.scroll set" \ -exportselection false \ -highlightthickness 0 \ -selectmode multiple \ -setgrid true set b($w.f.list) group_list_editor pack $w.f.scroll -side right -fill y pack $w.f.list -side left -expand 1 -fill both frame $w.buttons button $w.buttons.ok -text $t(ok) \ -command "GroupMessageListDone $w $handler 1" set b($w.buttons.ok) group_window_ok button $w.buttons.sel -text $t(select_all) \ -command "$w.f.list selection set 0 end" set b($w.buttons.sel) group_window_selall button $w.buttons.unsel -text $t(deselect_all) \ -command "$w.f.list selection clear 0 end" set b($w.buttons.unsel) group_window_unselall button $w.buttons.cancel -text $t(cancel) \ -command "GroupMessageListDone $w $handler 0" set b($w.buttons.cancel) cancel pack $w.buttons.ok \ $w.buttons.sel \ $w.buttons.unsel \ $w.buttons.cancel -side left -expand 1 pack $w.buttons -side bottom -fill x -pady 5 pack $w.f -expand 1 -fill both set fi 0 set li 0 foreach e [$fh(folder_handler) list "%u $option(list_format)"] { regexp {^([^ ]*) (.*)} $e unused uid list_entry if {"" == $fh(filter) || [string match -nocase "*$fh(filter)*" $list_entry]} { lappend fh($w.uids) $uid $w.f.list insert end $list_entry set rmapping($fi) $li incr li } incr fi } foreach i [$fh(folder_handler) flagged flagged 1] { if {[info exists rmapping($i)]} { $w.f.list selection set $rmapping($i) } } lappend fh(groupMessageLists) $w bind $w.f.list "GroupMessageListDone $w $handler 0" bind $w "$w.buttons.cancel invoke" ::tkrat::winctl::SetGeometry groupMessages $w $w.f.list set fh(grouplist) $w.f.list bind $w.f.list <1> "GroupListAnchor $handler \[%W index @%x,%y\]" bind $w.f.list \ "GroupListMotion $handler %W \[%W index @%x,%y\]" } # GroupListAnchor -- # # Called when user possibly starts a drag # # Arguments: # handler - The handler which identifies the folder window # index - The element under the pointer (must be a number). proc GroupListAnchor {handler index} { upvar \#0 $handler fh set fh(grouplist_last) $index set fh(grouplist_selection) [$fh(grouplist) curselection] if {[$fh(grouplist) selection includes $index]} { set fh(grouplist_mode) clear } else { set fh(grouplist_mode) set } $fh(grouplist) selection anchor $index } # GroupListMotion -- # # Called when user drags in listbox # # Arguments: # handler - The handler which identifies the folder window # w - The listbox widget. # index - The element under the pointer (must be a number). proc GroupListMotion {handler w index} { upvar \#0 $handler fh if {$fh(grouplist_last) == $index} { return } set anchor [$w index anchor] if {$index > $anchor || $fh(grouplist_last) > $anchor} { if {$index > $fh(grouplist_last)} { set d 1 set apply 1 } else { set d -1 set apply 0 } } else { if {$index < $fh(grouplist_last)} { set d -1 set apply 1 } else { set d 1 set apply 0 } } if {$apply} { $fh(grouplist) selection $fh(grouplist_mode) $fh(grouplist_last) $index } else { set i [expr $fh(grouplist_last)] while {$i != $index} { if {-1 == [lsearch $fh(grouplist_selection) $i]} { $fh(grouplist) selection clear $i } else { $fh(grouplist) selection set $i } incr i $d } } set fh(grouplist_last) $index } # # GroupMessageListUpdate -- # # Update the message list since the underlying folder was updated # # Arguments: # w - The group selection window # handler - The handler which identifies the folder window proc GroupMessageListUpdate {w handler} { upvar \#0 $handler fh global option foreach c [$w.f.list curselection] { set selected([lindex $fh($w.uids) $c]) 1 } set top [lindex [$w.f.list yview] 0] $w.f.list delete 0 end set fh($w.uids) {} foreach e [$fh(folder_handler) list "%u $option(list_format)"] { regexp {^([^ ]*) (.*)} $e unused uid list_entry if {"" != $fh(filter) && ![string match -nocase "*$fh(filter)*" $list_entry]} { continue } lappend fh($w.uids) $uid $w.f.list insert end $list_entry if {[info exists selected($uid)]} { $w.f.list selection set end } } $w.f.list yview moveto $top } # GroupMessageListDone -- # # Calls when the grouping is done # # Arguments: # w - The group selection window # handler - The handler which identifies the folder window # done - The users selection (1=ok, 0=cancel) proc GroupMessageListDone {w handler done} { upvar \#0 $handler fh global b option bind $w.f.list {} if {$done} { set candidates [$w.f.list curselection] set isset [$fh(folder_handler) flagged flagged 1] set toset {} set toclear {} set torefresh {} for {set i 0} {$i < [$w.f.list size]} {incr i} { set nv [expr {-1 != [lsearch $candidates $i]}] set ov [expr {-1 != [lsearch $isset $i]}] if {$nv != $ov} { if {$nv} { lappend toset $fh(mapping,$i) } else { lappend toclear $fh(mapping,$i) } lappend torefresh $i } } $fh(folder_handler) setFlag $toset flagged 1 $fh(folder_handler) setFlag $toclear flagged 0 foreach i $torefresh { FolderListRefreshEntry $handler $i } } ::tkrat::winctl::RecordGeometry groupMessages $w $w.f.list set index [lsearch $w $fh(groupMessageLists)] set fh(groupMessageLists) [lreplace $fh(groupMessageLists) $index $index] destroy $w unset fh($w.uids) foreach a [array names b $w*] { unset b($a) } } # GroupClear -- # # Removes the flag from every message # # Arguments: # handler - The handler which identifies the folder window proc GroupClear {handler} { upvar \#0 $handler fh global option foreach i [$fh(folder_handler) flagged flagged 1] { $fh(folder_handler) setFlag $i flagged 0 FolderListRefreshEntry $handler $fh(rmapping,$i) } } # SetupGroupMenu -- # # Setup the entries in the group menu # # Arguments: # m - The menu command name # handler - The handler which identifies the folder window proc SetupGroupMenu {m handler} { upvar \#0 $handler fh global t # Create groups if {$fh(num_messages) > 0} { set s normal } else { set s disabled } foreach i {1 2 3} { $m entryconfigure $i -state $s } # Group operations set num 0 if {![info exists fh(folder_handler)]} { set s disabled } elseif {[set num [llength [$fh(folder_handler) flagged flagged 1]]]} { set s normal } else { set s disabled } foreach i {4 6 7 8 9 11 12 13 15 16 17 18 19 20 22} { $m entryconfigure $i -state $s } # Number of grouped messages $m entryconfigure 4 -label "$t(clear_group) ($num)" # Disable some ops in drafts folder if {$s == "normal"} { if {"drafts" == $fh(special_folder)} { set s disabled } else { set s normal } foreach i {17 18 19 20} { $m entryconfigure $i -state $s } } # Disable dbinfo entry if first message is not dbase if {![info exists fh(folder_handler)] || "dbase" != [$fh(folder_handler) type]} { $m entryconfigure 22 -state disabled } } # GroupSameSubject -- # # Mark all messages in teh current folder which have the same subject # as the current one. # # Arguments: # handler - The handler which identifies the folder window # msg - The current message proc GroupSameSubject {handler msg} { upvar \#0 $handler fh global option regsub -all -nocase "^$option(re_regexp)" [$msg list "%s"] "" match set match [string trim $match] set subjects [$fh(folder_handler) list "%s"] set flag {} for {set i 0} {$i < [llength $subjects]} {incr i} { set subject [lindex $subjects $i] regsub -all -nocase "^$option(re_regexp)" $subject "" subject if {$match == [string trim $subject]} { lappend flag $i } } if {[string length $flag]} { SetFlag $handler flagged 1 $flag } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/help.tcl000066400000000000000000000070151137544547100205700ustar00rootroot00000000000000# help.tcl -- # # This file contains code which handles help windows # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Order of helptext entries set helporder { intro roles folders folderdef dbase deleting grouping userproc bugreport} # Help -- # # Creates a new help window and shows the requested help-entry (or an # introduction if none is specified). # # Arguments: # section - The section to show (may be empty) proc Help {{subject intro}} { global idCnt t b help option helporder # Initialize help texts (if needed) if {![info exists help(intro)]} { InitMessages $option(language) help } # Create identifier set id helpWin[incr idCnt] upvar \#0 $id hd set w .$id set hd(w) $w # Create toplevel toplevel $w -class TkRat wm title $w $t(help_window) # Populate window label $w.subjects -text $t(subjects) scrollbar $w.subjscroll \ -relief sunken \ -command "$w.subjlist yview" \ -highlightthickness 0 listbox $w.subjlist \ -yscroll "$w.subjscroll set" \ -relief sunken \ -bd 1 \ -exportselection false \ -highlightthickness 0 \ -selectmode single \ -width 20 \ -height 9 set hd(list) $w.subjlist set b($hd(list)) help_subjlist button $w.dismiss -text $t(dismiss) -command "destroy $w" set b($w.dismiss) dismiss scrollbar $w.textscroll \ -relief sunken \ -command "$w.texttext yview" \ -highlightthickness 0 text $w.texttext \ -yscroll "$w.textscroll set" \ -setgrid true \ -wrap word \ -relief sunken \ -bd 1 \ -highlightthickness 0 set hd(text) $w.texttext set b($hd(text)) help_text grid $w.subjects grid $w.subjlist $w.subjscroll -sticky nsew -pady 5 grid $w.dismiss - -column 2 -row 1 -padx 10 grid $w.texttext - - $w.textscroll -sticky nsew grid columnconfigure $w 0 -weight 1 grid rowconfigure $w 1 -weight 1 grid rowconfigure $w 2 -weight 10 # Bindings bind $w "$hd(text) yview scroll 1 pages" bind $w "$hd(text) yview scroll -1 pages" bind $hd(list) "SelectHelp $id" bind $hd(text) "DismissHelp $id" bind $w "DismissHelp $id" # Populate list foreach topic $helporder { $hd(list) insert end $help(title,$topic) } ::tkrat::winctl::SetGeometry help $w $hd(text) ShowHelp $id $subject } # SelectHelp -- # # Figure which subject was selected and show that # # Arguments: # id - The help-window identifier proc SelectHelp {id} { global helporder upvar \#0 $id hd set topic [lindex $helporder [$hd(list) curselection]] ShowHelp $id $topic } # ShowHelp -- # # Populates the help window # # Arguments: # id - The help-window identifier # topic - The topic to show proc ShowHelp {id topic} { global help helporder upvar \#0 $id hd # The subject list set i [lsearch -exact $helporder $topic] if {$i != [$hd(list) curselection]} { $hd(list) selection clear 0 end $hd(list) selection set $i } # The text window $hd(text) configure -state normal $hd(text) delete 0.0 end $hd(text) insert end $help($topic) $hd(text) configure -state disabled } # DismissHelp -- # # DImisses the help window # # Arguments: # id - The help-window identifier proc DismissHelp {id} { upvar \#0 $id hd ::tkrat::winctl::RecordGeometry help $hd(w) $hd(text) unset hd } tkrat_2.2cvs20100105-dfsg.orig/tkrat/html.tcl000066400000000000000000000241241137544547100206040ustar00rootroot00000000000000# html.tcl -- # # This file contains code which handles the actual displaying of an HTML # message or attachment # # # TkRat software and its included text is Copyright 1996-2000 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Don't fail if the http package isn't available. It'll just fail when it # comes time to fetch the image catch {package require http} bind HtmlClip { global htmlWinCursor set parent [winfo parent %W] set url [$parent href %x %y] if {![info exists htmlWinCursor($parent)]} { set htmlWinCursor($parent) [lindex [$parent configure -cursor] end] } if {[string length $url] > 0} { if {[string length $htmlWinCursor($parent)] == 0} { set htmlWinCursor($parent) "hand2" $parent configure -cursor $htmlWinCursor($parent) } } else { if {[string length $htmlWinCursor($parent)] > 0} { set htmlWinCursor($parent) "" $parent configure -cursor {} } } } bind HtmlClip { set ::htmlWinClick [[winfo parent %W] href %x %y] } bind HtmlClip { if { ![string compare $::htmlWinClick [[winfo parent %W] href %x %y]]} { set url $::htmlWinClick RatShowURLLaunch $url [winfo parent [winfo parent %W]] } } bind Html { ClearHtmlImages %W } # Contains the list of most recently used images set htmlImageList [list] # ShowTextHtml2 -- # # Show text/html entities, should handle different fonts... # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowTextHtml2 {handler body msg} { global idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo set tag t[incr idCnt] if {[info tclversion] < 8.5} { set frame [frame $handler.f[incr idCnt] -width [winfo width $handler]\ -height [winfo height $handler] -cursor left_ptr] set htmlwin $frame.html } else { set htmlwin $handler.f[incr idCnt] } # -base foo is there because if it is removed, Tkhtml crashes. When the # bug is fixed, it can be removed. html $htmlwin -base "foo" \ -fontcommand HtmlFontCmd \ -resolvercommand HtmlResolverCmd \ -imagecommand [list HtmlImageCmd $htmlwin] \ -background [$handler cget -background] \ -width [winfo width $handler] \ -exportselection true \ -bd 0 $htmlwin parse [$body data false] # Now that the data is parsed, check if there is a base set set base [$htmlwin token find base] if {[llength $base] > 0} { # Ok, the correct base is the first one found. Since it is a list, get # it. set base [lindex $base 0] # The base will be right after the href argument set idx [lsearch $base href] incr idx # Get the real base set base [lrange $base $idx $idx] # set the base of the widget with the correct version now $htmlwin configure -base $base } $handler insert insert " " "Center $tag" if {[info tclversion] < 8.5} { $htmlwin configure \ -xscrollcommand [list $frame.xscroll set] \ -yscrollcommand [list $frame.yscroll set] set yscroll [scrollbar $frame.yscroll -command [list $htmlwin yview]] set xscroll [scrollbar $frame.xscroll -command [list $htmlwin xview] \ -orient horizontal] bind $frame { bind [winfo parent %W] {} } grid $htmlwin -row 0 -column 0 -sticky news grid $yscroll -row 0 -column 1 -sticky ns grid $xscroll -row 1 -column 0 -sticky ew grid columnconfigure $frame 0 -weight 1 grid rowconfigure $frame 0 -weight 1 grid propagate $frame 0 $handler window create insert -window $frame set binding [list ResizeFrame $frame $handler -1 -1 \ $xscroll $yscroll] if {[string first $binding [bind $handler ]] == -1} { bind $handler +$binding } } else { $handler window create insert -window $htmlwin # This is ugly. For some reason does the widget not know its size # when the Configure event arrives here. But after a short delay # it does. bind $htmlwin {after 100 {HtmlReconfHeight %W}} } $handler insert insert "\n" $tag $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" bind $htmlwin.x <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" lappend fh(width_adjust) $htmlwin } # HtmlReconfHeight -- # # Reconfigures the height of the html widget to whatever is needed to # show the text # # Arguments: # w - html widget proc HtmlReconfHeight {w} { if {[winfo exists $w]} { set h [lindex [$w coords] 1] $w configure -height $h } } # HtmlFontCmd -- # # Selects font sizes when dislaying html messages # # Arguments: # size: Size of font to display # args: Other font modifiers (italic bold or fixed) proc HtmlFontCmd {size args} { global option # Default family and sizes set f $option(font_family_prop) foreach s {8 9 10 12 14 18 24} { lappend sizelist [expr $s+$option(font_size)-12] } # Default weight is Normal set w normal # Default angle is roman set a roman foreach o $args { if {[string equal "fixed" "$o"]} { set f $option(font_family_fixed) } elseif {[string equal "bold" "$o"]} { set w bold } elseif {[string equal "italic" "$o"]} { set a italic } } # Make sure the list is long enough. If it isn't, use the last value if {[llength $sizelist] < $size} { set size end } else { # Decrease the size since the lowest value allowed is 1 and # list indices start at 0 incr size -1 } # Ugh. RatCreateFont already constructs all the components of the font. So # we're actually removing information and adding it back just to change the # size. Maybe there's a better way. return [list [lindex $f 1] [lindex $sizelist $size] $a $w] } # HtmlImageCmd -- # # Fetches and creates an image to display in a HTML message # # Arguments: # frm: The HTML widget used to display images # src: SRC element of the tag # width: width of the image (added automatically, could be empty) # height: height of the image (added automatically, could be empty) # args: Other attributes given to the tag # # Returns: # The name of an image if it could be constructed correctly, an empty string # otherwise proc HtmlImageCmd {frm src width height args} { global htmlImageList global htmlImageArray global HtmlImages # Don't do anything if the html widget has been destroyed if {![winfo exists $frm]} { return } # Check cached images if {[lsearch $htmlImageList $src] != -1} { return $htmlImageArray($src) } if {[string match foo/cid:* $src]} { set filename [HtmlGetEmbeddedImage $src] } else { set filename [HtmlGetExternalImage $frm $src $width $height] } if {"" == $filename} { return "" } if {[catch {image create photo -file $filename} img]} { file delete -force -- $filename set retVal "" } else { lappend htmlImageList $src set htmlImageArray($src) $img file delete -force -- $filename # Make sure the window still exists before displaying if {[winfo exists $frm]} { lappend HtmlImages($frm) $img set retVal $img } else { # Otherwise, delete the image image delete $img return } } return $retVal } # HtmlGetEmbeddedImage -- # # Extract an image from an related bodypart # # Arguments: # src: SRC element of the tag # # Returns: # The name of a file which contains the image data. Or an empty string # if no image was downloaded. proc HtmlGetEmbeddedImage {src} { global related option rat_tmp if {![regsub "foo/cid:" $src {} id] || ![info exists related($id)]} { return "" } set filename $rat_tmp/rat.[RatGenId] set fid [open $filename w 0600] fconfigure $fid -encoding binary $related($id) saveData $fid false false close $fid return $filename } # HtmlGetExternalImage -- # # Fetches an external image to display in a HTML message # # Arguments: # frm: The HTML widget used to display images # src: SRC element of the tag # width: width of the image (added automatically, could be empty) # height: height of the image (added automatically, could be empty) # # Returns: # The name of a file which contains the image data. Or an empty string # if no image was downloaded. proc HtmlGetExternalImage {frm src width height} { global option rat_tmp if {$option(html_show_images) == 0} { return "" } if {$width < $option(html_min_image_size) && $height < $option(html_min_image_size)} { # Images that are too small may signal some spam-type of stuff return "" } if {![string match http://* $src]} { if {![string match http://* [$frm cget -base]]} { # Can't get image because it isn't http return "" } else { set src [$frm cget -base]/$src } } if {[catch {::http::geturl $src} token]} { return "" } set filename $rat_tmp/rat.[RatGenId] set fid [open $filename w 0600] fconfigure $fid -encoding binary puts -nonewline $fid [::http::data $token] close $fid return $filename } # HtmlResolverCmd -- # # URL resolver for HTML links # # Arguments: # base: The base URI # uri: the new URI # # Returns: # The URL if it starts with http://, otherwise returns foo proc HtmlResolverCmd {base uri} { if {[string match http://* $uri]} { return $uri } return $base/$uri } # ClearHtmlImages -- # # Delete images loaded by the HTML widget # # Arguments: # w: Name of widget containing the images # # Returns: # Nothing proc ClearHtmlImages {w} { global HtmlImages if {![info exists HtmlImages($w)]} { return } foreach img $HtmlImages($w) { catch {image delete $img} } unset HtmlImages($w) return "foo" } tkrat_2.2cvs20100105-dfsg.orig/tkrat/html3.tcl000066400000000000000000000134661137544547100206760ustar00rootroot00000000000000# html.tcl -- # # This file contains code which handles the actual displaying of an HTML # message or attachment # # # TkRat software and its included text is Copyright 1996-2006 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Don't fail if the http package isn't available. It'll just fail when it # comes time to fetch the image catch {package require http} namespace eval rat_html3 { } # ShowTextHtml -- # # Show text/html entities, should handle different fonts... # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowTextHtml3 {handler body msg} { global idCnt option upvar \#0 $handler fh \ msgInfo_$msg msgInfo # Window name if {[info tclversion] < 8.5} { set frame [frame $handler.f[incr idCnt] -cursor left_ptr \ -width [winfo width $handler]\ -height [winfo height $handler]] set htmlwin $frame.html } else { set htmlwin $handler.f[incr idCnt] } # Font sizes set s $option(font_size) for {set i 0} {$i < 4} {incr i} { if {$s > 10} { incr s -2 } else { incr s -1 } } for {set i 0} {$i < 7} {incr i} { lappend fonttable $s if {$s > 9} { incr s 2 } else { incr s } } html $htmlwin \ -shrink true \ -fonttable $fonttable \ -imagecmd [list rat_html3::imagecmd $htmlwin] \ -width [winfo width $handler] $htmlwin parse -final [$body data false] set tag t[incr idCnt] $handler insert insert " " "Center $tag" if {[info tclversion] < 8.5} { $htmlwin configure \ -xscrollcommand [list $frame.xscroll set] \ -yscrollcommand [list $frame.yscroll set] set yscroll [scrollbar $frame.yscroll -command [list $htmlwin yview]] set xscroll [scrollbar $frame.xscroll -command [list $htmlwin xview] \ -orient horizontal] bind $frame { bind [winfo parent %W] {} } grid $htmlwin -row 0 -column 0 -sticky news grid $yscroll -row 0 -column 1 -sticky ns grid $xscroll -row 1 -column 0 -sticky ew grid columnconfigure $frame 0 -weight 1 grid rowconfigure $frame 0 -weight 1 grid propagate $frame 0 set id [$handler window create insert -window $frame] set binding [list ResizeFrame $frame $handler -1 -1 \ $xscroll $yscroll] if {[string first $binding [bind $handler ]] == -1} { bind $handler +$binding } } else { set id [$handler window create insert -window $htmlwin] set bbox [$htmlwin bbox [$htmlwin node]] set width [expr [lindex $bbox 2] - [lindex $bbox 0]] $htmlwin configure -width $width if {$width > $fh(width)} { set fh(width) $width } } lappend htmlids $id $handler insert insert "\n" $tag $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" bind $htmlwin <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" lappend fh(width_adjust) $htmlwin } # rat_html3::imagecmd -- # # Fetches and creates an image to display in a HTML message # # Arguments: # w: The HTML widget used to display images # uri: The URI of the image # # Returns: # The name of an image if it could be constructed correctly, an empty string # otherwise proc rat_html3::imagecmd {w uri} { upvar \#0 rat_html3::imagemap_$w map global rat_html3::images # Check cached images if {[info exists map] && [info exists map($uri)]} { return $map($uri) } if {[string match cid:* $uri]} { set filename [get_embedded_image $uri] } else { set filename [get_external_image $uri] } set img "" if {$filename != "" && ![catch {image create photo -file $filename} img]} { set map($uri) $img } else { set img "" } file delete -force -- $filename if {"" == $img} { set img [image create photo] } lappend rat_html3::images($w) $img return $img } # rat_html3::get_embedded_image -- # # Extract an image from an related bodypart # # Arguments: # uri: The URI of the image # # Returns: # The name of a file which contains the image data. Or an empty string # if no image was downloaded. proc rat_html3::get_embedded_image {url} { global related option rat_tmp if {![regsub "cid:" $url {} id] || ![info exists related($id)]} { return "" } set filename $rat_tmp/htmlimg.[RatGenId] set fid [open $filename w 0600] fconfigure $fid -encoding binary $related($id) saveData $fid false false close $fid return $filename } # rat_html3::get_external_image -- # # Fetches an external image to display in a HTML message # # Arguments: # uri: The URI of the image # # Returns: # The name of a file which contains the image data. Or an empty string # if no image was downloaded. proc rat_html3::get_external_image {uri} { global option rat_tmp # Abort if... # ...we should not load external images # ...the uri does not start with http # ...the actual fetch failed if {$option(html_show_images) == 0 || ![string match http://* $uri] || [catch {::http::geturl $uri} token]} { return "" } # Store image in a file set filename $rat_tmp/extimg.[RatGenId] set fid [open $filename w 0600] fconfigure $fid -encoding binary puts -nonewline $fid [::http::data $token] close $fid return $filename } tkrat_2.2cvs20100105-dfsg.orig/tkrat/info.tcl000066400000000000000000000373611137544547100206020ustar00rootroot00000000000000# info.tcl -- # # This file contains rotines which provied the user with some information # about this program. # # # TkRat software and its included text is Copyright 1996-2005 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Version -- # # Opens a window and prints the version information in it. # # Arguments: proc Version {} { global t tkrat_version tkrat_version_date tcl_patchLevel tk_patchLevel set w .about if {[winfo exists $w]} { wm deiconify $w raise $w return } toplevel $w -relief raised -class TkRat wm title $w $t(about) wm minsize $w 1 1 catch {font create big -family Helvetica -size -24 -weight bold} catch {font create norm -family Helvetica -size -12 -weight bold} label $w.tkrat -text "TkRat v$tkrat_version" -font big label $w.date -text "$t(date): $tkrat_version_date" -font norm label $w.copyright -text "TkRat is copyright 1995-2005 by" -font norm label $w.author -text "Martin Forssn (maf@tkrat.org)" -font norm label $w.tcltk -text "$t(using) Tcl-$tcl_patchLevel/Tk-$tk_patchLevel" pack $w.tkrat \ $w.date \ $w.copyright \ $w.author \ $w.tcltk -side top -padx 5 if {![catch {package present Tkhtml 2.0} version]} { label $w.tkhtml -text "$t(using) TkHtml-$version" pack $w.tkhtml -side top -padx 5 } if {![catch {package present Img} version]} { label $w.img -text "$t(using) Img-$version" pack $w.img -side top -padx 5 } message $w.more -text $t(send_bugs_etc) -aspect 700 pack $w.more -side top -padx 5 button $w.ok \ -text $t(ok) \ -command "destroy $w" \ -width 20 pack $w.tkrat \ $w.date \ $w.copyright \ $w.author \ $w.tcltk \ $w.more -side top -padx 5 pack $w.ok -side top -padx 5 -pady 5 bind $w "$w.ok invoke" bind $w.ok "::tkrat::winctl::RecordGeometry about $w" ::tkrat::winctl::SetGeometry about $w } # Ratatosk -- # # Opens a window an displays a short text about ratatosk in it # # Arguments: proc Ratatosk {} { global t set w .ratatosk if {[winfo exists $w]} { wm deiconify $w raise $w return } toplevel $w -relief raised -class TkRat wm title $w Ratatosk wm minsize $w 1 1 button $w.ok \ -text $t(ok) \ -command "destroy $w" \ -width 20 message $w.message -aspect 400 -text $t(ratatosk) pack $w.message $w.ok -side top -padx 5 -pady 5 bind $w "$w.ok invoke" bind $w.ok "::tkrat::winctl::RecordGeometry ratatosk $w" ::tkrat::winctl::SetGeometry ratatosk $w } # InfoWelcome -- # # This is called when the user invokes tkrat for the first time and # shows a welcome message to the user. In this window the user may chose # user interface language and whether changes info should be displayed. # # Arguments: proc InfoWelcome {} { global b option changes welcomeLanguage # Init messages InitMessages $option(language) changes # Create window toplevel .welcome -class TkRat wm title .welcome $changes(welcome_title) # Populate window frame .welcome.message text .welcome.message.text -relief sunken -bd 1 \ -yscrollcommand ".welcome.message.scroll set" -setgrid 1 \ -height 30 -width 80 -highlightthickness 0 scrollbar .welcome.message.scroll -relief sunken -bd 1 \ -command ".welcome.message.text yview" -highlightthickness 0 pack .welcome.message.scroll -side right -fill y pack .welcome.message.text -expand 1 -fill both .welcome.message.text insert 0.0 $changes(welcome) .welcome.message.text configure -state disabled frame .welcome.b frame .welcome.b.lang label .welcome.b.lang.label -textvariable changes(language) menubutton .welcome.b.lang.menu -textvariable welcomeLanguage \ -indicatoron 1 -menu .welcome.b.lang.menu.m -relief raised -bd 1 \ -width 20 -anchor c menu .welcome.b.lang.menu.m -tearoff 0 pack .welcome.b.lang.menu \ .welcome.b.lang.label -side right set b(.welcome.b.lang.menu) welcome_lang frame .welcome.b.shutup label .welcome.b.shutup.label -textvariable changes(show_changes) menubutton .welcome.b.shutup.menu -textvariable welcomeChanges \ -indicatoron 1 -menu .welcome.b.shutup.menu.m -relief raised \ -bd 1 -width 20 -anchor c menu .welcome.b.shutup.menu.m -tearoff 0 pack .welcome.b.shutup.menu \ .welcome.b.shutup.label -side right set b(.welcome.b.shutup.menu) welcome_shutup button .welcome.b.cont -textvariable changes(continue) -bd 1 \ -command {destroy .welcome} pack .welcome.b.lang \ .welcome.b.shutup -side top -fill x -pady 4 pack .welcome.b.cont -side top -pady 10 set b(.welcome.b.cont) welcome_cont pack .welcome.message -side top -expand 1 -fill both -padx 5 -pady 5 pack .welcome.b set i 0 foreach l [GetLanguages] { .welcome.b.lang.menu.m add command -label [lindex $l 1] \ -command [list WelcomeLanguage $l] .welcome.b.lang.menu.m entryconfigure $i incr i if {![string compare [lindex $l 0] $option(language)]} { WelcomeLanguage $l } } bind .welcome ".welcome.b.cont invoke" ::tkrat::winctl::SetGeometry welcome .welcome tkwait window .welcome foreach bn [array names b .welcome.*] {unset b($bn)} } # WelcomeShowMenu -- # # Build the menu which asks the user if they want to see changes messages. # # Arguments: # font - which font to use (if not the default one) proc WelcomeShowMenu {{font {}}} { global option changes welcomeChanges set m .welcome.b.shutup.menu.m $m delete 0 end if {$option(info_changes)} { set welcomeChanges $changes(show) } else { set welcomeChanges $changes(dont_show) } $m add command -label $changes(show) \ -command "set welcomeChanges [list $changes(show)] ; \ set option(info_changes) 1" $m add command -label $changes(dont_show) \ -command "set welcomeChanges [list $changes(dont_show)] ; \ set option(info_changes) 0" if {[string length $font]} { $m entryconfigure 0 -font $font $m entryconfigure 1 -font $font } } # WelcomeLanguage -- # # Is called when the user changes the language in the welcome window. # # Arguments: # lang - The language information proc WelcomeLanguage {lang} { global welcomeLanguage option changes propNormFont set welcomeLanguage [lindex $lang 1] set option(language) [lindex $lang 0] InitMessages $option(language) changes InitMessages $option(language) balText wm title .welcome $changes(welcome_title) .welcome.message.text configure -state normal -font $propNormFont .welcome.message.text delete 0.0 end .welcome.message.text insert 0.0 $changes(welcome) .welcome.message.text configure -state disabled WelcomeShowMenu } # InfoFeatures -- # # Inform the user of new features since the last time. # # Arguments: # featureIndexes - List of features proc InfoFeatures {featureIndexes} { global option changesDone changesChanges \ tkrat_version features t b # Init messages InitMessages $option(language) features # Create window toplevel .changes -class TkRat wm title .changes $t(changes_title) # Message part frame .changes.message text .changes.message.text -relief sunken -bd 1 \ -yscrollcommand ".changes.message.scroll set" -setgrid 1 \ -height 30 -width 80 -highlightthickness 0 .changes.message.text tag configure feature -wrap word \ -rmargin 10 -lmargin1 10 -lmargin2 25 -spacing1 10 -tabs 25 scrollbar .changes.message.scroll -relief sunken -bd 1 \ -command ".changes.message.text yview" -highlightthickness 0 pack .changes.message.scroll -side right -fill y pack .changes.message.text -expand 1 -fill both # Buttons frame .changes.b checkbutton .changes.b.shutup -text $t(do_not_show_window_in_future) \ -onvalue 0 -offvalue 1 -variable option(info_changes) button .changes.b.cont -textvariable t(continue) -bd 1 \ -command {destroy .changes} pack .changes.b.cont -side bottom -pady 10 pack .changes.b.shutup -side left -padx 10 set b(.changes.b.cont) welcome_cont pack .changes.message -side top -expand 1 -fill both -padx 5 -pady 5 pack .changes.b -fill x bind .changes ".changes.b.cont invoke" ::tkrat::winctl::SetGeometry infoChanges .changes # Populate textwindow foreach featureIndex $featureIndexes { foreach f [split $features($featureIndex) "\n"] { .changes.message.text insert end "-\t$f\n" feature } } .changes.message.text configure -state disabled tkwait window .changes ::tkrat::winctl::RecordGeometry infoChanges .changes foreach bn [array names b .welcome.*] {unset b($bn)} } # SeeLog -- # # Shows the remebered old log messages # # Arguments: proc SeeLog {} { global idCnt t # Create identifier set id iw[incr idCnt] set w .$id # Create toplevel toplevel $w -class TkRat wm title $w $t(seelog_title) # Message part button $w.button -text $t(close) -command "destroy $w" listbox $w.list -yscroll "$w.scroll set" -relief sunken -bd 1 \ -selectmode extended scrollbar $w.scroll -relief raised -bd 1 \ -command "$w.list yview" pack $w.button -side bottom -padx 5 -pady 5 pack $w.scroll -side right -fill y pack $w.list -expand 1 -fill both foreach m [GetRatLog] { $w.list insert end $m } ::tkrat::winctl::SetGeometry seeLog $w $w.list bind $w.list "::tkrat::winctl::RecordGeometry seeLog $w $w.list" bind $w "$w.button invoke" } # SendBugReport -- # # Construct a skeleton bug report # # Arguments: # attachments - List of extra attachments. The argument is a list of lists # which looks like {name data} proc SendBugReport {{attachments {}}} { global idCnt t # Create identifier set id sb[incr idCnt] set w .$id upvar \#0 $id hd set hd(oldfocus) [focus] # Create the toplevel toplevel $w -bd 5 -class TkRat wm title $w $t(send_bug) set hd(w) $w set hd(attachments) $attachments # The contents label $w.slabel -text $t(bug_shortdesc): -anchor e entry $w.sentry -textvariable ${id}(subject) grid $w.slabel $w.sentry - -sticky we label $w.dlabel -text $t(bug_description) -justify left grid $w.dlabel - - -sticky w -pady 5 text $w.text -relief sunken -bd 1 -setgrid true \ -yscrollcommand "$w.scroll set" -wrap none scrollbar $w.scroll -relief sunken -bd 1 -takefocus 0 \ -command "$w.text yview" -highlightthickness 0 grid $w.text - $w.scroll -sticky nsew set hd(text) $w.text frame $w.f button $w.f.cancel -text $t(cancel) -command "destroy $w" button $w.f.continue -text $t(continue) -command "DoSendBugReport $id" pack $w.f.continue $w.f.cancel -side left -padx 10 grid $w.f - - grid columnconfigure $w 1 -weight 1 grid rowconfigure $w 2 -weight 1 # Binding and focus rat_edit::create $w.text focus $w.sentry ::tkrat::winctl::SetGeometry sendBug $w $w.text bind $w.text "::tkrat::winctl::RecordGeometry sendBug $w $w.text; unset $id" bind $w "$w.f.cancel invoke" } # DoSendBugReport -- # # Construct a skeleton bug report # # Arguments: # handler - name of global array holding data for the report proc DoSendBugReport {handler} { upvar \#0 $handler hd global idCnt option t option tkrat_version tkrat_version_date \ tcl_version tk_version tcl_patchLevel tk_patchLevel rat_lib \ rat_tmp set mhandler composeM[incr idCnt] upvar \#0 $mhandler mh set mh(to) maf@tkrat.org set mh(subject) $hd(subject) set mh(description) "Bug report" set mh(data) [$hd(text) get 1.0 end-1c] set mh(role) $option(default_role) set mh(data_tags) {} foreach a $hd(attachments) { set ahandler composeB[incr idCnt] upvar \#0 $ahandler ah lappend mh(attachmentList) $ahandler set ah(type) text set ah(subtype) plain set ah(description) [lindex $a 0] set ah(filename) $rat_tmp/rat.[RatGenId] set ah(removeFile) 1 set fh [open $ah(filename) w] set ah(parameter) "" set ah(disp_parm) "" puts $fh [lindex $a 1] close $fh } set ahandler composeB[incr idCnt] upvar \#0 $ahandler ah lappend mh(attachmentList) $ahandler set ah(type) text set ah(subtype) plain set ah(content_description) $t(configuration_information) set ah(filename) $rat_tmp/rat.[RatGenId] set ah(removeFile) 1 set ah(parameter) "" set ah(disp_parm) "" set fh [open $ah(filename) w] catch {exec uname -a} uname puts $fh "uname -a: '$uname'" puts $fh "Version: $tkrat_version ($tkrat_version_date)" if {[info exists rat_lib(version)]} { puts $fh "Libversion: $rat_lib(version) ($rat_lib(date))" } else { puts $fh "Libversion: older" } puts $fh \ "Tcl/Tk: $tcl_version/$tk_version ($tcl_patchLevel/$tk_patchLevel)" foreach n [lsort [array names tcl_platform]] { puts $fh "tcl_platform($n): '$tcl_platform($n)'" } foreach n [lsort [array names option]] { if {![regexp {,smtp_passwd} $n]} { puts $fh "option($n): '$option($n)'" } } close $fh catch {focus $hd(oldfocus)} destroy $hd(w) DoCompose $mhandler $mh(role) 0 1 } # Warn -- # # Warn user about something # # Arguments: # tag - Tag describing the warning proc Warn {tag} { global idCnt t option # Create identifier set id iw[incr idCnt] set w .$id set option($tag) 0 # Create toplevel toplevel $w -class TkRat wm title $w $t(warning) # Message part message $w.msg -justify left -text $t($tag) -aspect 600 \ -relief raised -bd 1 -padx 5 -pady 5 checkbutton $w.but -text $t(do_not_show_again) -variable option($tag) \ -onvalue 0 -offvalue 1 button $w.dismiss -text $t(dismiss) -command "destroy $w" pack $w.msg -side top pack $w.but -side top -anchor w -pady 5 -padx 5 pack $w.dismiss -pady 5 ::tkrat::winctl::SetGeometry warning $w bind $w "$w.dismiss invoke" tkwait window $w ::tkrat::winctl::RecordGeometry warning $w } # StartupInfo -- # # Give information when starting a new version for for the first time # # Arguments: proc StartupInfo {} { global option tkrat_version tkrat_version_date currentLanguage_t features # Check which version the user last used if {0 == $option(last_version_date)} { InfoWelcome # Reinitialize language (if needed) if {[string compare $option(language) $currentLanguage_t]} { InitMessages $option(language) t InitMessages $option(language) balText } set option(last_version_date) $tkrat_version_date InitMessages $option(language) features set fs [lsort -integer [array names features]] set option(last_seen_feature) [lindex $fs end] return 1 } elseif {$option(last_version_date) < $tkrat_version_date} { InitMessages $option(language) features set fs [lsort -integer [array names features]] if {$option(last_seen_feature) != [lindex $fs end]} { set start [expr [lsearch -exact $fs $option(last_seen_feature)]+1] InfoFeatures [lrange $fs $start end] } set option(last_seen_feature) [lindex $fs end] set option(last_version_date) $tkrat_version_date SaveOptions return 0 } else { return 0 } } # dumpenv -- # # Dump environment (commands and globals) to file set dumpno 0 proc dumpenv {} { global dumpno set name [format "dump_%03d" [incr dumpno]] set f [open $name w] # Dump commands foreach c [lsort [info commands]] { puts $f "cmd:$c" } # Dump globals foreach g [lsort [info globals]] { puts $f "global:$g" upvar \#0 $g v if {[array exists v]} { foreach n [lsort [array names v]] { puts $f " $n" } } } close $f } tkrat_2.2cvs20100105-dfsg.orig/tkrat/keydef.tcl000066400000000000000000000174741137544547100211210ustar00rootroot00000000000000# keydef.tcl -- # # This file contains code which handles the key definitions window # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # The order of the definitions set keyDefOrder(folder) {folder_key_find folder_key_compose folder_key_replya folder_key_replys folder_key_forward_i folder_key_forward_a folder_key_mvdb folder_key_bounce folder_key_sync folder_key_netsync folder_key_update folder_key_delete folder_key_undelete folder_key_markunread folder_key_flag folder_key_nextu folder_key_next folder_key_prev folder_key_home folder_key_bottom folder_key_pagedown folder_key_pageup folder_key_linedown folder_key_lineup folder_key_cycle_header folder_key_print folder_key_close folder_key_openfile folder_key_online folder_key_quit} set keyDefOrder(compose) {compose_key_send compose_key_abort compose_key_editor compose_key_undo compose_key_redo compose_key_cut compose_key_copy compose_key_paste compose_key_cut_all compose_key_wrap} # KeyDef -- # # Create a key definition window # # Arguments: # area - Identifies the area of keys to define proc KeyDef {area} { global option t b keyDefOrder # Create identifier set id kd upvar \#0 $id hd set w .$id if {[winfo exists $w]} { destroy $w unset hd } set hd(do) 0 set hd(state) "" set hd(w) $w # Create toplevel toplevel $w -class TkRat wm title $w $t(define_keys) # Buttons frame $w.but button $w.but.ok -text $t(ok) -command "KeyDefApply $area $id" button $w.but.delete -text $t(delete) \ -command "set ${id}(state) delete; set ${id}(message) \"$t(do_delete)\"" button $w.but.cancel -text $t(cancel) -command "destroy $w" pack $w.but.ok \ $w.but.delete \ $w.but.cancel -side left -expand 1 set b($w.but.ok) ok_and_apply set b($w.but.delete) keydef_delete set b($w.but.cancel) cancel pack $w.but -side bottom -fill x -pady 5 # State line label $w.msg -textvariable ${id}(message) -relief raised -bd 1 pack $w.msg -side bottom -fill x -pady 5 -padx 10 # The canvas frame $w.f -relief sunken -bd 1 scrollbar $w.f.scroll \ -relief sunken \ -bd 1 \ -command "$w.f.canvas yview" \ -highlightthickness 0 set hd(canvas) $w.f.canvas canvas $w.f.canvas \ -yscrollcommand "$w.f.scroll set" \ -highlightthickness 0 \ -background [$w.msg cget -background] frame $w.f.canvas.f set hd(cid) [$w.f.canvas create window 0 0 \ -anchor nw \ -window $w.f.canvas.f] set fr $w.f.canvas.f pack $w.f.scroll -side right -fill y pack $w.f.canvas -side left -expand 1 -fill both pack $w.f -fill both # Create key windows foreach n $keyDefOrder($area) { set hd($n) $option($n) label ${fr}.${n}_label -text $t($n) -anchor e button ${fr}.${n}_button -text $t(add_key) \ -command "AddKey ${fr}.${n}_f $n $id" set b(${fr}.${n}_button) keydef_add frame ${fr}.${n}_f -relief sunken -bd 1 set b(${fr}.${n}_f) keydef_def grid ${fr}.${n}_label ${fr}.${n}_f ${fr}.${n}_button -sticky we -pady 5 set hd(w_${n}) ${fr}.${n}_f PopulateKeyDef ${fr}.${n}_f $n $id if {![llength $hd($n)]} { button ${fr}.${n}_f.b -relief flat -state disabled pack ${fr}.${n}_f.b } } grid columnconfigure $w.f.canvas 1 -weight 1 bind $w.f.canvas "KeyDefClose $id" ::tkrat::winctl::SetGeometry keydef $w $w.f.canvas # Resize canvas update idletasks set bbox [$hd(canvas) bbox $hd(cid)] eval {$hd(canvas) configure -scrollregion $bbox} } # PopulateKeyDef -- # # Populates one keydef function # # Arguments: # w - The frame to add the keys in # name - The name of the definitions # handler - The handler for this keydef window proc PopulateKeyDef {w name handler} { global idCnt b upvar \#0 $handler hd foreach s [pack slaves $w] { destroy $s } foreach k $hd($name) { set hd($k) $name regsub {Key-} $k {} key set bn $w.b[incr idCnt] button $bn -text [string trim $key {<>}] -bd 1 \ -command "DeleteKeyDef $k $handler" pack $bn -side left -pady 2 -padx 2 set b($bn) keydef_def } } # AddKey -- # # Add a new key combination # # Arguments: # w - The frame to add the keys in # name - The name of the definitions # handler - The handler for this keydef window proc AddKey {w name handler} { upvar \#0 $handler hd global t set hd(mod) "" set hd(state) "" set hd(message) "" pack propagate $w 0 foreach s [pack slaves $w] { destroy $s } label $w.label -text $t(press_key) set hd(state) $t(press_key) pack $w.label -expand 1 bind $w.label "KeyEvent p %K $w $name $handler; break" bind $w.label "KeyEvent r %K $w $name $handler; break" focus $w.label } # KeyEvent -- # # Handle a key press or key release. # # Arguments: # e - Which event # key - The keysym # w - The frame to add the keys in # name - The name of the definitions # handler - The handler for this keydef window proc KeyEvent {e key w name handler} { upvar \#0 $handler hd global t if {[regexp {(Shift|Control|Alt|Mod[1-5]|Meta)(_[LR])?} $key tot mod]} { if {[string compare p $e]} { regsub "$mod-" $hd(mod) {} hd(mod) } else { set hd(mod) "$mod-$hd(mod)" } } elseif {[string compare r $e]} { set event "<$hd(mod)Key-$key>" set hd(state) "" if {[info exists hd($event)]} { # The key already exists set oname $hd($event) if { 0 == [RatDialog [winfo toplevel $w] $t(add_key) \ "$t(key_defined) $t($oname)" {} 0 \ $t(replace_key) $t(cancel)]} { # Remove old definition and update it set i [lsearch $hd($oname) $event] set hd($oname) [lreplace $hd($oname) $i $i] PopulateKeyDef $hd(w_$oname) $oname $handler lappend hd($name) $event set hd($event) $name } } else { lappend hd($name) $event set hd($event) $name } destroy [pack slaves $w] PopulateKeyDef $w $name $handler pack propagate $w 1 } } # DeleteKeyDef -- # # Delete a key definition # # Arguments: # event - The key event that should be deleted # handler - The handler for the keydef window proc DeleteKeyDef {event handler} { upvar \#0 $handler hd if {[string compare delete $hd(state)]} { return } set name $hd($event) set i [lsearch $hd($name) $event] set hd($name) [lreplace $hd($name) $i $i] PopulateKeyDef $hd(w_$name) $name $handler unset hd($event) set hd(state) "" set hd(message) "" } # KeyDefApply -- # # Apply the key definitions # # Arguments: # area - Identifies the area of keys to define # handler - The handler for this keydef window proc KeyDefApply {area handler} { upvar \#0 $handler hd global option b set changed 0 set remove {} foreach n [array names hd ${area}_key_*] { if {[string compare $option($n) $hd($n)]} { foreach e $option($n) { if { -1 == [lsearch $hd($n) $e]} { lappend remove $e } } set option($n) $hd($n) set changed 1 } } destroy $hd(w) if {$changed} { switch $area { folder { global folderWindowList foreach f [array names folderWindowList] { upvar \#0 $f fh foreach e $remove { bind $fh(w) $e {} } FolderBind $f } } compose { global composeWindowList foreach m $composeWindowList { upvar \#0 $m mh foreach e $remove { bind $mh(toplevel) $e {} } ComposeBind $m } } } SaveOptions } } # KeyDefClose -- # # Close the key definition window # # Arguments: # handler - The handler for this keydef window proc KeyDefClose {handler} { upvar \#0 $handler hd ::tkrat::winctl::RecordGeometry keydef $hd(w) $hd(canvas) foreach bn [array names b $hd(w).*] {unset b($bn)} unset hd } tkrat_2.2cvs20100105-dfsg.orig/tkrat/mime.tcl000066400000000000000000000051671137544547100205750ustar00rootroot00000000000000# mime.tcl -- # # This file contains procedures to determine the MIME type of a file. # # TkRat software and its included text is Copyright 1996-2000 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. set fileGroksMime 1 # RatType -- # # Determines the MIME type and encoding of the file. The algorithm is to # first determine if the file exists and its encoding, then run the file # command on it and parse the result. If the file command returns the MIME # type, it is used as is. If the file command returns a mix of MIME type and # English text, try to extract the MIME type from the returned string. # Otherwise tries to find a match in the typetable; if none is found, defaults # to application/octet-stream. # # Arguments: # fname - Name of the file to check proc RatType {fname} { global option global fileGroksMime if {![file exists $fname]} { error "error opening file $fname" } # Get the encoding set encoding [RatEncoding $fname] set mimetype "" if {-1 == [string first {--mime} $option(mimeprog)] && $fileGroksMime} { set cmd "exec $option(mimeprog) --mime [list $fname]" if {[catch {eval $cmd} mimetype]} { set fileGroksMime 0 set mimetype "" } } if {"" == $mimetype} { set mimetype [eval exec $option(mimeprog) [list $fname] 2>/dev/null] } # Parse the result if {[regexp {^[-a-z0-9A-Z]+/[-a-z0-9A-Z]+$} $mimetype]} { # Cool, the MIME type is set for us. Nothing to do! } elseif {[regexp {^([-a-z0-9A-Z]+)/([-a-z0-9A-Z]+),.*$} \ $mimetype -> partA partB]} { # Almost ok. The MIME type returns with stuff at the end. Strip the # stuff and just keep the MIME type. "stuff" is anything after the # first comma set mimetype "$partA/$partB" } elseif {[regexp {^([^:]+): ([-a-z0-9A-Z]+)/([-a-z0-9A-Z]+).*$} \ $mimetype -> name partA partB] \ && [string equal $name $fname]} { # Hmm... not cool. We get back the filename followed by the mime type. # We'll assume that there may or may not be a comma after the MIME # type. set mimetype $partA/$partB } else { # Ugh! The worst of all posisble worlds: the default file command! We # have no MIME type. So let's check it all against the # option(filetype) table set defaulttype "application/octet-stream" foreach {line} $option(typetable) { if {[string match [lindex $line 0] $mimetype]} { set defaulttype [lindex $line 1] break } } set mimetype $defaulttype } return [list $mimetype $encoding] } tkrat_2.2cvs20100105-dfsg.orig/tkrat/options.tcl000066400000000000000000000575061137544547100213450ustar00rootroot00000000000000# options.tcl -- # # This file contains defaults for all the options. These are just the # built in defaults. # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # OptionsInit -- # # Initialize the options to their default values # # Arguments: proc OptionsInit {} { global env option tkrat_version ratCurrent # The date of the last version used set option(last_version_date) 0 # Last of the features presented set option(last_seen_feature) 0 # Want information about changes? set option(info_changes) 1 # UI language set option(language) en # Search path for global configuration files set option(global_config_path) $env(CONFIG_DIR) # Personal config directory if {![info exists option(ratatosk_dir)]} { set option(ratatosk_dir) ~/.ratatosk } # Database directory set option(dbase_dir) $option(ratatosk_dir)/db # Directory to backup database messages to set option(dbase_backup) $option(ratatosk_dir)/backup # How long to wait between expiring the database (in days) set option(expire_interval) 7 # Userprocedures file set option(userproc) $option(ratatosk_dir)/userproc # Window size and positions file set option(placement) $option(ratatosk_dir)/placement # Main window name set option(main_window_name) "TkRat v$tkrat_version: %f (%r)" # Main window geometry set option(main_geometry) +0+50 # Icon name set option(icon_name) "TkRat v$tkrat_version: %f" # Default folder specification set option(default_folder) "INBOX file {} INBOX" # Format of list of messages set option(list_format) "%4S %6d %-24n %4B %t%s" # Which headers we should show set option(show_header) selected # Which the selected headers are: set option(show_header_selection) {From Subject Date To CC Reply-To} # Geometry of compose window set option(compose_geometry) +0+50 # Which headers to compose by default set option(compose_headers) {To Subject Cc} # Which editor to use (%s will be expanded to a filename) set option(editor) "emacs %s" # True if we always want to use the external editor set option(always_editor) 0 # List of SMTP hosts set option(r0,smtp_hosts) {localhost} # Username for SMTP set option(r0,smtp_user) {} # Password for SMTP set option(r0,smtp_passwd) {} # Default sening protocol set option(r0,sendprot) smtp # Default sending program set option(r0,sendprog) /usr/lib/sendmail # Can the sending program handle eightbit data set option(r0,sendprog_8bit) 1 # Default signature file set option(r0,signature) ~/.signature # The default reply_to address set option(r0,reply_to) "" # The defailt bcc address set option(r0,bcc) "" # The default From: address (may be empty) set option(r0,from) {} # Role name set option(r0,name) Standard # Default save outgoing set option(r0,save_outgoing) {} # PGP-key id set option(r0,pgp_keyid) {} # Default domain for unqualified addresses (defaults to domain of From:) set option(r0,uqa_domain) "" # Name for SMTP HELO/EHLO exchange (defaults to domain of From:) set option(r0,smtp_helo) "" # Validate SMTP server certificate or not set option(r0,validate_cert) 0 # Keep same sending preferences in all roles set option(r0,same_sending_prefs) 0 # User to sign as set option(r0,sign_as) {{} {}} # Default to signing set option(r0,sign_outgoing) 0 # Default role set option(default_role) r0 # List of roles set option(roles) {r0} # Which domain we are in set option(domain) {} # Leader string for replies set option(reply_lead) {> } # True (1) if we should show the watcher set option(watcher_enable) 1 # Time between checking for new mail in different folders set option(watcher_time) {30} # Geometry of watcher set option(watcher_geometry) -140+0 # Watcher max height set option(watcher_max_height) 10 # Which messages the watcher shall show ('new' or 'all') set option(watcher_show) new # How many times the bell should be run when new messages arive set option(watcher_bell) 2 # Format of list of messages in watcher set option(watcher_format) "%4S %-24n %s" # Print command set option(print_command) "lpr -P %p %s" # Headers to print set option(print_header) selected # Directory for temporary files set option(tmp) /tmp # Custom file command set option(mimeprog) "file" # Subject for replies to messages without subject set option(no_subject) "Re: (no subject)" # Default folder sort method set option(folder_sort) threaded # Message attribution set option(attribution) "On %d, %N wrote:" # Forwarded tag set option(forwarded_message) "------ Forwarded message ------" # File typing set option(typetable) { {"*gzip compressed*" application/octet-stream} {*GIF* image/gif} {*JPEG* image/jpeg} {*JPG* image/jpeg} {*PNG* image/png} {"*HTML document*" text/html} {"*8-bit u-law*" audio/basic} {*MP3* audio/mp3} {*PostScript* application/postscript} {*Word* application/msword} {*PDF* application/pdf} {*text* text/plain} {*data* application/octet-stream}} # True if we want to see ALL messages from c-client (including babble) set option(see_bable) 0 # True if we have looked for alias files set option(scan_aliases) 0 # Number of messages to remember set option(num_messages) 10 # True if we should lookup usernames in the local passwd-list set option(lookup_name) 1 # Default database expiration type set option(def_extype) remove # Default database expiration time set option(def_exdate) +365 # How many messages are required for one chunk (in dbase backup) set option(chunksize) 100 # Which message we should select when a folder is opened set option(start_selection) first_new # How long log messages should show (in ms) set option(log_timeout) 3 # Folder window key combination set option(folder_key_compose) set option(folder_key_close) { } set option(folder_key_openfile) set option(folder_key_quit) set option(folder_key_nextu) set option(folder_key_sync) set option(folder_key_netsync) set option(folder_key_update) set option(folder_key_delete) set option(folder_key_undelete) set option(folder_key_flag) set option(folder_key_next) { } set option(folder_key_prev) { } set option(folder_key_replya) set option(folder_key_replys) set option(folder_key_forward_i) set option(folder_key_forward_a) set option(folder_key_home) { } set option(folder_key_bottom) { } set option(folder_key_pagedown) { } set option(folder_key_pageup) { } set option(folder_key_linedown) {} set option(folder_key_lineup) {} set option(folder_key_cycle_header) set option(folder_key_find) set option(folder_key_bounce) set option(folder_key_markunread) set option(folder_key_print) set option(folder_key_online) set option(folder_key_mvdb) # Compose window key combinations set option(compose_key_send) set option(compose_key_abort) set option(compose_key_editor) set option(compose_key_undo) set option(compose_key_redo) set option(compose_key_cut) set option(compose_key_copy) set option(compose_key_cut_all) set option(compose_key_paste) set option(compose_key_wrap) # Alias window key combinations set option(alias_key_new_menu) set option(alias_key_read_menu) set option(alias_key_close_menu) set option(alias_key_delete_menu) set option(alias_key_edit_menu) # If we should check for stolen mail set option(mail_steal) 1 # Data for netscape inbox set option(ms_netscape_pref_file) $env(HOME)/.netscape/preferences set option(ms_netscape_mtime) 0 # True if we should remember the window positions set option(keep_pos) 1 # True if we should let the user specify from address. set option(use_from) 1 # The level of verboseness we should use when talking SMTP (0-3) set option(smtp_verbose) 2 # If we should try to send multiple letters though one channel set option(smtp_reuse) 1 # Override color resources set option(override_color) 1 # The color set set option(color_set) {\#dde3eb black white black} # Which icon to set {normal small none} set option(icon) normal # The default expression mode set option(expression_mode) basic # If we should start up in iconic mode set option(iconic) 0 # If the compose editor should warn about cutting all text etc set option(compose_warn) 1 # Mailcap path set option(mailcap_path) \ {~/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap} # Terminal command set option(terminal) "xterm -e sh -c" # Imap port set option(imap_port) 143 # Pop3 port set option(pop3_port) 110 # Default remote user set option(remote_user) $env(USER) # Default remote host set option(remote_host) "" # SMTP timeout set option(smtp_timeout) 120 # Should we sent even though we have a bad hostname? set option(force_send) 0 # Should we skip the signature of the message we are replying to set option(skip_sig) 1 # PGP operations set option(pgp_version) auto # Path to pgp program set option(pgp_path) {} # Extra pgp options set option(pgp_args) {} # Name of pgp keyring set option(pgp_keyring) {} # If we should encrypt outgoing letters set option(pgp_encrypt) 0 # Default url viewer set option(url_viewer) firefox # Name (and possibly path) of browser set option(browser_cmd) firefox # Where to show URLs set option(url_behavior) new_window # Color of URL set option(url_color) blue # System wide aliases set option(system_aliases) "System tkrat $env(CONFIG_DIR)/aliases" set option(use_system_aliases) 1 # Personal alias lists set option(addrbooks) \ [list [list Personal tkrat $option(ratatosk_dir)/aliases]] # Default alias book set option(default_book) Personal # Default browse mode set option(browse) folder # Caching data set option(cache_pgp) 1 set option(cache_pgp_timeout) 300 set option(cache_passwd) 1 set option(cache_passwd_timeout) 300 set option(cache_conn) 1 set option(cache_conn_timeout) 10 # URL protocols set option(urlprot) {http https ftp news telnet} # Balloon help set option(show_balhelp) 1 # Balloon help delay set option(balhelp_delay) 1500 # Automatically expunge on folder close set option(expunge_on_close) 1 # Checkpoint on window unmap set option(checkpoint_on_unmap) 1 # How often should it checkpoint the mailbox (when deiconfied) (seconds) set option(checkpoint_interval) 600 # List of known character sets set iso {} foreach e [encoding names] { if {[regsub iso8859 $e iso-8859 ne]} { lappend iso $ne } } set option(charsets) [concat us-ascii [lsort -command isosort $iso]] lappend option(charsets) iso-2022-jp lappend option(charsets) iso-2022-kr lappend option(charsets) utf-8 set option(charset_candidates) $option(charsets) # Automatically create sender field set option(create_sender) 0 # Unused option which must be here set option(tip) {} # Dynamic folder behaviour (expanded | closed) set option(dynamic_behaviour) expanded # If submenus should have a tearoff entry set option(tearoff) 0 # How long to delay certain menus (in milliseconds) set option(menu_delay) 200 # Where to store cached passwords set option(pwcache_file) $option(ratatosk_dir)/pwcache # If we should add the signature delimiter set option(sigdelimit) 1 # Do automatic line wrapping? set option(do_wrap) 1 # Place where lines wrap set option(wrap_length) 72 # Regexp for finding citation marks set option(citexp) {^[ ]*(([a-zA-Z0-9]+>[ ]*)|(>+[ ]*)+)?} # Regexp for finding indention set option(indexp) "^(\[ \t\]*)\[^ \t*.-\]" # Regexp for finding bullet characters set option(bullexp) "^(\[ \t\]*\[\\d*.-\]*\\)?\[ \t\]+)\[^ \t\]" # Should we wrap cited text automatically set option(wrap_cited) 0 # Directory to store local copies of disconnected folders set option(disconnected_dir) $option(ratatosk_dir)/disconnected # What to synchronize when doing a network synchronization # deferred_messages disconnected_mailboxes run_cmd cmd_to_run set option(network_sync) {1 1 0 {}} # Name of busy cursor set option(busy_cursor) watch # Regular expression which identifies the Re: part of subjects # will be applied with -nocase set option(re_regexp) {re:|sv:|aw:} # Printing defaults set option(print_pretty) 1 set option(print_dest) printer if {[info exists env(PRINTER)]} { set option(print_printer) $env(PRINTER) } else { set option(print_printer) ps } set option(print_file) {tkrat.ps} set option(print_papersize) A4 set option(print_papersizes) {{A4 {596 842}} {A3 {842 1191}} {Letter {612 792}} {Legal {612 1008}}} set option(print_orientation) portrait set option(print_fontsize) 12 set option(print_resolution) 72 set option(print_fontfamily) Times # additional Compose/Replies options set option(append_sig) 1 set option(reply_bottom) 1 # Font options set option(override_fonts) 1 set option(font_family_prop) helvetica set option(font_family_fixed) courier set option(font_size) 12 set option(watcher_font) {name 5x7} # Debug output dir set option(debug_file) $option(ratatosk_dir)/debug_log.txt # Wrap mode for shown messages set option(wrap_mode) word # Use input methodes? set option(useinputmethods) 0 # Path to ssh command set option(ssh_path) $env(SSH) # Template to ssh command set option(ssh_template) {%s %s -l %s exec /etc/r%sd} # SSH timeout set option(ssh_timeout) 15 # path to spell set option(spell_path) auto # Online/offline mode set option(start_online_mode) last set option(online) 1 # Template for new folder set option(template_folder) [list Template file {} $env(HOME)/FOO] # Show HTML images set option(html_show_images) 0 # Minimum acceptable size for images set option(html_min_image_size) 2 # Proxy server set option(html_proxy_host) "" # Proxy port set option(html_proxy_port) 8080 # HTTP timeout (milliseconds) set option(html_timeout) 10000 # How to encode parameters # rfc2231 - The standard way, but some clients do not understand it # rfc2047 - Illegal according to the standard but interoperates better # both - Duplicate all parameters which needs encoding and encode # in both systems. A compromise. set option(parm_enc) both # Should we prefer to show text parts over html? set option(prefer_other_over_html) 0 # Default spell checking language set option(def_spell_dict) auto # Dictionaries to auto test against # Empty means all available if using ispell and an automatic selection # if using aspell set option(auto_dicts) {} # Enable auto spell check when composing set option(autospell) 1 # Seconds between backups in compose window (0 means no backups) set option(compose_backup) 30 # Mark non-wrappable parts of messages set option(mark_nowrap) 0 # How long to keep a backup in the drafts folder after a compose window # has been closed. set option(compose_last_chance) 300 # SHow address autocompletion list set option(show_autocomplete) 1 # How many addresses to remember and use when popping up an # autocomplete menu. 0 means none set option(num_autocomplete_addr) 500 # How many autocomplete addresses to show in list set option(automplete_addr_num_suggestions) 20 # Initial directory for file selectors (empty means homedir) set option(initialdir) "" # Format of date in message list (look at 'man strftime' for details) set option(date_format) "%e %b" # tnef program which unpacks winmail.dat-attachments set option(tnef) tnef # tnef max size of contained file set option(tnef_max_size) 10000000 } # OptionsInitText -- # # Initialize options depending on the language # # Arguments: proc OptionsInitText {} { global option t # Watcher title set option(watcher_title) $t(watcher) } # OptionsRead -- # # Searches the filesystem for ratatoskrc files # # Arguments: proc OptionsRead {} { global option globalOption env # Read global files foreach dir $option(global_config_path) { if {[file readable $dir/ratatoskrc]} { source $dir/ratatoskrc } } # Take copy of global options foreach name [array names option] { set globalOption($name) $option($name) } # Read local modifications if {[file readable $option(ratatosk_dir)/ratatoskrc]} { source $option(ratatosk_dir)/ratatoskrc } # Read local overrides if {[file readable $option(ratatosk_dir)/ratatoskrc.tcl]} { source $option(ratatosk_dir)/ratatoskrc.tcl } # Setup list of charset candidates if {[info exists option(charset)]} { set option(charset_candidates) \ [linsert $option(charsets) 1 $option(charset)] set globalOption(charset) "" } } # SaveOptions -- # # Saves the users changes to the global options to disk. # # Arguments: proc SaveOptions {} { global option globalOption # Warning message set message {# # BEWARE of making changes to this file. It is automatically generated. # You can change the values in this file via the preferences window. # This file can only contain "set option() " lines, # everything else will be destroyed when the file is regenerated. } set fh [open $option(ratatosk_dir)/ratatoskrc w] puts $fh $message # Write only changed values to local file foreach name [lsort [array names globalOption]] { if {[info exists option($name)]} { if {[string compare $option($name) $globalOption($name)]} { puts $fh "set option($name) [list $option($name)]" } } } # Write the roles we have defined by ourselves foreach name [array names option r*,*] { if {![info exists globalOption($name)]} { puts $fh "set option($name) [list $option($name)]" } } close $fh } # ReadUserproc -- # # Source the users userproc file with some caution # # Arguments: proc ReadUserproc {} { global option t if {[file readable $option(userproc)]} { if {[catch "source $option(userproc)" message]} { Popup "$t(error_in_userproc): $message" } } } # InitCharsetAliases # # Initialize the aliases of charcter sets # # Arguments: proc InitCharsetAliases {} { global charsetAlias option charsetName charsetMapping \ charsetReverseMapping t ratCurrent # Mapping to tcl names set charsetMapping(us-ascii) ascii set charsetMapping(utf-8) utf-8 foreach e [encoding names] { if {[regsub iso8859 $e iso-8859 ne]} { set charsetMapping($ne) $e } } set charsetMapping(iso-2022-jp) iso2022-jp set charsetMapping(iso-2022-kr) iso2022-kr set charsetMapping(windows-1250) cp1250 set charsetMapping(windows-1251) cp1251 set charsetMapping(windows-1252) cp1252 set charsetMapping(windows-1253) cp1253 set charsetMapping(windows-1254) cp1254 set charsetMapping(windows-1255) cp1255 set charsetMapping(windows-1256) cp1256 set charsetMapping(windows-1257) cp1257 set charsetMapping(windows-1258) cp1258 set charsetMapping(windows-437) cp437 set charsetMapping(windows-737) cp737 set charsetMapping(windows-775) cp775 set charsetMapping(windows-850) cp850 set charsetMapping(windows-852) cp852 set charsetMapping(windows-855) cp855 set charsetMapping(windows-857) cp857 set charsetMapping(windows-860) cp860 set charsetMapping(windows-861) cp861 set charsetMapping(windows-862) cp862 set charsetMapping(windows-863) cp863 set charsetMapping(windows-864) cp864 set charsetMapping(windows-865) cp865 set charsetMapping(windows-866) cp866 set charsetMapping(windows-869) cp869 set charsetMapping(windows-874) cp874 set charsetMapping(windows-932) cp932 set charsetMapping(windows-936) cp936 set charsetMapping(windows-949) cp949 set charsetMapping(windows-950) cp950 set charsetMapping(gb2312) ascii set charsetMapping(gb1988) ascii set charsetMapping(gb12345) ascii set charsetMapping($t(system_default_charset)) system # These are predefined (remember to only use lowercase letters) #set charsetAlias(bar) iso-8859-1 # Read global files foreach dir $option(global_config_path) { if {[file readable $dir/charsetAliases]} { source $dir/charsetAliases } } # Read local modifications if {[file readable $option(ratatosk_dir)/charsetAliases]} { source $option(ratatosk_dir)/charsetAliases } # Build structure catch {unset charsetName} foreach c $option(charsets) { set charsetName($c) "" } foreach a [array names charsetAlias] { if {[info exists charsetName($charsetAlias($a))]} { lappend charsetName($charsetAlias($a)) $a } } foreach c [array names charsetMapping] { set charsetReverseMapping($charsetMapping($c)) $c } # Default character set for tcl if {![info exists option(charset)]} { set charset $ratCurrent(charset) if {[info exists charsetReverseMapping($charset)]} { set option(charset) $charsetReverseMapping($charset) } else { set option(charset) $charset } global globalOption set globalOption(charset) $option(charset) } } # InitPgp -- # # Initializes the pgp_version and pgp_prog variables if they are # no already set. # # Arguments: proc InitPgp {} { global option env if {"auto" != $option(pgp_version)} { return } set option(pgp_version) 0 foreach d [split $env(PATH) :] { if {[file executable $d/pgpk]} { set option(pgp_version) 5 set option(pgp_path) $d set option(pgp_keyring) "~/.pgp/pubring.pkr" return } if {[file executable $d/gpg]} { set option(pgp_version) gpg-1 set option(pgp_path) $d set option(pgp_keyring) "~/.pgp/pubring.pgp" } if {[file executable $d/pgp]} { catch {exec $d/pgp -v} out set version [lindex [lindex [split $out \n] 0] end] if {[regexp {^6\.} $version]} { set option(pgp_version) 6 } else { set option(pgp_version) 2 } set option(pgp_path) $d set option(pgp_keyring) "~/.pgp/pubring.pgp" } } } # isosort -- # # Sort iso-8859-X charcter set names # # Arguments: # i1, i2 - The names to be compared proc isosort {i1 i2} { set n1 [lindex [split $i1 -] 2] set n2 [lindex [split $i2 -] 2] if {[string length $n1] != [string length $n2]} { return [expr [string length $n1]-[string length $n2]] } else { return [expr $n1-$n2] } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/pgp.tcl000066400000000000000000000231151137544547100204250ustar00rootroot00000000000000# pgp.tcl -- # # This file contains code which handles pgp interaction # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # RatGetPGPPassPhrase -- # # Get the pgp pass phrase from the user # # Arguments: proc RatGetPGPPassPhrase {} { global idCnt t # Create identifier set id pgpPass[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -class TkRat wm title $w $t(pgp_pass_phrase) # Populate window label $w.label -text "$t(pgp_pass_phrase):" entry $w.entry -textvariable ${id}(phrase) -width 32 -show - button $w.button_ok -text $t(ok) -command "set ${id}(done) ok" button $w.button_cancel -text $t(abort) -command "set ${id}(done) abort" grid $w.label $w.entry -pady 5 -padx 5 grid $w.button_ok $w.button_cancel bind $w.entry "set ${id}(done) ok" wm protocol $w WM_DELETE_WINDOW "set ${id}(done) abort" ::tkrat::winctl::SetGeometry pgpPhrase $w ::tkrat::winctl::ModalGrab $w $w.entry bind $w "$w.button_cancel invoke" tkwait variable ${id}(done) ::tkrat::winctl::RecordGeometry pgpPhrase $w destroy $w set ret [list $hd(done) $hd(phrase)] unset hd return $ret } # RatPGPError -- # # Report an PGP error to the user. It should return either "ABORT" or # "RETRY". # # Arguments: # error - An error message proc RatPGPError {error} { global idCnt t # Create identifier set id pgpProblem[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -class TkRat wm title $w $t(pgp_problem) # Populate window frame $w.f pack $w.f -padx 5 -pady 5 -fill both -expand 1 label $w.f.label -text "$t(pgp_problem):" pack $w.f.label -side top -anchor w frame $w.f.t -relief sunken -bd 1 scrollbar $w.f.t.scroll \ -relief sunken \ -command "$w.f.t.text yview" \ -highlightthickness 0 text $w.f.t.text \ -yscroll "$w.f.t.scroll set" \ -setgrid 1 \ -relief raised \ -bd 0 \ -highlightthickness 0 pack $w.f.t.scroll -side right -fill y pack $w.f.t.text -side left -expand yes -fill both set errmsg [string map [list "\a" ""] $error] $w.f.t.text insert 1.0 $errmsg $w.f.t.text configure -state disabled pack $w.f.t -expand 1 -fill both frame $w.f.b button $w.f.b.retry -text $t(retry) -command "set ${id}(done) RETRY" button $w.f.b.abort -text $t(abort) -command "set ${id}(done) ABORT" pack $w.f.b.retry $w.f.b.abort -side left -expand 1 -pady 5 pack $w.f.b -side bottom -fill x wm protocol $w WM_DELETE_WINDOW "set ${id}(done) ABORT" ::tkrat::winctl::SetGeometry pgpError $w $w.f.t.text ::tkrat::winctl::ModalGrab $w bind $w "$w.f.b.abort invoke" tkwait variable ${id}(done) ::tkrat::winctl::RecordGeometry pgpError $w $w.f.t.text destroy $w set action $hd(done) unset hd return $action } # RatPGPGetIds -- # # Let the user select keys from her keyrings # # Arguments: # proc - procedure to call when done # arg - argument to procedure (before list of ids) proc RatPGPGetIds {proc arg} { global idCnt t option # List keys if {[catch {RatPGP listkeys} keylist]} { Popup $keylist return } # Create identifier set id pgpGet[incr idCnt] set w .$id upvar \#0 $id hd # Create toplevel toplevel $w -class TkRat wm title $w $t(select_keys) # Add text set hd(list) $w.l rat_textlist::create $hd(list) [lindex $keylist 0] pack $w.l -side top -fill both -expand 1 # Buttons frame $w.buttons button $w.buttons.ok -text $t(ok) \ -command "RatPGPGetIdsDone $w $id 1 [list $proc $arg]" button $w.buttons.sel -text $t(select_all) \ -command "rat_textlist::selection $hd(list) set 0 end" button $w.buttons.unsel -text $t(deselect_all) \ -command "rat_textlist::selection $hd(list) clear" button $w.buttons.cancel -text $t(cancel) \ -command "RatPGPGetIdsDone $w $id 0 [list $proc $arg]" pack $w.buttons.ok \ $w.buttons.sel \ $w.buttons.unsel \ $w.buttons.cancel -side left -expand 1 pack $w.buttons -side bottom -pady 5 -fill x # Populate text widget set hd(keys) {} foreach e [lindex $keylist 1] { lappend hd(keys) [lrange $e 0 1] set desc [lindex $e 2] foreach s [lindex $e 3] { set desc "$desc\n $s" } rat_textlist::insert $hd(list) end $desc } wm protocol $w WM_DELETE_WINDOW \ "RatPGPGetIdsDone $w $id 0 [list $proc $arg]" ::tkrat::winctl::SetGeometry pgpGet \ $w [rat_textlist::textwidget $hd(list)] bind $w "$w.buttons.cancel invoke" } # RatPGPGetIdsDone -- # # Calls when the selection is done # # Arguments: # w - The id selection window # handler - The handler which identifies the session window # done - The users selection (1=ok, 0=cancel) # proc - procedure to call when done # arg - argument to procedure (before list of ids) proc RatPGPGetIdsDone {w handler done proc arg} { upvar \#0 $handler hd global option if {$done} { set ids {} foreach s [rat_textlist::selection $hd(list) get] { lappend ids [lrange [lindex $hd(keys) $s] 0 1] } $proc $arg $ids } ::tkrat::winctl::RecordGeometry pgpGet \ $w [rat_textlist::textwidget $hd(list)] catch {focus $hd(oldfocus)} destroy $w unset hd } # RatPGPAddKeys -- # # Add keys to keyring # # Arguments: # keys - Keys to add # keyring - Keyring to add them to proc RatPGPAddKeys {keys {keyring ""}} { global idCnt option t rat_tmp # Create identifier set id pgpInt[incr idCnt] upvar \#0 $id hd # Setup file set hd(fileName) $rat_tmp/rat.[RatGenId] set f [open $hd(fileName) w] puts $f $keys close $f # Create command and run it if {"" != $option(pgp_path)} { set dir $option(pgp_path)/ } else { set dir "" } if {$option(pgp_version) == 2} { set cmd "${dir}pgp -ka $hd(fileName) $keyring" } elseif {$option(pgp_version) == 5} { set cmd "${dir}pgpk -ka $hd(fileName) $keyring" } elseif {$option(pgp_version) == "gpg-1"} { set cmd "${dir}gpg --no-secmem-warning -q --import $hd(fileName)" } elseif {$option(pgp_version) == 6} { set cmd "${dir}pgp -ka $hd(fileName) $keyring" } set cmd "$cmd; echo '$t(press_return_to_dismiss)'; read FOO" RatBgExec ${id}(existStatus) "$option(terminal) \"$cmd\"" trace variable hd(existStatus) w RatPGPAddKeysDone } # RatPGPAddKeysDone -- # # This gets called when the add command has run and should clean # things up. # # Arguments: # name1, name2 - Variable specifiers # op - Operation proc RatPGPAddKeysDone {name1 name2 op} { upvar \#0 $name1 hd file delete -force -- $hd(fileName) & unset hd } # SetupSignAsWidget -- # # Create a widget to select signing key # # w - Window to build # var - Name of global variable which holds the value proc SetupSignAsWidget {w var} { global t b upvar \#0 $var v set sa [lindex $v 1] if {"" == $sa} { set sa "-- $t(auto) --" } menubutton $w -text $sa -indicatoron 1 \ -menu $w.m -bd 2 -relief raised -anchor w -justify left set b($w) pref_sign_as menu $w.m -postcommand "PostSignAs $w $var" } # PostSignAs -- # # Post the SignAs menu # # Arguments # w - Widget # var - Name of global variable which holds the value proc PostSignAs {w var} { global t $w.m delete 0 end set al "-- $t(auto) --" $w.m add command -label $al -command \ "$w configure -text [list $al]; \ set $var {}" foreach k [lindex [RatPGP listkeys SecRing] 1] { if {[lindex $k 4]} { set label "[lindex $k 2] [join [lindex $k 3]]" set ln "[lindex $k 2]\n [join [lindex $k 3] { }]" $w.m add command -label $label -command \ "$w configure -text [list $ln] ; \ set $var [list [list [lindex $k 0] $ln]]" } } FixMenu $w.m } # PGPDetails -- # # Show detailed pgp settings when composing # # Arguments: # handler - message handler proc PGPDetails {handler} { global t fixedBoldFont upvar \#0 $handler mh # Create toplevel set w .pgp_details toplevel $w -class TkRat wm title $w $t(pgp_details) # Populate window labelframe $w.my -text $t(my_key) SetupSignAsWidget $w.my.k ${handler}(pgp_signer) pack $w.my.k -padx 2 -pady 2 -fill x labelframe $w.recipients -text $t(recipients) text $w.recipients.t -wrap none pack $w.recipients.t -padx 2 -pady 2 $w.recipients.t tag configure bold -font $fixedBoldFont -lmargin1 2 $w.recipients.t tag configure last -spacing3 5 -lmargin1 2 set na 0 foreach f {to cc} { foreach a [RatSplitAdr $mh($f)] { set key_info [lindex [RatAlias expand pgp $a $mh(role)] 0] if {1 < [llength $key_info]} { set key [lindex $key_info 1] } else { set key $key_info } $w.recipients.t insert end "$t($f): " bold $w.recipients.t insert end "$a\n" $w.recipients.t insert end "$t(key): $key\n" last incr na } } if {$na < 4} { set na 4 } $w.recipients.t configure -height [expr int($na*2.2+0.9)] button $w.close -text $t(close) -command "destroy $w" pack $w.my $w.recipients -side top -padx 2 -pady 2 -fill x pack $w.close -padx 2 -pady 2 bind $w "$w.close invoke" bind $w.close "::tkrat::winctl::RecordGeometry pgpDetails $w" ::tkrat::winctl::SetGeometry pgpDetails $w ::tkrat::winctl::ModalGrab $w } tkrat_2.2cvs20100105-dfsg.orig/tkrat/preferences.tcl000066400000000000000000001211421137544547100221370ustar00rootroot00000000000000# preferences.tcl -- # # This file controls the preferences window. There is just one preferences # window, if the user requests a new preferences window when the window # already exists, it is just unmapped and raised (if possible). # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # Preferences -- # # Make sure the user sees an preferences window. # # Arguments: proc Preferences {} { if {![winfo exists .pref]} { BuildPreferences } else { wm deiconify .pref } } # Images set checkmark_img [image create photo -data { R0lGODlhDwAPAIQcADSNHjWOHzeQIjmRIzuTJj6VKT+WKkGXLEKYLUicM0ue Nk6gOV+sTGqzWG61XHy+bH2/boHBconFeovGfJzQkKTUmafVm6nWnqvXoLPc qbzgs8Xkvf///////////////yH5BAEKAB8ALAAAAAAPAA8AAAUv4CeOZGme aKp+E7R+EfYuzHs0r5Og2VB9EsIGpUEEPoKH6gIoGF4KgOU1obyu2BAAOw==}] set error_img [image create photo -data { R0lGODlhDwAPAMZAALgEALoGCcUBDMUFAM8ABNEAAKcUFtEBFNsAAtsADd4B AL4NF6UaItwAGNsAKekAA84QJcMoI8MnMtEgPr5BULxMU7xOXLNXUs5MStdF WOBFTeJDU+VDWvBBSPg9VrhhcNBdYOhSYNpbb/xidsaAhvFyj99/feh+ktmJ jf55gr+XjfCChbqenNqSrt2WosOrtdmps/+kpPatoPmvtfq7t/jQy9fv09z7 6uP/+fH/6v/1/O3/+/758f/76/z+6v7+/v////////////////////////// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// /////////////////////////////////////////////////yH5BAEKAEAA LAAAAAAPAA8AAAfCgECCgh0BAwcYg4otFgwJCwILDRAiixkLAw0yMTQeCA0U gxaYBQs5PzgmAAMCFUAqEAUICAGnO6oFCg0nGAmzBQc8Oj8jCAUEAiEQBwUD AAc1OzkbCM4BEQerCAIJM9IbCc8FBRG+AAQHKTk+sQAPCREhBQ8AshwlJAIE A7QXQAYPBhAIYGMHjxQAzi1wAeRDtQcFfKBKMS+BKyAvJgQgkKDHjRsrCBCQ oAhGBQgKPITI4ODARUVAWIAwMACCBhSKAgEAOw==}] # BuildPreferences -- # # Builds the preferences window. # # Arguments: proc BuildPreferences {} { global t b pref option # Initialize data table foreach l [GetLanguages] { lappend lang [lrange $l 0 1] } # Give initial values set spell_dictionaries [list [list auto $t(auto)]] set pref(dictionaries) {} # Font families if {![info exists pref(families)]} { foreach f [lsort -dictionary [font families]] { lappend pref(families) [list $f [string totitle $f]] } } # The lists here can have the following elements # option var label_id value_list # checkbutton var label_id onvalue offvalue # bool var label_id # entry var label_id # entry_unit var label_id unit # spinbox var label_id from increment to # spinbox_unit var label_id unit from increment to # special var label_id special_proc # label label_text anchor # custom custom_proc set pref(roles) \ [list \ [list entry name role_name] \ [list entry signature signature_file] \ [list special save_outgoing save_out SetupDefaultSave] \ [list custom SetupRole]] set pref(roles,address) \ [list \ [list entry from use_from_address] \ [list entry bcc default_bcc] \ [list entry reply_to default_reply_to] \ [list message $t(tip) $t(test_by)] \ [list entry pgp_keyid pgp_keyid]] set pref(roles,sending) \ [list \ [list option sendprot sendprot \ [list [list smtp $t(smtp)] \ [list prog $t(user_program)]]] \ [list entry smtp_hosts smtp_hosts] \ [list entry smtp_user smtp_user] \ [list entry smtp_passwd passwd] \ [list bool validate_cert ssl_check_cert] \ [list entry sendprog sendprog] \ [list bool sendprog_8bit sendprog_8bit] \ [list bool same_sending_prefs same_sending_prefs]] set pref(roles,advanced) \ [list \ [list label $t(normally_ok) center] \ [list label $t(unqual_adr_domain) w] \ [list entry uqa_domain domain] \ [list label $t(smtp_from_long) w] \ [list entry smtp_helo host]] set pref(roles,pgp) \ [list \ [list bool sign_outgoing sign_outgoing] \ [list special sign_as sign_as PrefSetupSignAs]] set pref(appearance) \ [list \ [list option language language $lang] \ [list special color_set color_scheme SetupColor] \ [list option font_family_prop prop_font_family $pref(families)] \ [list option font_family_fixed fixed_font_family $pref(families)]\ [list spinbox font_size font_size 1 1 120] \ [list special watcher_font watcher_font\ [list SelectFont watcher_font]] \ [list bool useinputmethods useinputmethods] \ ] set pref(general) \ [list \ [list bool iconic startup_iconic] \ [list option start_online_mode start_mode \ [list [list last $t(som_last)] \ [list online $t(som_online)] \ [list offline $t(som_offline)]]] \ [list option start_selection start_selection \ [list [list first $t(first_message)] \ [list last $t(last_message)] \ [list first_new $t(first_new_message)] \ [list before_new $t(before_first_new_message)]]] \ [list entry list_format list_format] \ [list entry date_format date_format] \ [list entry show_header_selection show_header_selection] \ [list spinbox_unit checkpoint_interval \ checkpoint_interval $t(seconds) 0 30 1000000] \ [list bool expunge_on_close expunge_on_close] \ ] set pref(html) \ [list \ [list special url_viewer url_viewer SetupURLViewer] \ [list label "" center] \ [list label $t(html_messages) center] \ [list bool prefer_other_over_html avoid_html] \ [list bool html_show_images html_show_images] \ [list spinbox html_min_image_size html_min_image_size \ 0 1 1000000] \ [list entry html_proxy_host html_proxy_host] \ [list spinbox html_proxy_port html_proxy_port 1 1 65535] \ [list spinbox_unit html_timeout html_timeout $t(ms) \ 0 1000 10000000] \ ] set pref(new_messages) \ [list \ [list spinbox_unit watcher_time watcher_intervals $t(seconds) \ 0 1 1000000] \ [list spinbox_unit watcher_max_height max_height $t(lines) \ 1 1 1000] \ [list option watcher_show show_messages \ [list [list new $t(new)] [list all $t(all)]]] \ [list entry watcher_format list_format] \ [list spinbox watcher_bell bell_ringings 0 1 1000] \ ] set pref(composing) \ [list \ [list bool always_editor always_use_external_editor] \ [list entry compose_headers headers] \ [list spinbox_unit compose_backup compose_backup $t(seconds) \ 0 10 1000000] \ [list spinbox_unit compose_last_chance compose_last_chance \ $t(seconds) 0 30 1000000] \ [list entry re_regexp reply_regexp] \ [list entry attribution attribution] \ [list bool wrap_cited wrap_cited] \ [list bool skip_sig skip_sig] \ [list bool reply_bottom reply_bottom] \ ] set pref(spell_checking) \ [list \ [list option def_spell_dict dictionary $spell_dictionaries] \ [list special auto_dicts auto_dicts SetupCheckDicts] \ [list special spell_path spell_cmd SetupSpellPath] \ ] set pref(dbase) \ [list \ [list option def_extype extype \ [list [list none $t(none)] \ [list remove $t(remove)] \ [list incoming $t(incoming)] \ [list backup $t(backup)]]] \ [list entry_unit def_exdate exdate $t(days)] \ [list entry dbase_backup dbase_backup] \ ] set pref(paths) \ [list \ [list entry tmp tmp_dir] \ [list entry print_command print_command] \ [list entry terminal terminal_command] \ [list entry ssh_path ssh_path] \ [list entry tnef tnef_path] \ ] set pref(pgp) \ [list \ [list option pgp_version pgp_version \ [list [list 0 $t(none)] [list gpg-1 GPG-1] [list 2 PGP-2] \ [list 5 PGP-5] [list 6 PGP-6] [list auto $t(auto)]]] \ [list entry pgp_path pgp_path] \ [list entry pgp_args pgp_extra_args] \ [list entry pgp_keyring pgp_keyring] \ [list bool cache_pgp cache_passwd] \ [list spinbox_unit cache_pgp_timeout cache_timeout $t(seconds)\ 0 30 1000000] \ [list bool pgp_encrypt encrypt_outgoing] \ ] # Create top window structures set w .pref toplevel $w -class TkRat wm title $w $t(preferences) set pref(scrollpane) $w.pane set pref(pane) [rat_scrollframe::create $w.pane -bd 2 -relief flat] set pref(lastPref) "" set pref(selected) "" # Setup tree set pref(tree) [rat_tree::create $w.tree \ -sizeid prefTree -selectcallback PrefSelectPane] $pref(tree) autoredraw 0 set topnode [$pref(tree) gettopnode] set pref(rnode) \ [$topnode add folder -label $t(roles) -state open -id rnode] PrefPopulateRoles # Preferences foreach n {appearance general html new_messages composing spell_checking dbase paths pgp} { set n0 [lindex $n 0] set name $t($n0) if {1 != [llength $n]} { set node [$topnode add folder -label $name -state closed \ -id [list option $n0]] foreach n1 [lindex $n 1] { $node add leaf -label $t($n1) -id [list option $n0,$n1] } } else { $topnode add leaf -label $name -id [list option $n0] } } $pref(tree) redraw # The buttons frame $w.buttons button $w.buttons.ok -text $t(apply) -command "PrefApply $w" \ -default active -state disabled button $w.buttons.reset -text $t(reset) -state disabled \ -command { global pref foreach v $pref(vars,$pref(lastPref)) { set pref(opt,$v) $pref(old,$v) } foreach but $pref(traceButtons) { $but configure -state disabled } } button $w.buttons.close -text $t(close) \ -command "PrefCheck $w; \ ::tkrat::winctl::RecordGeometry preferences $w $w.pane; \ wm withdraw $w" pack $w.buttons.ok \ $w.buttons.reset \ $w.buttons.close -side left -expand 1 bind $w "PrefApply $w" set b($w.buttons.ok) apply_prefs set b($w.buttons.reset) reset_changes set b($w.buttons.close) dismiss set pref(traceButtons) [list $w.buttons.ok $w.buttons.reset] grid $w.tree $w.pane -sticky nwse grid ^ $w.buttons -sticky ew grid rowconfigure $w 0 -weight 1 grid columnconfigure $w 0 -weight 1 grid columnconfigure $w 1 -weight 5 grid propagate $w.pane 0 bind $w "$w.buttons.close invoke" ::tkrat::winctl::SetGeometry preferences $w $w.pane # Initialize tracing function set pref(traceChanges) 0 trace variable pref w PrefTraceProc } # PrefSelectPane -- # # Called when the user selects an item in the tree # # Arguments: # pane - pane to fill in proc PrefSelectPane {pane} { global pref if {"" == $pane} { return fail } PrefCheck $pref(pane) set pref(selected) $pane foreach c [winfo children $pref(pane)] { destroy $c } switch [lindex $pane 0] { option { PrefBuild [lindex $pane 1] $pref(pane) "" } role { PrefBuild [lindex $pane 2] $pref(pane) "[lindex $pane 1]," } rnode { PrefBuildRnode $pref(pane) } } rat_scrollframe::recalc $pref(scrollpane) return ok } # PrefBuild -- # # Build a preferences pane # # Arguments: # pane - Which pane of preferences to build # w - Name of frame to build it into # rp - Role prefix proc PrefBuild {pane w rp} { global t b option pref propNormFont tk_version set pref(traceChanges) 0 set row 0 set pref(lastPref) $pane set pref(rolePrefix) $rp set pref(vars,$pane) {} foreach p $pref($pane) { grid rowconfigure $w $row -weight 0 if {![regexp {^(label|message|custom)$} [lindex $p 0]]} { set var "${rp}[lindex $p 1]" set bvar [lindex $p 1] lappend pref(vars,$pane) $var if {[info exists option($var)]} { set pref(opt,$var) $option($var) } else { set pref(opt,$var) {} } } switch [lindex $p 0] { entry { label $w.r${row}_lab -text $t([lindex $p 2]): grid $w.r${row}_lab -row $row -sticky ne -pady 1 entry $w.r${row}_item -textvariable pref(opt,$var) grid $w.r${row}_item - -row $row -column 1 -sticky we set b($w.r${row}_item) pref_$bvar } entry_unit { label $w.r${row}_lab -text $t([lindex $p 2]): grid $w.r${row}_lab -row $row -sticky ne -pady 1 entry $w.r${row}_item -textvariable pref(opt,$var) label $w.r${row}_unit -text ([lindex $p 3]) grid $w.r${row}_item -row $row -column 1 -sticky we grid $w.r${row}_unit -row $row -column 2 -sticky w set b($w.r${row}_item) pref_$bvar } spinbox { label $w.r${row}_lab -text $t([lindex $p 2]): grid $w.r${row}_lab -row $row -sticky ne -pady 1 if {$tk_version >= 8.4} { spinbox $w.r${row}_item -textvariable pref(opt,$var) \ -from [lindex $p 3] -increment [lindex $p 4] \ -to [lindex $p 5] -validate all \ -vcmd [list ValidateInt %P [lindex $p 3] [lindex $p 5]] } else { entry $w.r${row}_item -textvariable pref(opt,$var) \ -validate all \ -vcmd [list ValidateInt %P [lindex $p 3] [lindex $p 5]] } grid $w.r${row}_item - -row $row -column 1 -sticky we set b($w.r${row}_item) pref_$bvar } spinbox_unit { label $w.r${row}_lab -text $t([lindex $p 2]): grid $w.r${row}_lab -row $row -sticky ne -pady 1 if {$tk_version >= 8.4} { spinbox $w.r${row}_item -textvariable pref(opt,$var) \ -from [lindex $p 4] -increment [lindex $p 5] \ -to [lindex $p 6] -validate all \ -vcmd [list ValidateInt %P [lindex $p 4] [lindex $p 6]] } else { entry $w.r${row}_item -textvariable pref(opt,$var) \ -validate all \ -vcmd [list ValidateInt %P [lindex $p 4] [lindex $p 6]] } label $w.r${row}_unit -text ([lindex $p 3]) grid $w.r${row}_item -row $row -column 1 -sticky we grid $w.r${row}_unit -row $row -column 2 -sticky w set b($w.r${row}_item) pref_$bvar } option { label $w.r${row}_lab -text $t([lindex $p 2]): grid $w.r${row}_lab -row $row -sticky ne -pady 1 OptionMenu $w.r${row}_item $var [lindex $p 3] grid $w.r${row}_item - -row $row -column 1 -sticky w set b($w.r${row}_item) pref_$bvar } checkbutton { checkbutton $w.r${row}_cb -text $t([lindex $p 2]) \ -variable pref(opt,$var) -onvalue [lindex $p 3] \ -offvalue [lindex $p 4] grid $w.r${row}_cb - -row $row -column 1 -sticky w set b($w.r${row}_cb) pref_$bvar } bool { checkbutton $w.r${row}_cb -text $t([lindex $p 2]) \ -variable pref(opt,$var) if {$pref(opt,$var)} { set pref(opt,$var) 1 } else { set pref(opt,$var) 0 } grid $w.r${row}_cb - -row $row -column 1 -sticky w set b($w.r${row}_cb) pref_$bvar } special { label $w.r${row}_lab -text $t([lindex $p 2]): grid $w.r${row}_lab -row $row -sticky ne -pady 1 eval "[lindex $p 3] $w.r${row}_item" grid $w.r${row}_item - -row $row -column 1 -sticky we } label { frame $w.r${row}_lab grid $w.r${row}_lab -row $row -columnspan 2 -sticky new -pady 1 rat_flowmsg::create $w.r${row}_lab.l -text [lindex $p 1] \ -anchor [lindex $p 2] -padx 0 -font $propNormFont pack $w.r${row}_lab.l -fill both -expand 1 } message { label $w.r${row}_lab -text [lindex $p 1] frame $w.r${row}_message grid $w.r${row}_lab -row $row -sticky ne -pady 1 grid $w.r${row}_message -row $row -column 1 -sticky we rat_flowmsg::create $w.r${row}_message.m \ -text [lindex $p 2] -padx 0 -anchor w pack $w.r${row}_message.m -fill both -expand 1 } custom { eval "[lindex $p 1] $w.r${row}_item" grid $w.r${row}_item - -row $row -column 1 -sticky we } default {puts "Internal error <$p>"} } if {![regexp {^(label|message|custom)$} [lindex $p 0]]} { set pref(old,$var) $pref(opt,$var) if {[RatIsLocked option([lindex $p 1])]} { $w.r${row}_item configure -state disabled -relief flat } } incr row } frame $w.space grid $w.space -row $row grid rowconfigure $w $row -weight 1 grid columnconfigure $w 1 -weight 1 foreach but $pref(traceButtons) { $but configure -state disabled } set pref(traceChanges) 1 } # PrefBuildRnode -- # # Build the roles pane # # Arguments: # w - Name of frame to build it into proc PrefBuildRnode {w} { global t b grid rowconfigure $w 0 -weight 0 grid rowconfigure $w 1 -weight 0 message $w.rnmsg -text $t(roles_expl) -aspect 700 button $w.nrole -text $t(create_new_role) -command PrefCreateRole grid $w.rnmsg - -row 0 -sticky ew -pady 5 grid x $w.nrole -sticky w } # PrefValidate -- # # Check for errors in new preferences # # Arguments: # parent - Parent window proc PrefValidate {parent} { global option pref t folderWindowList tk_version set rp $pref(rolePrefix) switch $pref(lastPref) { general { set r [RatCheckListFormat $pref(opt,list_format)] if {"ok" != $r} { Popup $r $parent return "fail" } } paths { if {![regexp %p $pref(opt,print_command)]} { Popup $t(no_pp_in_print_command) $parent return "fail" } } composing { if {[catch {regexp -nocase $pref(opt,re_regexp) ""} e]} { Popup "$t(re_regexp_error): $e" $parent return "fail" } } roles,sending { if {[string compare $option(${rp}sendprog) \ $pref(opt,${rp}sendprog)] && ![file executable [lindex $pref(opt,${rp}sendprog) 0]]} { Popup $t(warning_sendprog) $parent return "fail" } } watcher_format { set r [RatCheckListFormat $pref(opt,watcher_format)] if {"ok" != $r} { Popup $r $parent return "fail" } } } return "ok" } # PrefApply -- # # Applies any changes to the preferences made in the current window. # # Arguments: # parent - Parent window proc PrefApply {parent} { global option pref t folderWindowList tk_version if {"ok" != [PrefValidate $parent]} { return } set hasChanged 0 set needRestart 0 set rp $pref(rolePrefix) switch $pref(lastPref) { appearance { if {[string compare $option(useinputmethods) \ $pref(opt,useinputmethods)] && 8.3 <= $tk_version} { tk useinputmethods $pref(opt,useinputmethods) } } roles { if {[string compare $option(${rp}signature) \ $pref(opt,${rp}signature)] && 1 == [llength [info commands RatUP_Signature]]} { Popup $t(sig_cmd_takes_precedence) $parent } if {[string compare $option(${rp}name) $pref(opt,${rp}name)]} { set option(${rp}name) $pref(opt,${rp}name) PrefSortRoles PrefPopulateRoles $pref(tree) redraw set hasChanged 1 } } roles,sending { if {$pref(opt,${rp}same_sending_prefs) != $option(${rp}same_sending_prefs)} { if {1 == $pref(opt,${rp}same_sending_prefs)} { if [CheckSameSendingPrefs $rp] { set pref(opt,${rp}same_sending_prefs) 0 return } } set hasChanged 1 foreach r $option(roles) { set pref(opt,${r},same_sending_prefs) \ $pref(opt,${rp}same_sending_prefs) } } } html { if {[string compare $option(html_proxy_host) \ $pref(opt,html_proxy_host)]} { ::http::config -proxyhost $pref(opt,html_proxy_host) if {![string length $pref(opt,html_proxy_host)]} { set pref(opt,html_proxy_port) "" } } if {[string compare $option(html_proxy_port) \ $pref(opt,html_proxy_port)]} { ::http::config -proxyport $pref(opt,html_proxy_port) } } } foreach prefs [array names pref opt,*] { set opt [string range $prefs 4 end] if {![info exists option($opt)]} { continue } if {[string compare $option($opt) $pref(opt,$opt)]} { set option($opt) $pref(opt,$opt) set hasChanged 1 if { -1 != [lsearch -exact {language charset font_size main_window_name icon_name default_folder pgp_enable override_fonts prop_norm prop_light fixed_norm watcher_font charset color_set} \ $opt]} { set needRestart 1 } } } if {$hasChanged} { switch $pref(lastPref) { general { foreach f [array names folderWindowList] { Sync $f update } } pgp { InitPgp foreach v {pgp_version pgp_path pgp_keyring} { set pref(opt,$v) $option($v) } } roles,sending { if {1 == $option(${rp}same_sending_prefs)} { foreach r $option(roles) { foreach v {sendprot smtp_hosts validate_cert sendprog sendprog_8bit smtp_user smtp_passwd} { set option(${r},$v) $option(${rp}$v) set pref(opt,${r},$v) $option(${rp}$v) } } } } } SaveOptions } if {$needRestart} { Popup $t(need_restart) $parent } foreach but $pref(traceButtons) { $but configure -state disabled } } # PrefCheck -- # # Checks if there are any unapplied changes and if there is the user is # queried if he wants to apply them. # # Arguments: # parent - Parent of window proc PrefCheck {parent} { global option pref t foreach prefs [array names pref opt,*] { set opt [string range $prefs 4 end] if {![info exists option($opt)]} { continue } if {[string compare $option($opt) $pref(opt,$opt)]} { set value [RatDialog $parent $t(unapplied_changes_title) \ $t(unapplied_changes) {} 0 $t(apply) $t(discard)] if { 0 == $value } { PrefApply $parent } else { foreach n [array names pref opt,*] { set opt [string range $n 4 end] set pref(opt,$opt) $option($opt) } } return } } } # OptionMenu -- # # Generates an option menu. The generated menu will have window name "w" # and will set the "varName" variable. The different options are # controlled by the value arguments. Each value argument is a list of # two elements. The first is the value to set "varName" to and the second # is the text to show. The menubutton will use "textVar" as the textvariable. # # Arguments: # w - Name of menubutton to create # varid - Variable to set to value # values - A list of lists which describes the values of this button proc OptionMenu {w varid values} { upvar \#0 pref(opt,$varid) var global pref menubutton $w -textvariable pref(text,$varid) -indicatoron 1 \ -relief raised -menu $w.m -pady 1 set pref(w,$varid) $w menu $w.m -tearoff 0 PrefPopulateOptionsMenu $varid $values trace variable var w "PrefTraceOptionProc $varid" } proc PrefPopulateOptionsMenu {varid values} { upvar \#0 pref(opt,$varid) var upvar \#0 pref(text,$varid) text global pref set pref(values,$varid) $values $pref(w,$varid).m delete 0 end set width 10 set text "" foreach elem $values { if {![string compare [lindex $elem 0] $var]} { set text [lindex $elem 1] } $pref(w,$varid).m add command -label [lindex $elem 1] \ -command "set pref(opt,$varid) [list [lindex $elem 0]]" if { $width < [string length [lindex $elem 1]]} { set width [string length [lindex $elem 1]] } } if {"" == $text && 0 < [llength $values]} { set text [lindex [lindex $values 0] 1] } $pref(w,$varid) configure -width $width } proc PrefTraceOptionProc {varid args} { upvar \#0 pref(opt,$varid) var upvar \#0 pref(text,$varid) text upvar \#0 pref(values,$varid) values foreach v $values { if {![string compare $var [lindex $v 0]]} { set text [lindex $v 1] return } } } proc SetupColor {w} { global t option pref tk_version b frame $w menubutton $w.mb -textvariable pref(text,color_set) \ -indicatoron 1 -relief raised -menu $w.mb.m -pady 1 set b($w) pref_color_scheme set b($w.mb) pref_color_scheme menu $w.mb.m -tearoff 0 set width 20 foreach c { {\#dde3eb black white black} {PeachPuff2 black white black} {SlateBlue1 black white black} {SteelBlue4 white LightBlue black} {SkyBlue1 black white black} {aquamarine2 black white black} {SpringGreen4 black PaleGreen black} {gray85 black gray85 black}} { set name $t([lindex $c 0]) if {![string compare $c $option(color_set)]} { set pref(text,color_set) $name } $w.mb.m add command -label $name \ -command "set pref(opt,color_set) [list $c]; \ set pref(text,color_set) [list $name]" \ -background [lindex $c 0] -foreground [lindex $c 1] if { $width < [string length $name]} { set width [string length $name] } } $w.mb configure -width $width pack $w.mb -side left } # SetupNetworkSync -- # # Setup the network synchronization # # Arguments: proc SetupNetworkSync {} { global t option setupNS # Check if window already exists set w .setns if {[winfo exists $w]} { wm deiconify $w raise $w return } # Initialize variables set setupNS(w) $w set setupNS(deferred) [lindex $option(network_sync) 0] set setupNS(disconnected) [lindex $option(network_sync) 1] set setupNS(runcmd) [lindex $option(network_sync) 2] set setupNS(cmd) [lindex $option(network_sync) 3] # Create window toplevel $w -bd 5 -class TkRat wm title $w $t(setup_netsync) checkbutton $w.cmd -variable setupNS(runcmd) -text $t(run_command) entry $w.cmdentry -textvariable setupNS(cmd) grid $w.cmd $w.cmdentry -sticky w checkbutton $w.def -variable setupNS(deferred) -text $t(send_deferred) grid $w.def - -sticky w checkbutton $w.dis -variable setupNS(disconnected) \ -text $t(sync_offline_folders) grid $w.dis - -sticky w frame $w.f button $w.f.ok -text $t(ok) -default active -command { destroy $setupNS(w) set option(network_sync) [list $setupNS(deferred) \ $setupNS(disconnected) \ $setupNS(runcmd) \ $setupNS(cmd)] unset setupNS SaveOptions } button $w.f.cancel -text $t(cancel) \ -command {destroy $setupNS(w); unset setupNS} pack $w.f.ok $w.f.cancel -side left -expand 1 grid $w.f - -sticky nsew bind $w "$w.f.cancel invoke" } # SelectFont -- # # Show font selection # # Arguments: # f - font to select # w - window to build proc SelectFont {f w} { global pref fixedNormFont t b prefFontLabel set d [ConvertFontToText $pref(opt,$f)] set prefFontLabel($f) $w.l frame $w label $w.l -text $d -font [RatCreateFont $pref(opt,$f)] -anchor w pack $w.l -side left set b($w) pref_$f set b($w.l) pref_$f button $w.e -text $t(edit)... -command "::tkrat::fontedit::edit $f $w.l $w" pack $w.e -side right -fill x } # ConvertFontToText -- # # Convert a font specification to text # # Arguments: # s - A font specification proc ConvertFontToText {s} { global t if {"components" == [lindex $s 0]} { set d [concat [lindex $s 1] [lindex $s 2]] if {"bold" == [lindex $s 3]} { set d "$d $t(bold)" } if {"roman" != [lindex $s 4]} { set d "$d $t(italic)" } if {[lindex $s 5]} { set d "$d $t(underline)" } if {[lindex $s 6]} { set d "$d $t(overstrike)" } return $d } else { return [lindex $s 1] } } # SetupRole -- # # Setup the roles buttons # # Arguments: # w - Window to build proc SetupRole {w} { global t pref option frame $w # Find max length of labels set l1 [string length $t(set_as_default)] set l2 [string length $t(delete)] if {$l1 > $l2} { set l $l1 } else { set l $l2 } # Get role id and check for enablement set id [lindex [split $pref(rolePrefix) ,] 0] if {$id == $option(default_role)} { set s disabled } else { set s normal } button $w.default -text $t(set_as_default) -pady 0 -width $l -state $s \ -command "\ set option(default_role) $id; \ $w.default configure -state disabled; \ $w.delete configure -state disabled; \ PrefPopulateRoles; \ $pref(tree) redraw; \ SaveOptions \ " button $w.delete -text $t(delete) -pady 0 -width $l -state $s \ -command "PrefDeleteRole $id" pack $w.default $w.delete -side top -anchor w -pady 5 } # PrefDeleteRole -- # # Delete a role # # Arguments: # id - role id proc PrefDeleteRole {id} { global option pref vFolderDef t set fdls [VFoldersUsesRole $id] if {"" != $fdls} { Popup "$t(cant_delete_role_used): $fdls" return } set value [RatDialog $pref(pane) $t(delete) $t(are_you_sure_delete_role) \ {} 1 $t(delete) $t(cancel)] if {1 == $value} { return } foreach v [array names option $id,*] { unset option($v) } set i [lsearch -exact $option(roles) $id] set option(roles) [lreplace $option(roles) $i $i] set pref(selected) "" foreach c [winfo children $pref(pane)] { destroy $c } PrefPopulateRoles $pref(tree) redraw SaveOptions } # PrefTraceProc -- # # Preferences tracing procedure # # Arguments: # name1, name2 - Name of variable changed # op - Operation proc PrefTraceProc {name1 name2 op} { global pref option if {!$pref(traceChanges)} {return} if {[regexp {^opt,} $name2]} { set state disabled foreach v $pref(vars,$pref(lastPref)) { if {$option($v) != $pref(opt,$v)} { set state normal break } } foreach but $pref(traceButtons) { $but configure -state $state } } } # PrefPopulateRoles -- # # Populate the roles branch in the preferences tree # # Arguments: proc PrefPopulateRoles {} { global pref option t $pref(rnode) clear foreach r $option(roles) { set name $option($r,name) if {$r == $option(default_role)} { set name "$name ($t(default))" } set node [$pref(rnode) add folder -label $name -state closed \ -id [list role $r roles]] foreach p {address sending pgp advanced} { $node add leaf -label $t($p) -id [list role $r roles,$p] } } if {"role" == [lindex $pref(selected) 0]} { $pref(tree) select $pref(selected) } } # PrefCreateRole -- # # Create a new role # # Arguments: proc PrefCreateRole {} { global pref option t foreach rid $option(roles) { lappend ids [string range $rid 1 end] } set id "r[expr {[lindex [lsort -integer $ids] end]+1}]" foreach vn [array names option $option(default_role),*] { set v [lindex [split $vn ,] 1] set option($id,$v) $option($vn) } set option($id,name) $t(new_role) lappend option(roles) $id PrefSortRoles SaveOptions PrefPopulateRoles $pref(tree) select [list role $id roles] $pref(tree) redraw PrefSelectPane [list role $id roles] } # PrefSortRoles -- # # Sort the list of roles. This function updates the option(roles) list # # Arguments: proc PrefSortRoles {} { global option set option(roles) [lsort -command PrefCmpRoles $option(roles)] } proc PrefCmpRoles {a b} { global option return [string compare $option($a,name) $option($b,name)] } # SetupDefaultSave -- # # Setup the default save folder # # Arguments: # w - Window to build proc SetupDefaultSave {w} { global t pref option b menubutton $w -textvariable pref(text,save_outgoing) -indicatoron 1 \ -relief raised -menu $w.m -pady 1 menu $w.m -postcommand "PopulateDefaultSave $w.m" if {"" == $pref(opt,$pref(rolePrefix)save_outgoing)} { SelectDefaultSave {} } else { SelectDefaultSave $pref(opt,$pref(rolePrefix)save_outgoing) } set b($w) pref_save_outgoing } proc PopulateDefaultSave {m} { global t $m delete 0 end VFolderBuildMenu $m 0 "SelectDefaultSave" 1 $m add command -label "-- $t(none) --" -command {SelectDefaultSave ""} FixMenu $m } proc SelectDefaultSave {id} { global t pref vFolderDef set pref(opt,$pref(rolePrefix)save_outgoing) $id if {"" == $id || ![info exists vFolderDef($id)]} { set pref(text,save_outgoing) "-- $t(none) --" } else { set pref(text,save_outgoing) [lindex $vFolderDef($id) 0] } } # CheckSameSendingPrefs -- # # Checks if all roles uses the same sending preferences as the ones # currently being shown. If not show a dialog and give the user the # option to abort # # Arguments: # rp - role we are currently editing proc CheckSameSendingPrefs {rp} { global option pref t set differs 0 foreach r $option(roles) { foreach v {sendprot smtp_hosts validate_cert sendprog sendprog_8bit} { if {$option(${r},$v) != $pref(opt,${rp}$v)} { set differs 1 break } } } if {1 == $differs} { return [RatDialog .pref $t(send_settings_differs) \ $t(roles_use_other_send_settings) {} \ 1 $t(continue) $t(cancel)] } else { return 0 } } # PrefSetupSignAs -- # # Draw the sign-as parts of the preference dialog. # # Arguments: # w - Window to build proc PrefSetupSignAs {w} { global pref SetupSignAsWidget $w pref(opt,$pref(rolePrefix)sign_as) } # SetupCheckDicts -- # # Setup the check dictionaries stuff # # Arguments: # w - Window to build proc SetupCheckDicts {w} { global pref b t frame $w -bd 2 scrollbar $w.scroll \ -bd 1 \ -highlightthickness 0 \ -command "$w.list yview" listbox $w.list \ -yscroll "$w.scroll set" \ -bd 1 \ -exportselection false \ -highlightthickness 0 \ -selectmode multiple button $w.mark_all -text $t(mark_all) -command "CheckDictsMarkAll $w" button $w.clear_all -text $t(clear_all) -command "CheckDictsClearAll $w" grid $w.list - - $w.scroll -sticky nsew grid x $w.mark_all $w.clear_all x grid rowconfigure $w 0 -weight 1 grid columnconfigure $w 0 -weight 1 set pref(list,auto_dicts) $w.list set pref(dictionaries_state) normal PrefPopulateCheckDicts $w.list configure -height 10 bind $w.list <> {CheckDictsSelect %W} set b($w.list) pref_auto_dicts } proc PrefPopulateCheckDicts {} { global pref tk_version if {$tk_version >= 8.4} { $pref(list,auto_dicts) configure -state normal } $pref(list,auto_dicts) delete 0 end foreach l $pref(dictionaries) { $pref(list,auto_dicts) insert end [string totitle $l] if {-1 != [lsearch -exact $pref(opt,auto_dicts) $l] || 0 == [llength $pref(opt,auto_dicts)]} { $pref(list,auto_dicts) selection set end } } if {$tk_version >= 8.4} { $pref(list,auto_dicts) configure -state $pref(dictionaries_state) } } proc CheckDictsSelect {w} { global pref set n {} if {$pref(dictionaries_state) == "normal"} { foreach s [$w curselection] { lappend n [lindex $pref(dictionaries) $s] } } set pref(opt,auto_dicts) $n } proc CheckDictsMarkAll {w} { global pref set pref(opt,auto_dicts) $pref(dictionaries) $pref(list,auto_dicts) selection set 0 end } proc CheckDictsClearAll {w} { global pref set pref(opt,auto_dicts) {} $pref(list,auto_dicts) selection clear 0 end } # SetupSpellPath -- # # Setup the spell checker path entry # # Arguments: # w - Window to build proc SetupSpellPath {w} { global pref b checkmark_img frame $w entry $w.entry -textvariable pref(opt,spell_path) label $w.status -image $checkmark_img pack $w.status -side right -fill y pack $w.entry -fill x -pady 2 set b($w.entry) pref_spell_path set pref(last,spell_path) "" trace variable pref(opt,spell_path) w [list SpellPathChanged $w.status] bind $w.entry [list UpdateSpellPath $w.status 0] bind $w.entry [list UpdateSpellPath $w.status 0] UpdateSpellPath $w.status 1 } # SpellPathChanged -- # # Called when the pref(opt,spell_path) variable has been updated # # Arguments: # lab - label argument to UpdateSpellPath # normal trace function arguments proc SpellPathChanged {lab args} { global pref if {[info exists pref(spell_path_afterid)]} { after cancel $pref(spell_path_afterid) } set pref(spell_path_afterid) [after 1000 UpdateSpellPath $lab 0] } # UpdateSpellPath -- # # Check which dictionaries the spelling-checker provides and update # the display accordingly # # Arguments: # lab - label to configure icon of # force - if true update everything even if the value has not changed proc UpdateSpellPath {lab force} { global t pref option b checkmark_img error_img # Cancel any outstanding calls if {[info exists pref(spell_path_afterid)]} { after cancel $pref(spell_path_afterid) } # Ignore if unchanged if {$pref(last,spell_path) == $pref(opt,spell_path) && !$force} { return } set old_spell_path $option(spell_path) set option(spell_path) $pref(opt,spell_path) set spell_dictionaries [list [list auto $t(auto)]] set pref(dictionaries) [lsort [rat_spellutil::get_dicts 1]] set option(spell_path) $old_spell_path set pref(last,spell_path) $pref(opt,spell_path) foreach l $pref(dictionaries) { lappend spell_dictionaries [list $l [string totitle $l]] } PrefPopulateOptionsMenu def_spell_dict $spell_dictionaries if {0 == [llength $pref(dictionaries)]} { if {"" == [rat_spellutil::get_cmd]} { set err no_spell } else { set err no_dictionaries } lappend pref(dictionaries) $t($err) set pref(dictionaries_state) disabled set b($lab) $err $lab configure -image $error_img } else { set pref(dictionaries_state) normal $lab configure -image $checkmark_img catch {unset b($lab)} } PrefPopulateCheckDicts } # SetupURLViewer -- # # Setup the url viewer entries # # Arguments: # w - Window to populate proc SetupURLViewer {w} { global pref t option b foreach var {url_behavior browser_cmd} { set pref(opt,$var) $option($var) set pref(old,$var) $pref(opt,$var) } frame $w OptionMenu $w.viewer url_viewer \ [list [list RatUP "$t(userproc): RatUP_ShowURL"] \ [list mozilla "Mozilla"] \ [list firefox "Firefox"] \ [list galeon "Galeon"] \ [list netscape "Netscape"] \ [list opera "Opera"] \ [list lynx Lynx] \ [list other $t(other)]] set b($w.viewer) pref_url_viewer trace variable pref(opt,url_viewer) w PrefUpdateURLCmd lappend pref(vars,$pref(lastPref)) browser_cmd lappend pref(vars,$pref(lastPref)) url_behavior label $w.mode_label -text $t(open_in): OptionMenu $w.mode url_behavior \ [list [list old_window $t(reuse_old_window)] \ [list new_window $t(new_window)] \ [list new_tab $t(new_tab)]] set b($w.mode) pref_url_behavior label $w.cmd_label -text $t(cmd): -anchor w entry $w.cmd -textvariable pref(opt,browser_cmd) set b($w.cmd) pref_url_viewer_cmd grid $w.viewer - -sticky w grid $w.mode_label $w.mode -sticky w grid $w.cmd_label $w.cmd -sticky we grid columnconfigure $w 1 -weight 1 } # PrefUpdateURLCmd -- # # Trace function for url_viewer # # Arguments: # Standard trace function arguments proc PrefUpdateURLCmd {args} { global pref switch $pref(opt,url_viewer) { "RatUP" {set pref(opt,browser_cmd) ""} "mozilla" {set pref(opt,browser_cmd) "mozilla"} "firefox" {set pref(opt,browser_cmd) "firefox"} "galeon" {set pref(opt,browser_cmd) "galeon"} "netscape" {set pref(opt,browser_cmd) "netscape"} "opera" {set pref(opt,browser_cmd) "opera"} "lynx" {set pref(opt,browser_cmd) \ {xterm -T "Lynx:%u" +sb -e lynx "%u"}} "other" {set pref(opt,browser_cmd) "other_browser %u"} } } # ValidateInt -- # # Check that the goiven command is an integer in the given interval # # Arguments: # value - Value to check # min - The smallest valid value # max - The largest valid value proc ValidateInt {value min max} { if {![string is integer $value] || $value < $min || $value > $max} { return 0 } else { return 1 } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/print.tcl000066400000000000000000000314701137544547100207760ustar00rootroot00000000000000# print.tcl -- # # Handles printing of a message # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. # Print -- # # Shows the print dialog # # Arguments: # handler - Handler of the corresponding folder command # which - Which messages that should be printed "group" or "selected" proc Print {handler which} { global option idCnt t b set msgs [GetMsgSet $handler $which] if {0 == [llength $msgs]} { return } # Create identifier set id p[incr idCnt] upvar \#0 $id hd set w .$id set hd(w) $w set hd(oldfocus) [focus] # Create toplevel toplevel $w -class TkRat wm title $w $t(print_setup) # Print setup button button $w.ps -text $t(print_setup)... -command PrintSetup -pady 1 grid x $w.ps -sticky e -pady 5 -padx 5 set b($w.ps) print_setup # What to print label $w.label -text $t(what_to_print): -width 25 -anchor w grid $w.label - -sticky w -padx 5 frame $w.f -relief ridge -bd 2 grid $w.f - -sticky nsew -padx 5 # Header set label $w.f.hs_label -text $t(headers): -anchor e set m $w.f.hs_menu.m menubutton $w.f.hs_menu -indicatoron 1 -relief raised -menu $m -pady 1 \ -textvariable ${id}(header_set) -width 12 menu $m -tearoff 0 foreach i {none selected all} { $m add command -label $t($i) \ -command "set ${id}(header_set) $t($i); set ${id}(hs) $i" } set hd(hs) $option(show_header) set hd(header_set) $t($hd(hs)) grid $w.f.hs_label $w.f.hs_menu -sticky w set b($w.f.hs_menu) print_headers # Parts of message (if any) if {1 < [llength $msgs]} { checkbutton $w.f.attachments \ -variable ${id}(attachments) \ -text $t(print_attachments) grid $w.f.attachments - -sticky w set b($w.f.attachments) print_attachments } else { set hd(state) body set hd(bodys) [PrintAddBodyparts $id $w.f [$msgs body] ""] } # Print and cancel buttons OkButtons $w $t(print) $t(cancel) "Print2 $id" grid $w.buttons - -sticky nsew -pady 5 # Bindings wm protocol $w WM_DELETE_WINDOW "Print2 $id 0" bind $w "Print2 $id 1" ::tkrat::winctl::SetGeometry print $w focus $w set hd(msgs) $msgs } # PrintAddBodyparts -- # # Add bodyparts to list # # Arguments: # handler - Handler identifying the print window # w - Window to add them to # body - Bodypart to add # indent - Indention to add proc PrintAddBodyparts {handler w body indent} { upvar \#0 $handler hd global t b set type [string tolower [$body type]] if {"multipart" == [lindex $type 0]} { set bd {} foreach c [$body children] { set b2 [PrintAddBodyparts $handler $w $c " $indent"] if {[llength $bd]} { set bd [concat $bd $b2] } else { set bd $b2 } } return $bd } if {"body" == $hd(state)} { set indent "" set hd($body) 1 } if {"" == [set desc [string range [$body description] 0 40]]} { if {"body" == $hd(state)} { if {"text" == [lindex $type 0]} { set desc "$t(body): [lindex $type 0]/[lindex $type 1]" } else { set desc "[lindex $type 0]/[lindex $type 1]" } set hd(state) attachment } else { set desc "$t(attachment): [lindex $type 0]/[lindex $type 1]" } } set hd($body) 1 checkbutton $w.b$body -variable ${handler}($body) -text "$indent$desc" grid $w.b$body - -sticky w set b($w.b$body) print_bodypart return $body } # Print2 -- # # Second stage of print functions, the user has decided... # # Arguments: # handler - Handler identifying the print window # print - True if we should print proc Print2 {handler print} { upvar \#0 $handler hd global option b if {$print} { set children [winfo children .] foreach c $children { blt_busy hold $c } update idletasks if {1 < [llength $hd(msgs)]} { foreach m $hd(msgs) { set hd(state) body set hd(bodys) {} PrintGetWhich $handler [$m body] DoPrintMsg $hd(hs) $m $hd(bodys) } } else { set bodys {} foreach bd $hd(bodys) { if {$hd($bd)} { lappend bodys $bd } } DoPrintMsg $hd(hs) $hd(msgs) $bodys } foreach c $children { blt_busy release $c } } foreach bn [array names b $hd(w).*] {unset b($bn)} catch {focus $hd(oldfocus)} ::tkrat::winctl::RecordGeometry print $hd(w) destroy $hd(w) unset hd } # PrintGetWhich -- # # Gets which boyparts to print, either just the first text-bodypart or # all non-multiparts. # # Arguments: # handler - Handler identifying the print window # body - Current bodypart to maybe include proc PrintGetWhich {handler body} { upvar \#0 $handler hd set type [string tolower [lindex [$body type] 0]] if {"multipart" == $type} { foreach c [$body children] { PrintGetWhich $handler $c } } else { if {"text" == $type && "body" == $hd(state)} { lappend hd(bodys) $body set hd(state) attachment } elseif {$hd(attachments)} { lappend hd(bodys) $body } } } # DoPrintMsg -- # # Actually do the printing of a message # # Arguments: # hs - Set of headers to print # msg - Message to print # bodys - Bodyparts to print proc DoPrintMsg {hs msg bodys} { global option rat_tmp if {"printer" == $option(print_dest)} { set fileName $rat_tmp/rat.[RatGenId] } else { set fileName $option(print_file) } catch { set tmpFH [open $fileName w] if {$option(print_pretty)} { RatPrettyPrintMsg $tmpFH $hs $msg $bodys } else { PlainPrintMsg $tmpFH $hs $msg $bodys } close $tmpFH if {"printer" == $option(print_dest)} { ExecPrintCommand $fileName } } err if {$err != ""} { Popup $err } } # PlainPrintMsg -- # # Do the simple printing of a message # # Arguments: # channel - Channel to print to # hs - Set of headers to print # msg - Message to print # bodys - Bodyparts to print proc PlainPrintMsg {channel hs msg bodys} { global option switch $hs { all { foreach h [$msg headers] { puts $channel "[lindex $h 0]: [lindex $h 1]" } puts $channel "" } selected { foreach h [$msg headers] { set header([string tolower [lindex $h 0]]) [lindex $h 1] } foreach f $option(show_header_selection) { set n [string tolower $f] if {[info exists header($n)]} { puts $channel "$f: $header($n)" } } unset header puts $channel "" } default { } } set first 1 foreach b $bodys { if {$first} { set first 0 } else { puts $channel "_______________________________________________________________________________" } $b saveData $channel 0 1 } } # ExecPrintCommand -- # # Actually does the printing # # Arguments: # fileName - The name of the file to print proc ExecPrintCommand {fileName} { global option idCnt env # Create identifier set id print[incr idCnt] upvar ${id}(fileName) ifn set ifn $fileName set cmd $option(print_command) regsub "%p" $cmd $option(print_printer) cmd if { 0 == [regsub "%s" $cmd $fileName cmd]} { set cmd "cat $fileName | $cmd" } regsub {^~/} $cmd $env(HOME)/ cmd regsub -all {[ ]~/} $cmd " $env(HOME)/" cmd RatBgExec ${id}(exitStatus) $cmd uplevel #0 "trace variable ${id}(exitStatus) w PrintDone" } # PrintDone -- # # Is called by the trace callback when the print command is done. It is # meant to do any cleaning up. # # Arguments: # name1, name2 - Variable specifiers # op - Operation proc PrintDone {name1 name2 op} { upvar \#0 $name1 pr file delete -force -- $pr(fileName) & unset pr } # PrintSetup -- # # Shows the print setup dialog # # Arguments: proc PrintSetup {} { global t option b set w .ps if {[winfo exists $w]} { wm deiconify $w raise $w return } set id ps upvar \#0 $id hd set hd(w) $w set hd(pretty) $option(print_pretty) set hd(dest) $option(print_dest) set hd(printer) $option(print_printer) set hd(file) $option(print_file) set hd(papersize) $option(print_papersize) set hd(orientation) $option(print_orientation) set hd(fontfamily) $option(print_fontfamily) set hd(ori) $t($hd(orientation)) set hd(fontsize) $option(print_fontsize) set hd(resolution) $option(print_resolution) toplevel $w -class TkRat wm title $w $t(print_setup) # Destination label $w.dest -text $t(destination): -anchor w frame $w.d -relief ridge -bd 2 radiobutton $w.d.printer -text $t(printer) -variable ${id}(dest) \ -value printer -anchor w -command "PrintSetDest $id" entry $w.d.pentry -width 15 -textvariable ${id}(printer) radiobutton $w.d.file -text $t(file) -variable ${id}(dest) \ -value file -anchor w -command "PrintSetDest $id" entry $w.d.fentry -width 15 -textvariable ${id}(file) button $w.d.browse -text $t(browse)... -pady 1 \ -command "Browse $w ${id}(file) any" grid $w.d.printer $w.d.pentry -sticky ew grid $w.d.file $w.d.fentry $w.d.browse -sticky ew set b($w.d.printer) dest_printer set b($w.d.pentry) dest_printer set b($w.d.file) dest_file set b($w.d.fentry) dest_file set b($w.d.browse) file_browse set hd(dest_printer) $w.d.pentry set hd(dest_file) $w.d.fentry PrintSetDest $id # Mode frame $w.m label $w.m.l -text $t(mode): -anchor e radiobutton $w.m.plain -text $t(plain_text) -variable ${id}(pretty) \ -value 0 -command "rat_ed::enabledisable 0 $w.p" -anchor w radiobutton $w.m.pretty -text $t(pretty_ps) -variable ${id}(pretty) \ -value 1 -command "rat_ed::enabledisable 1 $w.p" -anchor w grid $w.m.l $w.m.plain -sticky we grid x $w.m.pretty -sticky we grid columnconfigure $w.m 1 -weight 1 set b($w.m.plain) plain_text set b($w.m.pretty) pretty_ps frame $w.p -relief ridge -bd 2 label $w.p.size -text $t(paper_size): -anchor e set m $w.p.msize.m menubutton $w.p.msize -textvariable ${id}(papersize) -menu $m -pady 1 \ -relief raised -width 10 menu $m foreach s $option(print_papersizes) { set n [lindex $s 0] $m add command -label $n -command "set ${id}(papersize) $n" } grid $w.p.size $w.p.msize - -sticky ew set b($w.p.msize) paper_size label $w.p.ori -text $t(orientation): -anchor e set m $w.p.mori.m menubutton $w.p.mori -textvariable ${id}(ori) -menu $m -pady 1 \ -relief raised -width 10 menu $m foreach o {portrait landscape} { $m add command -label $t($o) \ -command "set ${id}(ori) $t($o); set ${id}(orientation) $o" } grid $w.p.ori $w.p.mori - -sticky ew set b($w.p.mori) orientation label $w.p.font -text $t(font): -anchor e set m $w.p.mfont.m menubutton $w.p.mfont -textvariable ${id}(fontfamily) -menu $m -pady 1 \ -relief raised -width 15 menu $m foreach f {Times Helvetica Courier} { $m add command -label $f -command "set ${id}(fontfamily) $f" } grid $w.p.font $w.p.mfont - -sticky ew set b($w.p.mfont) print_font label $w.p.fontsize -text $t(font_size): -anchor e entry $w.p.efsize -width 5 -textvariable ${id}(fontsize) label $w.p.funit -text $t(points) -anchor w grid $w.p.fontsize $w.p.efsize $w.p.funit -sticky ew set b($w.p.efsize) print_fontsize label $w.p.res -text $t(pic_res): -anchor e entry $w.p.eres -width 5 -textvariable ${id}(resolution) label $w.p.runit -text $t(dpi) -anchor w grid $w.p.res $w.p.eres $w.p.runit -sticky ew set b($w.p.eres) print_res grid columnconfigure $w.p 3 -weight 1 # Buttons OkButtons $w $t(apply) $t(cancel) "PrintSetup2 $id" pack $w.dest \ $w.d \ $w.m \ $w.p \ $w.buttons -side top -expand 1 -fill both ::tkrat::winctl::SetGeometry printSetup $w bind $w "PrintSetup2 $id 1" rat_ed::enabledisable $hd(pretty) $w.p wm protocol $w WM_DELETE_WINDOW "PrintSetup2 $id 0" } # PrintSetDest -- # # Enable/disable widgets according to destination radiobutton value # # Arguments: # handler - Handler for the print setup window proc PrintSetDest {handler} { upvar \#0 $handler hd if {"printer" == $hd(dest)} { rat_ed::enable $hd(dest_printer) rat_ed::disable $hd(dest_file) } else { rat_ed::disable $hd(dest_printer) rat_ed::enable $hd(dest_file) } } # PrintSetup2 -- # # Called when the print setup window is done # # Arguments: # handler - Handler identifying the print setup window # apply - Boolean stating if we should apply the settings or not proc PrintSetup2 {handler apply} { upvar \#0 $handler hd global option if {$apply} { set changed 0 foreach v {dest printer file pretty papersize fontfamily fontsize resolution orientation} { if {$hd($v) != $option(print_$v)} { set option(print_$v) $hd($v) incr changed } } if {$changed} { SaveOptions } } ::tkrat::winctl::RecordGeometry printSetup $hd(w) wm withdraw $hd(w) } tkrat_2.2cvs20100105-dfsg.orig/tkrat/show.tcl000066400000000000000000001413341137544547100206230ustar00rootroot00000000000000# show.tcl -- # # This file contains code which handles the actual displaying of a message # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # A list of types we can show set GoodTypes [list text/plain message/rfc822 image/gif image/ppm image/pgm] set ImageTypes [list gif ppm pgm] # A list of parameters to show for different types set showParams(text/plain) {charset} set showParams(message/external-body) {access-type site server name directory} # A list of address headers set showAddrHdr {from to cc bcc reply_to Return_Path} # handlers for different bodyparts set mimeHandler(text/plain) { ShowTextPlain $handler $body $msg} set mimeHandler(text/enriched) { ShowTextEnriched $handler $body $msg} set mimeHandler(text) { ShowTextOther $handler $body $msg} set mimeHandler(multipart/alternative) { ShowMultiAlt $handler $body $msg } set mimeHandler(multipart/encrypted) { ShowMultiEnc $handler $body $msg } set mimeHandler(multipart/related) { ShowMultiRelated $handler $body $msg} set mimeHandler(multipart) { ShowMultiMixed $handler $body $msg } set mimeHandler(message/rfc822) { ShowMessage 0 $handler \ [$body message] } set mimeHandler(image) { ShowImage $handler $body \ [lindex $type 1]} set mimeHandler(application/pgp-keys) { ShowPGPKeys $handler $body } set mimeHandler(application/pgp) { ShowTextPlain $handler $body $msg} set mimeHandler(application/ms-tnef) { ShowMSTnef $handler $body } if {![catch {package require Img} err]} { lappend GoodTypes image/jpeg image/tiff image/bmp image/xpm image/png \ image/pjpeg image/x-portable-bitmap image/x-portable-graymap \ image/x-portable-pixmap image/x-png image/x-bmp lappend ImageTypes jpeg tiff bmp xpm png pjpeg x-png } if {![catch {package require Tkhtml 3.0} err]} { lappend GoodTypes text/html set mimeHandler(text/html) {RatBusy {ShowTextHtml3 $handler $body $msg}} } elseif {![catch {package require Tkhtml 2.0} err]} { lappend GoodTypes text/html set mimeHandler(text/html) {RatBusy {ShowTextHtml2 $handler $body $msg}} } # ShowNothing -- # # Clear the show window # # Arguments: # handler - The handler which identifies the show text widget proc ShowNothing handler { upvar \#0 $handler fh $handler configure -state normal $handler delete 0.0 end $handler configure -state disabled set fh(width_adjust) {} } # Show -- # # Shows the given message in the show portion of a folder window # # Arguments: # handler - The handler which identifies the show text widget # msg - Message to show # browse - True if we should use browse mode showing proc Show {handler msg browse} { global option b fixedBoldFont upvar \#0 $handler fh \ msgInfo_$msg msgInfo if {[info exists fh(current_msg)]} { upvar \#0 msgInfo_$fh(current_msg) oldMsgInfo set oldMsgInfo(scrollpos) [lindex [$handler yview] 0] } set fh(current_msg) $msg set fh(sigstatus) pgp_none set fh(pgpOutput) "" set fh(browse) $browse set fh(toplevel) [winfo toplevel $handler] set fh(width_adjust) {} # Enable updates & clear data $handler configure -state normal $handler delete 0.0 end eval "$handler tag delete [$handler tag names]" # Reset the tags to a known status $handler tag configure HeaderName -font $fixedBoldFont $handler tag configure Center -justify center -spacing1 5m -spacing3 5m $handler tag configure CenterNoSpacing -justify center if { 4 < [winfo cells $handler]} { $handler tag configure URL -foreground $option(url_color) -underline 1 $handler tag configure Found -background #ff8000 } else { $handler tag configure URL -underline 1 $handler tag configure Found -borderwidth 2 -relief raised } $handler tag bind URL "$handler config -cursor hand2" $handler tag bind URL "$handler config -cursor xterm" # Delete old subwindows foreach slave [winfo children $handler] { destroy $slave } # Do other misc cleanup if {[info exists fh(show_cleanup_cmds)]} { foreach clear $fh(show_cleanup_cmds) { eval $clear } } set fh(show_cleanup_cmds) {} foreach n [array names fh charset_*] { unset fh($n) } foreach bn [array names b $handler.*] {unset b($bn)} foreach bn [array names b $fh(struct_menu)*] {unset b($bn)} bind $handler {ResizeBodyText %W %w} set width [expr [winfo width $fh(text_frame)] - \ [winfo width $fh(text_yscroll)]] set fh(width) $width # Show the message if {[catch "ShowMessage 1 $handler $msg" errmsg]} { Popup $errmsg $fh(toplevel) } set s [lsearch -exact [pack slaves $fh(text_frame)] $fh(text_xscroll)] if {$fh(width) > $width} { if {$s == -1} { pack $fh(text_xscroll) -side bottom -fill x } } else { if {$s != -1} { pack forget $fh(text_xscroll) } } BuildStructMenu $handler $msg # Prepare for URL searching $handler mark set searched 1.0 # Don't allow the user to change this $handler configure -state disabled # Scroll to last position if any if {[info exists msgInfo(scrollpos)]} { $handler yview moveto $msgInfo(scrollpos) } # Return the signature status set msgInfo(pgp,signed_parts) $fh(signed_parts) return [list $fh(sigstatus) $fh(pgpOutput)] } # InsertHeader -- # # Inserts a header row into the text widget. # # Arguments: # gtag - Tag to add to text elements # w - Text widget to insert into # header - List of header rows proc InsertHeader {gtag w header {width 1}} { global t showAddrHdr upvar \#0 $w fh set hn [lindex $header 0] set hni [string map {- _} [string tolower $hn]] if {[info exists t($hni)]} { set hn $t($hni) } $w insert insert [format "%${width}s: " $hn] "HeaderName $gtag" if {-1 == [lsearch -exact $showAddrHdr $hni]} { $w insert insert "[lindex $header 1]\n" $gtag } else { set first 1 foreach a [RatSplitAdr [lindex $header 1]] { if {0 == $first} { $w insert insert ",\n[format "%${width}s " ""]" $gtag } $w insert insert "[string trim $a]" $gtag set first 0 } $w insert insert "\n" $gtag } } # ShowMessage -- # # Inserts a message entity at the end. This is done by first inserting the # selected headers and then the body via ShowBody. # # Arguments: # first - Indicates if this is the top message or an embedded message # handler - The handler which identifies the show text widget # msg - Message to show proc ShowMessage {first handler msg} { global option t idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo set tag t[incr idCnt] if {![info exists msgInfo(show,$msg)]} { set msgInfo(show,$msg) 1 } if {0 == $first} { $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $msg\]" } else { $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y" set fh(signed_parts) {} } if {![string length $msg]} { $handler insert insert "\[$t(empty_message)\]" $tag return } # Add a newline if this isn't the first message if {0 == $first} { $handler insert insert "\n" $tag } switch $fh(show_header) { all { foreach h [$msg headers] { InsertHeader $tag $handler $h } } selected { foreach h [$msg headers] { lappend header([string tolower [lindex $h 0]]) [lindex $h 1] } set length 5 foreach f $option(show_header_selection) { if { $length < [string length $f]} { set length [string length $f] } } foreach f $option(show_header_selection) { set n [string tolower $f] if {[info exists header($n)]} { foreach h $header($n) { InsertHeader $tag $handler [list $f $h] $length } } } } default { } } if {$fh(browse)} { if {![info exists msgInfo(browse)]} { if {1 == $first} { set msgInfo(browse) $fh(browse) } else { set msgInfo(browse) 0 } } } else { set msgInfo(browse) 0 } if {$msgInfo(browse)} { button $handler.download -text $t(show_body) \ -command "Show $handler $msg 0" -cursor top_left_arrow $handler window create end -window $handler.download -padx 20 -pady 20 set fh(sigstatus) pgp_none return } # Insert the body set body [$msg body] if {![info exists msgInfo(show,$body)]} { set msgInfo(show,$body) 1 } if {$msgInfo(show,$msg)} { $handler insert insert "\n" if {![info exists msgInfo(show,$body)]} { set msgInfo(show,$body) 1 } } else { set msgInfo(show,$body) 0 } ShowBody $handler $body $msg if {1 == $first && ![string compare pgp_good $fh(sigstatus)] && [string compare pgp_good [$body sigstatus]]} { set fh(sigstatus) pgp_part } } # ShowBody -- # # Inserts a bodypart entity at the end, this is really just a switch which # calls upon the commands which does the actual work. # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowBody {handler body msg} { global option mimeHandler idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo if {!$msgInfo(show,$body)} { return } # Update signature status if {[string compare pgp_none [set sigstatus [$body sigstatus]]]} { set fh(sigstatus) $sigstatus lappend fh(signed_parts) $body if {[string length $fh(pgpOutput)]} { set fh(pgpOutput) "$fh(pgpOutput)\n\n[$body getPGPOutput]" } else { set fh(pgpOutput) [$body getPGPOutput] } } # We will need this later on. set type [string tolower [$body type]] # Switch for subroutines which does the actual drawing set tp [join $type /] if {[string equal $tp application/octet-stream]} { set type [GetHandlerFromExtension [$body filename]] set tp [join $type /] } if {[info exists mimeHandler($tp)]} { set mh $mimeHandler($tp) } elseif {[info exists mimeHandler([lindex $type 0])]} { set mh $mimeHandler([lindex $type 0]) } else { set mh { ShowDefault $handler $body } } eval $mh } # ShowTextPlain -- # # Show text/plain entities, should handle different fonts... # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowTextPlain {handler body msg} { global option b charsetName propNormFont idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo set tag t[incr idCnt] $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" if {[$body isGoodCharset]} { $handler insert insert [$body data false] $tag } else { global t set w $handler.w[incr idCnt] frame $w -relief raised -bd 2 -cursor top_left_arrow if {[string length [$body description]]} { label $w.desc -text "$t(description): [$body description]" \ -font $propNormFont pack $w.desc -side top -anchor nw } set charset [$body parameter charset] label $w.type \ -text "$t(here_is_text) '$charset' $t(which_cant_be_shown)" label $w.modlab -text $t(interpret_as): menubutton $w.mode -menu $w.mode.m -indicatoron 1 -relief raised \ -textvariable ${handler}(mode,$body) set b($w.mode) howto_display button $w.save -text $t(save_to_file) \ -command "SaveBody $body $fh(toplevel)" set b($w.save) save_bodypart pack $w.type -side top -anchor w pack $w.modlab $w.mode -side left pack $w.save -side left -padx 20 set l 0 menu $w.mode.m -tearoff 0 foreach c $option(charsets) { if {0 < [string length $charsetName($c)]} { set name "$c ($charsetName($c))" } else { set name $c } $w.mode.m add command \ -label $name \ -command "ShowTextCharset $body $msg $handler $handler \ $tag $c" } if {![info exists msgInfo(show,$body,how)]} { set msgInfo(show,$body,how) $option(charset) } set fh(mode,$body) $msgInfo(show,$body,how) $handler window create insert -window $w -pady 5 foreach win [concat $w [pack slaves $w]] { bind $win <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" } $handler insert insert "\n" $tag $handler mark set ${body}_s insert $handler mark set ${body}_e insert $handler mark gravity ${body}_s left ShowTextCharset $body $msg $handler $handler $tag \ $msgInfo(show,$body,how) } } # ShowTextCharset -- # # Shows text in a nonstandard character set # # Arguments: # body - The bodypart to show # msg - The message name # hd - The array to use for global variables # w - The handler which identifies the show text widget # tag - The tag this entity should have # charset - What to assume proc ShowTextCharset {body msg hd w tag charset} { upvar \#0 $hd fh \ msgInfo_$msg msgInfo global t set oldState [$w cget -state] $w configure -state normal $w mark set oldInsert insert $w delete ${body}_s ${body}_e $w mark set insert ${body}_s $w mark gravity ${body}_e right set fh(mode,$body) $charset set msgInfo(show,$body,how) $charset $w insert insert [$body data false $charset] $tag $w mark set insert oldInsert $w configure -state $oldState $w mark gravity ${body}_e left } # ShowTextEnriched -- # # Show text/enriched entities # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowTextEnriched {handler body msg} { global option b charsetName propNormFont idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo set tag t[incr idCnt] $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" if {"::rat_enriched::show" == [info commands ::rat_enriched::show]} { rat_enriched::show $handler [$body data false] $tag } else { $handler insert insert [$body data false] $tag } } # ShowTextOther -- # # Show text parts other that text/plain. # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowTextOther {handler body msg} { global t b fixedBoldFont idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo set type [$body type] set typename [string tolower [lindex $type 0]/[lindex $type 1]] set mailcap [$body findShowCommand] set width 0 set w $handler.w[incr idCnt] frame $w -relief raised -bd 2 -cursor top_left_arrow if {[string length [lindex $mailcap 4]]} { if {![catch {image create bitmap $body.icon \ -file [lindex $mailcap 4]}]} { label $w.icon -image $body.icon pack $w.icon -side left } } text $w.text -relief flat -cursor top_left_arrow \ -background [$w cget -background] $w.text tag configure Bold -font $fixedBoldFont if {[string length [$body description]]} { set width [string length "$t(description): [$body description]"] $w.text insert insert "$t(description): " Bold [$body description]\n } if {[string length [lindex $mailcap 3]]} { set l [string length "$t(type_description): [lindex $mailcap 3]"] if { $l > $width } { set width $l } $w.text insert insert "$t(type_description): " Bold \ [lindex $mailcap 3]\n } set typestring [string tolower [lindex $type 0]/[lindex $type 1]] set size [RatMangleNumber [$body size]] set l [string length "$t(here_is): $typestring ($size bytes)"] if { $l > $width } { set width $l } $w.text insert insert "$t(here_is): " Bold "$typestring ($size bytes)" {} $w.text configure \ -height [lindex [split [$w.text index end-1c] .] 0] \ -width $width \ -state disabled pack $w.text -side top -anchor w if {[string length [lindex $mailcap 0]]} { button $w.view_ext -text $t(view) \ -command "RunMailcap $body [list $mailcap]" pack $w.view_ext -side left -padx 10 } if {![info exists msgInfo(show,$body,tp)]} { if {[string length [lindex $mailcap 0]]} { set msgInfo(show,$body,tp) 0 } else { set msgInfo(show,$body,tp) 1 } } checkbutton $w.tp -text $t(view_as_text) \ -relief raised \ -variable msgInfo_${msg}(show,$body,tp) \ -command "ShowTextOtherDo $handler $body $msg" \ -padx 4 -pady 4 button $w.save -text $t(save_to_file) \ -command "SaveBody $body $fh(toplevel)" button $w.view_int -text $t(view_source) -command "ShowSource $body" pack $w.tp $w.save $w.view_int -side left -padx 5 set b($w.tp) view_as_text set b($w.save) save_bodypart set b($w.view_int) view_source $handler window create insert -window $w -padx 5 -pady 5 foreach win [concat $w [pack slaves $w]] { bind $win <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" } $handler insert insert "\n" $handler mark set ${body}_s insert $handler mark set ${body}_e insert $handler mark gravity ${body}_s left ShowTextOtherDo $handler $body $msg } # ShowTextOtherDo -- # # Subfunction of ShowTextOther shows not/shows the text as text/plain # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowTextOtherDo {handler body msg} { upvar \#0 $handler fh \ msgInfo_$msg msgInfo set oldState [$handler cget -state] $handler configure -state normal $handler delete ${body}_s ${body}_e $handler mark set insert ${body}_s $handler mark gravity ${body}_e right if {$msgInfo(show,$body,tp)} { ShowTextPlain $handler $body $msg } $handler mark set insert ${body}_e $handler mark gravity ${body}_e left $handler configure -state $oldState } # ShowMultiAlt -- # # Show a multipart/alternative object # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowMultiAlt {handler body msg} { global GoodTypes option upvar \#0 $handler fh \ msgInfo_$msg msgInfo if {![info exists msgInfo(alternated,$body)]} { set found 0 set msgInfo(alternated,$body) 1 set children [$body children] foreach c $children { set msgInfo(show,$c) 0 } for {set i [expr {[llength $children]-1}]} {$i >= 0} {incr i -1} { set child [lindex $children $i] set tc $child set typelist [$child type] while {1 == [regexp -nocase multipart [lindex $typelist 0]]} { set tc [lindex [$tc children] 0] set typelist [$tc type] } set type [string tolower [lindex $typelist 0]/[lindex $typelist 1]] if { -1 != [lsearch $GoodTypes $type] } { if {$i > 0 && (($type == "text/html" && $option(prefer_other_over_html)) || $type == "text/plain")} { set pc [lindex $children [expr $i-1]] set ptl [$pc type] set pt [string tolower [lindex $ptl 0]/[lindex $ptl 1]] if {-1 != [lsearch $GoodTypes $pt]} { set msgInfo(show,$pc) 1 set found 1 break } } set msgInfo(show,$child) 1 set found 1 break } } if {!$found} { foreach child [$body children] { set msgInfo(show,$child) 1 } } } foreach child [$body children] { ShowBody $handler $child $msg } } # ShowMultiRelated -- # # Show a multipart/related object # This is a very simple implementation which only shows the first child. # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowMultiRelated {handler body msg} { upvar \#0 $handler fh \ msgInfo_$msg msgInfo global related set children [$body children] if {[info exists related]} { unset related } foreach c [lrange $children 1 end] { set id [string trim [$c id] "<>"] if {"" != $id} { set related($id) $c } set msgInfo(show,$c) 0 } set typelist [[lindex $children 0] type] set type [string tolower [lindex $typelist 0]/[lindex $typelist 1]] if {"text/html" == $type} { set msgInfo(show,[lindex $children 0]) 1 } else { foreach c $children { set msgInfo(show,$c) 1 } } foreach child [$body children] { ShowBody $handler $child $msg } } # ShowMultiEnc -- # # Show a multipart/encrypted object (which we failed to decrypt) # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowMultiEnc {handler body msg} { upvar \#0 $handler fh global idCnt set children [$body children] if {[llength $children] != 2} { return } set b [lindex $children 1] set tag t[incr idCnt] $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" set data [string map [list "\r" ""] [$b data false]] $handler insert insert $data $tag } # ShowMultiMixed -- # # Show a multipart/mixed object # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # msg - The message name proc ShowMultiMixed {handler body msg} { global option t idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo set first 1 foreach child [$body children] { # Add horizontal lines between the different parts if {$first} { set first 0 } else { if {[$handler compare insert > "insert linestart"]} { $handler insert insert "\n" } frame $handler.f[incr idCnt] -width 12c -height 2 \ -relief sunken -bd 2 $handler window create insert -window $handler.f$idCnt -pady 10 $handler insert insert "\n" } # Show the bodypart if {![info exists msgInfo(show,$child)]} { set msgInfo(show,$child) $msgInfo(show,$msg) } ShowBody $handler $child $msg } } # ShowImage -- # # Show image/* entities # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # subtype - The type of image proc ShowImage {handler body subtype} { global option rat_tmp idCnt global ImageTypes upvar \#0 $handler fh if {[lsearch $ImageTypes $subtype] != "-1"} { set tag t[incr idCnt] set filename $rat_tmp/rat.[RatGenId] set fid [open $filename w] fconfigure $fid -encoding binary $body saveData $fid 0 0 close $fid if {[catch {image create photo -file $filename} img]} { Popup $img $fh(toplevel) file delete -force -- $filename ShowDefault $handler $body return } file delete -force -- $filename set imgheight [image height $img] set imgwidth [image width $img] set frame [frame $handler.f[incr idCnt] -cursor left_ptr] set c [canvas $frame.canvas -width $imgwidth \ -height $imgheight \ -xscrollcommand [list $frame.xscroll set] \ -yscrollcommand [list $frame.yscroll set] \ -scrollregion [list 0 0 $imgwidth $imgheight]] set yscroll [scrollbar $frame.yscroll -command [list $c yview]] set xscroll [scrollbar $frame.xscroll -command [list $c xview] \ -orient horizontal] grid $c -row 0 -column 0 -sticky news if {$imgheight > [winfo height $handler] && [info tclversion] < 8.5} { $frame configure -height [winfo height $handler] grid $yscroll -row 0 -column 1 -sticky ns } else { $frame configure -height $imgheight } if {$imgwidth > $fh(width)} { if {[info tclversion] < 8.5} { $frame configure -width $fh(width) grid $xscroll -row 1 -column 0 -sticky ew } else { set fh(width) $imgwidth $frame configure -width $imgwidth } } else { $frame configure -width $imgwidth } grid columnconfigure $frame 0 -weight 1 grid rowconfigure $frame 0 -weight 1 grid propagate $frame 0 $c create image 0 0 -image $img -anchor nw $handler insert insert "\n" $tag ;# virtual spacing1 $handler insert insert " " "CenterNoSpacing $tag" $handler window create insert -window $frame $handler insert insert "\n\n" $tag ;# NL plus virtual spacing3 lappend fh(show_cleanup_cmds) "image delete $img" $handler tag bind $tag <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" bind $c <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" # Avoid processing new events (like selecting new message) update idletasks if {[info tclversion] < 8.5} { set binding [list ResizeFrame $frame $handler \ $imgheight $imgwidth $xscroll $yscroll] if {[string first $binding [bind $handler ]] == -1} { bind $handler +$binding } ResizeFrame $frame $handler $imgheight $imgwidth $xscroll $yscroll } } else { ShowDefault $handler $body } } # ShowPGPKeys -- # # Handles embedded pgp keys # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show proc ShowPGPKeys {handler body} { global t b idCnt upvar \#0 $handler fh set mailcap [$body findShowCommand] set w $handler.w[incr idCnt] frame $w -relief raised -bd 2 -cursor top_left_arrow if {[string length [lindex $mailcap 4]]} { if {![catch {image create bitmap $body.icon \ -file [lindex $mailcap 4]}]} { label $w.icon -image $body.icon pack $w.icon -side left } } label $w.label -relief flat -text $t(embedded_pgp_keys) pack $w.label -side top -anchor w button $w.add -text $t(add_to_keyring) \ -command "RatPGP add \[$body data 0\]" button $w.save -text $t(save_to_file) \ -command "SaveBody $body $fh(toplevel)" button $w.view_int -text $t(view_source) -command "ShowSource $body" set b($w.add) add_to_keyring set b($w.save) save_bodypart set b($w.view_int) view_source pack $w.add $w.save $w.view_int -side left -padx 5 $handler window create insert -window $w -padx 5 -pady 5 foreach win [concat $w [pack slaves $w]] { bind $win <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" } $handler insert insert "\n" } # ShowDefault -- # # The default type shower, just iserts a marker that here is an object # of this type. # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show # desc - Extra description proc ShowDefault {handler body {desc {}}} { global t fixedBoldFont idCnt upvar \#0 $handler fh set type [$body type] set typename [string tolower [lindex $type 0]/[lindex $type 1]] set mailcap [$body findShowCommand] set width 0 set w $handler.w[incr idCnt] frame $w -relief raised -bd 2 -cursor top_left_arrow if {[string length [lindex $mailcap 4]]} { if {![catch {image create bitmap $body.icon \ -file [lindex $mailcap 4]}]} { label $w.icon -image $body.icon pack $w.icon -side left } } text $w.text -relief flat -cursor top_left_arrow \ -background [$w cget -background] $w.text tag configure Bold -font $fixedBoldFont if {[string length [$body description]]} { set width [string length "$t(description): [$body description]"] $w.text insert insert "$t(description): " Bold [$body description]\n } if {[string length $desc]} { set l [string length $desc] if { $l > $width } { set width $l } $w.text insert insert $desc\n Bold } if {[string length [lindex $mailcap 3]]} { set l [string length "$t(type_description): [lindex $mailcap 3]"] if { $l > $width } { set width $l } $w.text insert insert "$t(type_description): " \ Bold [lindex $mailcap 3]\n } set l [string length "$t(here_is): [string tolower [lindex $type 0]/[lindex $type 1]] ([RatMangleNumber [$body size]] bytes)"] if { $l > $width } { set width $l } $w.text insert insert "$t(here_is): " Bold \ "[string tolower [lindex $type 0]/[lindex $type 1]]" {} \ " ([RatMangleNumber [$body size]] bytes)" set filename [$body filename] if {[string length $filename]} { $w.text insert insert "\n$t(filename): " Bold "$filename" {} } $w.text configure \ -height [lindex [split [$w.text index end-1c] .] 0] \ -width $width \ -state disabled pack $w.text -side top -anchor w if {[string length [lindex $mailcap 0]]} { button $w.view_ext -text $t(view) \ -command "RunMailcap $body {$mailcap}" set b($w.view_ext) run_mailcap pack $w.view_ext -side left -padx 10 } button $w.save -text $t(save_to_file) \ -command "SaveBody $body $fh(toplevel)" button $w.view_int -text $t(view_source) -command "ShowSource $body" set b($w.save) save_bodypart set b($w.view_int) view_source pack $w.save $w.view_int -side left -padx 5 if {[$handler compare insert > "insert linestart"]} { $handler insert insert "\n" } $handler window create insert -window $w -padx 5 -pady 5 foreach win [concat $w [pack slaves $w]] { bind $win <3> "tk_popup $fh(struct_menu) %X %Y \ \[lsearch \[set ${handler}(struct_list)\] \ $body\]" } $handler insert insert "\n" } # ShowHome -- # # Scroll to the top of the document # # Arguments: # handler - The handler which identifies the show text widget proc ShowHome {handler} { $handler yview moveto 0 } # ShowBottom -- # # Scroll to the bottom of the document # # Arguments: # handler - The handler which identifies the show text widget proc ShowBottom {handler} { $handler yview moveto 1 } # ShowPageDown -- # # Move the show one page down # # Arguments: # handler - The handler which identifies the show text widget proc ShowPageDown {handler} { $handler yview scroll 1 pages } # ShowPageUp -- # # Move the show one page up # # Arguments: # handler - The handler which identifies the show text widget proc ShowPageUp {handler} { $handler yview scroll -1 pages } # ShowLineDown -- # # Move the show one line down # # Arguments: # handler - The handler which identifies the show text widget proc ShowLineDown {handler} { $handler yview scroll 1 units } # ShowLineUp -- # # Move the show one line up # # Arguments: # handler - The handler which identifies the show text widget proc ShowLineUp {handler} { $handler yview scroll -1 units } # SaveBody -- # # Save a bodypart to file. # # Arguments: # body - The bodypart to save # parent - Parent of window proc SaveBody {body parent} { global idCnt t option set convertNL [regexp -nocase text [lindex [$body type] 0]] set filename [rat_fbox::run \ -ok $t(save) \ -mode save \ -parent $parent \ -title $t(save_to_file) \ -initialdir $option(initialdir) \ -initialfile [$body filename]] if {"" == $filename} { return } if {$option(initialdir) != [file dirname $filename]} { set option(initialdir) [file dirname $filename] SaveOptions } if { 0 == [catch [list open $filename w] fh]} { $body saveData $fh false $convertNL close $fh } else { RatLog 4 "$t(save_failed): $fh" } } # BuildStructMenu # # Builds the structure menu # # Arguments: # handler - The handler which identifies the show text widget # m - The menu to build # msg - The message we should display the structure of proc BuildStructMenu {handler msg} { upvar \#0 $handler fh \ msgInfo_$msg msgInfo # Clear the old menu $fh(struct_menu) delete 0 end foreach slave [winfo children $fh(struct_menu)] { destroy $slave } set fh(struct_list) {} # Check if we got anything if {![string length $msg]} { return } if {$msgInfo(browse)} { return } BuildStructEntry $handler $fh(struct_menu) $msg [$msg body] "" FixMenu $fh(struct_menu) } # BuildStructEntry # # Builds an entry in the structure menu # # Arguments: # handler - The handler which identifies the show text widget # m - The menu to build # msg - The message we should display the structure of # body - The bodypart to describe # preamble- The preamble to add before this entry proc BuildStructEntry {handler m msg body preamble} { global b idCnt upvar \#0 $handler fh \ msgInfo_$msg msgInfo if {![string length $body]} { return } lappend fh(struct_list) $body set sm $m.m[incr idCnt] set typepair [$body type] set type [string tolower [lindex $typepair 0]/[lindex $typepair 1]] $m add cascade -label $preamble$type -menu $sm set b($m,[$m index end]) bodypart_entry menu $sm -tearoff 0 \ -disabledforeground [$m cget -activeforeground] \ -postcommand [list PopulateStructEntry $handler $sm $msg $body $type] # See if we have children to show switch -glob $type { message/rfc822 { if {[catch {$body message} message]} { return } set fh(struct_list) [lreplace $fh(struct_list) end end $message] if {[string length $message]} { BuildStructEntry $handler $m $message [$message body] \ "$preamble " } } multipart/* { foreach c [$body children] { BuildStructEntry $handler $m $msg $c "$preamble " } } } } # PopulateStructEntry -- # # Populates a struct menu entry # # Arguments: # handler - The handler which identifies the show text widget # m - Menu to populate # msg - Message the body belongs to # body - Body to use # type - Type of body proc PopulateStructEntry {handler m msg body type} { global t b showParams upvar \#0 $handler fh \ msgInfo_$msg msgInfo $m delete 0 end foreach slave [winfo children $m] { destroy $slave } if {[string length [$body description]]} { $m add command -label [$body description] -state disabled } set sigstatus [$body sigstatus] if {[string compare pgp_none $sigstatus]} { $m add command -label "$t(signature): $t($sigstatus)" -state disabled } if {[$body encoded]} { $m add command -label "$t(decoded)" -state disabled } # Do parameters if {[info exists showParams($type)]} { set sp $showParams($type) foreach param $sp { set value [$body parameter $param] if {[string length $param]} { $m add command -label "$t($param): $value" -state disabled } } } else { set sp {{}} } if {"" != [$body filename]} { $m add command -label "$t(filename): [$body filename]" \ -state disabled } $m add command -label "$t(size): [RatMangleNumber [$body size]]" \ -state disabled $m add separator if {![info exists msgInfo(show,$body)]} { set msgInfo(show,$body) 1 } $m add checkbutton -label $t(show) -onvalue 1 -offvalue 0 \ -variable msgInfo_${msg}(show,$body) \ -command "Show $handler $fh(current_msg) 0" set b($m,[$m index end]) bodypart_show if {"message/rfc822" == $type} { $m add cascade -label $t(copy) -menu $m.copy menu $m.copy -postcommand \ [list ShowCopyEmbedded $handler $body $m.copy] } $m add command -label $t(save_to_file)... \ -command "SaveBody $body $fh(toplevel)" set b($m,[$m index end]) bodypart_save $m add command -label $t(view_source)... -command "ShowSource $body" set b($m,[$m index end]) view_source bind $m "after idle {if {\[winfo exists $m\]} {$m delete 0 end}}" } # ShowCopyEmbedded -- # # Popup to copy menu for an embedded message # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart containing the message to copy # m - The menu to populate proc ShowCopyEmbedded {handler body m} { global t $m delete 0 end VFolderBuildMenu $m 0 "CopyEmbedded $body" 1 $m add separator $m add command -label $t(to_file)... \ -command "CopyEmbedded $body \ \[InsertIntoFile [winfo toplevel $handler]\]" $m add command -label $t(to_dbase)... \ -command "CopyEmbedded $body \ \[InsertIntoDBase [winfo toplevel $handler]\]" FixMenu $m } # CopyEmbedded -- # # Actually copy an embedded message # # Arguments: # body - Bodypart containing the message # copy_to - Folder to copy to proc CopyEmbedded {body copy_to} { if {1 == [llength $copy_to]} { global vFolderDef set copy_to $vFolderDef($copy_to) } [$body message] copy $copy_to } # RunMailcap -- # # Runs the mailcap entry for a body # # Arguments: # body - The handler which identifies the entity to show # mailcap - The mailcap entry to use proc RunMailcap {body mailcap} { global option t idCnt rat_tmp set id id[incr idCnt] upvar \#0 $id rm set cmd "sh -c {[lindex $mailcap 0]}" # Fix files set rm(fileName) $rat_tmp/[$body filename gen_if_empty] if {[regexp %s $cmd]} { set cmd [string map [list %s $rm(fileName)] $cmd] } else { if {[lindex $mailcap 1]} { Popup "$t(cant_pipe): \"$cmd\"" unset rm return } set cmd "cat $rm(fileName) | $cmd" } set f [open $rm(fileName) w] $body saveData $f 0 0 close $f if {[lindex $mailcap 1]} { # This command needs a terminal RatBgExec ${id}(existStatus) "$option(terminal) $cmd" } elseif {[lindex $mailcap 2]} { # This command produces lots of output set w .$id toplevel $w -class TkRat wm title $w $t(external_viewer) text $w.text \ -yscroll "$w.scroll set" \ -relief sunken -bd 1 \ -setgrid 1 \ -highlightthickness 0 scrollbar $w.scroll \ -relief sunken \ -bd 1 \ -command "$w.text yview" \ -highlightthickness 0 button $w.button -text $t(dismiss) -command "destroy $w" pack $w.button -side bottom -pady 5 pack $w.scroll -side right -fill y pack $w.text -expand 1 -fill both bind $w.text \ "::tkrat::winctl::RecordGeometry extView $w $w.text" ::tkrat::winctl::SetGeometry extView $w $w.text bind $w "$w.button invoke" $w.text insert insert [eval "exec $cmd"] $w.text configure -state disabled file delete -force -- $rm(fileName) & unset rm return } else { # This command manages its own output RatBgExec ${id}(existStatus) $cmd } trace variable rm(existStatus) w RunMailcapDone } # RunMailcapDone -- # # This gets called when the show command has run and should clean # things up. # # Arguments: # name1, name2 - Variable specifiers # op - Operation proc RunMailcapDone {name1 name2 op} { upvar \#0 $name1 rm after 30000 "file delete -force -- $rm(fileName)" unset rm } # RatShowURL -- # # Invoke an URL browser. # # Arguments: # url - URL to browse # win - Text widget containing URL # tag - Tag of url in text widget # x, y - Mouse coordinates proc RatShowURL {url win tag x y} { global option # Flash URL for feedback $win tag configure $tag -underline 1 update idletasks # Check if we should abort if {-1 == [lsearch -exact [$win tag names @$x,$y] $tag]} { return } RatShowURLLaunch $url $win } # RatShowURLLaunch -- # # Actually launch the URL browser. # # Arguments: # url - URL to browse # win - Text widget containing URL proc RatShowURLLaunch {url win} { global option urlstatus set url [string map { ! \\! $ %24 , %2c & \\& ( \\( ) \\) ? \\? * \\* [ \\[ ] \\] \\ \\\\ } $url] # Start viewer switch -regexp $option(url_viewer) { RatUP { if {[catch {RatUP_ShowURL $url} error]} { Popup $error [winfo toplevel $win] } } netscape|opera|mozilla|firefox { switch $option(url_behavior) { old_window {set extra ""} new_window {set extra ",new-window"} new_tab {set extra ",new-tab"} } trace variable urlstatus($url) w [list RatMaybeStartBrowser $url] catch {RatBgExec urlstatus($url) \ "$option(browser_cmd) -remote \ \"openURL($url$extra)\" >&/dev/null"} } galeon { switch $option(url_behavior) { old_window {set extra "--existing"} new_window {set extra "--new-window"} new_tab {set extra "--new-tab"} } RatBgExec unused "$option(browser_cmd) $extra \"$url\" >/dev/null" } lynx { set cmd [string map [list %u $url] $option(browser_cmd)] if {[catch {eval exec $cmd &} error]} { Popup $error [winfo toplevel $win] } } other { set cmd [string map [list %u $url] $option(browser_cmd)] if {[catch {eval exec $cmd &} error]} { Popup $error [winfo toplevel $win] } } } } # RatMaybeStartBrowser -- # # Start browser, if the first invokation failed. # # Arguments: # url - URL to show # name1, name2, op - trace information proc RatMaybeStartBrowser {url name1 name2 op} { upvar \#0 ${name1}($name2) status trace vdelete ${name1}($name2) w [list RatMaybeStartBrowser $url] if {0 == $status} { unset status return } unset status global option if {[catch {eval exec $option(browser_cmd) "$url" &} error]} { Popup $error } } # RatFindURL -- # # Search for an URL in a part of a text widget. This one must find a lot # of different formats. For example (URLs with surrounding text) # # Foo http://www.tkrat.org bar # Foo bar # Foo Bar # # Arguments: # t - Text widget # start - Index to start searching at # end - Index to end searching at proc RatFindURL {t start end} { global option set exp1 "<([join $option(urlprot) |]):(\[^>\])*>" set exp2 "([join $option(urlprot) |])://(\[a-zA-Z0-9\\-\\.@:\]+)+(:(\[0-9\]+))?(/\[^ \"]*)?\[a-zA-Z0-9/%#\]" set found [$t search -nocase -regexp -count len "($exp1)|($exp2)" \ $start $end] if {[string length $found]} { if {"<" == [$t get $found]} { return [list $found+1c $found+${len}c-1c] } else { return [list $found $found+${len}c] } } else { return {} } } # RatScrollShow -- # # Scroll the show text widget and search for URL's if needed # # Arguments: # t - The text widget # scrollw - The scrollbar widget # sfrac - The new start of shown section # efrac - The new end of shown section proc RatScrollShow {t scrollw sfrac efrac} { $scrollw set $sfrac $efrac if {[$t compare searched >= @0,20000]} { return } global idCnt upvar \#0 $t fh set start searched set end [$t index @0,20000+1c+20l] set result [RatFindURL $t $start $end] while {[llength $result] == 2} { set tag t[incr idCnt] set s [lindex $result 0] set e [lindex $result 1] $t tag add URL $s $e $t tag add $tag $s $e set url [string map {% %%} [$t get $s $e]] $t tag bind $tag [list RatShowURL $url %W $tag %x %y] $t tag bind $tag <1> \ "$t tag configure $tag -underline 0; update idletasks" set result [RatFindURL $t $e $end] } $t mark set searched $end } # ResizeFrame -- # # Resize frames containing scrolled information # # Arguments: # frame - The frame to resize # parent - The frame's parent # reqHeight - required height to see the scrollable item completely # reqWidth - required width to see the scrollable item completely # xscroll - The X axis scrollbar # yscroll - The Y axis scrollbar # # Note: # A value of -1 for reqWidth or reqHeight will show both scrollbars all the # time. The frame will then take the entire available space of the parent. proc ResizeFrame {frame parent reqHeight reqWidth xscroll yscroll} { set parWidth [winfo width $parent] set parHeight [winfo height $parent] set totalHeight $reqHeight set totalWidth $reqWidth if {![winfo exists $frame]} { return } # Take care of the easy stuff first. If reqWeight or reqHeight is -1 then # we *always* want scrollbars if {$reqHeight == -1 || $reqHeight == -1} { $frame configure -height $parHeight -width $parWidth return } # Determine if we need scrollbars. The deal is that if reqWidth or # reqHeight is -1 then always put the scrollbars if {[expr {$reqHeight > $parHeight}] && [info tclversion] < 8.5} { incr totalWidth [winfo width $yscroll] } if {[expr {$reqWidth > $parWidth}]} { incr totalHeight [winfo height $xscroll] } if {[expr {$totalHeight > $parHeight}] && [info tclversion] < 8.5} { $frame configure -height $parHeight -bg blue grid $yscroll -row 0 -column 1 -sticky ns } else { $frame configure -height $totalHeight grid forget $yscroll } if {[expr {$totalWidth > $parWidth}]} { $frame configure -width $parWidth grid $xscroll -row 1 -column 0 -sticky ew } else { $frame configure -width $totalWidth grid forget $xscroll } } # GetHandlerFromExtension -- # # Determines the real MIME type base on the extension of the file # in the attachement. This should only be called if the type is # application/octet-stream. # # Arguments: # filename - The name of the file when saved # # Returns: # The correct MIME type if it is one known by TkRat. Otherwise, returns # application/octet-stream proc GetHandlerFromExtension {filename} { global mimeHandler global GoodTypes set mimeType [list application octet-stream] set extension [string tolower [file extension $filename]] # Remove the '.' in the extension set extension [string trim $extension .] # Check image types if {[lsearch -exact $GoodTypes image/$extension] != -1} { set mimeType [list image $extension] } elseif {[string equal jpg $extension]} { # JPEGs often end in .jpg set mimeType [list image jpeg] } return $mimeType } # ShowMSTnef -- # # Show a application/ms-tnef body, if the tnef program can be found # # Arguments: # handler - The handler which identifies the show text widget # body - The bodypart to show proc ShowMSTnef {handler body} { upvar \#0 $handler fh global idCnt option rat_tmp fixedBoldFont t # Save body data to file set filename $rat_tmp/tnef.[RatGenId] set fid [open $filename w] fconfigure $fid -encoding binary $body saveData $fid 0 0 close $fid # Check if tnef program is present and generate list of contents if {[catch {open "|$option(tnef) --list $filename" r} tnef]} { ShowDefault $handler $body $t(no_tnef_found) file delete -force $filename return } # Read list of contents set contents {} while {-1 != [gets $tnef line]} { lappend contents $line } close $tnef # Remove file file delete -force $filename # Add markers foreach c $contents { set w $handler.w[incr idCnt] frame $w -relief raised -bd 2 -cursor top_left_arrow label $w.label -font $fixedBoldFont -text $c pack $w.label -side top -anchor w button $w.save -text $t(save_to_file) \ -command [list SaveTnefBody $body $c $fh(toplevel)] pack $w.save -side left -padx 5 $handler insert insert "\n" $handler window create insert -window $w -padx 5 -pady 5 $handler insert insert "\n" } } # SaveTnefBody -- # # Save a part of a tnef bodypart to file. # # Arguments: # body - The bodypart containing winmail.dat # child - Which of the children in the winmail.dat to save # parent - Parent of window proc SaveTnefBody {body child parent} { global idCnt t option rat_tmp regsub -all {\\} $child / childs set filename [rat_fbox::run \ -ok $t(save) \ -mode save \ -parent $parent \ -title $t(save_to_file) \ -initialdir $option(initialdir) \ -initialfile $childs] if {"" == $filename} { return } if {$option(initialdir) != [file dirname $filename]} { set option(initialdir) [file dirname $filename] SaveOptions } set dir $rat_tmp/tnef.[RatGenId] file mkdir $dir # Save body data to file set winmail $rat_tmp/tnef.[RatGenId] set fid [open $winmail w] fconfigure $fid -encoding binary $body saveData $fid 0 0 close $fid if {[catch {open "|$option(tnef) --maxsize=$option(tnef_max_size) --directory=$dir $winmail" w} tnef]} { RatLog 4 "$t(save_failed): $tnef" file delete -force $dir file delete -force $winmail return } if {[catch {close $tnef} err]} { RatLog 4 "$t(save_failed): $err" file delete -force $dir file delete -force $winmail return } file delete -force $winmail if {[catch {file copy -force $dir/$childs $filename} err]} { RatLog 4 "$t(save_failed): $err" file delete -force $dir return } file delete -force $dir } tkrat_2.2cvs20100105-dfsg.orig/tkrat/source.tcl000066400000000000000000000064241137544547100211430ustar00rootroot00000000000000# source.tcl -- # # This file contains code which handles shows the source of a bodypart # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # ShowSource # # Shows the source of a bodypart # # Arguments: # body - The bodypart to show proc ShowSource {body} { global idCnt t showParams option fixedNormFont # Create identifier set id sourceWin[incr idCnt] set w .$id # Create toplevel toplevel $w -class TkRat wm title $w $t(source) # Type set type [$body type] set tstring [string tolower [lindex $type 0]/[lindex $type 1]] frame $w.type label $w.type.label -width 15 -anchor e -text $t(type): label $w.type.value -text $tstring -font $fixedNormFont pack $w.type.label \ $w.type.value -side left pack $w.type -side top -anchor w # Description if {[string length [$body description]]} { frame $w.desc label $w.desc.label -width 15 -anchor e -text $t(description): label $w.desc.value -text [$body description] -font $fixedNormFont pack $w.desc.label \ $w.desc.value -side left pack $w.desc -side top -anchor w } # Size frame $w.size label $w.size.label -width 15 -anchor e -text $t(size): label $w.size.value -text [RatMangleNumber [$body size]] \ -font $fixedNormFont pack $w.size.label \ $w.size.value -side left pack $w.size -side top -anchor w # Parameters set typepair [$body type] set type [string tolower [lindex $typepair 0]/[lindex $typepair 1]] if {[info exists showParams($type)]} { foreach p $showParams($type) { frame $w.p_$p label $w.p_$p.label -width 15 -anchor e -text $t($p): label $w.p_$p.value -text [$body parameter $p] -font $fixedNormFont pack $w.p_$p.label \ $w.p_$p.value -side left pack $w.p_$p -side top -anchor w } } # Encodings frame $w.enc label $w.enc.label -width 15 -anchor e -text $t(encoding): label $w.enc.value -text [$body encoding] -font $fixedNormFont pack $w.enc.label \ $w.enc.value -side left pack $w.enc -side top -anchor w # The data frame $w.text -relief sunken -bd 1 scrollbar $w.text.scroll \ -relief sunken \ -command "$w.text.text yview" \ -highlightthickness 0 text $w.text.text \ -yscroll "$w.text.scroll set" \ -setgrid true \ -wrap char \ -bd 0 \ -highlightthickness 0 pack $w.text.scroll -side right -fill y pack $w.text.text -expand yes -fill both pack $w.text -side top -expand yes -fill both # The buttons button $w.dismiss -text $t(dismiss) -command "destroy $w" button $w.save -text $t(save_to_file)... -command "SaveBody $body $w" pack $w.save \ $w.dismiss -side left -expand 1 -pady 5 # Insert the source into the text widget set data [string map [list "\r\n" "\n"] [$body data true]] $w.text.text insert end $data $w.text.text configure -state disabled bind $w "$w.dismiss invoke" bind $w.text.text \ "::tkrat::winctl::RecordGeometry showSource $w $w.text.text" ::tkrat::winctl::SetGeometry showSource $w $w.text.text } tkrat_2.2cvs20100105-dfsg.orig/tkrat/start.tcl000077500000000000000000000631341137544547100210040ustar00rootroot00000000000000# # TkRat software and its included text is Copyright 1996-2005 by # Martin Forssn # # The full text of the legal notices is contained in the file called # COPYRIGHT, included with this distribution. proc TkRatStart {} { global t tkrat_version tkrat_version_date idCnt inbox expAfter logAfter \ statusBacklog currentColor ratLogBottom ratLogTop vFolderDef \ option currentLanguage_t tk_patchLevel vFolderInbox \ folderWindowList openFolders folderChanged propNormFont \ propLightFont fixedNormFont fixedBoldFont watcherFont \ ISO_Left_Tab tk_version rat_tmp tklead folderUnseen propBigFont \ fixedItalicFont # Base package requirements package require -exact ratatosk 2.2 # Function to let client know we have started proc RatPing {} { return pong } # Initialize variables set tkrat_version 2.2dev set tkrat_version_date 20050717 set idCnt 0 set inbox "" set expAfter {} set logAfter {} set statusBacklog {} set currentColor {} set ratLogBottom 0 set ratLogTop 0 # Bindings bind Entry {focus [tk_focusNext %W]} if {[catch {bind all {focus [tk_focusPrev %W]}}]} { set ISO_Left_Tab Shift-Tab } else { set ISO_Left_Tab ISO_Left_Tab } # Initialize RatGenId # Force load of package OptionsInit InitMessages $option(language) t OptionsInitText OptionsRead InitCharsetAliases InitPgp if {$tk_version >= 8.3} { tk useinputmethods $option(useinputmethods) } if {$tk_version == 8.3} { package require rat_compat 1.0 rat_compat::init8_3 } # Reinitialize language (if needed) if {[string compare $option(language) $currentLanguage_t]} { InitMessages $option(language) t } if {0 != $option(last_version_date) && "$option(last_version_date)" != $tkrat_version_date} { NewVersionUpdate } # Update the default fonts if {$option(font_size) > 10} { set big_size [expr 10+($option(font_size)-10)*2] } else { set big_size [expr $option(font_size)+2] } set propBigFont [RatCreateFont \ [list components $option(font_family_prop) \ $big_size bold roman 1 0]] set propNormFont [RatCreateFont \ [list components $option(font_family_prop) \ $option(font_size) bold roman 0 0]] set propLightFont [RatCreateFont \ [list components $option(font_family_prop) \ $option(font_size) normal roman 0 0]] set fixedNormFont [RatCreateFont \ [list components $option(font_family_fixed) \ $option(font_size) normal roman 0 0]] set fixedBoldFont [RatCreateFont \ [list components $option(font_family_fixed) \ $option(font_size) bold roman 0 0]] set fixedItalicFont [RatCreateFont \ [list components $option(font_family_fixed) \ $option(font_size) normal italic 0 0]] set watcherFont [RatCreateFont $option(watcher_font)] if {$option(override_fonts)} { set pri interactive } else { set pri widgetDefault } option add *TkRat*font $propLightFont $pri option add *TkRat*Entry.font $fixedNormFont $pri option add *TkRat*Text.font $fixedNormFont $pri option add *TkRat*Listbox.font $fixedNormFont $pri option add *TkRat*RatList*Listbox.font $propNormFont $pri option add *TkRat*RatTree*font $propLightFont $pri option add *Menu.tearOff $option(tearoff) widgetDefault option add *TkRat*Button.padY 1 widgetDefault option add *TkRat*Button.padX 2 widgetDefault option add *TkRat*Menubutton.padY 1 widgetDefault option add *TkRat*Menubutton.padX 2 widgetDefault option add *TkRat*Menu.activeBorderWidth 0 widgetDefault bind Menubutton {event generate %W } bind Menubutton {event generate %W } # Extra package requirements package require rat_list 1.0 package require rat_fbox 1.1 package require rat_balloon 1.0 package require rat_edit 1.1 package require rat_textlist 1.0 package require blt_busy 1.0 package require rat_ed 1.0 package require rat_ispell 1.1 package require rat_tree 1.0 package require rat_enriched 1.0 package require rat_flowmsg 1.0 package require rat_scrollframe 1.0 package require rat_textspell 1.0 package require rat_find 1.0 package require rat_table 1.0 # Change the color if {$option(override_color)} { option add *TkRat*foreground black interactive option add *TkRat*background \#dde3eb interactive eval "SetColor $option(color_set)" } # Redo bindings for entry and text to make the selection work more # intuitive if {$tk_version >= 8.4} { set tklead "tk::" } else { set tklead "tk" } bind Entry <1> "${tklead}EntryButton1 %W %x" bind Text <1> "${tklead}TextButton1 %W %x %y" # Bind global window close bind TkRat {destroy %W} # Make sure our config directory exists if {![file isdirectory $option(ratatosk_dir)]} { catch {file mkdir [RatTildeSubst $option(ratatosk_dir)]} result if {[string length $result]} { Popup [concat \ "$t(failed_create) \"$option(ratatosk_dir)\":"\ "$result.\n$t(do_without_dir)"] } } # Initialize balloon help InitMessages $option(language) balText rat_balloon::Init b balText # Give info about new features (or run first use wizard) if {"$option(last_version_date)" != $tkrat_version_date} { set isFirstUse [StartupInfo] } else { set isFirstUse 0 } # Read misc files VFolderRead AliasRead if { 3 > $option(scan_aliases) } { ScanAliases } ReadUserproc ::tkrat::winctl::ReadPos if {[file readable $option(ratatosk_dir)/expressions]} { ExpRead } if {$isFirstUse} { FirstUseWizard SaveOptions } # Setup trace of folderWindoList set openFolders {} trace variable folderWindowList wu RatTraceFolder # Setup online status switch $option(start_online_mode) { online { set option(online) 1 } offline { set option(online) 0 } default {} } if { 0 <= [expr {[RatDaysSinceExpire]-$option(expire_interval)}]} { catch {Expire} err } else { set expAfter \ [after [expr {($option(expire_interval)- \ [RatDaysSinceExpire])*24*60*60*1000}] Expire] } # Load watcher set folderChanged(no_such_folder) 0 set folderUnseen(no_such_folder) 0 trace variable folderChanged wu WatcherTrig trace variable folderUnseen w WatcherTrig } # RatCreateFont -- # # Create a font # # Arguments: # s - Specification, one of: # {components FAMILY SIZE WEIGHT SLANT UNDERLINE OVERSTRIKE} # {name FONT_NAME} proc RatCreateFont {s} { if {"components" == [lindex $s 0]} { set res [list [lindex $s 1] -[lindex $s 2] [lindex $s 3] [lindex $s 4]] if {[lindex $s 5]} { lappend res underline } if {[lindex $s 6]} { lappend res overstrike } return $res } else { return [lindex $s 1] } } # RatLog: # See ../doc/interface proc RatLog {level message {mode time}} { global statusText option logAfter ratLogBottom ratLogTop ratLog \ statusBacklog option statusId switch $level { 0 {set n BABBLE:} 1 {set n PARSE:} 2 {set n INFO:} 3 {set n WARN:} 4 {set n ERROR:} 5 {set n FATAL:} default {set n $level:} } set ratLog($ratLogTop) [format "%-8s %s" $n $message] incr ratLogTop if {$ratLogTop > [expr {$ratLogBottom+$option(num_messages)}]} { for {} {$ratLogTop > [expr {$ratLogBottom+$option(num_messages)}]} \ {incr ratLogBottom} { unset ratLog($ratLogBottom) } } if { 3 < $level} { # Fatal if {![string compare nowait $mode]} { after 1 Popup [list $message] } else { Popup $message } } else { if {$level > 0} { if {![string compare explicit $mode]} { if {[string length $logAfter]} { after cancel $logAfter set statusBacklog {} set logAfter {} } set statusText $message set statusId $ratLogTop } else { if {[string length $logAfter]} { lappend statusBacklog $message } else { set statusText $message set logAfter [after [expr {$option(log_timeout)*1000}] \ RatLogAfter] set statusId "" } } update idletasks } } return $ratLogTop } # RatLogAfter -- # # Show the next queued messagefor log display (if any). # # Arguments: proc RatLogAfter {} { global statusText logAfter statusBacklog option statusId if {[llength $statusBacklog]} { set statusText [lindex $statusBacklog 0] set statusBacklog [lrange $statusBacklog 1 end] set logAfter [after [expr {$option(log_timeout)*1000}] RatLogAfter] } else { set statusText "" set logAfter {} } set statusId "" } # RatClearLog -- # # Remove an explicit log message # # Arguments: # id - the id of the message to remove proc RatClearLog {id} { global statusText statusId if {![string compare $id $statusId]} { set statusId "" set statusText "" } } # GetRatLog -- # # Return the saved log messages # # Arguments: proc GetRatLog {} { global ratLogBottom ratLogTop ratLog set result {} for {set i $ratLogBottom} {$i < $ratLogTop} {incr i} { lappend result $ratLog($i) } return $result } # OkButtons # # Build two buttons and let the left one be surrounded by a frame. The # buttons will be created inside a $w.buttons frame (the frame will # also be created). The $w window will also be bound so that a press # on the Return key also sets the ${id}(done) to 1. # # Arguments: # w - Window in which to build the frame # t1, t2 - The text in the two buttons # cmd - Command which will be run when either button is pressed. # The command will get a '1' or a '0' as argument. proc OkButtons {w t1 t2 cmd} { frame $w.buttons button $w.buttons.ok -text $t1 -command "$cmd 1" -default active button $w.buttons.cancel -text $t2 -command "$cmd 0" pack $w.buttons.ok \ $w.buttons.cancel -side left -expand 1 bind $w "$cmd 1" bind $w "$cmd 0" wm protocol [winfo toplevel $w] WM_DELETE_WINDOW "$cmd 0" } # RatMkAccelerator -- # # Creates the accelerator entry for a key-binding # # Arguments: # keylist - Index into options array to get key combinations proc RatMkAccelerator {keylist} { global option accelerator foreach k $option($keylist) { if {[info exists a]} { if {[string length $k] < [string length $a]} { set a $k } } else { set a $k } } if {[info exists a]} { set n {} foreach k [split [string trim $a "<>"] -] { switch $k { "Key" {} "Control" {lappend n "Ctrl"} "Meta" {lappend n "M"} "exclam" {lappend n "!"} "quotedbl" {lappend n "\""} "numbersign" {lappend n "#"} "dollar" {lappend n "\$"} "percent" {lappend n "%"} "ampersand" {lappend n "&"} "parenleft" {lappend n "("} "parenright" {lappend n ")"} "asterisk" {lappend n "*"} "plus" {lappend n "+"} "comma" {lappend n ","} "minus" {lappend n "-"} "period" {lappend n "."} "slash" {lappend n "/"} "colon" {lappend n ":"} "semicolon" {lappend n ";"} "less" {lappend n "<"} "equal" {lappend n "="} "greater" {lappend n ">"} "question" {lappend n "?"} "at" {lappend n "@"} "bracketleft" {lappend n "["} "backslash" {lappend n "\\"} "bracketright" {lappend n "]"} "asciicircum" {lappend n "^"} "underscore" {lappend n "_"} "braceleft" {lappend n "{"} "bar" {lappend n "|"} "braceright" {lappend n "}"} default {lappend n $k} } } set key [join $n -] } else { set key "" } set accelerator($keylist) $key } # RatBind -- # # Bind the specified keys to the specified function # # Arguments: # w - Window to bind in # keylist - Index into options array to get key combinations # function - Function to bind the keys to proc RatBind {w keylist function} { global option accelerator if {![info exists accelarator($keylist)]} { RatMkAccelerator $keylist } foreach k $option($keylist) { if {0 < [regsub < $k $inc2} { incr light($i) $inc1 } else { incr light($i) $inc2 } if {$light($i) > 255} { set light($i) 255 } } set new(activeBackground) [format #%02x%02x%02x $light(0) \ $light(1) $light(2)] set new(selectBackground) $darkerBg set new(troughColor) $darkerBg set new(selectColor) #b03060 return [array get new] } # SetIcon -- # # Set the icon bitmap # # Arguments: # w - window to set the icon for # icon - the name of the icon proc SetIcon {w icon} { global env switch $icon { normal { if {[file readable $env(LIBDIR)/tkrat.xbm]} { wm iconbitmap $w @$env(LIBDIR)/tkrat.xbm wm iconmask $w @$env(LIBDIR)/tkratmask.xbm } } small { if {[file readable $env(LIBDIR)/tkrat_small.xbm]} { wm iconbitmap $w @$env(LIBDIR)/tkrat_small.xbm wm iconmask $w @$env(LIBDIR)/tkrat_smallmask.xbm } } none { wm iconbitmap $w "" wm iconmask $w "" } } } # FixMenu -- # # Fixes a menu if it is to big to fit on the screen. This should be called # as a postcommand and it will only check one menu, no cascades etc. # # Arguments: # m - The menu to fix proc FixMenu {m} { set height [winfo screenheight $m] if { [$m yposition last] > $height} { global t # Calculate breakpoint. We assue all entries are of uniform height set i [expr {([$m index last]*$height)/[$m yposition last]-1}] $m insert $i cascade -label $t(more) -menu $m.m if {![winfo exists $m.m]} { menu $m.m -postcommand "FixMenu $m.m" } else { $m.m delete 0 end } incr i while {$i <= [$m index last]} { switch [$m type $i] { separator { $m.m add separator } command { $m.m add command \ -label [$m entrycget $i -label] \ -command [$m entrycget $i -command] } cascade { $m.m add cascade \ -label [$m entrycget $i -label] \ -menu [$m entrycget $i -menu] } } $m delete $i } } } # AliasRead -- # # Read aliases from default file. # # Arguments: proc AliasRead {} { global option aliasBook set as $option(addrbooks) if {$option(use_system_aliases)} { lappend as $option(system_aliases) } foreach a $as { set book [lindex $a 0] set aliasBook(changed,$book) 0 switch [lindex $a 1] { tkrat { set f [lindex $a 2] if {[file readable $f]} { catch {RatAlias read $book $f} } set dir [file dirname $f] if {([file isfile $f] && [file writable $f]) || (![file exists $f] && [file isdirectory $dir] && [file writable $dir])} { set aliasBook(writable,$book) 1 } else { set aliasBook(writable,$book) 0 } } mail { set f [lindex $a 2] if {[file readable $f]} { ReadMailAliases $f $book } set aliasBook(writable,$book) 0 } elm { set f [lindex $a 2] if {[file readable $f]} { ReadElmAliases $f $book } set aliasBook(writable,$book) 0 } pine { set f [lindex $a 2] if {[file readable $f]} { ReadPineAliases $f $book } set aliasBook(writable,$book) 0 } } } } # FindAccelerators -- # # Finds suitable accelerator keys for a bunch of strings. The result is # an array where the keys are the different ids and the contents are # the index of the character to use as accelerator. # # Arguments: # var - Name of array (in callers context) to place result in # ids - List of ids of strings to search # used - List of already use accelerators proc FindAccelerators {var ids {used {}}} { upvar $var result global t foreach id $ids { set tot [string length $t($id)] set sub [string length [string trimleft $t($id) $used]] if {$sub > 0} { set result($id) [expr {$tot - $sub}] set used ${used}[string index $t($id) $result($id)] } else { set result($id) -1 } } } # SetupShortcuts -- # # Setup suitable keyboard shortcuts for a number of buttons. This includes: # - Figuring out sutable accelerators # - Marking them by underlining # - Adding keybindings # # Arguments: # buttons - List of buttons to work on proc SetupShortcuts {buttons} { set top [winfo toplevel [lindex $buttons 0]] # Remove old bindings foreach b [bind $top] { if {[string match "" $b]} { bind $top $b {} } } set u " " foreach but $buttons { set text [string tolower [$but cget -text]] regsub -all {[^\w ]} $text " " text if {![regexp -indices "(^| )(\[^$u\])\\w" $text unused unused loc]} { if {![regexp -indices "\[^$u\]" $text loc]} { $but configure -underline -1 continue } } set c [string index $text [lindex $loc 0]] bind $top "$but invoke ; break" bind $top "$but invoke ; break" $but configure -underline [lindex $loc 0] set u "$u$c" } } # RatTraceFolder -- # # Traces the folder window list # # Arguments: # as provided by trace proc RatTraceFolder {args} { global openFolders folderWindowList set openFolders {} foreach h [array names folderWindowList] { lappend openFolders $folderWindowList($h) } } # RatExec -- # # Used by the client to send commands to us # # Arguments: # cmds - Commands to execute proc RatExec {cmds} { foreach cmd $cmds { if {2 == [llength $cmd]} { set arg [lindex $cmd 1] } else { set arg "" } switch -glob -- [lindex $cmd 0] { open* { global folderWindowList idCnt if {"open" == [lindex $cmd 0] && [array size folderWindowList]} { set handler [lindex [array names folderWindowList] 0] } else { global idCnt option vFolderDef vFolderInbox set w .f[incr idCnt] toplevel $w -class TkRat set title [string map {%f . %r ?} \ $option(main_window_name)] wm title $w $title set ititle [string map {%f .} $option(icon_name)] wm iconname $w $ititle SetIcon $w $option(icon) ::tkrat::winctl::Place folderWindow $w set handler [FolderWindowInit $w $arg] ::tkrat::winctl::Place folderWindow $w if {$option(iconic)} { wm iconify $w } else { wm deiconify $w } } return [RatExecOpen $handler $arg] } blank { global idCnt option vFolderDef vFolderInbox set w .f[incr idCnt] toplevel $w -class TkRat set title [string map {%f . %r ?} $option(main_window_name)] wm title $w $title set ititle [string map {%f .} $option(icon_name)] wm iconname $w $ititle SetIcon $w $option(icon) if {"" == $arg} { set arg [lindex $vFolderDef($vFolderInbox) 0] } ::tkrat::winctl::Place folderWindow $w set handler [FolderWindowInit $w $arg] ::tkrat::winctl::Place folderWindow $w FolderWindowClear $handler if {$option(iconic)} { wm iconify $w } else { wm deiconify $w } } compose { return [ComposeClient $arg] } mailto { return [MailtoClient $arg] } netsync { return [RatExecNetsync $arg] } } } } # RatExecOpen -- # # Executes the open command from the client # # Arguments: # handler - Handler of window to use # name - name or spec of folder to open proc RatExecOpen {handler name} { global vFolderDef vFolderInbox option if {"" == $name} { set spec $vFolderInbox set check 1 } elseif {[llength $name] >= 4 && [regexp {file|mh|dbase|imap|pop3|dynamic|dis} [lindex $name 1]]} { set spec $name set check 0 } else { foreach i [array names vFolderDef] { if {$name == [lindex $vFolderDef($i) 0]} { set spec $i break } } if {![info exists spec]} { error "No such folder '$name'" } set check 0 } if {![FolderFailedOpen check $vFolderDef($spec)]} { VFolderOpen $handler $spec } } # RatExecNetsync -- # # Executes the netsync command from the client # # Arguments: # what - Which parts of the network sync to perform proc RatExecNetsync {what} { global option set old $option(network_sync) if {[llength $what]} { set ns_send 0 set ns_fetch 0 set ns_cmd 0 foreach w $what { switch $w { send {set ns_send 1} fetch {set ns_fetch 1} cmd {set ns_cmd 1} default {error "Illegal netsync arg '$w'"} } } set option(network_sync) [list $ns_send $ns_fetch $ns_cmd] } RatBusy {NetworkSync} set option(network_sync) $old } # Browse -- # # Browse for a file, fetches the default from the give var and returns the # result in the same. # # Arguments: # w - Parent window of browser # var - Name of variable containing name of file # mode - Mode of file dialog proc Browse {w var mode} { global env t option upvar \#0 $var filein if {"" != $filein} { if {[file isdirectory $filein]} { set dir $filein set file "" } else { set dir [file dirname $filein] set file [file tail $filein] } } else { set dir $option(initialdir) set file "" } set r [rat_fbox::run \ -parent $w \ -initialdir $dir \ -initialfile $file \ -title $t(select_file) \ -ok $t(ok) \ -mode $mode] if {"" != $r} { if {$option(initialdir) != [file dirname $r]} { set option(initialdir) [file dirname $r] SaveOptions } uplevel \#0 [list set $var $r] } } # IsExecutable -- # # Checks if a given command matches any executable file (by searching path # if needed). # # Arguments: # cmd - Command to check proc IsExecutable {cmd} { global env if {[regexp / $cmd]} { return [file executable $cmd] } foreach p [split $env(PATH) :] { if {[file executable $p/$cmd]} { return 1 } } return 0 } # DumpStack -- # # Dumps the tcl calling stack # # Arguments: proc DumpStack {} { for {set i 1} {$i < [info level]} {incr i} { puts "$i: [info level $i]" } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/tkrat.in000077500000000000000000000006641137544547100206170ustar00rootroot00000000000000#!/bin/sh # the next line restarts using the correct version of wish \ exec @wish@ "$0" "$@" set env(LIBDIR) [pwd]/../tkrat set env(CONFIG_DIR) . set env(COMPRESS) @compress@ set env(CSUFFIX) @csuffix@ set env(SSH) "@ssh@" # This version of the tkrat file is only intended to be used in # the development tree. set auto_path [concat [pwd]/.. $env(LIBDIR) $auto_path] package forget ratatosk ratatosk_old TkRatClientStart tkrat_2.2cvs20100105-dfsg.orig/tkrat/vfolder.tcl000066400000000000000000000576501137544547100213130ustar00rootroot00000000000000# vfolder.tcl -- # # This file contains commands which interacts with the user about performing # different types of folder operations # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # The ident of the next defined vfolder set vFolderDefIdent 0 # The ident of the next defined vFolderStruct set vFolderStructIdent 0 set vFolderLastUsedList {} proc AddToLastUsedList {id} { global vFolderLastUsedList set res [list $id] set l [llength $vFolderLastUsedList] set i 0 set n 1 while {$i < $l} { if { $id != [lindex $vFolderLastUsedList $i] } { lappend res [lindex $vFolderLastUsedList $i] incr n 1 if { $n >= 8 } break } incr i 1 } set vFolderLastUsedList $res } # VFolderRead -- # # Reads the list of vfolders from disk. If needed it calls FixVFolderList # which upgrades the list from the old format to the new. # # Arguments: proc VFolderRead {} { global vFolderDef vFolderVersion option vfolder_list vFolderInbox \ vFolderStruct mailServer vFolderSave t vFolderSpecials \ vFolderOutgoing vFolderHold if {[file readable $option(ratatosk_dir)/vfolderlist]} { source $option(ratatosk_dir)/vfolderlist } # Upgrade old versions if { 0 == [info exists vFolderVersion]} { if {[info exists vfolder_list]} { FixVFolderList set vFolderVersion 4 } else { set vFolderDef(0) {{} struct {} {1}} set vFolderDef(1) $option(default_folder) set vFolderVersion 7 set vFolderInbox 1 } } if {3 == $vFolderVersion} { foreach id [array names vFolderDef] { if { {} != [lindex $vFolderDef($id) 2]} { set vFolderDef($id) [lreplace $vFolderDef($id) 2 0 {}] } } set vFolderVersion 4 } if {4 == $vFolderVersion} { UpgradeVFolderList4to5 } if {5 == $vFolderVersion} { UpgradeVFolderList5to6 } if {6 == $vFolderVersion} { UpgradeVFolderList6to7 } if {7 == $vFolderVersion} { UpgradeVFolderList7to8 } if {8 != $vFolderVersion} { Popup $t(unsupported_folder_file) exit 1 } if {![info exists vFolderInbox]} { set vFolderInbox 0 } if {![info exists vFolderDef($vFolderInbox)]} { set vFolderInbox [lindex [lsort -integer [array names vFolderDef]] 0] } if {[info exists vFolderSave]} { foreach r $option(roles) { set option($r,save_outgoing) $vFolderSave } unset vFolderSave # We have to save everything here since we have moved data from # one file to the other. SaveOptions VFolderWrite } if {![info exists vFolderHold]} { set id [VFolderGetID] set vFolderDef($id) [list $t(drafts) file {monitor 1} \ "$option(ratatosk_dir)/drafts.mbx"] set vFolderHold $id } else { set vFolderDef($vFolderHold) [lreplace $vFolderDef($vFolderHold) 0 0 $t(drafts)] } if {![info exists vFolderOutgoing]} { set id [VFolderGetID] set vFolderDef($id) [list $t(outgoing) file {monitor 1} \ "$option(ratatosk_dir)/outgoing.mbx"] set vFolderOutgoing $id } else { set vFolderDef($vFolderOutgoing) [lreplace $vFolderDef($vFolderOutgoing) 0 0 $t(outgoing)] } if {![info exists vFolderSpecials]} { set id [VFolderGetID] set vFolderDef($id) [list $t(specials) struct {} \ [list $vFolderOutgoing $vFolderHold]] set vFolderSpecials $id } CheckVFolderList RatCreateFolder -mbx $vFolderDef($vFolderHold) RatCreateFolder -mbx $vFolderDef($vFolderOutgoing) } # CheckVFolderList -- # # Checks the consistency of the folder-list. It removes any unreferenced # entries and removes refewrences to bad entries. # # Arguments: proc CheckVFolderList {} { global vFolderDef vFolderSpecials vFolderOutgoing vFolderHold set errors {} foreach v [array names vFolderDef] { set ref($v) 0 } # Check that the root entry exists and is a struct incr ref(0) if {[info exists vFolderDef(0)] && "struct" == [lindex $vFolderDef(0) 1]} { CheckVFDWalk 0 } else { lappend errors "Missing struct entry 0" set vFolderDef(0) [list "Repaired" struct {} {}] } # Check that the specials entry exists and is a struct if {[info exists vFolderDef($vFolderSpecials)] && "struct" == [lindex $vFolderDef($vFolderSpecials) 1]} { incr ref($vFolderSpecials) CheckVFDWalk $vFolderSpecials } else { puts "No specials" lappend errors "Missing struct entry for specials" set vFolderDef($vFolderSpecials) \ [list "Repaired" struct {} [list $vFolderOutgoing $vFolderHold]] } # Check that all entries are references exactly once foreach v [lsort -integer [array names ref]] { if {0 == $ref($v)} { lappend errors "Folder $v not referenced, removing" unset vFolderDef($v) } } foreach e $errors { puts $e } } # CheckVFDWalk -- # # Walks the vfolderdef, we know that the id we are passed exists and contains # a struct. # # Arguments: # id - Id of struct to walk proc CheckVFDWalk {id} { global vFolderDef upvar ref ref \ errors errors # Walk through all referenced entries and see that they exists set nr {} set modified 0 if {"struct" == [lindex $vFolderDef($id) 1]} { set pos 3 } else { set pos 5 } set ids [lindex $vFolderDef($id) $pos] foreach r $ids { if {![info exists vFolderDef($r)]} { lappend errors "Referenced entry $r does not exist" incr modified } elseif {0 != $ref($r)} { lappend errors "Folder $r referenced multiple times" incr modified } else { lappend nr $r incr ref($r) if {"struct" == [lindex $vFolderDef($r) 1] || "import" == [lindex $vFolderDef($r) 1]} { CheckVFDWalk $r } } } if {0 < $modified} { set vFolderDef($id) [lreplace $vFolderDef($id) $pos $pos $nr] } } # SelectFileFolder -- # # Presents a file selector to the user and opens the selected folder (if any) # # Arguments: # parent - parent window proc SelectFileFolder {parent} { global t option set fh [rat_fbox::run \ -title $t(open_file) \ -mode any \ -initialdir $option(initialdir) \ -ok $t(open) \ -parent $parent] # Do open if {$fh != ""} { if {$option(initialdir) != [file dirname $fh]} { set option(initialdir) [file dirname $fh] SaveOptions } return [list RatOpenFolder [list $fh file {} $fh]] } else { return "" } } # SelectDbaseFolder -- # # Lets the user search the database # # Arguments: # parent - parent window proc SelectDbaseFolder {parent} { global idCnt t b # Create identifier set id vfolderWinID[incr idCnt] set w .$id upvar \#0 $id hd set hd(done) 0 set hd(op) and # Create toplevel toplevel $w -class TkRat -bd 5 wm title $w $t(open_dbase) wm transient $w $parent # Fill in times set dinfo [RatDbaseInfo] set hd(int_from) [clock format [lindex $dinfo 1] -format "%Y-%m-%d 00:00"] set hd(int_to) [clock format [lindex $dinfo 2] -format "%Y-%m-%d 23:59"] # Populate window label $w.interval -text $t(time_interval): -anchor e frame $w.int entry $w.int.from -width 18 -textvariable ${id}(int_from) label $w.int.divider -text "-" entry $w.int.to -width 18 -textvariable ${id}(int_to) grid $w.int.from $w.int.divider $w.int.to -sticky ew grid columnconfigure $w.int 0 -weight 1 grid columnconfigure $w.int 2 -weight 1 set b($w.int.from) dbs_time_from set b($w.int.to) dbs_time_to grid $w.interval $w.int - -sticky ew -pady 5 label $w.operation -text $t(operation): -anchor e radiobutton $w.and -text $t(op_and) -variable ${id}(op) -value and \ -anchor w radiobutton $w.or -text $t(op_or) -variable ${id}(op) -value or -anchor w set b($w.and) dbs_and set b($w.or) dbs_or grid $w.operation $w.and -sticky ew grid x $w.or -sticky ew label $w.line_expl -text $t(line_expl) grid x $w.line_expl -sticky ew -pady 5 foreach el {subject {all_addresses all_addr_detail} to from cc} { if {[llength $el] > 1} { label $w.${e}_rlabel -text "($t([lindex $el 1]))" -anchor w set l $w.${e}_rlabel set e [lindex $el 0] } else { set l "-" set e $el } label $w.${e}_label -text $t($e): -anchor e entry $w.${e}_entry -textvariable ${id}($e) -relief sunken grid $w.${e}_label $w.${e}_entry $l -sticky ew set b($w.${e}_entry) dbs_$e } frame $w.separator -height 4 grid x $w.separator foreach el {keywords {complete_msg_text slow}} { if {[llength $el] > 1} { label $w.${e}_rlabel -text "($t([lindex $el 1]))" -anchor w set l $w.${e}_rlabel set e [lindex $el 0] } else { set l "-" set e $el } label $w.${e}_label -text $t($e): -anchor e entry $w.${e}_entry -textvariable ${id}($e) -relief sunken grid $w.${e}_label $w.${e}_entry $l -sticky ew set b($w.${e}_entry) dbs_$e } OkButtons $w $t(search) $t(cancel) "set ${id}(done)" grid $w.buttons - - -sticky we ::tkrat::winctl::SetGeometry selectDbaseFolder $w ::tkrat::winctl::ModalGrab $w $w.subject_entry set cont 1 while {$cont} { tkwait variable ${id}(done) set cont 0 if {1 == $hd(done)} { set start_s [clock format [clock seconds] -format "%Y-%m-%d 00:00"] set start_i [clock scan $start_s] set end_i [expr $start_i+24*60*60] if {[catch {clock scan $hd(int_from) -base $start_i} \ hd(int_from_parsed)]} { Popup $t(illegal_from_date) $w set cont 1 continue } if {[catch {clock scan $hd(int_to) -base $end_i} \ hd(int_to_parsed)]} { Popup $t(illegal_to_date) $w set cont 1 continue } } } # Do search set ret "" if {1 == $hd(done)} { set exp [list "int" $hd(int_from_parsed) $hd(int_to_parsed) $hd(op)] foreach e {keywords subject all_addresses to from cc} { if {[string length $hd($e)]} { lappend exp $e $hd($e) } } if {[string length $hd(complete_msg_text)]} { lappend exp "all" $hd(complete_msg_text) } if {[string compare $hd(op) $exp]} { set ret [list RatOpenFolder \ [list "Dbase search" dbase {} {} {} $exp]] } else { Popup $t(emptyexp) $parent } } foreach bn [array names b $w.*] {unset b($bn)} ::tkrat::winctl::RecordGeometry selectDbaseFolder $w destroy $w unset hd return $ret } # VFolderAddItem -- # # Add an item to a menu # # Arguments: # m - Menu to add to # id - Id of item to add # elem - Item to add # cmd - Command to execute when an item is choosen # write - If this argument is 1 the folders are going to be used for # inserting messages. proc VFolderAddItem {m id elem cmd write} { global openFolders option vFolderMonitorFH folderExists folderUnseen \ currentColor if {[llength $currentColor] > 3} { set unreadBg [option get $m troughColor Color] } else { set unreadBg [$m cget -activebackground] } if {1 == [llength $elem]} { global vFolderDef set elem $vFolderDef($id) } if {![string compare dynamic [lindex $elem 1]] && (0 == $write || "expanded" == $option(dynamic_behaviour))} { regsub -all {[\. ]} $elem _ nid set nm $m.m$nid $m add cascade -label [lindex $elem 0] -menu $nm if {![winfo exists $nm]} { menu $nm -postcommand [list VFolderBuildDynamic $nm \ $elem $cmd $write] } } elseif {[string compare pop3 [lindex $elem 1]] || 0==$write} { $m add command -label [lindex $elem 0] -command "$cmd [list $id]" if {[info exists vFolderMonitorFH($id)]} { if {[info exists folderUnseen($vFolderMonitorFH($id))]} { set unseen $folderUnseen($vFolderMonitorFH($id)) set exists $folderExists($vFolderMonitorFH($id)) $m entryconfigure last -accelerator "($unseen/$exists)" if { $folderUnseen($vFolderMonitorFH($id)) > 0 } { $m entryconfigure last \ -background $unreadBg } } else { unset vFolderMonitorFH($id) } } if {0 == $write && -1 != [lsearch -exact $openFolders $elem]} { $m entryconfigure [$m index end] -state disabled } } } # VFolderBuildDynamic -- # # Populate a dynamic menu # # Arguments: # m - The menu in which to insert the entries # elem - The folder definition # cmd - Command to execute when an item is choosen # write - If this argument is 1 the folders are going to be used for # inserting messages. proc VFolderBuildDynamic {m elem cmd write} { global t $m delete 0 end if {$write} { $m add command -label $t(auto_select) -command "$cmd [list $elem]" } foreach f [lsort [glob -nocomplain [lindex $elem 3]/*]] { if {[file isfile $f]} { $m add command -label [file tail $f] \ -command "$cmd {[list [file tail $f] file \ [lindex $elem 2] $f]}" } } FixMenu $m } # VFolderBuildMenu -- # # Constructs a menu of vfolders. When one item in the menu is choosen # $cmd is executed and the vfolder id of the choosen folder is # appended to $cmd. If the write argument is 1 then only those # folders that can be written to are included in the menu. # # Arguments: # m - The menu in which to insert the entries # id - The id of the struct to start with # cmd - Command to execute when an item is choosen # write - If this argument is 1 the folders are going to be used for # inserting messages. proc VFolderBuildMenu {m id cmd write} { global vFolderDef idCnt t idmap$m $m configure -tearoffcommand VFolderTearoffMenu if {![info exists vFolderDef($id)]} { return } switch [lindex $vFolderDef($id) 1] { struct {set i 3} import {set i 5} } foreach sid [lindex $vFolderDef($id) $i] { set name [lindex $vFolderDef($sid) 0] if {"struct" == [lindex $vFolderDef($sid) 1] || "import" == [lindex $vFolderDef($sid) 1]} { set nm $m.m$sid $m add cascade -label $name -menu $nm if {![winfo exists $nm]} { menu $nm -postcommand "$nm delete 0 end; \ VFolderBuildMenu \ $nm $sid [list $cmd] $write; \ FixMenu $nm" } } else { VFolderAddItem $m $sid $sid $cmd $write set idmap${m}($sid) [$m index last] } } } # VFolderTearoffMenu -- # # Add traces and bindings for torn off menus. # # Arguments: # oldmenu - Old menu name # menu - New menu proc VFolderTearoffMenu {oldmenu menu} { global folderExists folderUnseen idmap$oldmenu idCnt set var idmap[incr idCnt] upvar \#0 $var v upvar \#0 idmap$oldmenu idmap foreach i [array names idmap] { set v($i) $idmap($i) } set cmd "after 100 VFolderTrace $var $menu" trace variable folderExists wu $cmd trace variable folderUnseen wu $cmd bind $menu "+ trace vdelete folderExists wu \"$cmd\" trace vdelete folderUnseen wu \"$cmd\" unset $var " } # VFolderTrace -- # # Trace function for vfolder menus # # Arguments: # var - Variable containing id-mappings # menu - Menu containig entry # name1, name2, op proc VFolderTrace {var menu name1 name2 op} { global vFolderDef vFolderMonitorID folderUnseen folderExists upvar \#0 $var v if {![info exists vFolderMonitorID($name2)] || ![info exists v($vFolderMonitorID($name2))]} { # Ignore this folder return } if {"w" == $op} { set a ($folderUnseen($name2)/$folderExists($name2)) } else { set a "" } $menu entryconfigure $v($vFolderMonitorID($name2)) -accelerator $a } # VFolderDoOpen -- # # Opens the specified vfolder # # Arguments: # id - Identity of folder # vfolder - The definition of the vfolder to be opened proc VFolderDoOpen {id vfolder} { global vFolderWatch vFolderName vFolderDef vFolderHold option set f [RatOpenFolder $vfolder] array set flag [lindex $vfolder 2] set vFolderWatch($f) 0 if {"" != $id} { global vFolderMonitorFH vFolderMonitorID folderUnseen set vFolderMonitorFH($id) $f set vFolderMonitorID($f) $id } if {[info exists flag(watch)] && $flag(watch)} { set vFolderWatch($f) 1 set vFolderName($f) [lindex $vfolder 0] } if {$vfolder == $vFolderDef($vFolderHold)} { after [expr $option(compose_last_chance)*1000] VFolderPurgeBackups $f } return $f } # VFolderOpen -- # # Opens the specified vfolder # # Arguments: # handler - The handler to the folder window which requested the open # vfolder - The definition of the vfolder to be opened. If it is only # one word then it is expected to the ID of a folder to open proc VFolderOpen {handler vfolder} { global t inbox vFolderDef vFolderInbox option folderWindowList \ vFolderLastUsedList upvar \#0 $handler fh set fh(special_folder) none if {1 == [llength $vfolder]} { global vFolderDef vFolderOutgoing vFolderHold set id $vfolder set vfolder $vFolderDef($id) if {$id == $vFolderOutgoing || $id == $vFolderHold} { set fh(special_folder) drafts } else { AddToLastUsedList $id } } else { set id "" } array set features [lindex $vfolder 2] # Initialize browse mode switch $option(browse) { normal {set mode 0} browse {set mode 1} folder { if {![info exists features(browse)]} { set features(browse) 0 } set mode $features(browse) } } set fh(browse) $mode set folder [FolderRead $handler [list VFolderDoOpen $id $vfolder] \ [lindex $vfolder 0]] if {[string length $folder]} { set folderWindowList($handler) $folder } if {![string compare $vfolder $vFolderDef($vFolderInbox)]} { set inbox $folder } } # VFolderInsert -- # # Inserts the given messages into the given vfolder # # Arguments: # handler - The handler to the folder window which requested the operation # advance - 1 if we should move to the next message on success # delete - 1 if we shoudl delete moved messages # messages - The messages to move # vfolder - The definition of the vfolder to be opened. If it is only # one word then it is expected to the ID of a folder to open proc VFolderInsert {handler advance delete messages vfolder} { if {1 == [llength $vfolder]} { global vFolderDef AddToLastUsedList $vfolder set vfolder $vFolderDef($vfolder) } RatBusy [list VFolderInsertDo $handler $advance $messages $vfolder $delete] } proc VFolderInsertDo {handler advance messages vfolder delete} { upvar \#0 $handler fh global option t if {![llength $vfolder]} { return } if {1 == [llength $vfolder]} { global vFolderDef set vfolder $vFolderDef($vfolder) } # There is no need to open dbase folders before inserting # messages. But other types of folders benefit from opening # because that means that each copied message does not need to # open it again. if {"dbase" != [lindex $vfolder 1]} { set f [RatOpenFolder append $vfolder] } set toDelete {} foreach msg $messages { if {[catch {$msg copy $vfolder} result]} { RatLog 4 "$t(insert_failed): $result" break } else { if {$delete} { lappend toDelete [$fh(folder_handler) find $msg] } } } if {[llength $toDelete] > 0} { # Do all the flag updates at once for performance reasons SetFlag $handler deleted 1 $toDelete } if { 1 == $advance } { FolderNext $handler } if {[info exists f]} { $f close } } # InsertIntoFile -- # # Inserts the given message into a file. The user must specify the file. # # Arguments: # parent - Parent window proc InsertIntoFile {parent} { global t option set f [rat_fbox::run \ -ok $t(ok) \ -title $t(save_to_file) \ -initialdir $option(initialdir) \ -mode any \ -parent $parent] if { $f != "" } { if {$option(initialdir) != [file dirname $f]} { set option(initialdir) [file dirname $f] SaveOptions } set result [list $f file {} $f] } else { set result {} } return $result } # InsertIntoDBase -- # # Inserts the given message into the database. The user must specify # some of the arguments for the insert operation. # # Arguments: # parent - Parent window proc InsertIntoDBase {parent} { global idCnt t b option # Create identifier set id f[incr idCnt] set w .$id upvar \#0 $id hd set hd(done) 0 # Create toplevel toplevel $w -bd 5 -class TkRat wm title $w $t(insert_into_dbase) wm transient $w $parent # Populate window frame $w.keywords label $w.keywords.label -text $t(keywords): entry $w.keywords.entry -textvariable ${id}(keywords) -relief sunken \ -width 20 pack $w.keywords.entry \ $w.keywords.label -side right set b($w.keywords.entry) keywords frame $w.extype label $w.extype.label -text $t(extype): frame $w.extype.b radiobutton $w.extype.b.none -text $t(none) -variable ${id}(extype) \ -value none set b($w.extype.b.none) exp_none radiobutton $w.extype.b.remove -text $t(remove) -variable ${id}(extype) \ -value remove set b($w.extype.b.remove) exp_remove radiobutton $w.extype.b.incoming -text $t(incoming) \ -variable ${id}(extype) -value incoming set b($w.extype.b.incoming) exp_incoming radiobutton $w.extype.b.backup -text $t(backup) -variable ${id}(extype) \ -value backup set b($w.extype.b.backup) exp_backup pack $w.extype.b.none \ $w.extype.b.remove \ $w.extype.b.incoming \ $w.extype.b.backup -side top -anchor w pack $w.extype.b \ $w.extype.label -side right -anchor nw frame $w.exdate label $w.exdate.label -text $t(exdate): entry $w.exdate.entry -textvariable ${id}(exdate) -relief sunken \ -width 20 pack $w.exdate.entry \ $w.exdate.label -side right set b($w.exdate.entry) exp_date frame $w.buttons button $w.buttons.ok -default active -text $t(insert) \ -command "set ${id}(done) 1" button $w.buttons.cancel -text $t(cancel) -command "set ${id}(done) 0" pack $w.buttons.ok \ $w.buttons.cancel -side left -expand 1 -pady 4 pack $w.keywords \ $w.extype \ $w.exdate \ $w.buttons -side top -fill both set hd(extype) $option(def_extype) set hd(exdate) $option(def_exdate) bind $w "$w.buttons.ok invoke" bind $w "$w.buttons.cancel invoke" wm protocol $w WM_DELETE_WINDOW "set ${id}(done) 0" ::tkrat::winctl::SetGeometry insertIntoDbase $w ::tkrat::winctl::ModalGrab $w $w.keywords.entry tkwait variable ${id}(done) # Do insert if { 1 == $hd(done) } { set exp [list and keywords $hd(keywords)] set result [list DBase dbase {} $hd(extype) $hd(exdate) $exp] } else { set result {} } ::tkrat::winctl::RecordGeometry insertIntoDbase $w foreach bn [array names b $w.*] {unset b($bn)} destroy $w unset hd return $result } # VFoldersUsesRole -- # # Returns a list of names of folders which references the given role. # # Arguments: # role - Role to look for proc VFoldersUsesRole {role} { global vFolderDef set results {} foreach id [array names vFolderDef] { set f(speed) "" unset f array set f [lindex $vFolderDef($id) 2] if {[info exists f(role)] && $role == $f(role)} { lappend results [lindex $vFolderDef($id) 0] } } return $results } # VFolderPurgeBackups # # Removes old stale backups from a folder # # Arguments: # fh - Folder handler proc VFolderPurgeBackups {fh} { global option set cutoff [expr [clock seconds] - $option(compose_last_chance)] set deleted {} set num_msgs [lindex [$fh info] 1] for {set i 0} {$i <$num_msgs} {incr i} { set msg [$fh get $i] foreach h [$msg headers] { if {"X-TkRat-Internal-AutoBackup" == [lindex $h 0]} { if {[lindex $h 1] < $cutoff} { lappend deleted $i } break } } } if {[llength $deleted]} { $fh setFlag $deleted deleted 1 $fh update sync } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/vfolderdef.tcl000066400000000000000000001417601137544547100217660ustar00rootroot00000000000000# vfolderdef.tcl - # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # # Create and edit vfolders # # Folders are stored in the vfolderlist file. This file is a valid tcl-file # and is read by sourcing it. It should define four variables and two-three # arrays of lists. The variables are: # vFolderVersion - Version of this file, this should be '8' # vFolderInbox - The identifier of the default folder, this is the first # folder which is opened upon startup. # vFolderSpecials - List of special folders. Currently only the following two # vFolderOutgoing - Id of folder holding the outqueue # vFolderHold - Id of folder where held messagaes are kept # # The first array is vFolderStruct which defines the layout of the folder # menu. Index '0' is the top and must always exist. Each item in this # array is a list {name contents} where contents is a list of indexes. # Positive indexes refers to the vFolderDef array and negative indexes # to other entries in the vFolderStruct array. # # Each virtual folder is described by one entry in the vFolderDef array. # Each entry in the array is a list. The three first elements in this list # are the same for all folder types: # {NAME TYPE FLAGS TYPE_SPECIFIC_ELEMENTS} # Flags is a list of flags and their values, suitable for the "array set" cmd. # Valid flags are: sort, browse, monitor, watch, subscribed # The following is a list of all the different folder types and their elems # # file: {name file flags filename} # mh: {name mh flags path_to_mh_dir} # dbase: {name dbase flags extype exdate expression} # imap: {name imap flags imap_host folder} # pop3: {name pop3 flags pop3_host} # dynamic: {name dynamic flags path_to_dir policy} # disconnected: {name dis flags imap_server folder} # # The following are entries which does not refer to any physical folders # Instead they contain references and structures. # Menu struct: {name struct flags {ids}} # import: {name import flags folderdef pattern {ids}} # # # The final array is mailServer. Indexes into this array are, for imap servers # the display name of the server and for pop3 servers an integer. # Each entry in the array contains the following list: # {host port flags user} # Valid flags are: pop3 ssl validate-cert # # # OLD FORMATS # imap: {name imap flags {{{host:port}folder}} user} # pop3: {name pop3 flags {{{host/pop3}}} user} # disconnected: {name dis flags {{{host:port}folder}} user} # VFolderDef -- # # Create the vfolderdef window # # Arguments: proc VFolderDef {} { global t b vf vfd_old vFolderDef vFolderSpecials # Create identifier set id vfolderdef set w .$id if {[winfo exists $w]} { wm deiconify $w raise $w return } upvar \#0 $id hd set vf(w) $w set vf(done) 0 set vf(dragging) 0 set vf(oldfocus) [focus] set vf(drag_after) {} set vf(w) $w set vf(selected) {} set vf(changed) 0 set vf(unapplied_changes) 0 set vfd_old(marker) {} # Create toplevel toplevel $w -class TkRat wm title $w $t(vfolderdef) # Populate window frame $w.mbar -relief raised -bd 1 set m $w.mbar.a.m menubutton $w.mbar.a -menu $m -text $t(actions) -underline 0 menu $m $m add command -label $t(new_folder_wizard)... \ -command "VFolderWizardStart menu" $m add command -label $t(new_submenu)... -command "VFolderNewStruct menu" $m add separator $m add command -label $t(reimport_all) -command VFolderReimportAll set b($m,[$m index end]) reimport_all $m add separator $m add command -label $t(close) -command "VFolderWinClose 0" set b($m,[$m index end]) dismiss pack $w.mbar.a -side left -padx 5 bind $w "VFolderWinClose 0" # This has to be created after the menubar to make the Destroy logic work frame $w.d # Paning button frame $w.d.handle -width 10 -height 10 \ -relief raised -borderwidth 2 \ -cursor sb_h_double_arrow set b($w.d.handle) pane_button # Folder tree set vf(tree) [rat_tree::create $w.d.t -sizeid vFolderDef \ -selectcallback VFolderSelect \ -movenotify VFolderCheckChanges] set vf(top) [$vf(tree) gettopnode] # Find max width of labels set vf(mw) 10 foreach l {name type pathname keywords extype mail_server mbox role pattern reimport_when sort_order host user connect flags} { if {[string length $t($l)] > $vf(mw)} { set vf(mw) [string length $t($l)] } } # Font is proportional set vf(mw) [expr {int($vf(mw)*0.9)}] # Folder details frame $w.d.r -relief sunken set vf(detframe) $w.d.r.details set vf(details) [rat_scrollframe::create $vf(detframe) -bd 10 \ -highlightthickness 0] label $vf(details).name_lab -width $vf(mw) entry $vf(details).name -width 40 -state disabled -relief flat grid $vf(details).name_lab $vf(details).name -sticky ew frame $w.d.r.buttons -bd 10 button $w.d.r.buttons.apply -text $t(apply_changes) -state disabled \ -command VFolderApply button $w.d.r.buttons.restore -text $t(restore_values) -state disabled \ -command VFolderRestore pack $w.d.r.buttons.apply $w.d.r.buttons.restore \ -side left -expand 1 -anchor s set vf(but_apply) $w.d.r.buttons.apply set vf(but_restore) $w.d.r.buttons.restore grid $w.d.r.details -column 1 -row 1 -sticky nsew grid $w.d.r.buttons -column 1 -row 2 -sticky nsew grid columnconfigure $w.d.r 1 -weight 1 grid rowconfigure $w.d.r 1 -weight 1 grid rowconfigure $vf(details) 100 -weight 1 pack $w.mbar -side top -fill x pack $w.d -fill both -expand 1 # Do packing of paning window VFolderPane [::tkrat::winctl::GetPane vFolderDef] place $w.d.t -relheight 1 place $w.d.r -relheight 1 -relx 1 -anchor ne place $w.d.handle -anchor s raise $w.d.handle bind $w.d \ "set ${id}(W) \[winfo width %W\]; \ set ${id}(X0) \[winfo rootx %W\]; \ set y \[expr \[winfo height %W\] - 10\];\ place configure $w.d.handle -y \$y" bind $w.d.handle \ "VFolderPane \[expr (%X-\$${id}(X0))/\$${id}(W).0\]" menu $w.mf -tearoff 0 $w.mf add command -label $t(delete)... -command "VFolderDeleteFolder" set vf(folder_menu_delete) [$w.mf index end] $w.mf add command -label $t(new_folder_wizard)... \ -command "VFolderWizardStart tree" $w.mf add command -label $t(new_submenu)... \ -command "VFolderNewStruct tree" set vf(folder_menu) $w.mf menu $w.mm -tearoff 0 $w.mm add command -label $t(delete)... -command "VFolderDeleteServer" set b($w.mm,[$w.mm index end]) vd_delete set vf(mailserver_menu) $w.mm # Create images (images lent from tk sources) if {![info exists vf(folder)]} { set vf(folder) [image create photo -data { R0lGODlhEAAMAKEAAAD//wAAAPD/gAAAACH5BAEAAAAALAAAAAAQAAwAAAIghINhyycvVFsB QtmS3rjaH1Hg141WaT5ouprt2HHcUgAAOw==}] set vf(file) [image create photo -data { R0lGODlhDAAMAKEAALLA3AAAAP//8wAAACH5BAEAAAAALAAAAAAMAAwAAAIgRI4Ha+IfWHsO rSASvJTGhnhcV3EJlo3kh53ltF5nAhQAOw==}] set vf(dbase) [image create photo -data { R0lGODlhEAAMAKEAAAD//wAAAPD/gP///yH+Dk1hZGUgd2l0aCBHSU1QACH5BAEAAAAALAAA AAAQAAwAAAImhIMZxhcCo0DtyTtZwpMeqGzZx0ULWY6AlZ5rCmqwurKS19RhDhQAOw==}] set vf(imap) [image create photo -data { R0lGODlhEAAMAKEAAAD//wAAAPD/gP///yH5BAEAAAAALAAAAAAQAAwAAAIkhA+hi50CRXAo SDupsTGfmWFY9T3dt6SXRG4m5LlhGmv2jdsFADs=}] set vf(pop3) [image create photo -data { R0lGODlhEAAMAKEAAAD//wAAAPD/gP///yH5BAEAAAAALAAAAAAQAAwAAAIihA+hi50CRXAo SDupsTG3jGHaxEHegobS+HQUmGryTNdAAQA7}] } bind $w.mbar VFolderWinCleanup bind $w { if {"normal" == [[winfo toplevel %W].d.r.buttons.apply cget -state]} { VFolderApply } } bind $vf(but_restore) {%W invoke; break} $vf(tree) autoredraw 0 # Add IMAP-servers set vf(imapitem) [$vf(top) add folder -image $vf(folder) \ -label $t(imap_servers) -state closed] VFolderAddMailServers # Add special folders set item [$vf(top) add folder -image $vf(folder) -label $t(specials) \ -state closed -zone specials] foreach sid [lindex $vFolderDef($vFolderSpecials) 3] { VFDInsert $item end $sid specials } # Add normal folders set item [$vf(top) add folder -image $vf(folder) -label $t(folders) \ -state open -zone folders] foreach sid [lindex $vFolderDef(0) 3] { VFDInsert $item end $sid folders } set vf(folderitem) $item $vf(tree) autoredraw 1 $vf(tree) redraw ::tkrat::winctl::SetGeometry vFolderDef $w $w.d } # VFolderWinClose -- # # Close a vfolderdef window # # Arguments: # force - True if we can not abort proc VFolderWinClose {force} { global b vf t if {!$force} { # Check if it is ok if {0 == [VFolderChangeOk]} { return } # Do we have a wizard up? set wizards 0 foreach w [winfo children .] { if {[string match ".vfolderwizard*" $w]} { wm deiconify $w incr wizards } } if { 0 != $wizards} { Popup $t(cant_close_while_wizards) return } } destroy $vf(w) } # VFolderWinCleanup -- # # Cleanup when closing VFolderWindow # # Arguments: proc VFolderWinCleanup {} { global vf b VFolderCheckChanges ::tkrat::winctl::RecordGeometry vFolderDef $vf(w) $vf(w).d $vf(pane) catch {focus $vf(oldfocus)} foreach bn [array names b $vf(w).*] {unset b($bn)} unset vf } # VFolderPane -- # # Pane the vfolderdef window # # Arguments: # x - X position of dividing line proc VFolderPane {x} { global vf if {$x < 0.01 || 0.99 < $x} return # Prevents placing into inaccessibility (off the window). set w $vf(w) set vf(pane) $x place $w.d.t -relwidth $x place $w.d.r -relwidth [expr {1.0 - $x}] place $w.d.handle -relx $x } # VFolderAddMailServers -- # # Adds the mail-servers to the tree. # # Arguments: # im - Imap top item proc VFolderAddMailServers {} { global mailServer vf $vf(imapitem) clear foreach m [lsort -dictionary [array names mailServer]] { if {-1 != [lsearch -exact [lindex $mailServer($m) 2] pop3]} { continue } set iid [list imap $m] $vf(imapitem) add item -image $vf(imap) -label $m -id $iid $vf(tree) bind $iid <3> \ [list VFolderPostMenu %X %Y $iid $vf(mailserver_menu)] } } # VFolderGetItemName -- # # Gets the name to insert into the tree. # # Arguments: # id - folder id proc VFolderGetItemName {id} { global vFolderDef vFolderInbox t set d $vFolderDef($id) array set f [lindex $d 2] set ta {} if {![string compare $vFolderInbox $id]} { lappend ta INBOX } if {[info exists f(monitor)] && $f(monitor)} { lappend ta $t(monitored) } if {[info exists f(watch)] && $f(watch)} { lappend ta $t(watched) } return "[lindex $d 0] [join $ta ,]" } # VFolderGetItemImage -- # # Returns the image to use for a certain item # # Arguments: # id - folder id proc VFolderGetItemImage {id} { global vFolderDef vf switch -regexp [lindex $vFolderDef($id) 1] { imap|dis { return $vf(imap) } pop3 { return $vf(pop3) } import { return $vf(folder) } dbase { return $vf(dbase) } default { return $vf(file) } } } # VFDInsert -- # # Insert folder items into the tree # # Arguments: # item - Item command # pos - Position to insert in # id - ID of vFolderDef to insert # zone - Zone to drop folders in proc VFDInsert {item pos id zone} { global vFolderDef vf set d $vFolderDef($id) set text [VFolderGetItemName $id] set iid [list folder $id] if {"struct" == [lindex $d 1] || "import" == [lindex $d 1]} { if {"struct" == [lindex $d 1]} { set loc 3 set z2 $zone } else { set loc 5 set z2 {} } set i [$item add folder -image $vf(folder) -label $text -id $iid \ -position $pos -zone $z2 -dropin $zone] set vf(item,$id) $i foreach sid [lindex $d $loc] { VFDInsert $i end $sid $z2 } } else { set image [VFolderGetItemImage $id] $item add entry -image $image -label $text -id $iid -position $pos \ -dropin $zone } if {"folders" == $zone} { $vf(tree) bind $iid <3> \ [list VFolderPostMenu %X %Y $iid $vf(folder_menu)] } } # VFolderChangeOk -- # # Check if it is ok to change the details part. Returns '0' if not. # # Arguments: proc VFolderChangeOk {} { global vf vfd t if {$vf(unapplied_changes)} { set value [RatDialog $vf(w) $t(unapplied_changes_title) \ $t(unapplied_changes) {} 0 $t(apply) $t(discard) $t(cancel)] if {0 == $value} { VFolderApply } elseif {2 == $value} { return 0 } set vf(unapplied_changes) 0 if {1 == $vfd(new)} { VFolderRemoveNew } } return 1 } # VFolderSelect -- # # Callback, called when the user selects something in the folder-list # # Arguments: # id - Id of selected item proc VFolderSelect {ident} { global vf t # Check if changed if {0 == [VFolderChangeOk]} { return cancel } switch [lindex $ident 0] { imap { VFolderSetupMailServer imap [lindex $ident 1] 0} folder { VFolderSetupDetails [lindex $ident 1] 0 {} } default { foreach c [winfo children $vf(details)] { destroy $c } } } rat_scrollframe::recalc $vf(detframe) return ok } # VFolderClearWindow -- # # Clears the window # # Arguments: proc VFolderClearWindow {} { global vf vfd vfd_old foreach c [winfo children $vf(details)] { destroy $c } # Work around bug in tk (reported & fixed Oct 2002) grid size $vf(details) unset vfd_old set vfd_old(marker) {} trace vdelete vfd w VFolderDefChange } # VFolderSetupDetails -- # # Setup the details frame # # Arguments: # id - Id of element to show # new - Boolean indicating if this is a new object or not # pos - Where to insert a new object {struct-id index} proc VFolderSetupDetails {id new pos} { global vf vfd vfd_old vFolderDef vFolderInbox t b mailServer \ option env vFolderHold vFolderOutgoing VFolderClearWindow set w $vf(details) set vfd(olddef) $vFolderDef($id) set vfd(name) [lindex $vfd(olddef) 0] set vfd(mode) folder set vfd(type) [lindex $vfd(olddef) 1] set vfd(typename) $t([lindex $vfd(olddef) 1]) set vfd(id) $id set vfd(new) $new set vfd(import_on_create) 0 # Top information if {$id == $vFolderHold} { label $w.is_hold -text $t(is_hold_folder) -pady 10 -anchor n grid $w.is_hold - -sticky ew } elseif {$id == $vFolderOutgoing} { label $w.is_outgoing -text $t(is_outgoing_folder) -pady 10 -anchor n grid $w.is_outgoing - -sticky ew } label $w.name_lab -text $t(name): -width $vf(mw) -anchor e entry $w.name -width 40 -textvariable vfd(name) grid $w.name_lab $w.name -sticky ew label $w.type_lab -text $t(type): -anchor e set m $w.type_mb.m menubutton $w.type_mb -textvariable vfd(typename) -anchor w -menu $m \ -highlightthickness 1 grid $w.type_lab $w.type_mb -sticky ew -pady 5 if {$new} { set imaps {} set popi 0 foreach ms [lsort -dictionary [array names mailServer]] { if { -1 == [lsearch -exact [lindex $mailServer($ms) 2] pop3]} { lappend imaps $ms } elseif {[string is integer $ms] && $ms > $popi} { set popi $ms } } set def(file) [list {} file {} {}] set def(mh) [list {} mh {} {}] set def(dbase) [list {} dbase {} $option(def_extype) \ $option(def_exdate) {and keywords {}}] set def(imap) [list {} imap {} [lindex $imaps 0] {}] set def(pop3) [list {} pop3 {} [expr {$popi + 1}]] set def(dynamic) [list {} dynamic {} {} sender] menu $m foreach ft {file mh dbase imap pop3 dynamic} { $m add command -label $t($ft) -command \ "set vFolderDef($id) \[lreplace [list $def($ft)] 0 0 \ \$vfd(name)\]; \ [list VFolderSetupDetails $id $new $pos]; \ update; focus \[tk_focusNext $w.type_mb\]" if {"imap" == $ft && 0 == [llength $imaps]} { $m entryconfigure end -state disabled } } foreach ft {file mh imap} { $m add command -label "$t(import) ($t($ft))" -command \ "set vFolderDef($id) \[lreplace \ \[list {} import {reimport manually} \ [list $def($ft)] {*}\] 0 0 \$vfd(name)\]; \ [list VFolderSetupDetails $id $new $pos]; \ update; focus \[tk_focusNext $w.type_mb\]" if {"imap" == $ft && 0 == [llength $imaps]} { $m entryconfigure end -state disabled } } if {"struct" != $vfd(type)} { $w.type_mb configure -bd 2 -indicatoron 1 -relief raised \ -takefocus 1 } if {"pop3" == $vfd(type) && "" == [lindex $vfd(olddef) 3]} { # Find unique name set i 1 while {[info exists mailServer($i)]} { incr i } set n $i set mailServer($n) [list {} {} {} $env(USER)] set d [lreplace $vfd(olddef) 3 3 $n] } $vf(but_apply) configure -text $t(create) -state normal $vf(but_restore) configure -text $t(cancel) -state normal set vf(unapplied_changes) 1 } else { $w.type_mb configure -state disabled -indicatoron 0 $vf(but_apply) configure -text $t(apply_changes) -state disabled $vf(but_restore) configure -text $t(restore_values) -state disabled } # Do magic if we are looking at an import folder if {"import" == [lindex $vfd(olddef) 1]} { set vfd(is_import) 1 set vfd(pattern) [lindex $vfd(olddef) 4] array set vfd [lindex $vfd(olddef) 2] set d [lindex $vfd(olddef) 3] set vfd(typename) "$vfd(typename) ($t([lindex $d 1]))" set vfd(type) [lindex $d 1] set vfd(manage) 0 } else { set d $vfd(olddef) set vfd(is_import) 0 if {"pop3" == [lindex $d 1]} { set vfd(manage) 0 } else { set vfd(manage) $vfd(new) } } # Do type specific things switch -regexp [lindex $d 1] { file|mh|dynamic { set vfd(filename) [RatDecodeQP system [lindex $d 3]] label $w.file_label -text $t(pathname): -anchor e entry $w.file_entry -textvariable vfd(filename) -width 40 set b($w.file_entry) vd_pathname grid $w.file_label $w.file_entry -sticky ew button $w.fbrowse -text $t(browse)... if {"dynamic" == [lindex $d 1]} { $w.fbrowse configure -command "Browse $w vfd(filename) dirok" } else { $w.fbrowse configure -command "Browse $w vfd(filename) any" } set b($w.fbrowse) file_browse grid x $w.fbrowse -sticky e } dbase { set vfd(keywords) [lindex [lindex $d 5] 2] set vfd(extype) [lindex $d 3] set vfd(exdate) [lindex $d 4] label $w.kw_lab -text $t(keywords): -anchor e entry $w.kw_entry -textvariable vfd(keywords) -width 20 set b($w.kw_entry) keywords label $w.extype_lab -text $t(extype): -anchor e frame $w.extype foreach et {none remove incoming backup} { radiobutton $w.extype.$et -anchor w -text $t($et) \ -variable vfd(extype) -value $et set b($w.extype.$et) exp_$et pack $w.extype.$et -side top -fill x } label $w.exdate_lab -text $t(exdate): -anchor e entry $w.exdate_entry -textvariable vfd(exdate) set b($w.exdate_entry) exp_date grid $w.kw_lab $w.kw_entry -sticky we grid $w.extype_lab $w.extype -sticky wen grid $w.exdate_lab $w.exdate_entry -sticky we } imap|dis { set vfd(mail_server) [lindex $d 3] set vfd(mailbox_path) [RatDecodeQP system [lindex $d 4]] if {"dis" == [lindex $d 1]} { set vfd(disconnected) 1 } else { set vfd(disconnected) 0 } label $w.ms_lab -text $t(mail_server): -anchor e set m $w.ms_but.m menubutton $w.ms_but -textvariable vfd(mail_server) \ -relief raised -indicatoron 1 -menu $m -takefocus 1 \ -highlightthickness 1 menu $m foreach ms [lsort -dictionary [array names mailServer]] { if { -1 == [lsearch -exact [lindex $mailServer($ms) 2] pop3]} { $m add command -label $ms \ -command [list set vfd(mail_server) $ms] } } set b($w.ms_but) mail_server grid $w.ms_lab $w.ms_but -sticky ew -pady 5 label $w.mp_lab -text $t(mbox): -anchor e entry $w.mp_entry -textvariable vfd(mailbox_path) -width 20 set b($w.mp_entry) vd_mbox grid $w.mp_lab $w.mp_entry -sticky we checkbutton $w.disconnected -text $t(enable_offline) \ -variable vfd(disconnected) set b($w.disconnected) use_as_disconnected grid x $w.disconnected -sticky w if {0 == $vfd(is_import)} { checkbutton $w.create -text $t(create_mailbox_on_server) \ -variable vfd(manage) set b($w.create) create_mailbox_on_server grid x $w.create -sticky w } } pop3 { set vfd(mail_server) [lindex $d 3] label $w.ms_lab -text $t(mail_server): -anchor e VFolderSetupMailServerDetails $w $vfd(mail_server) } } if {1 == $vfd(is_import)} { frame $w.sp3 -height 16 grid $w.sp3 label $w.pl -text $t(pattern): -anchor e entry $w.pe -textvariable vfd(pattern) set b($w.pe) vd_pattern grid $w.pl $w.pe -sticky ew checkbutton $w.sub -text $t(subscribed_only) \ -variable vfd(subscribed) set b($w.sub) vd_subscribed grid x $w.sub -sticky w frame $w.sp4 -height 10 grid $w.sp4 label $w.when_lab -text $t(reimport_when): -anchor e radiobutton $w.rei_manually -variable vfd(reimport) -value manually \ -text $t(reimport_manually) -anchor w grid $w.when_lab $w.rei_manually -sticky ew set b($w.rei_manually) vd_rei_manually radiobutton $w.rei_session -variable vfd(reimport) -value session \ -text $t(reimport_session) grid x $w.rei_session -sticky w set b($w.rei_session) vd_rei_session if {0 == $new} { button $w.reimport_now -text $t(reimport_now) -pady 0 \ -command "VFolderReimport $id" grid x $w.reimport_now -sticky w } else { frame $w.sp5 -height 10 grid $w.sp5 set vfd(import_on_create) 1 checkbutton $w.import_on_create -text $t(import_on_create) \ -variable vfd(import_on_create) grid x $w.import_on_create -sticky w set b($w.import_on_create) vd_import_on_create } } # Show flags for everything except menus if {"struct" != [lindex $vfd(olddef) 1]} { set vfd(sort) default set vfd(role) default foreach v {browse monitor watch trace subscribed inbox} { set vfd($v) 0 } if {$id == $vFolderInbox} { set vfd(inbox) 1 } array set vfd [lindex $vfd(olddef) 2] set vfd(sort_l) $t(sort_$vfd(sort)) if {"default" == $vfd(role)} { set vfd(role_l) $t(default) } else { set vfd(role_l) $option($vfd(role),name) } label $w.role_label -text $t(role): -anchor e set m $w.role_m.m menubutton $w.role_m -textvariable vfd(role_l) \ -relief raised -indicatoron 1 -menu $m -highlightthickness 1 \ -takefocus 1 set b($w.role_m) vd_role menu $m -tearoff 0 $m add command -label $t(default) -command "set vfd(role) default; \ set vfd(role_l) [list $t(default)]" foreach r $option(roles) { $m add command -label $option($r,name) \ -command "set vfd(role) $r; \ set vfd(role_l) [list $option($r,name)]" } label $w.sort_label -text $t(sort_order): -anchor e set m $w.sort_m.m menubutton $w.sort_m -textvariable vfd(sort_l) \ -relief raised -indicatoron 1 -menu $m -highlightthickness 1 \ -takefocus 1 set b($w.sort_m) vd_sort menu $m -tearoff 0 foreach o {default threaded folder reverseFolder date reverseDate \ size reverseSize subject subjectonly} { $m add command -label $t(sort_$o) \ -command "set vfd(sort) $o; \ set vfd(sort_l) [list $t(sort_$o)]" } checkbutton $w.browse -text $t(browse_mode) -variable vfd(browse) set b($w.browse) browse_mode checkbutton $w.monitor -text $t(monitor_mbox) -variable vfd(monitor) \ -command "if !\$vfd(monitor) {set vfd(watch) 0}" set b($w.monitor) vd_monitor checkbutton $w.watch -text " $t(watch_mbox)" -variable vfd(watch) \ -command "if \$vfd(watch) {set vfd(monitor) 1}" set b($w.watch) vd_watch checkbutton $w.inbox -text $t(incom_mbox) -variable vfd(inbox) set b($w.inbox) vd_setinbox if {$vfd(inbox)} { $w.inbox configure -state disabled } frame $w.sp1 -height 16 frame $w.sp2 -height 16 grid x $w.sp1 grid $w.role_label $w.role_m -sticky ew -pady 5 grid $w.sort_label $w.sort_m -sticky ew -pady 5 grid x $w.browse -sticky w grid x $w.monitor -sticky w grid x $w.watch -sticky w if {1 != $vfd(is_import)} { grid x $w.inbox -sticky w } grid x $w.sp2 } else { set vfd(mode) struct } # Create copy of values foreach v [array names vfd] { set vfd_old($v) $vfd($v) } trace variable vfd w VFolderDefChange focus $w.name $w.name icursor end if {$new} { $w.name selection range 0 end } } # VFolderSetupMailerverDetails -- # # Adds the widgets to a details frame # # Argument: # w - window to add to # id - Id of mail server proc VFolderSetupMailServerDetails {w id} { global b t vfd mailServer option if {[info exists mailServer($id)]} { set ms $mailServer($id) } else { set ms [list $option(remote_host) {} pop3 $option(remote_user)] } set flags [lindex $ms 2] foreach f {ssl notls novalidate-cert secure debug} { if {-1 != [lsearch -exact $flags $f]} { set vfd($f) 1 } else { set vfd($f) 0 } } set i [lsearch -glob $flags ssh-cmd*] if {-1 != $i} { set vfd(ssh_cmd) [lindex [lindex $flags $i] 1] } else { set vfd(ssh_cmd) $option(ssh_template) } set vfd(host) [lindex $ms 0] set vfd(user) [lindex $ms 3] set vfd(port) {} set port [lindex $ms 1] if {"" == $port} { set vfd(method) rsh } elseif {("imap" == $vfd(type) && !$vfd(ssl) && 143 == $port) || ("pop3" == $vfd(type) && !$vfd(ssl) && 110 == $port) || ("imap" == $vfd(type) && $vfd(ssl) && 993 == $port) || ("pop3" == $vfd(type) && $vfd(ssl) && 995 == $port)} { set vfd(method) tcp_default } else { set vfd(method) tcp_custom set vfd(port) $port } if {$vfd(ssl)} { set vfd(priv) ssl set vfd(notls) 1 } elseif {0 == $vfd(notls)} { set vfd(priv) tls } else { set vfd(priv) none } frame $w.msp1 -height 16 grid $w.msp1 label $w.host_lab -text $t(host): -anchor e entry $w.host -width 40 -textvariable vfd(host) set b($w.host) vd_host grid $w.host_lab $w.host -sticky ew label $w.user_lab -text $t(user): -anchor e entry $w.user -width 40 -textvariable vfd(user) set b($w.user) vd_user grid $w.user_lab $w.user -sticky ew frame $w.msp2 -height 10 grid $w.msp2 label $w.conn_lab -text $t(connect): -anchor e radiobutton $w.conn_tcpdef -text $t(tcp_default) -variable vfd(method) \ -value tcp_default -anchor w set b($w.conn_tcpdef) vd_tcp_default frame $w.conn_tcpcust radiobutton $w.conn_tcpcust.b -text $t(tcp_custom): -variable vfd(method) \ -value tcp_custom entry $w.conn_tcpcust.e -width 6 -textvariable vfd(port) pack $w.conn_tcpcust.b $w.conn_tcpcust.e -side left set b($w.conn_tcpcust.b) vd_tcp_custom set b($w.conn_tcpcust.e) vd_tcp_custom radiobutton $w.conn_rsh -text $t(rsh_ssh) -variable vfd(method) \ -value rsh set b($w.conn_rsh) vd_rsh grid $w.conn_lab $w.conn_tcpdef - -sticky ew grid x $w.conn_tcpcust -sticky w grid x $w.conn_rsh -sticky w label $w.ssh_lab -text $t(ssh_command): -anchor e entry $w.ssh_cmd -width 40 -textvariable vfd(ssh_cmd) grid $w.ssh_lab $w.ssh_cmd -sticky ew set b($w.ssh_cmd) ssh_command frame $w.msp3 -height 10 grid $w.msp3 label $w.priv_lab -text $t(privacy): -anchor e radiobutton $w.priv_ssl -text $t(use_ssl) -anchor w \ -variable vfd(priv) -value ssl \ -command "set vfd(ssl) 1;set vfd(notls) 1" radiobutton $w.priv_tls -text $t(try_tls) \ -variable vfd(priv) -value tls \ -command "set vfd(ssl) 0;set vfd(notls) 0" radiobutton $w.priv_none -text $t(no_encryption) \ -variable vfd(priv) -value none \ -command "set vfd(ssl) 0;set vfd(notls) 1" set b($w.priv_ssl) vd_priv_ssl set b($w.priv_tls) vd_priv_tls set b($w.priv_none) vd_priv_none grid $w.priv_lab $w.priv_ssl -sticky ew grid x $w.priv_tls -sticky w grid x $w.priv_none -sticky w frame $w.msp4 -height 10 grid $w.msp4 label $w.flags_lab -text $t(flags): -anchor e checkbutton $w.flag_secure -text $t(imap_secure) -variable vfd(secure) \ -anchor w set b($w.flag_secure) vd_secure checkbutton $w.flag_checkc -text $t(ssl_check_cert) \ -variable vfd(novalidate-cert) -onvalue 0 -offvalue 1 set b($w.flag_checkc) vd_flag_checkc checkbutton $w.flag_debug -text $t(debug_cclient) -variable vfd(debug) set b($w.flag_debug) vd_debug_cclient grid $w.flags_lab $w.flag_secure -sticky ew grid x $w.flag_checkc -sticky w grid x $w.flag_debug -sticky w VFolderMailServerSetupState {} } # VFolderSetupMailServer -- # # Setup the details frame # # Arguments: # type - Type of mail-server # id - Id of element to show # new - Boolean indicating if this is a new object or not proc VFolderSetupMailServer {type id new} { global vf vfd vfd_old vFolderDef vFolderInbox t b VFolderClearWindow set w $vf(details) set vfd(new) $new set vfd(mode) mail_server set vfd(type) $type set vfd(id) $id set vfd(name) $id # Top information label $w.name_lab -text $t(name): -width $vf(mw) -anchor e entry $w.name -width 40 -textvariable vfd(name) grid $w.name_lab $w.name -sticky ew VFolderSetupMailServerDetails $w $id # Create copy of values foreach v [array names vfd] { set vfd_old($v) $vfd($v) } if {$vfd(new)} { $vf(but_apply) configure -text $t(create) -state normal $vf(but_restore) configure -text $t(cancel) -state normal } else { $vf(but_apply) configure -text $t(apply_changes) -state disabled $vf(but_restore) configure -text $t(restore_values) -state disabled } trace variable vfd w VFolderDefChange if {$new} { focus $w.name $w.name selection range 0 end $w.name icursor end } else { focus $w.host $w.host icursor end } } # VFolderMailServerSetupState -- # # Setup the state of the buttons in the MailServer pane # # Arguments: # elem - Name of changed element in vfs proc VFolderMailServerSetupState {elem} { global vf vfd ratHaveOpenSSL set w $vf(details) if {"method" == $elem && "tcp_custom" == $vfd(method)} { focus $w.conn_tcpcust.e } if {"tcp_custom" == $vfd(method)} { $w.conn_tcpcust.e configure -state normal -takefocus 1 } else { $w.conn_tcpcust.e configure -state disabled -takefocus 0 } if {0 == $ratHaveOpenSSL || "rsh" == $vfd(method)} { set ssl_state disabled set checkc_state disabled set secure_state disabled set vfd(ssl) 0 set vfd(notls) 1 set vfd(novalidate-cert) 1 set vfd(priv) none } else { set ssl_state normal if {"none" != $vfd(priv)} { set secure_state normal set checkc_state normal } else { set secure_state disabled set vfd(secure) 0 set checkc_state disabled set vfd(novalidate-cert) 1 } } if {"rsh" == $vfd(method)} { set ssh_state normal } else { set ssh_state disabled } $w.priv_ssl configure -state $ssl_state $w.priv_tls configure -state $ssl_state $w.flag_secure configure -state $secure_state $w.flag_checkc configure -state $checkc_state if {1 > [string compare "8.3.1" [info patchlevel]]} { $w.ssh_lab configure -state $ssh_state } if {"normal" == $ssh_state} { set col [option get $w.ssh_cmd activeForeground Color] } else { set col [option get $w.ssh_cmd disabledForeground Color] } $w.ssh_cmd configure -state $ssh_state -fg $col } # VFolderDefChange -- # # Trace procedure called whenever the user changes the vfolder definition # # Arguments: # name1, name2, op - Trace arguments proc VFolderDefChange {name1 name2 op} { global vf vfd if {"mail_server" == $vfd(mode) || "pop3" == $vfd(type)} { VFolderMailServerSetupState $name2 } $vf(but_apply) configure -state normal $vf(but_restore) configure -state normal set vf(changed) 1 set vf(unapplied_changes) 1 } # VFolderWrite -- # # Writes the list of vfolders to disk # # Arguments: proc VFolderWrite {} { global option vFolderDef vFolderVersion vFolderInbox \ mailServer vFolderOutgoing vFolderHold vFolderSpecials # Do nothing on errors if {[catch {open $option(ratatosk_dir)/vfolderlist w} fh]} { return } puts $fh "set vFolderVersion $vFolderVersion" if {"" != $vFolderInbox} { puts $fh "set vFolderInbox $vFolderInbox" } puts $fh "set vFolderHold $vFolderHold" puts $fh "set vFolderOutgoing $vFolderOutgoing" puts $fh "set vFolderSpecials $vFolderSpecials" foreach s [array names mailServer] { puts $fh [list set mailServer($s) $mailServer($s)] } foreach elem [array names vFolderDef] { puts $fh "set vFolderDef($elem) [list $vFolderDef($elem)]" } close $fh } # VFolderCheckChanges -- # # Checks if the definitions have changed in any way and if so rewrites # them. # # Arguments: proc VFolderCheckChanges {} { global vf vFolderDef vFolderSpecials # Has the tree-structure changed if {0 != [$vf(tree) getnumchanges]} { incr vf(changed) foreach i [array names vFolderDef] { if {"struct" == [lindex $vFolderDef($i) 1] && $vFolderSpecials != $i} { unset vFolderDef($i) } } VFolderReconstructStruct 0 {} $vf(folderitem) } if {$vf(changed)} { VFolderWrite } set vf(changed) 0 } # VFolderReconstructStruct -- # # Reconstruct a struct entry in the vFolderDef array from the tree widget # # Arguments: # nid - Node id in vFolderDef # name - name of nore # node - Node in tree to read proc VFolderReconstructStruct {nid name node} { global vFolderDef set contents {} foreach i [$node list] { set id [lindex [lindex $i 2] 1] lappend contents $id if {"node" == [lindex $i 0]} { VFolderReconstructStruct $id [lindex $i 1] [lindex $i 3] } } if {[info exists vFolderDef($nid)]} { switch [lindex $vFolderDef($nid) 1] { struct {set index 3} import {set index 5} } set vFolderDef($nid) \ [lreplace $vFolderDef($nid) $index $index $contents] } else { set vFolderDef($nid) [list [string trim $name] struct {} $contents] } } # VFolderConstructDef -- # # Reconstruct the folder definition # # Arguments: proc VFolderConstructDef {} { global vfd vfd_old vf t env set flags [list sort $vfd(sort) browse $vfd(browse) \ monitor $vfd(monitor) watch $vfd(watch) role $vfd(role)] switch -regexp $vfd(type) { file { if {"" == $vfd(filename)} { Popup "$t(illegal_file_spec): $vfd(filename)" $vf(w) return "" } regsub {/$} $env(HOME) {} home regsub ^~/ $vfd(filename) $home/ path set path [RatEncodeQP system $path] set def [list $vfd(name) file $flags $path] } mh { if {"" == $vfd(filename)} { Popup $t(need_mh_name) $vf(w) return "" } set path [RatEncodeQP system $vfd(filename)] set def [list $vfd(name) mh $flags $path] } dbase { if {"" == $vfd(keywords)} { Popup $t(need_keyword) $vf(w) return "" } set def [list $vfd(name) dbase $flags $vfd(extype) \ $vfd(exdate) [list and keywords $vfd(keywords)]] } imap|dis { if {"" == $vfd(mail_server)} { Popup $t(need_mail_server) $vf(w) return "" } if {1 == $vfd(disconnected)} { set vfd(type) dis } else { set vfd(type) imap } if {$vfd(new) && $vfd(manage)} { set vfd(mailbox_path) [RatEncodeMutf7 $vfd(mailbox_path)] } set path [RatEncodeQP system $vfd(mailbox_path)] set def [list $vfd(name) $vfd(type) $flags $vfd(mail_server) $path] if {!$vfd(is_import) && "dis" == $vfd(type) \ && $vfd_old(mailbox_path) != $vfd(mailbox_path)} { set f [RatOpenFolder $def] $f close } } pop3 { if {"" == $vfd(mail_server)} { Popup $t(need_mail_server) $vf(w) return "" } set def [list $vfd(name) pop3 $flags $vfd(mail_server)] } dynamic { if {![file isdirectory $vfd(filename)]} { Popup "$t(illegal_file_spec): $vfd(filename)" $vf(w) return "" } set path [RatEncodeQP system $vfd(filename)] set def [list $vfd(name) dynamic $flags $path sender] } } if {$vfd(is_import)} { set flags [list subscribed $vfd(subscribed) \ reimport $vfd(reimport)] set def [list $vfd(name) import $flags $def $vfd(pattern) {}] } return $def } # VFolderGetID -- # # Get an ID to use for a new vFolderDef # # Arguments: proc VFolderGetID {} { global vFolderDef set max [lindex [lsort -integer -decreasing [array names vFolderDef]] 0] return [expr $max + 1] } # VFolderApply -- # # Apply the changes in the current details view # # Arguments: proc VFolderApply {} { global vf vfd vfd_old vFolderDef mailServer t vFolderInbox env option \ folderWindowList if {0 == [string length $vfd(name)]} { Popup $t(need_name) $vf(w) return } set redraw 0 $vf(tree) autoredraw 0 if {"mail_server" == $vfd(mode) || "pop3" == $vfd(type)} { if {"" == $vfd(host) || "" == $vfd(user)} { Popup $t(need_host_and_user) $vf(w) $vf(tree) autoredraw 1 return } if {$vfd(id) != $vfd(name) && "mail_server" == $vfd(mode)} { if {[info exists mailServer($vfd(name))]} { Popup "$t(a_mailserver_named) $vfd(name) $t(already_exists)" $vf(tree) autoredraw 1 return } foreach id [array names vFolderDef] { if {"import" == [lindex $vFolderDef($id) 1]} { set d [lindex $vFolderDef($id) 3] set i 1 } else { set d $vFolderDef($id) set i 0 } if {[regexp {imap|pop3|dis} [lindex $d 1]] && $vfd(id) == [lindex $d 3]} { set d [lreplace $d 3 3 $vfd(name)] if {$i} { set vFolderDef($id) [lreplace $vFolderDef($id) 3 3 $d] } else { set vFolderDef($id) $d } } } unset mailServer($vfd(id)) } set port "" set flags {} switch $vfd(method) { tcp_default { if {"pop" == $vfd(type)} { if {$vfd(ssl)} { set port 995 } else { set port 110 } } else { if {$vfd(ssl)} { set port 993 } else { set port 143 } } } tcp_custom { set port $vfd(port) } rsh { set port {} } } if {"pop3" == $vfd(type)} { lappend flags pop3 } foreach f {ssl notls novalidate-cert secure debug} { if {$vfd($f)} { lappend flags $f } } if {$vfd(ssh_cmd) != $option(ssh_template)} { lappend flags [list ssh-cmd $vfd(ssh_cmd)] } if {"mail_server" == $vfd(mode)} { set n $vfd(name) } else { set n $vfd(mail_server) } set mailServer($n) [list $vfd(host) $port $flags $vfd(user)] if {"mail_server" == $vfd(mode)} { VFolderAddMailServers set redraw 1 $vf(tree) select [list $vfd(type) $vfd(name)] } } if {"folder" == $vfd(mode)} { if {$vfd(name) != $vfd_old(name) || $vfd(new)} { set redraw 1 } set def [VFolderConstructDef] if {"" == $def} { $vf(tree) autoredraw 1 return } # If we have changed any significant parts of the folder definition # and we have any open instances then close all open instances. if {![VFolderSameBase $vFolderDef($vfd(id)) $def] && "" != [set oh [RatGetOpenHandler $vFolderDef($vfd(id))]]} { $oh close 1 foreach fhd [array names folderWindowList] { if {"$oh" == $folderWindowList($fhd)} { FolderWindowClear $fhd } } } if {$vfd(monitor) != $vfd_old(monitor)} { global vFolderMonitorFH vFolderMonitorID folderExists incr redraw if {$vfd(monitor)} { if {![catch {RatOpenFolder $def} nhd]} { set vFolderMonitorFH($vfd(id)) $nhd set vFolderMonitorID($nhd) $vfd(id) set folderExists($nhd) $folderExists($nhd) } } else { catch {$vFolderMonitorFH($vfd(id)) close} catch {unset vFolderMonitorID($vFolderMonitorFH)} catch {unset vFolderMonitorFH($vfd(id))} } } if {$vfd(watch) != $vfd_old(watch)} { incr redraw } if {$vfd_old(inbox) != $vfd(inbox)} { incr redraw if {$vfd(inbox)} { set oldId $vFolderInbox set vFolderInbox $vfd(id) set text [VFolderGetItemName $oldId] $vf(tree) itemchange [list folder $oldId] -label $text } else { set vFolderInbox {} } } if {$vfd(type) == "imap" && $vfd_old(type) == "dis"} { RatDeleteDisconnected $vfd(olddef) } set vFolderDef($vfd(id)) $def if {$redraw} { set text [VFolderGetItemName $vfd(id)] set image [VFolderGetItemImage $vfd(id)] set iid [list folder $vfd(id)] if {"import" == [lindex $def 1]} { set pos [$vf(tree) getpos $iid] # We can't use itemchange here since we must change # from folder to struct $vf(tree) delete $iid VFDInsert [lindex $pos 0] [lindex $pos 1] $vfd(id) folders } else { $vf(tree) itemchange $iid -label $text -image $image } } if {$vfd(manage)} { RatBusy { if {$vfd(new)} { if {[catch {RatCreateFolder $def} err]} { if {1 == [RatDialog $vf(w) ! \ "$t(mailbox_create_failed) $err" \ {} 0 $t(continue) $t(abort)]} { trace vdelete vfd w VFolderDefChange $vf(but_apply) configure -state disabled $vf(but_restore) configure -state disabled set vf(unapplied_changes) 0 $vf(tree) delete [list folder $vfd(id)] $vf(tree) redraw unset vFolderDef($vfd(id)) foreach c [winfo children $vf(details)] { destroy $c } return } } } } } } elseif {"struct" == $vfd(mode)} { if {$vfd(name) != $vfd_old(name)} { $vf(tree) itemchange [list folder $vfd(id)] -label $vfd(name) set redraw 1 set vFolderDef($vfd(id)) \ [lreplace $vFolderDef($vfd(id)) 0 0 $vfd(name)] } } $vf(tree) autoredraw 1 if {$redraw} { $vf(tree) redraw } $vf(but_apply) configure -state disabled $vf(but_restore) configure -state disabled set vf(unapplied_changes) 0 if {$vfd(new)} { if {"mail_server" == $vfd(mode)} { VFolderSetupMailServer $vfd(type) $vfd(name) 0 } else { if {1 == $vfd(import_on_create)} { VFolderReimport $vfd(id) } VFolderSetupDetails $vfd(id) 0 {} } } # Create copy of values foreach v [array names vfd] { set vfd_old($v) $vfd($v) } VFolderCheckChanges return } # VFolderSameBase -- # # Check if the two given folder definitions points to the same base folder # # Arguments: # def1, def2 - Two folder definitions proc VFolderSameBase {def1 def2} { if {[lindex $def1 1] != [lindex $def2 1]} { return 0 } for {set i 3} {$i <= [llength $def1]} {incr i} { if {[lindex $def1 $i] != [lindex $def2 $i]} { return 0 } } return 1 } # VFolderRemoveNew -- # # Invoked when canceling creating a folder # # Arguments: proc VFolderRemoveNew {} { global vfd vf mailServer vFolderDef if {"mail_server" == $vfd(mode)} { $vf(tree) delete [list $vfd(type) $vfd(id)] unset mailServer($vfd(name)) } else { $vf(tree) delete [list folder $vfd(id)] unset vFolderDef($vfd(id)) } foreach c [winfo children $vf(details)] { destroy $c } if {[info exists vfd(old)]} { unset vfd_old set vfd_old(marker) {} } trace vdelete vfd w VFolderDefChange } # VFolderRestore -- # # Restore values in folder definition # # Arguments: proc VFolderRestore {} { global vfd vfd_old vf vFolderDef if {0 == $vfd(new)} { foreach v [array names vfd_old] { set vfd($v) $vfd_old($v) } } else { VFolderRemoveNew } set vf(unapplied_changes) 0 $vf(but_apply) configure -state disabled $vf(but_restore) configure -state disabled VFolderCheckChanges } # VFolderNewStruct -- # # Initiate creation of a new folder structure object # # Arguments: # context - menu or tree, describes from where we were invoked proc VFolderNewStruct {context} { global vf vFolderDef option t # Check if it is ok if {0 == [VFolderChangeOk]} { return } # Determine where to place it if {"menu" == $context} { set vf(into_item) $vf(folderitem) set vf(into_pos) end } # Create template set id [VFolderGetID] set vFolderDef($id) [list $t(new_submenu) struct {} {}] $vf(tree) autoredraw 0 VFDInsert $vf(into_item) $vf(into_pos) $id folders $vf(tree) select [list folder $id] $vf(tree) autoredraw 1 $vf(tree) redraw VFolderSetupDetails $id 1 {} } # VFolderAddFolder -- # # Adds a folder created by the wizard # # Arguments: # context - Add context # def - Folder definition proc VFolderAddFolder {context def} { global vf vFolderDef # Determine where to place it if {"menu" == $context} { set vf(into_item) $vf(folderitem) set vf(into_pos) end } set id [VFolderGetID] set vFolderDef($id) $def $vf(tree) autoredraw 0 VFDInsert $vf(into_item) $vf(into_pos) $id folders $vf(tree) select [list folder $id] $vf(tree) autoredraw 1 $vf(tree) redraw VFolderSetupDetails $id 0 {} return $id } # VFolderPostMenu: # # Post a the menu for a given entry. # # Arguments: # x, y - Position of pointer, this is where we will popup the menu # id - Id of selected element # m - Menu to popup proc VFolderPostMenu {x y id m} { global vf vFolderInbox b option set pos [$vf(tree) getpos $id] set vf(into_item) [lindex $pos 0] set vf(into_pos) [expr {[lindex $pos 1]+1}] set vf(template) [lindex $id 1] set vf(iid) $id set delete_state normal set delete_bal vd_delete set rid [lindex $id 1] if {"folder" == [lindex $id 0] && $rid == $vFolderInbox} { set delete_state disabled set delete_bal cant_delete_inbox } else { foreach r $option(roles) { if {$rid == $option($r,save_outgoing)} { set delete_state disabled set delete_bal "cant_delete_used" break } } } $m entryconfigure $vf(folder_menu_delete) -state $delete_state set b($m,$vf(folder_menu_delete)) $delete_bal tk_popup $m $x $y } # VFolderReimport # # Reimport a folder now. First we must get the definition and then we can do # the import # # Arguments: # id - Id of folder to reimport. proc VFolderReimport {id} { global vFolderDef vf if {![VFolderChangeOk]} { return } RatBusy {RatImport $id} VFolderRedrawSubtree $id } # VFolderRedrawSubtree # # Redraws a subtree in the list. Useful after reimporting stuff etc. # # Arguments: # id - Id of folder to redraw the children of proc VFolderRedrawSubtree {id} { global vFolderDef vf $vf(tree) autoredraw 0 $vf(item,$id) clear foreach sid [lindex $vFolderDef($id) 5] { VFDInsert $vf(item,$id) end $sid {} } $vf(tree) autoredraw 1 $vf(tree) redraw VFolderCheckChanges } # VFolderDeleteServer -- # # Perhaps delete the current server object # # Arguments: proc VFolderDeleteServer {} { global t vf vfd vFolderDef mailServer set u "" foreach id [array names vFolderDef] { if {"import" == [lindex $vFolderDef($id) 1]} { set f [lindex $vFolderDef($id) 3] } else { set f $vFolderDef($id) } if {[regexp "imap|pop3|dis" [lindex $f 1]] && [lindex $vf(iid) 1] == [lindex $f 3]} { lappend ids $id set u "$u\n\t[lindex $f 0]" } } if {"" != $u} { Popup "$t(mailserver_used): $u" $vf(w) return } $vf(tree) delete $vf(iid) unset mailServer($vf(template)) if {[info exists vfd(id)] && $vfd(id) == [lindex $vf(iid) 1]} { VFolderClearWindow } VFolderCheckChanges } # VFolderDeleteFolder -- # # Perhaps delete the current folder object # # Arguments: proc VFolderDeleteFolder {} { global t vf vfd vFolderDef option mailServer set id [lindex $vf(iid) 1] if {"struct" == [lindex $vFolderDef($id) 1]} { set children [lindex $vFolderDef($id) 3] } elseif {"import" == [lindex $vFolderDef($id) 1]} { set children [lindex $vFolderDef($id) 5] } else { set children {} } if {0 < [llength $children]} { if {[RatDialog $vf(w) ! $t(item_not_empty) {} 0 $t(delete) \ $t(cancel)]} { return } } set keep [RatDialog $vf(w) ! $t(delete_what_folder) {} 1 \ $t(delete_both) $t(only_in_tkrat)] $vf(tree) delete $vf(iid) set idlist $id for {set i 0} {$i < [llength $idlist]} {incr i} { set id [lindex $idlist $i] if {0 == $keep} { RatDeleteFolder $vFolderDef($id) } if {"struct" == [lindex $vFolderDef($id) 1]} { set idlist [concat $idlist [lindex $vFolderDef($id) 3]] } elseif {"import" == [lindex $vFolderDef($id) 1]} { set idlist [concat $idlist [lindex $vFolderDef($id) 5]] } elseif {"pop3" == [lindex $vFolderDef($id) 1]} { unset mailServer([lindex $vFolderDef($id) 3]) } elseif {"dis" == [lindex $vFolderDef($id) 1]} { RatDeleteDisconnected $vFolderDef($id) } unset vFolderDef($id) } if {[info exists vfd(id)] && $vfd(id) == $id} { VFolderClearWindow } VFolderCheckChanges } # VFolderReimportAll - # # Reimport all import-folders # # Arguments: proc VFolderReimportAll {} { global vFolderDef vf if {![info exists vf(w)]} { set vf(w) "" } if {[winfo exists $vf(w)]} { if {![VFolderChangeOk]} { return } $vf(tree) autoredraw 0 } RatBusy { foreach id [array names vFolderDef] { if {![info exists vFolderDef($id)]} { continue } if {"import" != [lindex $vFolderDef($id) 1]} { continue } RatImport $id if {[winfo exists $vf(w)] && [info exists vf(item,$id)]} { $vf(item,$id) clear foreach sid [lindex $vFolderDef($id) 5] { VFDInsert $vf(item,$id) end $sid {} } } } } if {[winfo exists $vf(w)]} { $vf(tree) autoredraw 1 $vf(tree) redraw } } tkrat_2.2cvs20100105-dfsg.orig/tkrat/vfolderwizard.tcl000066400000000000000000000723421137544547100225270ustar00rootroot00000000000000# vfolderwizard.tcl - # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # # # Functions which implements the folder creation wizard # ############################################################################### # Generic functions proc VFolderWizardStart {context } { global t propBigFont idCnt set id vfolderwizard[incr idCnt] set w .$id upvar \#0 $id hd set hd(history) {} set hd(top) $w set hd(context) $context # Create toplevel and insert infrastructure toplevel $w -class TkRat wm title $w $t(vfolderdef) label $w.title -textvariable ${id}(title) -font $propBigFont set hd(body) [rat_scrollframe::create $w.body -bd 10] frame $w.buttons button $w.buttons.prev -text "< $t(wiz_previous)" -state disabled \ -command "set cmd \[lindex \$${id}(history) end-1\]; \ set ${id}(history) \[lreplace \$${id}(history) end-1 end\]; \ \$cmd $id" button $w.buttons.next -text "$t(wiz_next) >" -state disabled \ -command "VFolderWizardStep1Done $id" button $w.buttons.cancel -text $t(wiz_cancel) \ -command "::tkrat::winctl::RecordGeometry vFolderWizard $w $w.body; unset $id; destroy $w" pack $w.buttons.cancel -side right -padx 20 -pady 5 pack $w.buttons.next $w.buttons.prev -side right pack $w.title -side top -fill x pack $w.buttons -side bottom -fill x pack $w.body -fill both -expand 1 set hd(next) $w.buttons.next set hd(prev) $w.buttons.prev set hd(cancel) $w.buttons.cancel set hd(bodym) $w.body bind $w "$hd(next) invoke" bind $w "$w.buttons.cancel invoke" wm protocol $w WM_DELETE_WINDOW "$w.buttons.cancel invoke" grid propagate $w.body 0 grid columnconfigure $w.body 0 -weight 1 ::tkrat::winctl::SetGeometry vFolderWizard $w $w.body VFolderWizardStep1 $id } proc VFolderWizardReset {id} { upvar \#0 $id hd global t eval destroy [winfo children $hd(body)] grid columnconfigure $hd(body) 0 -weight 0 grid columnconfigure $hd(body) 1 -weight 0 $hd(next) configure -text "$t(wiz_next) >" # Work around bug in grid (reported and fixed Oct 2002) grid size $hd(body) } proc VFolderWizardStep1 {id} { upvar \#0 $id hd global t vFolderDef # Come up with name suggestion set max 0 foreach i [array names vFolderDef] { if {[regexp "$t(folder) (\[0-9\]+)" \ [lindex $vFolderDef($i) 0] unused num] && $num > $max} { set max $num } } set hd(name) "Folder [incr max]" if {![info exists hd(type)]} { set hd(type) file } VFolderWizardReset $id lappend hd(history) VFolderWizardStep1 # Setup the protocl selection set hd(title) $t(type) label $hd(body).label -text $t(select_type_of_folder) grid $hd(body).label -pady 10 set a [list $hd(next) $hd(prev)] foreach tt {{file VFolderWizardFile} {imap VFolderWizardIMAP} {dbase VFolderWizardDBase} {mh VFolderWizardMH} {pop VFolderWizardPOP} {dynamic VFolderWizardDynamic}} { set type [lindex $tt 0] radiobutton $hd(body).$type -anchor w \ -variable ${id}(type) -value $type -text $t(fw_$type) \ -command "$hd(next) configure -state normal; \ set ${id}(nextcmd) [lindex $tt 1]" grid $hd(body).$type -sticky we lappend a $hd(body).$type if {$type == $hd(type)} { set hd(nextcmd) [lindex $tt 1] } } $hd(prev) configure -state disabled $hd(next) configure -command "\$${id}(nextcmd) $id" if {"" != $hd(type)} { $hd(next) configure -state normal } grid rowconfigure $hd(body) 20 -weight 1 SetupShortcuts $a } proc VFolderWizardTestImport {id} { upvar \#0 $id hd global mailServer idCnt t # Create window set w .w[incr idCnt] toplevel $w -class TkRat wm title $w $t(test_import) if {"imap" == $hd(type)} { label $w.server_l -text $t(server): -anchor e label $w.server -text $hd(server) -anchor w grid $w.server_l $w.server -sticky ew } label $w.path_l -text $t(path): -anchor e label $w.path -text $hd(path) -anchor w grid $w.path_l $w.path -sticky ew frame $w.f scrollbar $w.f.scroll \ -relief sunken \ -bd 1 \ -highlightthickness 0 \ -command "$w.f.list yview" listbox $w.f.list \ -yscroll "$w.f.scroll set" \ -relief sunken \ -bd 1 \ -exportselection false \ -highlightthickness 0 pack $w.f.scroll -side right -fill y pack $w.f.list -expand 1 -fill both grid $w.f - -pady 10 -padx 5 -sticky nsew button $w.close -text $t(dismiss) -command "destroy $w" grid $w.close - -pady 10 grid columnconfigure $w 0 -weight 1 grid columnconfigure $w 1 -weight 1 grid rowconfigure $w 2 -weight 1 bind $w "$w.close invoke" ::tkrat::winctl::SetGeometry testImportResult $w $w.f.list bind $w.f.list \ "::tkrat::winctl::RecordGeometry testImportResult $w $w.f.list" # Do test import set cleanup "" switch $hd(type) { imap { if {"new" == $hd(server_action)} { set mailServer($hd(server)) $hd(mailServer) set cleanup "unset mailServer($hd(server))" } set def [list $hd(name) imap {} $hd(server) $hd(path)] } file { set def [list $hd(name) file {} $hd(path)] } mh { set def [list $hd(name) mh {} $hd(path)] } } set r [RatBusy [list RatTestImport "%" $def]] if {"" != $cleanup} { eval $cleanup } # Build result set result {} foreach i $r { set f "[lindex $i 2]" if {-1 == [lsearch -exact [lindex $i 0] noinferiors]} { set f "$f[lindex $i 1]" } lappend result $f } # Show result eval $w.f.list insert end [lsort $result] } proc VFolderWizardShowImport {id} { upvar \#0 $id hd global t VFolderWizardReset $id $hd(next) configure -text $t(close) \ -command "$hd(cancel) configure -state normal; $hd(cancel) invoke" $hd(prev) configure -state disable $hd(cancel) configure -state disable grid columnconfigure $hd(body) 1 -weight 1 set hd(num_found) 0 set hd(last_found) {} set hd(title) $t(importing)... rat_flowmsg::create $hd(body).message -text "$t(currently_importing)" grid $hd(body).message - -pady 10 frame $hd(body).space1 -height 20 grid $hd(body).space1 label $hd(body).found_l -text " $t(found):" -anchor e label $hd(body).counter -textvariable ${id}(num_found) -anchor w grid $hd(body).found_l $hd(body).counter -pady 5 -sticky ew label $hd(body).last_l -text " $t(last):" -anchor e label $hd(body).last -textvariable ${id}(last_found) -anchor w grid $hd(body).last_l $hd(body).last -pady 5 -sticky ew update set hd(next_update) [expr [clock clicks -milliseconds]+100] RatBusy {RatImport $hd(folder_id) "VFolderWizardImportCallback $id"} VFolderRedrawSubtree $hd(folder_id) } proc VFolderWizardImportCallback {id folder flags} { upvar \#0 $id hd incr hd(num_found) set hd(last_found) $folder set now [clock clicks -milliseconds] if {$now > $hd(next_update)} { update set hd(next_update) [expr [clock clicks -milliseconds]+100] } } proc VFolderWizardServerCheck {id} { upvar \#0 $id hd global t set state normal if {"" == $hd(host) || "" == $hd(name) || "" == $hd(user) || ("tcp_custom" == $hd(method) && "" == $hd(port)) || ("rsh" == $hd(method) && "" == $hd(ssh_cmd))} { set state disabled } $hd(next) configure -state $state } proc VFolderWizardBuildNewServer {id prot} { upvar \#0 $id hd global option set port "" set flags {} if {"pop3" == $prot} { lappend flags pop3 set defaultPort 110 } else { set defaultPort 143 } switch $hd(method) { tcp_default { set port $defaultPort } tcp_custom { set port $hd(port) } rsh { set port {} } } foreach f {ssl notls novalidate-cert} { if {$hd($f)} { lappend flags $f } } if {$hd(ssh_cmd) != $option(ssh_template)} { lappend flags [list ssh-cmd $hd(ssh_cmd)] } return [list $hd(host) $port $flags $hd(user)] } proc CheckCClientFolder {id def} { upvar \#0 $id hd global t mailServer if {"new" == $hd(server_action)} { set mailServer($hd(server)) $hd(mailServer) } # Test import just this name # not-exist -> new folder # selectable -> insert as existing # inferiors -> import set import_result [RatBusy [list RatTestImport "" $def]] if {0 == [llength $import_result]} { # Did not exist, create it RatBusy {catch {RatCreateFolder $def} r} if {"1" != $r} { return } set hd(folder_id) [VFolderAddFolder $hd(context) $def] } else { set flags [lindex [lindex $import_result 0] 0] if {-1 == [lsearch -exact $flags noselect]} { # Selectable, add it set hd(folder_id) [VFolderAddFolder $hd(context) $def] } if {-1 == [lsearch -exact $flags noinferiors]} { # Can have inferiors, see if it does set import_result [RatBusy [list RatTestImport "%" $def]] if {0 < [llength $import_result]} { # There are inferiors, import them set def [list $hd(name) import {} $def * {}] set hd(folder_id) [VFolderAddFolder $hd(context) $def] } } } if {"new" == $hd(server_action)} { VFolderAddMailServers } if {"import" == [lindex $def 1]} { VFolderWizardShowImport $id } else { $hd(cancel) invoke } } ############################################################################### # File folders proc VFolderWizardFile {id} { upvar \#0 $id hd global t VFolderWizardReset $id grid columnconfigure $hd(body) 1 -weight 1 lappend hd(history) VFolderWizardFile set hd(title) $t(define_file_folder) rat_flowmsg::create $hd(body).message -text "$t(why_filefolder)" grid $hd(body).message - -pady 10 label $hd(body).name_l -text $t(name): -anchor e entry $hd(body).name_e -textvariable ${id}(name) grid $hd(body).name_l $hd(body).name_e -sticky we frame $hd(body).space -height 20 grid $hd(body).space if {![info exists hd(path)]} { set hd(path) "[pwd]/" } label $hd(body).file_l -text $t(path): -anchor e entry $hd(body).file_e -textvariable ${id}(path) button $hd(body).browse -text $t(browse)... \ -command "Browse $hd(body) ${id}(path) dirok" grid $hd(body).file_l $hd(body).file_e -sticky we grid x $hd(body).browse -sticky e frame $hd(body).space2 -height 20 grid $hd(body).space2 $hd(next) configure -text $t(wiz_finish) \ -command "VFolderWizardFileDone $id" rat_scrollframe::recalc $hd(bodym) $hd(prev) configure -state normal SetupShortcuts [list $hd(next) $hd(prev) $hd(body).browse] $hd(body).name_e selection range 0 end $hd(body).name_e icursor end focus $hd(body).name_e } proc VFolderWizardFileDone {id} { upvar \#0 $id hd global t set def [list $hd(name) file {} $hd(path)] if {[file isdirectory $hd(path)]} { set def [list $hd(name) import {} $def * {}] } elseif {[file exists $hd(path)]} { RatBusy {set fail [catch {RatCheckFolder $def} i]} if {$fail} { return } } else { RatBusy {catch {RatCreateFolder $def} r} if {"1" != $r} { return } } set hd(folder_id) [VFolderAddFolder $hd(context) $def] if {[file isdirectory $hd(path)]} { VFolderWizardShowImport $id } else { $hd(cancel) invoke } } ############################################################################### # IMAP folders proc VFolderWizardIMAP {id} { upvar \#0 $id hd global t mailServer VFolderWizardReset $id lappend hd(history) VFolderWizardIMAP set imaps {} foreach ms [lsort -dictionary [array names mailServer]] { if { -1 == [lsearch -exact [lindex $mailServer($ms) 2] pop3]} { lappend imaps $ms } } if {![info exists hd(server)]} { if {0 == [llength $imaps]} { set hd(server_action) new VFolderWizardIMAPStep1Done $id return } else { set hd(server) [lindex $imaps 0] set hd(server_action) reuse } } grid columnconfigure $hd(body) 1 -weight 1 set hd(title) $t(define_imap_folder) rat_flowmsg::create $hd(body).message -text "$t(imap_step1)" grid $hd(body).message - -pady 10 frame $hd(body).space1 -height 20 grid $hd(body).space1 radiobutton $hd(body).new -text $t(define_new) -value new -anchor w \ -variable ${id}(server_action) -command "VFolderWizardIMAPCheck $id" grid $hd(body).new -sticky we frame $hd(body).space2 -height 10 grid $hd(body).space2 radiobutton $hd(body).reuse -text $t(reuse_imap) \ -variable ${id}(server_action) \ -value reuse -command "VFolderWizardIMAPCheck $id" menubutton $hd(body).sel -indicatoron 1 -relief raised -bd 1 \ -menu $hd(body).sel.m -textvariable ${id}(server) -width 20 \ -takefocus 1 -highlightthickness 1 menu $hd(body).sel.m foreach m $imaps { $hd(body).sel.m add command -label $m -command \ [list VFolderWizardIMAPSelServer $id $m] } grid $hd(body).reuse $hd(body).sel -sticky w $hd(next) configure -command "VFolderWizardIMAPStep1Done $id" VFolderWizardIMAPCheck $id rat_scrollframe::recalc $hd(bodym) SetupShortcuts [list $hd(next) $hd(prev) $hd(body).new $hd(body).reuse] $hd(prev) configure -state normal } proc VFolderWizardIMAPCheck {id} { upvar \#0 $id hd set state normal if {"reuse" == $hd(server_action) && "" == $hd(server)} { set state disabled } $hd(next) configure -state $state } proc VFolderWizardIMAPSelServer {id server} { upvar \#0 $id hd set hd(server_action) reuse set hd(server) $server VFolderWizardIMAPCheck $id } proc VFolderWizardIMAPStep1Done {id} { upvar \#0 $id hd if {"reuse" == $hd(server_action)} { VFolderWizardIMAPPath $id } else { VFolderWizardIMAPServer $id } } proc VFolderWizardIMAPServer {id {mode wizard}} { global t option env upvar \#0 $id hd if {"wizard" == $mode} { VFolderWizardReset $id lappend hd(history) VFolderWizardIMAPServer } # Defaults if {![info exists hd(user)]} { set hd(user) $env(USER) } if {![info exists hd(method)]} { set hd(method) tcp_default set hd(ssh_cmd) $option(ssh_template) set hd(priv) tls set hd(ssl) 0 set hd(notls) 0 } rat_flowmsg::create $hd(body).message -text "$t(imap_def)" grid $hd(body).message - -pady 10 label $hd(body).host_l -text $t(host): -anchor e entry $hd(body).host_e -textvariable ${id}(host) grid $hd(body).host_l $hd(body).host_e -sticky ew bind $hd(body).host_e "VFolderWizardServerCheck $id" label $hd(body).user_l -text $t(user): -anchor e entry $hd(body).user_e -textvariable ${id}(user) grid $hd(body).user_l $hd(body).user_e -sticky ew bind $hd(body).user_e "VFolderWizardServerCheck $id" frame $hd(body).msp2 -height 10 grid $hd(body).msp2 label $hd(body).conn_l -text $t(connect): -anchor e radiobutton $hd(body).conn_tcpdef -text $t(tcp_default) \ -variable ${id}(method) -value tcp_default -anchor w \ -command "VFolderWizardServerCheck $id" frame $hd(body).conn_tcpcust radiobutton $hd(body).conn_tcpcust.b -text $t(tcp_custom): \ -variable ${id}(method) -value tcp_custom \ -command "VFolderWizardServerCheck $id" entry $hd(body).conn_tcpcust.e -width 6 -textvariable ${id}(port) pack $hd(body).conn_tcpcust.b $hd(body).conn_tcpcust.e -side left radiobutton $hd(body).conn_rsh -text $t(rsh_ssh) -variable ${id}(method) \ -value rsh -command "VFolderWizardServerCheck $id" grid $hd(body).conn_l $hd(body).conn_tcpdef - -sticky ew grid x $hd(body).conn_tcpcust -sticky w grid x $hd(body).conn_rsh -sticky w label $hd(body).ssh_l -text $t(ssh_command): -anchor e entry $hd(body).ssh_e -width 40 -textvariable ${id}(ssh_cmd) grid $hd(body).ssh_l $hd(body).ssh_e -sticky ew bind $hd(body).ssh_e "VFolderWizardServerCheck $id" frame $hd(body).msp3 -height 10 grid $hd(body).msp3 label $hd(body).priv_l -text $t(privacy): -anchor e radiobutton $hd(body).priv_ssl -text $t(use_ssl) -anchor w \ -variable ${id}(priv) -value ssl \ -command "set ${id}(ssl) 1;set ${id}(notls) 1" radiobutton $hd(body).priv_tls -text $t(try_tls) \ -variable ${id}(priv) -value tls \ -command "set ${id}(ssl) 0;set ${id}(notls) 0" radiobutton $hd(body).priv_none -text $t(no_encryption) \ -variable ${id}(priv) -value none \ -command "set ${id}(ssl) 0;set ${id}(notls) 1" grid $hd(body).priv_l $hd(body).priv_ssl -sticky ew grid x $hd(body).priv_tls -sticky w grid x $hd(body).priv_none -sticky w frame $hd(body).msp4 -height 10 grid $hd(body).msp4 label $hd(body).flags_l -text $t(flags): -anchor e checkbutton $hd(body).flag_checkc -text $t(ssl_check_cert) \ -variable ${id}(novalidate-cert) -onvalue 0 -offvalue 1 -anchor w grid $hd(body).flags_l $hd(body).flag_checkc -sticky ew if {"wizard" == $mode} { $hd(next) configure -state disabled \ -command "VFolderWizardIMAPPath $id" rat_scrollframe::recalc $hd(bodym) } VFolderWizardServerCheck $id focus $hd(body).host_e } proc VFolderWizardIMAPPath {id} { upvar \#0 $id hd global t mailServer option # Possibly setup server if {"new" == $hd(server_action)} { set hd(mailServer) [VFolderWizardBuildNewServer $id imap] set hd(server) $hd(host) set i 1 while {[info exists mailServer($hd(server))]} { set hd(server) "$hd(host)-[incr i]" } } if {![info exists hd(path)]} { set hd(path) "" } # Populate window VFolderWizardReset $id lappend hd(history) VFolderWizardIMAPPath grid columnconfigure $hd(body) 1 -weight 1 rat_flowmsg::create $hd(body).message -text "$t(why_imap_folder)" grid $hd(body).message - -pady 10 label $hd(body).name_l -text $t(name): -anchor e entry $hd(body).name_e -textvariable ${id}(name) grid $hd(body).name_l $hd(body).name_e -sticky we frame $hd(body).space -height 20 grid $hd(body).space label $hd(body).path_l -text $t(path): -anchor e entry $hd(body).path_e -textvariable ${id}(path) grid $hd(body).path_l $hd(body).path_e -sticky we frame $hd(body).space2 -height 20 grid $hd(body).space2 rat_flowmsg::create $hd(body).dismess -text "$t(imap_offline)" grid $hd(body).dismess - checkbutton $hd(body).use_dis -variable ${id}(use_dis) \ -text $t(enable_offline) grid $hd(body).use_dis - -sticky w -padx 10 frame $hd(body).space3 -height 20 grid $hd(body).space3 rat_flowmsg::create $hd(body).testmsg -text "$t(imap_test_import)" grid $hd(body).testmsg - button $hd(body).test -text $t(test_import)... \ -command "VFolderWizardTestImport $id" grid $hd(body).test - $hd(next) configure -text $t(wiz_finish) \ -command "VFolderWizardIMAPDone $id" rat_scrollframe::recalc $hd(bodym) SetupShortcuts [list $hd(next) $hd(prev)] $hd(body).name_e selection range 0 end $hd(body).name_e icursor end focus $hd(body).name_e } proc VFolderWizardIMAPDone {id} { upvar \#0 $id hd if $hd(use_dis) { set proto dis } else { set proto imap } set def [list $hd(name) $proto {} $hd(server) $hd(path)] CheckCClientFolder $id $def } ############################################################################### # MH folders proc VFolderWizardMH {id} { upvar \#0 $id hd global t VFolderWizardReset $id grid columnconfigure $hd(body) 1 -weight 1 lappend hd(history) VFolderWizardMH if {![info exists hd(path)]} { set hd(path) "" } set hd(server_action) none rat_flowmsg::create $hd(body).message -text "$t(why_mhfolder)" grid $hd(body).message - -pady 10 label $hd(body).name_l -text $t(name): -anchor e entry $hd(body).name_e -textvariable ${id}(name) grid $hd(body).name_l $hd(body).name_e -sticky we -pady 10 label $hd(body).path_l -text $t(path): -anchor e entry $hd(body).path_e -textvariable ${id}(path) grid $hd(body).path_l $hd(body).path_e -sticky we -pady 10 rat_flowmsg::create $hd(body).testmsg -text "$t(mh_test_import)" grid $hd(body).testmsg - button $hd(body).test -text $t(test_import)... \ -command "VFolderWizardTestImport $id" grid $hd(body).test - $hd(next) configure -text $t(wiz_finish) \ -command "VFolderWizardMHDone $id" rat_scrollframe::recalc $hd(bodym) SetupShortcuts [list $hd(next) $hd(prev)] $hd(body).name_e selection range 0 end $hd(body).name_e icursor end focus $hd(body).name_e $hd(prev) configure -state normal } proc VFolderWizardMHDone {id} { upvar \#0 $id hd set def [list $hd(name) mh {} $hd(path)] CheckCClientFolder $id [list $hd(name) mh {} $hd(path)] } ############################################################################### # DBase folders proc VFolderWizardDBase {id} { upvar \#0 $id hd global t option VFolderWizardReset $id grid columnconfigure $hd(body) 1 -weight 1 lappend hd(history) VFolderWizardDBase set hd(title) $t(define_dbase_folder) rat_flowmsg::create $hd(body).message -text $t(why_dbase_folder) grid $hd(body).message - - -pady 10 label $hd(body).name_l -text $t(name): -anchor e entry $hd(body).name_e -textvariable ${id}(name) grid $hd(body).name_l $hd(body).name_e - -sticky we frame $hd(body).space -height 20 grid $hd(body).space if {![info exists hd(extype)]} { set hd(extype) $option(def_extype) set hd(exdate) $option(def_exdate) } label $hd(body).kw_lab -text $t(keywords): -anchor e entry $hd(body).kw_entry -textvariable ${id}(keywords) -width 20 grid $hd(body).kw_lab $hd(body).kw_entry - -sticky we -pady 10 label $hd(body).exdate_lab -text $t(exdate): -anchor e entry $hd(body).exdate_entry -textvariable ${id}(exdate) label $hd(body).exdate_unit -text ($t(days)) -anchor w grid $hd(body).exdate_lab $hd(body).exdate_entry \ $hd(body).exdate_unit -sticky we -pady 10 label $hd(body).extype_lab -text $t(extype): -anchor e frame $hd(body).extype foreach et {none remove incoming backup} { radiobutton $hd(body).extype.$et -anchor w -text $t($et) \ -variable ${id}(extype) -value $et pack $hd(body).extype.$et -side top -fill x } grid $hd(body).extype_lab $hd(body).extype - -sticky wen $hd(next) configure -text $t(finish) \ -command "VFolderWizardDBaseDone $id" rat_scrollframe::recalc $hd(bodym) SetupShortcuts [list $hd(next) $hd(prev)] $hd(body).name_e selection range 0 end $hd(body).name_e icursor end focus $hd(body).name_e $hd(prev) configure -state normal } proc VFolderWizardDBaseDone {id} { upvar \#0 $id hd global t set def [list $hd(name) dbase {} $hd(extype) $hd(exdate) \ [list and keywords $hd(keywords)]] set hd(folder_id) [VFolderAddFolder $hd(context) $def] $hd(cancel) invoke } ############################################################################### # POP3 folders proc VFolderWizardPOP {id {mode wizard}} { global t option env upvar \#0 $id hd # Defaults if {![info exists hd(user)]} { set hd(user) $env(USER) } if {![info exists hd(method)]} { set hd(method) tcp_default set hd(ssh_cmd) $option(ssh_template) set hd(priv) tls set hd(ssl) 0 set hd(notls) 0 } set hd(title) $t(define_pop_folder) if {"wizard" == $mode} { VFolderWizardReset $id lappend hd(history) VFolderWizardPop rat_flowmsg::create $hd(body).message -text "$t(why_pop)" grid $hd(body).message - -pady 10 label $hd(body).name_l -text $t(name): -anchor e entry $hd(body).name_e -textvariable ${id}(name) grid $hd(body).name_l $hd(body).name_e - -sticky we frame $hd(body).space -height 20 grid $hd(body).space } label $hd(body).host_l -text $t(host): -anchor e entry $hd(body).host_e -textvariable ${id}(host) grid $hd(body).host_l $hd(body).host_e -sticky ew label $hd(body).user_l -text $t(user): -anchor e entry $hd(body).user_e -textvariable ${id}(user) grid $hd(body).user_l $hd(body).user_e -sticky ew frame $hd(body).msp2 -height 10 grid $hd(body).msp2 label $hd(body).conn_l -text $t(connect): -anchor e radiobutton $hd(body).conn_tcpdef -text $t(tcp_default) \ -variable ${id}(method) -value tcp_default -anchor w \ -command "VFolderWizardServerCheck $id" frame $hd(body).conn_tcpcust radiobutton $hd(body).conn_tcpcust.b -text $t(tcp_custom): \ -variable ${id}(method) -value tcp_custom \ -command "VFolderWizardServerCheck $id" entry $hd(body).conn_tcpcust.e -width 6 -textvariable ${id}(port) pack $hd(body).conn_tcpcust.b $hd(body).conn_tcpcust.e -side left radiobutton $hd(body).conn_rsh -text $t(rsh_ssh) -variable ${id}(method) \ -value rsh -command "VFolderWizardServerCheck $id" grid $hd(body).conn_l $hd(body).conn_tcpdef - -sticky ew grid x $hd(body).conn_tcpcust -sticky w grid x $hd(body).conn_rsh -sticky w label $hd(body).ssh_l -text $t(ssh_command): -anchor e entry $hd(body).ssh_e -width 40 -textvariable ${id}(ssh_cmd) grid $hd(body).ssh_l $hd(body).ssh_e -sticky ew frame $hd(body).msp3 -height 10 grid $hd(body).msp3 label $hd(body).priv_l -text $t(privacy): -anchor e radiobutton $hd(body).priv_ssl -text $t(use_ssl) -anchor w \ -variable ${id}(priv) -value ssl \ -command "set ${id}(ssl) 1;set ${id}(notls) 1" radiobutton $hd(body).priv_tls -text $t(try_tls) \ -variable ${id}(priv) -value tls \ -command "set ${id}(ssl) 0;set ${id}(notls) 0" radiobutton $hd(body).priv_none -text $t(no_encryption) \ -variable ${id}(priv) -value none \ -command "set ${id}(ssl) 0;set ${id}(notls) 1" grid $hd(body).priv_l $hd(body).priv_ssl -sticky ew grid x $hd(body).priv_tls -sticky w grid x $hd(body).priv_none -sticky w frame $hd(body).msp4 -height 10 grid $hd(body).msp4 label $hd(body).flags_l -text $t(flags): -anchor e checkbutton $hd(body).flag_checkc -text $t(ssl_check_cert) \ -variable ${id}(novalidate-cert) -onvalue 0 -offvalue 1 -anchor w grid $hd(body).flags_l $hd(body).flag_checkc -sticky ew if {"wizard" == $mode} { rat_flowmsg::create $hd(body).conn -text "$t(check_on_finish)" grid $hd(body).conn - -pady 10 $hd(next) configure -state disabled -text $t(wiz_finish) \ -command "VFolderWizardPOPDone $id" rat_scrollframe::recalc $hd(bodym) focus $hd(body).name_e SetupShortcuts [list $hd(next) $hd(prev)] $hd(prev) configure -state normal } else { focus $hd(body).host_e } foreach w [list $hd(body).host_e $hd(body).user_e $hd(body).ssh_e \ $hd(body).conn_tcpcust.e] { bind $w "VFolderWizardServerCheck $id" bind $w "VFolderWizardServerCheck $id" } VFolderWizardServerCheck $id } proc VFolderWizardPOPDone {id} { upvar \#0 $id hd global t option mailServer # Find a free pop-server-index set popi 0 foreach ms [array names mailServer] { if { -1 != [lsearch -exact [lindex $mailServer($ms) 2] pop3] && [string is integer $ms] && $ms > $popi} { set popi $ms } } incr popi # Create folder definition set def [list $hd(name) pop3 {} $popi] # Create server definition set mailServer($popi) [VFolderWizardBuildNewServer $id pop3] VFolderAddMailServers RatBusy {set fail [catch {RatCheckFolder $def} i]} if {$fail} { Popup "$t(failed_to_open_mailbox): $i" $hd(top) unset mailServer($popi) return } set hd(folder_id) [VFolderAddFolder $hd(context) $def] $hd(cancel) invoke } ############################################################################### # Dynamic folders proc VFolderWizardDynamic {id} { upvar \#0 $id hd global t VFolderWizardReset $id grid columnconfigure $hd(body) 1 -weight 1 lappend hd(history) VFolderWizardDynamic set hd(title) $t(define_dynamic_folder) rat_flowmsg::create $hd(body).message -text "$t(why_dynamic_folder)" grid $hd(body).message - -pady 10 label $hd(body).name_l -text $t(name): -anchor e entry $hd(body).name_e -textvariable ${id}(name) grid $hd(body).name_l $hd(body).name_e -sticky we frame $hd(body).space -height 20 grid $hd(body).space if {![info exists hd(path)]} { set hd(path) "[pwd]/" } label $hd(body).dir_l -text $t(path): -anchor e entry $hd(body).dir_e -textvariable ${id}(path) button $hd(body).browse -text $t(browse)... \ -command "Browse $hd(body) ${id}(path) dirok" grid $hd(body).dir_l $hd(body).dir_e -sticky we grid x $hd(body).browse -sticky e $hd(next) configure -text $t(finish) \ -command "VFolderWizardDynamicDone $id" rat_scrollframe::recalc $hd(bodym) SetupShortcuts [list $hd(next) $hd(prev) $hd(body).browse] $hd(body).name_e selection range 0 end $hd(body).name_e icursor end focus $hd(body).name_e $hd(prev) configure -state normal } proc VFolderWizardDynamicDone {id} { upvar \#0 $id hd global t # Create directory if nonexisting was specified in create mode if {![file exists $hd(path)]} { if {[catch {file mkdir $hd(path)} err]} { Popup "$t(failed_create) '$hd(path)': $err" return } } # Make sure we have a directory name if {![file isdirectory $hd(path)]} { set hd(path) [file dirname $hd(path)] } set def [list $hd(name) dynamic {} $hd(path)] set hd(folder_id) [VFolderAddFolder $hd(context) $def] $hd(cancel) invoke } tkrat_2.2cvs20100105-dfsg.orig/tkrat/watcher.tcl000066400000000000000000000203321137544547100212720ustar00rootroot00000000000000# watcher.tcl -- # # This file contains code which handles the watcher window. # # # TkRat software and its included text is Copyright 1996-2004 by # Martin Forssn # # The full text of the legal notice is contained in the file called # COPYRIGHT, included with this distribution. # WatcherInit -- # # Initializes some watcher variables # # Arguments: # handler - The folder handler to which this watcher is connected proc WatcherInit {handler} { global folderUnseen folderChanged upvar \#0 $handler hd set hd(watcher_unseen) $folderUnseen($handler) set hd(watcher_folderChanged) $folderChanged($handler) } # WatcherCreate -- # # Builds the watcher window # # Arguments: proc WatcherCreate {} { global t b idCnt option watcherWins vFolderName watcherFont # Create toplevel set id watcher[incr idCnt] upvar \#0 $id whd set w .$id set whd(watcher_w) $w set whd(watcher_list) $w.list set whd(watcher_size) "" toplevel $w -class TkRat wm title $w $option(watcher_title) wm protocol $w WM_DELETE_WINDOW "WatcherSleep $id" # Populate window frame $w.info label $w.info.name -textvariable ${id}(name) \ -font $watcherFont -relief raised -bd 1 -anchor w label $w.info.size -textvariable ${id}(watcher_size) \ -font $watcherFont -width 11 -relief raised -bd 1 pack $w.info.size -side right pack $w.info.name -fill x -expand 1 pack $w.info -side top -fill x -expand 1 scrollbar $w.scroll \ -relief raised \ -bd 1 \ -highlightthickness 0 \ -command "$w.list yview" listbox $whd(watcher_list) \ -yscroll "$w.scroll set" \ -relief raised \ -bd 1 \ -font $watcherFont \ -exportselection false \ -highlightthickness 0 set b($whd(watcher_list)) watcher pack $w.scroll -side right -fill y pack $w.list -side left -expand 1 -fill both ::tkrat::winctl::SetGeometry watcher $w $whd(watcher_list) foreach but {<1>