/* * pipe_message.c - dlfunc for Exim * https://mta.org.ua/exim-4.94-conf/dlfunc/pipe_message/pipe_message.c * * Copyright (C) 2018-2020 Victor Ustugov * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Based on source code from http://www.ols.es/exim/dlext/ * by David Saez */ #include #include #include #include #include #include #include #include //#define DLFUNC_IMPL //#include "local_scan.h" //#include "macros.h" #include "exim.h" //# define string_copy(s) string_copy_function(s) //# define string_copyn(s, n) string_copyn_function((s), (n)) //# define string_copy_taint(s, t) string_copy_taint_function((s), (t)) extern uschar * string_copy_function(const uschar *); extern uschar * string_copyn_function(const uschar *, int n); extern uschar * string_copy_taint_function(const uschar *, BOOL tainted); #define string_sprintf(fmt, ...) \ string_sprintf_trc(fmt, US __FUNCTION__, __LINE__, __VA_ARGS__) extern uschar *string_sprintf_trc(const char *, const uschar *, unsigned, ...) ALMOST_PRINTF(1,4); #define PARENT_READ readpipe[0] #define CHILD_WRITE readpipe[1] #define CHILD_READ writepipe[0] #define PARENT_WRITE writepipe[1] #define MAX_ARGS 16 int remap_pipe_stdin_stdout(int rpipe, int wpipe); int pipe_stream(char *cmd, FILE *fmsg, uschar **yield); extern uschar *tod_stamp(int); extern uschar *spool_directory; static pid_t pid = 0; static int has_timeout = 0; //------------------------------------------------------------------------- int pipe_message(uschar **yield, int argc, uschar *argv[]) { FILE *fmsg; char mbox_path[512]; if (argc != 1) { *yield = string_copy(US"Invalid number of arguments"); return ERROR; } // get message body stream if (split_spool_directory == 0) { sprintf(mbox_path, "%s/input/%s-D", spool_directory, message_id); } else { sprintf(mbox_path, "%s/input/%s/%s-D", spool_directory, message_subdir, message_id); } fmsg = fopen(mbox_path,"rb"); if (!fmsg) { *yield = string_copy(US"Unable to spool message"); return ERROR; } (void)fseek(fmsg, SPOOL_DATA_START_OFFSET, SEEK_SET); return pipe_stream((char *)argv[0], fmsg, yield); } //------------------------------------------------------------------------- void timout_alarm(int a) { if (pid) kill(pid,SIGTERM); } //------------------------------------------------------------------------- int pipe_stream(char *cmd, FILE *fmsg, uschar **yield) { FILE *fout; //, *flog; char buffer[1024], exename[512], *args[MAX_ARGS], *s; int nargs,status,res; int writepipe[2], /* parent -> child */ readpipe [2]; /* child -> parent */ header_line *my_headerlist; uschar *address; if (pipe(readpipe) == -1) { fclose(fmsg); *yield = string_copy(US"Unable to create read pipe"); return ERROR; } if (pipe(writepipe) == -1) { close(CHILD_READ); close(CHILD_WRITE); fclose(fmsg); *yield = string_copy(US"Unable to create write pipe"); return ERROR; } pid = fork(); if (pid < 0) { fclose(fmsg); close(CHILD_READ); close(CHILD_WRITE); close(PARENT_READ); close(PARENT_WRITE); *yield = string_copy(US"Unable to fork"); return ERROR; } // Child process --------------------------------------- if (!pid) { // Close unused streams close(PARENT_READ); close(PARENT_WRITE); // Redirect stdin/stdout if (!remap_pipe_stdin_stdout(CHILD_READ,CHILD_WRITE)) { log_write(0,LOG_MAIN,"PIPE: Cannot remap stdin/stdout (%s)",strerror(errno)); _exit(-2); return 0; } // Prepare args strcpy(exename,cmd); strtok(exename," "); args[0] = exename; for (nargs=1; nargs\n", address); write(PARENT_WRITE,buffer,strlen(buffer)); // fprintf(flog,buffer); } address = expand_string(US"${if def:received_for{$received_for}}"); if (!address || !*address) address = expand_string(US"${local_part}@${domain}"); if (address && *address) { sprintf(buffer, "Envelope-To: %s\n", address); write(PARENT_WRITE,buffer,strlen(buffer)); // fprintf(flog,buffer); } sprintf(buffer, "Delivery-date: %s\n", tod_stamp(tod_full)); write(PARENT_WRITE,buffer,strlen(buffer)); // fprintf(flog,buffer); my_headerlist = header_list; for (my_headerlist = header_list; my_headerlist; my_headerlist = my_headerlist->next) { if (my_headerlist->type == '*') continue; write(PARENT_WRITE,my_headerlist->text,my_headerlist->slen); // fprintf(flog,(char *)my_headerlist->text); } // Write message body to pipe write(PARENT_WRITE,"\n",1); // fprintf(flog,"\n"); while (!feof(fmsg) && fgets(buffer,sizeof(buffer),fmsg)) { write(PARENT_WRITE,buffer,strlen(buffer)); // fprintf(flog,buffer); } // Close unused handlers close(PARENT_WRITE); fclose(fmsg); // fclose(flog); // Read from pipe *yield = string_copy(US""); while (!feof(fout) && fgets(buffer,sizeof(buffer),fout)) { *yield = string_sprintf("%s%s", "pipe_message", *yield, buffer); } // Wait for pipe to finish close(PARENT_READ); waitpid(pid,&status,0); // Disable timeout alarm(0); pid = 0; if (has_timeout) { log_write(0,LOG_MAIN,"Pipe timeout expired"); *yield = string_copy(US"Pipe timeout expired"); return ERROR; } else if (WIFSIGNALED(status)) { log_write(0,LOG_MAIN,"Pipe exited by signal %d", WTERMSIG(status)); *yield = string_sprintf("Pipe exited by signal %d", "pipe_message", WTERMSIG(status)); return ERROR; } /* if (WIFEXITED(status)) { res = WEXITSTATUS(status); } */ return OK; } //------------------------------------------------------------------------- int remap_pipe_stdin_stdout(int rpipe, int wpipe) { if (dup2(rpipe, 0) == -1) return 0; close(rpipe); if (dup2(wpipe, 1) == -1) return 0; close(wpipe); return 1; }