215d42df82
- add a new knob, WITH_SQL_REMOVE_DELETED, for explicitly activating the new code for removing entries from the SQL log file when a user or domain is deleted; - add real NOPORTDOCS support and install the README.* files in addition to the doc_html/ and man_html/ directories if NOPORTDOCS is not set; - fix a couple of spelling and grammar errors in the new README.vpopmaild; - add several sanity checks; - remove the last traces of the ActiveDirectory backend; - various other minor changes.
324 lines
10 KiB
C
324 lines
10 KiB
C
diff -urN -x .svn ../../vendor/vpopmail/vdelivermail.c ./vdelivermail.c
|
|
--- ../../vendor/vpopmail/vdelivermail.c Wed Oct 4 13:19:16 2006
|
|
+++ ./vdelivermail.c Wed Oct 4 15:44:20 2006
|
|
@@ -66,6 +66,7 @@
|
|
|
|
#define FILE_SIZE 156
|
|
char loop_buf[FILE_SIZE];
|
|
+char spam_buf[FILE_SIZE];
|
|
|
|
#define MSG_BUF_SIZE 5000
|
|
char msgbuf[MSG_BUF_SIZE];
|
|
@@ -78,9 +79,6 @@
|
|
#define EXIT_OK 0
|
|
#define EXIT_OVERQUOTA EXIT_BOUNCE
|
|
|
|
-/* from qmail's wait.h for run_command() */
|
|
-#define wait_exitcode(w) ((w) >> 8)
|
|
-
|
|
/* Forward declarations */
|
|
int process_valias(void);
|
|
void get_arguments(int argc, char **argv);
|
|
@@ -93,7 +91,10 @@
|
|
void usernotfound(void);
|
|
int is_loop_match( const char *dt, const char *address);
|
|
int deliver_quota_warning(const char *dir, const char *q);
|
|
-
|
|
+#ifdef SPAM_THRESHOLD
|
|
+int is_spam(int threshold);
|
|
+int is_spam_match(char *xsl, int threshold);
|
|
+#endif
|
|
|
|
/* print an error string and then exit
|
|
* vexit() never returns, so vexiterr() and vexit() should actually return void
|
|
@@ -201,7 +202,7 @@
|
|
if ( is_domain_valid(TheDomain) != 0 )
|
|
vexiterr (EXIT_BOUNCE, "invalid domain name");
|
|
|
|
- strncpy(TheUserFull, TheUser, sizeof(TheUserFull));
|
|
+ snprintf (TheUserFull, sizeof(TheUserFull), "%s", TheUser);
|
|
|
|
#ifdef QMAIL_EXT
|
|
/* !! Shouldn't this work its way backwards, and try all possibilities?
|
|
@@ -247,7 +248,7 @@
|
|
|
|
/* check for wildcard if there's no match */
|
|
if(tmpstr == NULL) {
|
|
- for(i=strlen(TheUser);i >= 0 && j != 1;--i) {
|
|
+ for(i=strlen(TheUser);i > 0 && j != 1;--i) {
|
|
if(TheUser[i-1]=='-') {
|
|
tmpuser[0] = '\0';
|
|
strncat(tmpuser,TheUser,i);
|
|
@@ -410,6 +411,10 @@
|
|
char local_file_new[FILE_SIZE];
|
|
size_t headerlen;
|
|
int write_fd;
|
|
+#ifdef SPAMC
|
|
+ int nread;
|
|
+ int pim[2];
|
|
+#endif
|
|
char quota[80];
|
|
|
|
headerlen = strlen (extra_headers);
|
|
@@ -435,6 +440,49 @@
|
|
return(-2);
|
|
}
|
|
|
|
+#ifdef SPAMC
|
|
+ /* fork the SpamAssassin client - patch by Alex Dupre */
|
|
+ if (!pipe(pim)) {
|
|
+ pid = vfork();
|
|
+ switch (pid) {
|
|
+ case -1:
|
|
+ close(pim[0]);
|
|
+ close(pim[1]);
|
|
+ break;
|
|
+ case 0:
|
|
+ close(pim[0]);
|
|
+ dup2(pim[1], 1);
|
|
+ close(pim[1]);
|
|
+ if (execl(SPAMC, SPAMC, "-u", maildir_to_email(maildir), 0) == -1) {
|
|
+ while ((nread = read(0, msgbuf, MSG_BUF_SIZE)) > 0)
|
|
+ write(1, msgbuf, nread);
|
|
+ _exit(0);
|
|
+ }
|
|
+ }
|
|
+ close(pim[1]);
|
|
+ dup2(pim[0], 0);
|
|
+ close(pim[0]);
|
|
+ }
|
|
+#ifdef SPAM_THRESHOLD
|
|
+ /* silently delete message if spam level > SPAM_THRESHOLD */
|
|
+ if (is_spam(SPAM_THRESHOLD) == 1) {
|
|
+ close(write_fd);
|
|
+ unlink(local_file_tmp);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+#ifdef MAKE_SEEKABLE
|
|
+ if (!Seekable(0))
|
|
+ MakeSeekable(stdin);
|
|
+#endif
|
|
+
|
|
+ if (lseek(0, 0L, SEEK_SET) < 0) {
|
|
+ printf("lseek errno=%d\n", errno);
|
|
+ return -2;
|
|
+ }
|
|
+#endif
|
|
+#endif
|
|
+
|
|
if (fdcopy (write_fd, read_fd, extra_headers, headerlen) != 0) {
|
|
/* Did the write fail because we were over quota? */
|
|
if ( errno == EDQUOT ) {
|
|
@@ -549,10 +597,10 @@
|
|
if (user_over_maildirquota(address,format_maildirquota(quota))==1) {
|
|
|
|
/* check for over quota message in domain */
|
|
- sprintf(tmp_file, "%s/.over-quota.msg",TheDomainDir);
|
|
+ snprintf(tmp_file, sizeof(tmp_file), "%s/.over-quota.msg",TheDomainDir);
|
|
if ( (fs=fopen(tmp_file, "r")) == NULL ) {
|
|
/* if no domain over quota then check in vpopmail dir */
|
|
- sprintf(tmp_file, "%s/%s/.over-quota.msg",VPOPMAILDIR,DOMAINS_DIR);
|
|
+ snprintf(tmp_file, sizeof(tmp_file), "%s/%s/.over-quota.msg",VPOPMAILDIR,DOMAINS_DIR);
|
|
fs=fopen(tmp_file, "r");
|
|
}
|
|
|
|
@@ -579,10 +627,10 @@
|
|
if (domain_over_maildirquota(address)==1)
|
|
{
|
|
/* check for over quota message in domain */
|
|
- sprintf(tmp_file, "%s/.over-quota.msg",TheDomainDir);
|
|
+ snprintf(tmp_file, sizeof(tmp_file), "%s/.over-quota.msg",TheDomainDir);
|
|
if ( (fs=fopen(tmp_file, "r")) == NULL ) {
|
|
/* if no domain over quota then check in vpopmail dir */
|
|
- sprintf(tmp_file, "%s/%s/.over-quota.msg",VPOPMAILDIR,DOMAINS_DIR);
|
|
+ snprintf(tmp_file, sizeof(tmp_file), "%s/%s/.over-quota.msg",VPOPMAILDIR,DOMAINS_DIR);
|
|
fs=fopen(tmp_file, "r");
|
|
}
|
|
|
|
@@ -673,9 +721,12 @@
|
|
}
|
|
|
|
close(fdm);
|
|
- waitpid(inject_pid,&child,0);
|
|
- xcode = wait_exitcode(child);
|
|
- if (xcode == 0) return;
|
|
+ if (waitpid(inject_pid,&child,0) <= 0 || !WIFEXITED(child)) {
|
|
+ xcode = EXIT_DEFER;
|
|
+ } else {
|
|
+ xcode = WEXITSTATUS(child);
|
|
+ if (xcode == 0) return;
|
|
+ }
|
|
vexiterr (xcode, "system error calling qmail-inject");
|
|
}
|
|
}
|
|
@@ -775,6 +826,7 @@
|
|
printf("Unable to fork: %d.", errno);
|
|
vexit(EXIT_DEFER);
|
|
case 0:
|
|
+ setenv("SHELL", "/bin/sh", 1);
|
|
args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0;
|
|
sig_catch(SIGPIPE,SIG_DFL);
|
|
execv(*args,args);
|
|
@@ -782,9 +834,9 @@
|
|
exit(EXIT_DEFER); /* the child's exit code will get caught below */
|
|
}
|
|
|
|
- wait(&wstat);
|
|
- waitpid(wstat,&child,0);
|
|
- switch(wait_exitcode(wstat))
|
|
+ if (waitpid(child,&wstat,0) < 0 || !WIFEXITED(wstat))
|
|
+ vexit(EXIT_DEFER);
|
|
+ switch(WEXITSTATUS(wstat))
|
|
{
|
|
case 64: case 65: case 70: case 76: case 77: case 78: case 100: case 112: vexit(EXIT_BOUNCE);
|
|
case 99: vexit(99); /* not sure about this, when does it exit 99? */
|
|
@@ -830,9 +882,13 @@
|
|
|
|
/* if we find the line, return error (looping) */
|
|
if (is_loop_match(loop_buf, address)==1 ) {
|
|
+ /* seek to the end of stdin */
|
|
+ fseek(stdin, 0L, SEEK_END);
|
|
/* return the loop found */
|
|
return(1);
|
|
} else if (*loop_buf == '\r' || *loop_buf == '\n') {
|
|
+ /* seek to the end of stdin */
|
|
+ fseek(stdin, 0L, SEEK_END);
|
|
/* end of headers return not found looping message value */
|
|
return(0);
|
|
}
|
|
@@ -949,10 +1005,10 @@
|
|
FILE *fs;
|
|
char tmp_file[256];
|
|
|
|
- sprintf(tmp_file, "%s/.no-user.msg",TheDomainDir);
|
|
+ snprintf(tmp_file, sizeof(tmp_file), "%s/.no-user.msg",TheDomainDir);
|
|
if ( (fs=fopen(tmp_file, "r")) == NULL ) {
|
|
/* if no domain no user then check in vpopmail dir */
|
|
- sprintf(tmp_file, "%s/%s/.no-user.msg",VPOPMAILDIR,DOMAINS_DIR);
|
|
+ snprintf(tmp_file, sizeof(tmp_file), "%s/%s/.no-user.msg",VPOPMAILDIR,DOMAINS_DIR);
|
|
fs=fopen(tmp_file, "r");
|
|
}
|
|
if ( fs == NULL ) {
|
|
@@ -999,7 +1055,7 @@
|
|
struct stat sb;
|
|
char quotawarnmsg[BUFF_SIZE];
|
|
|
|
- sprintf (quotawarnmsg, "%s%s", dir, "/quotawarn");
|
|
+ snprintf (quotawarnmsg, sizeof(quotawarnmsg), "%s%s", dir, "/quotawarn");
|
|
time(&tm);
|
|
|
|
/* Send only one warning every 24 hours */
|
|
@@ -1017,12 +1073,12 @@
|
|
close(fd);
|
|
|
|
/* Look in the domain for a .quotawarn.msg */
|
|
- sprintf(quotawarnmsg, "%s/.quotawarn.msg", TheDomainDir);
|
|
+ snprintf(quotawarnmsg, sizeof(quotawarnmsg), "%s/.quotawarn.msg", TheDomainDir);
|
|
if ( ((read_fd = open(quotawarnmsg, O_RDONLY)) < 0) ||
|
|
(stat(quotawarnmsg, &sb) != 0)) {
|
|
|
|
/* if that fails look in vpopmail dir */
|
|
- sprintf(quotawarnmsg, "%s/%s/.quotawarn.msg", VPOPMAILDIR, DOMAINS_DIR);
|
|
+ snprintf(quotawarnmsg, sizeof(quotawarnmsg), "%s/%s/.quotawarn.msg", VPOPMAILDIR, DOMAINS_DIR);
|
|
if ( ((read_fd = open(quotawarnmsg, O_RDONLY)) < 0) ||
|
|
(stat(quotawarnmsg, &sb) != 0)) {
|
|
return 0;
|
|
@@ -1063,3 +1119,97 @@
|
|
|
|
return (strcasecmp (compare, (dt+14)) == 0);
|
|
}
|
|
+
|
|
+#ifdef SPAM_THRESHOLD
|
|
+/* Check for a spam message
|
|
+ * This is done by checking for a matching line
|
|
+ * in the email headers for X-Spam-Level: which
|
|
+ * we put in each spam email
|
|
+ *
|
|
+ * Return 1 if spam
|
|
+ * Return 0 if not spam
|
|
+ * Return -1 on error
|
|
+ */
|
|
+int is_spam(int threshold)
|
|
+{
|
|
+ int i;
|
|
+ int found;
|
|
+
|
|
+#ifdef MAKE_SEEKABLE
|
|
+ if (!Seekable(0))
|
|
+ MakeSeekable(stdin);
|
|
+#endif
|
|
+
|
|
+ if ( lseek(0, 0L, SEEK_SET) < 0 ) {
|
|
+ printf("lseek errno=%d\n", errno);
|
|
+ return(-1);
|
|
+ }
|
|
+
|
|
+ while (fgets(spam_buf, sizeof(spam_buf), stdin) != NULL){
|
|
+
|
|
+ /* if we find the line, return error (looping) */
|
|
+ if (strncmp(spam_buf, "X-Spam-Level: ", 14) == 0 &&
|
|
+ is_spam_match(spam_buf, threshold) ==1) {
|
|
+
|
|
+ /* seek to the end of stdin */
|
|
+ lseek(0, 0L, SEEK_END);
|
|
+
|
|
+ /* return the spam found */
|
|
+ return(1);
|
|
+
|
|
+ /* check for the start of the body, we only need
|
|
+ * to check the headers.
|
|
+ */
|
|
+ } else {
|
|
+
|
|
+ /* walk through the charaters in the body */
|
|
+ for (i = 0, found = 0; spam_buf[i] != 0 && found == 0; ++i) {
|
|
+ switch(spam_buf[i]) {
|
|
+
|
|
+ /* skip blank spaces and new lines */
|
|
+ case ' ':
|
|
+ case '\n':
|
|
+ case '\t':
|
|
+ case '\r':
|
|
+ break;
|
|
+
|
|
+ /* found a non blank, so we are still
|
|
+ * in the headers
|
|
+ */
|
|
+ default:
|
|
+
|
|
+ /* set the found non blank char flag */
|
|
+ found = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* if the line only had blanks, then it is the
|
|
+ * delimiting line between the headers and the
|
|
+ * body. We don't need to check the body for
|
|
+ * the X-Spam-Level: line. Hence, we
|
|
+ * are done with our search and can return the
|
|
+ * spam not found value
|
|
+ */
|
|
+ if (found == 0) {
|
|
+ /* seek to the end of stdin */
|
|
+ lseek(0, 0L, SEEK_END);
|
|
+ /* return not found spam message value */
|
|
+ return(0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* if we get here then there is either no body
|
|
+ * or SpamAssassin drop it, so mark the message
|
|
+ * as spam.
|
|
+ */
|
|
+ return(1);
|
|
+}
|
|
+
|
|
+int is_spam_match(char *xsl, int threshold)
|
|
+{
|
|
+ if (strlen(xsl) - strlen("X-Spam-Level: ") > threshold) return(1);
|
|
+ else return(0);
|
|
+}
|
|
+#endif
|