SOURCES: tpop3d-cvs20050822.patch (NEW), tpop3d-cvs20041005.patch ...
arekm
arekm at pld-linux.org
Mon Aug 22 22:03:42 CEST 2005
Author: arekm Date: Mon Aug 22 20:03:42 2005 GMT
Module: SOURCES Tag: HEAD
---- Log message:
- up cvs patch to 20050822
---- Files affected:
SOURCES:
tpop3d-cvs20050822.patch (NONE -> 1.1) (NEW), tpop3d-cvs20041005.patch (1.1 -> NONE) (REMOVED)
---- Diffs:
================================================================
Index: SOURCES/tpop3d-cvs20050822.patch
diff -u /dev/null SOURCES/tpop3d-cvs20050822.patch:1.1
--- /dev/null Mon Aug 22 22:03:42 2005
+++ SOURCES/tpop3d-cvs20050822.patch Mon Aug 22 22:03:37 2005
@@ -0,0 +1,2080 @@
+Index: tpop3d/CHANGES
+Index: tpop3d/INSTALL
+diff -u tpop3d/INSTALL:1.22 tpop3d/INSTALL:1.24
+--- tpop3d/INSTALL:1.22 Thu Jan 9 23:25:59 2003
++++ tpop3d/INSTALL Sun May 22 11:54:12 2005
+@@ -1,5 +1,5 @@
+ Installation instructions for tpop3d
+-$Id$
++$Id$
+
+ Thanks to the efforts of Mark Longair, tpop3d can now be configured and built
+ using the standard GNU autoconf approach. For most users, it should be
+@@ -19,7 +19,7 @@
+ also review the contents of README.darwin.
+
+ NB that tpop3d now obeys the --sysconfdir of configure, so you will need to
+-set --sysconfidr=/etc if you already have a /etc/tpop3d.conf and do not wish to
++set --sysconfdir=/etc if you already have a /etc/tpop3d.conf and do not wish to
+ move it.
+
+ * Authentication
+@@ -56,6 +56,7 @@
+ {plaintext}... plain text password used for APOP
+ {mysql}... password hashed using MySQL's PASSWORD()
+ {md5}... straight MD5 hash
++ {sha1}... straight SHA1 hash
+
+ Note that to use APOP, the plaintext password type must be specified for
+ a user, but this does not stop them logging in via any other method with
+Index: tpop3d/Makefile.am
+diff -u tpop3d/Makefile.am:1.41 tpop3d/Makefile.am:1.42
+--- tpop3d/Makefile.am:1.41 Mon Nov 24 19:58:28 2003
++++ tpop3d/Makefile.am Fri Aug 5 14:48:22 2005
+@@ -4,12 +4,12 @@
+ #
+ # Copyright (c) 2001 Chris Lightfoot, Mark Longair. All rights reserved.
+ #
+-# $Id$
++# $Id$
+ #
+
+ sbin_PROGRAMS = tpop3d
+
+-tpop3d_SOURCES = auth_mysql.c auth_pgsql.c auth_ldap.c auth_other.c \
++tpop3d_SOURCES = auth_mysql.c auth_pgsql.c auth_ldap.c auth_other.c auth_gdbm.c \
+ auth_perl.c auth_pam.c auth_passwd.c auth_flatfile.c \
+ authcache.c authswitch.c buffer.c cfgdirectives.c config.c \
+ connection.c ioabs_tcp.c ioabs_tls.c listener.c locks.c \
+@@ -21,7 +21,7 @@
+ auth_passwd.h auth_flatfile.h auth_pgsql.h authswitch.h \
+ buffer.h config.h connection.h listener.h locks.h mailbox.h \
+ md5.h password.h pidfile.h signals.h stringmap.h tls.h \
+- tokenise.h vector.h util.h
++ tokenise.h vector.h util.h auth_gdbm.h
+
+ CFLAGS += -Wall -g -O2 -DCONFIG_DIR='"@sysconfdir@"' # -Wstrict-prototypes
+
+Index: tpop3d/README
+diff -u tpop3d/README:1.22 tpop3d/README:1.23
+--- tpop3d/README:1.22 Mon Nov 24 19:59:13 2003
++++ tpop3d/README Tue Feb 3 19:52:02 2004
+@@ -1,7 +1,7 @@
+ tpop3d, copyright (c) 2000-2003 Chris Lightfoot <chris at ex-parrot.com>
+ http://www.ex-parrot.com/~chris/tpop3d/
+
+-$Id$
++$Id$
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free Software
+@@ -23,6 +23,7 @@
+ auth-mysql a vmail-sql style MySQL database; see
+ http://www.ex-parrot.com/~chris/vmail-sql/
+ or another database with user-specified SQL queries
++ auth-pgsql a PostgreSQL database
+ auth-ldap an LDAP directory
+ auth-flatfile /etc/passwd-style flat files
+ auth-other an external program
+Index: tpop3d/README.auth_mysql
+diff -u tpop3d/README.auth_mysql:1.7 tpop3d/README.auth_mysql:1.8
+--- tpop3d/README.auth_mysql:1.7 Wed Nov 7 00:50:41 2001
++++ tpop3d/README.auth_mysql Wed Sep 29 20:57:59 2004
+@@ -1,5 +1,5 @@
+ README for MySQL authentication in tpop3d
+-$Id$
++$Id$
+
+ The auth-mysql authentication driver was developed for use with the vmail-sql
+ database schema for virtual domains, though it is easy to adjust for a
+@@ -80,6 +80,7 @@
+ {crypt_md5}... crypt_md5 hash
+ {plaintext}... plaintext password
+ {mysql}... password hashed using MySQL's PASSWORD()
++ {sha1} simple SHA1 password
+ {md5} or no prefix old-style simple MD5 password
+
+ {crypt} is the format of the system crypt(3) function-- two salt characters
+@@ -89,10 +90,11 @@
+ in crypt(3), but for portability it is included as a separate case.
+ {plaintext} is a password saved in plain text; this format is useful if you
+ want to support authentication by the APOP shared-secret mechanism. {mysql}
+- is the format produced by the PASSWORD() function in MySQL. {md5} is a
+- simple MD5 hash of the password, without salt, saved as a 32-character hex
+- string. This is the historical format for vmail-sql, and so is the default
+- if no prefix is used. This may go away at some point.
++ is the format produced by the PASSWORD() function in MySQL. {sha1} is a
++ simple 20-character hex SHA1 hash of the password. {md5} is a simple MD5
++ hash of the password, without salt, saved as a 32-character hex string or a
++ 24-character base64 one. This is the historical format for vmail-sql, and so
++ is the default if no prefix is used. This may go away at some point.
+
+ Now, we compare the user's password against the stored hash or password,
+ and decide whether to authenticate the user or not. If this does not
+Index: tpop3d/TODO
+diff -u tpop3d/TODO:1.46 tpop3d/TODO:1.48
+--- tpop3d/TODO:1.46 Mon Nov 24 19:58:28 2003
++++ tpop3d/TODO Sun Jun 20 00:06:30 2004
+@@ -1,5 +1,5 @@
+ Things To Do
+-$Id$
++$Id$
+
+ * A proper code audit
+
+@@ -96,3 +96,18 @@
+
+ * Latency vs. throughput tradeoff under high load?
+
++* Deferred writes in connection_sendmessage -- that should bring the cost of
++ sending messages down to the cost of substituting \r\n for \n, etc.
++
++* Configurable (or adaptive?) SO_SNDBUF
++ We probably don't have enough information to measure the necessary
++ parameters (though in principle we could measure the delay between sending
++ +OK... and receiving a new command), so just make this a config file option.
++
++* NTLM authentication
++
++* STLS+APOP issue (email of 20040306)
++
++* Repeated messages (email of 20040325)
++
++* Maildir semantics (email of 20040331)
+Index: tpop3d/auth_flatfile.c
+diff -u tpop3d/auth_flatfile.c:1.2 tpop3d/auth_flatfile.c:1.3
+--- tpop3d/auth_flatfile.c:1.2 Mon Feb 17 23:18:32 2003
++++ tpop3d/auth_flatfile.c Sun Jun 20 00:06:30 2004
+@@ -11,7 +11,7 @@
+ #endif /* HAVE_CONFIG_H */
+
+ #ifdef AUTH_FLATFILE
+-static const char rcsid[] = "$Id$";
++static const char rcsid[] = "$Id$";
+
+ #include <sys/types.h>
+
+@@ -86,7 +86,7 @@
+ * second field to the password hash. Any subsequent fields are ignored. */
+ static char *read_user_passwd(const char *local_part, const char *domain) {
+ FILE *fp = NULL;
+- char *filename = NULL;
++ char *filename = NULL, *result = NULL;
+ struct sverr err;
+ static char *buf, *pwhash;
+ static size_t buflen;
+@@ -149,6 +149,8 @@
+ if ((end = strchr(pwhash, ':')))
+ *end = 0;
+
++ result = pwhash;
++
+ break;
+ }
+
+@@ -159,7 +161,7 @@
+ if (filename)
+ xfree(filename);
+
+- return pwhash;
++ return result;
+ }
+
+ /* auth_flatfile_new_user_pass:
+Index: tpop3d/auth_gdbm.c
+diff -u /dev/null tpop3d/auth_gdbm.c:1.1
+--- /dev/null Mon Aug 22 19:57:08 2005
++++ tpop3d/auth_gdbm.c Fri Aug 5 14:48:22 2005
+@@ -0,0 +1,196 @@
++/*
++ * auth_gdbm.c:
++ * Authenticate users using a GNU dbm file
++ *
++ * Based on auth_flatfile.h by Angel Marin, designed for tpop3d by
++ * Daniel Tiefnig at Inode, Austria. <d.tiefnig at inode.at>
++ *
++ * Copyright (c) 2004 Daniel Tiefnig. All rights reserved. This
++ * software is free software, you can modify and/or redistribute
++ * it as tpop3d itself. See the file COPYING in the base directory
++ * of your tpop3d distribution.
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "configuration.h"
++#endif /* HAVE_CONFIG_H */
++
++#ifdef AUTH_GDBM
++
++#include <sys/types.h>
++
++#ifdef HAVE_CRYPT_H /* XXX */
++#include <crypt.h>
++#endif
++
++#include <unistd.h>
++#include <grp.h>
++#include <pwd.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <syslog.h>
++#include <gdbm.h>
++
++#include "auth_gdbm.h"
++#include "authswitch.h"
++#include "password.h"
++#include "config.h"
++#include "util.h"
++
++static gid_t virtual_gid;
++static uid_t virtual_uid;
++static char *user_passwd_file;
++GDBM_FILE dbf;
++int persistent;
++
++/* auth_gdbm_init:
++ * Initialise the driver. Reads the config directives. */
++int auth_gdbm_init() {
++ char *s;
++
++ /* Obtain uid to use */
++ if ((s = config_get_string("auth-gdbm-mail-user"))) {
++ if (!parse_uid(s, &virtual_uid)) {
++ log_print(LOG_ERR, _("auth_gdbm_init: auth-gdbm-mail-user directive `%s' does not make sense"), s);
++ return 0;
++ }
++ } else {
++ log_print(LOG_ERR, _("auth_gdbm_init: no auth-gdbm-mail-user directive in config"));
++ return 0;
++ }
++
++ /* Obtain gid to use */
++ if ((s = config_get_string("auth-gdbm-mail-group"))) {
++ if (!parse_gid(s, &virtual_gid)) {
++ log_print(LOG_ERR, _("auth_gdbm_init: auth-gdbm-mail-group directive `%s' does not make sense"), s);
++ return 0;
++ }
++ } else {
++ log_print(LOG_ERR, _("auth_gdbm_init: no auth-gdbm-mail-group directive in config"));
++ return 0;
++ }
++
++ /* Obtain path to passwd file */
++ if ((s = config_get_string("auth-gdbm-passwd-file"))) {
++ user_passwd_file = s;
++ } else {
++ log_print(LOG_ERR, _("auth_gdbm_init: no auth-gdbm-passwd-file directive in config"));
++ return 0;
++ }
++
++ /* persistent GDBM filehandle? */
++ if (config_get_bool("auth-gdbm-persistent")) {
++ persistent = 1;
++ if((dbf=gdbm_open(user_passwd_file, 0, GDBM_READER, 0644, 0)) == NULL) {
++ log_print(LOG_ERR, _("auth_gdbm_init: could not open GNU dbm file"));
++ return 0;
++ }
++ } else {
++ persistent = 0;
++ }
++
++ return 1;
++}
++
++/* auth_gdbm_new_user_pass:
++ * Attempt to authenticate user and pass using a GNU dbm file,
++ * as configured at compile-time.
++ * This is a virtual-domains authenticator. */
++authcontext auth_gdbm_new_user_pass(const char *user, const char *local_part, const char *domain, const char *pass, const char *clienthost /* unused */, const char *serverhost /* unused */) {
++ authcontext a = NULL;
++ char *who, *address;
++ datum key, value;
++
++ if (!local_part) return NULL;
++
++ who = username_string(user, local_part, domain);
++
++ address = xmalloc(strlen(local_part) + strlen(domain) +2);
++ sprintf(address, "%s@%s", local_part, domain);
++ key.dptr = address;
++ key.dsize = strlen(address);
++
++ if (persistent) {
++ value = gdbm_fetch(dbf,key);
++ } else {
++ if((dbf=gdbm_open(user_passwd_file, 0, GDBM_READER, 0644, 0)) == NULL) {
++ log_print(LOG_ERR, _("auth_gdbm_init: could not open GNU dbm file"));
++ return 0;
++ }
++ value = gdbm_fetch(dbf,key);
++ gdbm_close(dbf);
++ }
++
++ xfree(address);
++ if(value.dptr == NULL) {
++ log_print(LOG_ERR, _("auth_gdbm_new_user_pass: could not find user %s"), who);
++ return a;
++ }
++
++ if (check_password(who, value.dptr, pass, "{crypt}"))
++ a = authcontext_new(virtual_uid, virtual_gid, NULL, NULL, NULL);
++ else
++ log_print(LOG_ERR, _("auth_gdbm_new_user_pass: failed login for %s"), who);
++
++ xfree(value.dptr);
++
++ return a;
++}
++
++/* auth_gdbm_new_apop:
++ * Attempt to authenticate user via APOP using a GNU dbm file,
++ * as configured at compile-time.
++ * This is a virtual-domains authenticator. */
++authcontext auth_gdbm_new_apop(const char *user, const char *local_part, const char *domain, const char *timestamp, const unsigned char *digest, const char *clienthost /* unused */, const char *serverhost /* unused */) {
++ authcontext a = NULL;
++ char *who, *address;
++ datum key, value;
++
++ if (!local_part) return NULL;
++
++ who = username_string(user, local_part, domain);
++
++ address = xmalloc(strlen(local_part) + strlen(domain) +2);
++ sprintf(address, "%s@%s", local_part, domain);
++ key.dptr = address;
++ key.dsize = strlen(address);
++
++ if (persistent) {
++ value = gdbm_fetch(dbf,key);
++ } else {
++ if((dbf=gdbm_open(user_passwd_file, 0, GDBM_READER, 0644, 0)) == NULL) {
++ log_print(LOG_ERR, _("auth_gdbm_init: could not open GNU dbm file"));
++ return 0;
++ }
++ value = gdbm_fetch(dbf,key);
++ gdbm_close(dbf);
++ }
++
++ xfree(address);
++ if(value.dptr == NULL) {
++ log_print(LOG_ERR, _("auth_gdbm_new_apop: could not find user %s"), who);
++ return a;
++ }
++
++ if (check_password_apop(who, value.dptr, timestamp, digest))
++ a = authcontext_new(virtual_uid, virtual_gid, NULL, NULL, NULL);
++ else
++ log_print(LOG_ERR, _("auth_gdbm_new_apop: failed login for %s"), who);
++
++ xfree(value.dptr);
++
++ return a;
++}
++
++void auth_gdbm_postfork() {
++ if (persistent)
++ gdbm_close(dbf);
++}
++
++void auth_gdbm_close() {
++ if (persistent)
++ gdbm_close(dbf);
++}
++
++#endif /* AUTH_GDBM */
+Index: tpop3d/auth_gdbm.h
+diff -u /dev/null tpop3d/auth_gdbm.h:1.1
+--- /dev/null Mon Aug 22 19:57:08 2005
++++ tpop3d/auth_gdbm.h Fri Aug 5 14:48:22 2005
+@@ -0,0 +1,35 @@
++/*
++ * auth_gdbm.h:
++ * Authenticate users using a GNU dbm file
++ *
++ * Based on auth_flatfile.h by Angel Marin, designed for tpop3d by
++ * Daniel Tiefnig at Inode, Austria. <d.tiefnig at inode.at>
++ *
++ * Copyright (c) 2004 Daniel Tiefnig. All rights reserved. This
++ * software is free software, you can modify and/or redistribute
++ * it as tpop3d itself. See the file COPYING in the base directory
++ * of your tpop3d distribution.
++ */
++
++#ifndef __AUTH_GDBM_H_ /* include guard */
++#define __AUTH_GDBM_H_
++
++#ifdef HAVE_CONFIG_H
++#include "configuration.h"
++#endif /* HAVE_CONFIG_H */
++
++#ifdef AUTH_GDBM
++
++#include "authswitch.h"
++
++/* auth_gdbm.c */
++int auth_gdbm_init(void);
++authcontext auth_gdbm_new_user_pass(const char *user, const char *local_part, const char *domain, const char *pass, const char *clienthost, const char *serverhost);
++authcontext auth_gdbm_new_apop(const char *user, const char *local_part, const char *domain, const char *timestamp, const unsigned char *digest, const char *clienthost, const char *serverhost);
++void auth_gdbm_postfork(void);
++void auth_gdbm_close(void);
++
++
++#endif /* AUTH_GDBM */
++
++#endif /* __AUTH_GDBM_H_ */
+Index: tpop3d/auth_mysql.c
+diff -u tpop3d/auth_mysql.c:1.54 tpop3d/auth_mysql.c:1.56
+--- tpop3d/auth_mysql.c:1.54 Mon Jul 14 23:31:20 2003
++++ tpop3d/auth_mysql.c Thu Aug 19 23:57:05 2004
+@@ -11,7 +11,7 @@
+ #endif /* HAVE_CONFIG_H */
+
+ #ifdef AUTH_MYSQL
+-static const char rcsid[] = "$Id$";
++static const char rcsid[] = "$Id$";
+
+ #include <sys/types.h> /* BSD needs this here, apparently. */
+
+@@ -44,7 +44,7 @@
+ * [2] unix user
+ * [3] mailbox type
+ */
+-char *user_pass_query_template =
++static char *user_pass_query_template =
+ "SELECT concat(domain.path, '/', popbox.mbox_name), popbox.password_hash, "
+ "domain.unix_user, 'bsd' "
+ "FROM popbox, domain "
+@@ -52,7 +52,7 @@
+ "AND popbox.domain_name = '$(domain)' "
+ "AND popbox.domain_name = domain.domain_name";
+
+-char *apop_query_template =
++static char *apop_query_template =
+ "SELECT concat(domain.path, '/', popbox.mbox_name), popbox.password_hash, "
+ "domain.unix_user, 'bsd' "
+ "FROM popbox, domain "
+@@ -60,20 +60,20 @@
+ "AND popbox.domain_name = '$(domain)' "
+ "AND popbox.domain_name = domain.domain_name";
+
+-char *onlogin_query_template = NULL;
++static char *onlogin_query_template = NULL;
+
+ /* GID used to access mail spool (if any). */
+-int use_gid;
+-gid_t mail_gid;
++static int use_gid;
++static gid_t mail_gid;
+
+ static char *substitute_query_params(const char *temp, const char *user, const char *local_part, const char *domain, const char *clienthost, const char *serverhost);
+
+ /*
+ * Connection to the MySQL server.
+ */
+-MYSQL *mysql = NULL;
+-tokens mysql_servers;
+-char mysql_driver_active = 0;
++static MYSQL *mysql = NULL;
++static tokens mysql_servers;
++static char mysql_driver_active = 0;
+
+ /* get_mysql_server:
+ * If we are not currently connected to a MySQL server, or if the current MySQL
+@@ -267,7 +267,7 @@
+
+ /* User was not lying (about her password) */
+ if (!parse_uid((const char*)row[2], &uid)) {
+- log_print(LOG_ERR, _("auth_mysql_new_apop: unix user `%s' for %s does not make sense"), row[3], who);
++ log_print(LOG_ERR, _("auth_mysql_new_apop: unix user `%s' for %s does not make sense"), row[2], who);
+ break;
+ }
+
+@@ -367,7 +367,7 @@
+ }
+
+ if (!parse_uid((const char*)row[2], &uid)) {
+- log_print(LOG_ERR, _("auth_mysql_new_user_pass: unix user `%s' for %s does not make sense"), row[3], who);
++ log_print(LOG_ERR, _("auth_mysql_new_user_pass: unix user `%s' for %s does not make sense"), row[2], who);
+ break;
+ }
+
+Index: tpop3d/auth_pgsql.c
+diff -u tpop3d/auth_pgsql.c:1.1 tpop3d/auth_pgsql.c:1.2
+--- tpop3d/auth_pgsql.c:1.1 Mon Jul 14 23:31:20 2003
++++ tpop3d/auth_pgsql.c Thu Aug 19 23:57:05 2004
+@@ -13,7 +13,7 @@
+ #endif /* HAVE_CONFIG_H */
+
+ #ifdef AUTH_PGSQL
+-static const char rcsid[] = "$Id$";
++static const char rcsid[] = "$Id$";
+
+ #include <sys/types.h> /* BSD needs this here, apparently. */
+
+@@ -105,7 +105,7 @@
+ * [2] unix user
+ * [3] mailbox type
+ */
+-char *user_pass_query_template =
++static char *user_pass_query_template =
+ "SELECT domain.path || '/' || popbox.mbox_name, popbox.password_hash, "
+ "domain.unix_user, 'bsd' "
+ "FROM popbox, domain "
+@@ -113,7 +113,7 @@
+ "AND popbox.domain_name = '$(domain)' "
+ "AND popbox.domain_name = domain.domain_name";
+
+-char *apop_query_template =
++static char *apop_query_template =
+ "SELECT domain.path || '/' || popbox.mbox_name, popbox.password_hash, "
+ "domain.unix_user, 'bsd' "
+ "FROM popbox, domain "
+@@ -121,11 +121,11 @@
+ "AND popbox.domain_name = '$(domain)' "
+ "AND popbox.domain_name = domain.domain_name";
+
+-char *onlogin_query_template = NULL;
++static char *onlogin_query_template = NULL;
+
+ /* GID used to access mail spool (if any). */
+-int use_gid;
+-gid_t mail_gid;
++static int use_gid;
++static gid_t mail_gid;
+
+ static char *substitute_query_params(const char *temp, const char *user, const char *local_part, const char *domain, const char *clienthost, const char *serverhost);
+
+@@ -148,7 +148,7 @@
+ * Initialise the database connection driver. Clears the config directives
+ * associated with the database so that a user cannot recover them with a
+ * debugger. */
+-PGconn *pg_conn;
++static PGconn *pg_conn;
+
+ int auth_pgsql_init(void) {
+ char *username = NULL, *password = NULL, *hostname = NULL, *database = NULL, *localhost = "localhost", *s;
+Index: tpop3d/authswitch.c
+diff -u tpop3d/authswitch.c:1.42 tpop3d/authswitch.c:1.45
+--- tpop3d/authswitch.c:1.42 Mon Nov 24 19:58:28 2003
++++ tpop3d/authswitch.c Fri Aug 5 14:48:22 2005
+@@ -6,7 +6,7 @@
+ *
+ */
+
+-static const char rcsid[] = "$Id$";
++static const char rcsid[] = "$Id$";
+
+ #ifdef HAVE_CONFIG_H
+ #include "configuration.h"
+@@ -52,6 +52,10 @@
+ #include "auth_passwd.h"
+ #endif /* AUTH_PASSWD */
+
++#ifdef AUTH_GDBM
++#include "auth_gdbm.h"
++#endif /* AUTH_GDBM */
++
+ #ifdef USE_WHOSON
+ #include <whoson.h>
+ #endif
+@@ -122,6 +126,13 @@
+ "flatfile",
+ _X("Uses /etc/passwd-style flat files")},
+ #endif /* AUTH_FLATFILE */
++
++#ifdef AUTH_GDBM
++ /* Authenticate against GNU dbm files. */
++ {auth_gdbm_init, auth_gdbm_new_apop, auth_gdbm_new_user_pass, NULL, auth_gdbm_postfork, auth_gdbm_close,
++ "gdbm",
++ _X("Uses GNU dbm files")},
++#endif /* AUTH_GDBM */
+ };
<<Diff was trimmed, longer than 597 lines>>
More information about the pld-cvs-commit
mailing list