Back to index

courier  0.68.2
Classes | Defines | Functions
courierfax.c File Reference
#include "courier.h"
#include "moduledel.h"
#include "comqueuename.h"
#include "comfax.h"
#include "rfc822/rfc822.h"
#include "waitlib/waitlib.h"
#include "unicode/unicode.h"
#include "numlib/numlib.h"
#include <stdlib.h>
#include <locale.h>
#include <langinfo.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <pwd.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "comctlfile.h"
#include "comstrtotime.h"
#include "comstrtimestamp.h"
#include "comverp.h"
#include <sys/stat.h>
#include <sys/types.h>
#include "faxconvert.h"
#include "sendfax.h"
#include "faxtmpdir.h"

Go to the source code of this file.

Classes

struct  faxconv_err_args

Defines

#define WEXITSTATUS(stat_val)   ((unsigned)(stat_val) >> 8)
#define WIFEXITED(stat_val)   (((stat_val) & 255) == 0)

Functions

static void fax (struct moduledel *)
static RETSIGTYPE killit (int n)
int main (int argc, char **argv)
static RETSIGTYPE faxabort (int n)
static int read_childerrmsg (pid_t child_proc, int errfd, int(*errhandler)(int, char *, void *), void *errhandler_arg)
static int faxconvert_cleanup (int dummy, char *errmsg, void *vp)
int faxconvert (const char *, int, int *)
static int faxsend_cleanup (int, char *, void *)

Class Documentation

struct faxconv_err_args

Definition at line 206 of file courierfax.c.

Collaboration diagram for faxconv_err_args:
Class Members
struct ctlfile * ctf
struct sort_file_list * file_list
int is_locked
int n_cover_pages
unsigned nreceip

Define Documentation

#define WEXITSTATUS (   stat_val)    ((unsigned)(stat_val) >> 8)

Definition at line 45 of file courierfax.c.

#define WIFEXITED (   stat_val)    (((stat_val) & 255) == 0)

Definition at line 48 of file courierfax.c.


Function Documentation

static void fax ( struct moduledel p) [static]

Definition at line 230 of file courierfax.c.

{
       struct ctlfile ctf;
       unsigned nreceipients=p->nreceipients;
       const char *host=p->host;
       const char *receipient;
       unsigned nrecipient=(unsigned)atol(p->receipients[0]);
       int pipefd[2];
       pid_t child_proc;
       int faxopts;
       int n_cover_pages;
       FILE *fp;

       struct faxconv_err_args err_args;

       struct sort_file_list *page_list, *pp;


#if     HAVE_SETPGRP
#if     SETPGRP_VOID
        setpgrp();
#else
        setpgrp(0, 0);
#endif
#else
#if     HAVE_SETPGID
        setpgid(0, 0);
#endif
#endif

       if (comgetfaxopts(host, &faxopts))
       {
              clog_msg_start_err();
              clog_msg_str("courierfax: FATAL: invalid host");
              clog_msg_send();
              exit(1);
       }

       putenv(faxopts & FAX_LOWRES ? "FAXRES=lo":"FAXRES=hi");

       if (nreceipients != 1)
       {
              clog_msg_start_err();
              clog_msg_str("courierfax: FATAL: # receipients must be 1");
              clog_msg_send();
              exit(1);
       }

       receipient=p->receipients[1];
       nrecipient=(unsigned)atol(p->receipients[0]);

       if (ctlfile_openi(p->inum, &ctf, 0))
              clog_msg_errno();

       /* Convert message to fax format, use a child process */

       signal(SIGTERM, faxabort);

       if (pipe(pipefd) < 0)
       {
              clog_msg_errno();
       }

       child_proc=fork();
       if (child_proc < 0)
       {
              clog_msg_errno();
              return;
       }

       if (child_proc == 0)
       {
              const char *fn;

              close(0);
              if (open("/dev/null", O_RDONLY) != 0)
                     clog_msg_errno();

              close(pipefd[0]);
              close(1);
              if (dup(pipefd[1]) != 1)
              {
                     perror("dup");
                     exit(1);
              }
              close(2);
              if (dup(pipefd[1]) != 2)
              {
                     fprintf(stderr, "ERROR: dup failed\n");
                     exit(1);
              }
              close(pipefd[1]);

              libmail_changeuidgid(MAILUID, MAILGID);

              fn=qmsgsdatname(p->inum);

              if (faxconvert(fn, faxopts, &n_cover_pages))
                     exit (1);

              if ((fp=fopen(FAXTMPDIR "/.ncoverpages", "w")) == NULL ||
                  fprintf(fp, "%d\n", n_cover_pages) < 0 ||
                  fflush(fp) < 0)
                     exit(1);
              fclose(fp);
              exit(0);
       }

       close(pipefd[1]);

       err_args.ctf= &ctf;
       err_args.nreceip=nrecipient;

       if (read_childerrmsg(child_proc, pipefd[0],
                          &faxconvert_cleanup, &err_args))
       {
              ctlfile_close(&ctf);
              return;
       }

       /* Hit it */

       if ((fp=fopen(FAXTMPDIR "/.ncoverpages", "r")) == NULL ||
           fscanf(fp, "%d", &n_cover_pages) != 1)
       {
              if (fp)
                     fclose(fp);

              ctlfile_append_reply(err_args.ctf,
                                 err_args.nreceip,
                                 "Internal error: cannot read number of cover pages",
                                 COMCTLFILE_DELFAIL, 0);
              ctlfile_close(&ctf);
              exit(0);
       }

       page_list=read_dir_sort_filenames(FAXTMPDIR, FAXTMPDIR "/");

       if (!page_list)
       {
              clog_msg_start_err();
              clog_msg_str("courierfax: INTERNAL ERROR - no pages to xmit.");
              clog_msg_send();
              exit(1);
       }

       /* Keep trying until the modem line is unlocked */

       do
       {
              if (pipe(pipefd) < 0)
              {
                     clog_msg_errno();
              }

              child_proc=fork();
              if (child_proc < 0)
              {
                     clog_msg_errno();
                     return;
              }

              if (child_proc == 0)
              {
                     unsigned page_cnt=0;
                     char **argvec;

                     close(pipefd[0]);
                     close(0);
                     if (open("/dev/null", O_RDONLY) != 0)
                     {
                            perror("/dev/null");
                            exit(1);
                     }
                     close(1);
                     if (dup(pipefd[1]) != 1)
                     {
                            perror("dup");
                            exit(1);
                     }
                     close(2);
                     if (dup(pipefd[1]) != 2)
                     {
                            fprintf(stderr, "ERROR: dup failed\n");
                            exit(1);
                     }
                     close(pipefd[1]);

                     for (pp=page_list; pp; pp=pp->next)
                            ++page_cnt;

#if 0
                     while (page_list)
                     {
                            unlink(page_list->filename);
                            page_list=page_list->next;
                     }

                     exit(0);
#endif

                     argvec=(char **)courier_malloc(sizeof(char *)*
                                                 (page_cnt+10));

                     argvec[0]=SENDFAX;
                     argvec[1]="-v";
                     argvec[2]="-r";

                     page_cnt=3;

                     if (faxopts & FAX_LOWRES)
                            argvec[page_cnt++]="-n";

                     argvec[page_cnt++]=(char *)receipient;

                     while (page_list)
                     {
                            argvec[page_cnt++]=page_list->filename;
                            page_list=page_list->next;
                     }
                     argvec[page_cnt]=0;
                     execv(SENDFAX, argvec);
                     perror(SENDFAX);
                     exit(1);
              }
              close(pipefd[1]);

              err_args.ctf= &ctf;
              err_args.nreceip=nrecipient;
              err_args.file_list=page_list;
              err_args.is_locked=0;
              err_args.n_cover_pages=n_cover_pages;

              if (read_childerrmsg(child_proc, pipefd[0],
                                 &faxsend_cleanup, &err_args) == 0)
              {
                     size_t npages=0;
                     char fmtbuf[NUMBUFSIZE];
                     char fmtbuf1[NUMBUFSIZE];
                     char fmtbuf2[NUMBUFSIZE+100];

                     for (pp=page_list; pp; pp=pp->next)
                            ++npages;

                     libmail_str_size_t(npages, fmtbuf);
                     libmail_str_size_t(n_cover_pages, fmtbuf1);

                     sprintf(fmtbuf2,
                            "%s pages - including %s cover page(s)"
                            " - sent by fax.", fmtbuf, fmtbuf1);

                     ctlfile_append_reply(&ctf, (unsigned)
                                        nrecipient,
                                        fmtbuf2,
                                        COMCTLFILE_DELSUCCESS, " l");
                     break;
              }
       } while (err_args.is_locked);

       ctlfile_close(&ctf);
       rmdir_contents(FAXTMPDIR);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static RETSIGTYPE faxabort ( int  n) [static]

Definition at line 137 of file courierfax.c.

{
       signal(SIGTERM, SIG_IGN);
       kill(-getpid(), SIGTERM);
#if RETSIGTYPE != void
       return (0);
#endif
}

Here is the caller graph for this function:

int faxconvert ( const char *  ,
int  ,
int *   
)

Definition at line 118 of file faxconvert.c.

{
       int fd=open(filename, O_RDONLY);
       struct rfc2045 *rfc2045p;
       int rc;
       struct faxconvert_info info;

       /* Open the MIME message, and parse it */

       if (fd < 0)
       {
              perror(filename);
              return (-1);
       }

       if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)   /* I'm a lazy SOB */
       {
              perror(filename);
              close(fd);
              return (-1);
       }

       rfc2045p=rfc2045_fromfd(fd);

       if (!rfc2045p)
       {
              perror(filename);
              return (-1);
       }

       /*
       ** Ok, clean out SUBTMPDIR and FAXTMPDIR.  We run each filter and tell
       ** it to spit its output into SUBTMPDIR, then, we scan SUBTMPDIR,
       ** pick up whatever we find there, and move it to FAXTMPDIR.
       */

       rmdir_contents( SUBTMPDIR );
       rmdir_contents( FAXTMPDIR);

       info.topp=rfc2045p;
       info.fd=fd;
       info.partnum=0;
       info.cover_page_part=NULL;
       info.cover_page_fd= -1;
       info.npages=0;
       info.unknown_list=0;
       info.flags=flags;
       rc=faxconvert_recursive(rfc2045p, &info);

       /* Generate a cover page, now */

       if (rc == 0)
       {
              if (info.cover_page_part == NULL)
              {
                     fprintf(stderr, "Error: no cover page (empty document)?\n");
                     rc= -1;
              }
              else
              {
                     rc= convert_cover_page(info.cover_page_part,
                                          &info,
                                          info.cover_page_fd,
                                          ncoverpages);
              }
       }

       rfc2045_free(rfc2045p);
       close(fd);
       rmdir_contents(SUBTMPDIR);
       return (rc);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int faxconvert_cleanup ( int  dummy,
char *  errmsg,
void *  vp 
) [static]

Definition at line 216 of file courierfax.c.

{
       struct faxconv_err_args *args=(struct faxconv_err_args *)vp;

       ctlfile_append_reply(args->ctf, args->nreceip,
                          errmsg,
                          COMCTLFILE_DELFAIL_NOTRACK, 0);
       return (-1);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int faxsend_cleanup ( int  errcode,
char *  errmsg,
void *  vp 
) [static]

Definition at line 493 of file courierfax.c.

{
       struct faxconv_err_args *args=(struct faxconv_err_args *)vp;
       unsigned pages_sent=0;
       char *p, *q;

       int i;
       time_t now_time;

       unsigned coverpage_cnt=0;
       unsigned page_cnt=0;

       /* Check how many files sendfax renamed (were succesfully sent) */

       while (args->file_list)
       {
              if (access(args->file_list->filename, 0) == 0)
                     break;

              if (coverpage_cnt < args->n_cover_pages)
                     ++coverpage_cnt;
              else
                     ++pages_sent;
              args->file_list=args->file_list->next;
       }

       /* Strip out any blank lines in captured output from sendfax */

       for (p=q=errmsg; *p; p++)
       {
              if (*p == '\n' && (p[1] == '\n' || p[1] == 0))
                     continue;

              *q++=*p;
       }
       *q=0;

       /* Find the last message from sendfax */

       for (p=q=errmsg; *p; p++)
       {
              if (*p != '\n')
                     continue;

              *p=0;

              /* Dump sendfax's output to the log */

              if (*q)
              {
                     clog_msg_start_info();
                     clog_msg_str("courierfax: " SENDFAX ": ");
                     clog_msg_str(q);
                     clog_msg_send();
              }
              q=p+1;
       }

       if (*q)       /* Last line of the error message */
       {
              clog_msg_start_info();
              clog_msg_str("courierfax: " SENDFAX ": ");
              clog_msg_str(q);
              clog_msg_send();
       }
       else   /* Default message */
       {
              q=SENDFAX ": completed.";
       }

       /*
       ** Ugly hack: capture the following message from sendfax:
       **
       ** /usr/sbin/sendfax: cannot access fax device(s) (locked?)
       */

#if 0
       lockflag=0;
       p=strchr(q, ':');
       if (p)
       {
              static const char msg1[]="cannot access fax device";
              static const char msg2[]="locked";

              ++p;
              while (*p && isspace((int)(unsigned char)*p))
                     p++;

              if (*p && strncasecmp(p, msg1, sizeof(msg1)-1) == 0)
              {
                     p += sizeof(msg1);
                     while (*p && !isspace((int)(unsigned char)*p))
                            ++p;

                     p=strchr(p, '(');

                     if (p && strncmp(p+1, msg2, sizeof(msg2)-1) == 0)
                     {
                            args->is_locked=1;
                            clog_msg_start_info();
                            clog_msg_str("courierfax: detected locked"
                                        " modem line, sleeping...");
                            clog_msg_send();
                            sleep(60);
                            return (-1);
                     }
              }
       }
#else

       if (errcode == 2)
       {
              args->is_locked=1;
              clog_msg_start_info();
              clog_msg_str("courierfax: detected locked"
                          " modem line, sleeping...");
              clog_msg_send();
              sleep(60);
              return (-1);
       }
#endif

       ctlfile_append_connectioninfo(args->ctf, args->nreceip,
                                  COMCTLFILE_DELINFO_REPLY, q);

       sprintf(errmsg, "%u cover pages, %u document pages sent.",
              coverpage_cnt, page_cnt);

       i=ctlfile_searchfirst(args->ctf, COMCTLFILE_FAXEXPIRES);

       time(&now_time);
       ctlfile_append_reply(args->ctf, args->nreceip,
                          errmsg,
                          (pages_sent == 0 &&
                           i >= 0 &&
                           errcode < 10 &&
                           now_time < strtotime(args->ctf->lines[i]+1)
                           ? COMCTLFILE_DELDEFERRED:
                           COMCTLFILE_DELFAIL_NOTRACK), 0);
       return (-1);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static RETSIGTYPE killit ( int  n) [static]

Definition at line 61 of file courierfax.c.

{
       module_signal(SIGKILL);
       exit(0);
#if RETSIGTYPE != void
       return (0);
#endif
}

Here is the call graph for this function:

Here is the caller graph for this function:

int main ( int  argc,
char **  argv 
)

Definition at line 74 of file courierfax.c.

{
       struct moduledel *p;
       int waitstat;
       const char *cp;

       clog_open_syslog("courierfax");
       if (chdir(getenv("COURIER_HOME")))
              clog_msg_errno();

       cp=getenv("MAXDELS");
       if (!cp || atoi(cp) != 1)
       {
              clog_msg_start_err();
              clog_msg_str("FATAL: courierfax module misconfiguration, MAXDELS must be 1");
              clog_msg_send();
              exit(0);
       }

       cp=getenv("MAXRCPT");
       if (!cp || atoi(cp) != 1)
       {
              clog_msg_start_err();
              clog_msg_str("FATAL: courierfax module misconfiguration, MAXRCPT must be 1");
              clog_msg_send();
              exit(0);
       }

       module_init(0);
       while ((p=module_getdel()) != NULL)
       {
              pid_t  pid;
              unsigned delid;

              delid=atol(p->delid);

              if ((pid=module_fork(delid, 0)) == -1)
              {
                     clog_msg_prerrno();
                     module_completed(delid, delid);
                     continue;
              }

              if (pid == 0)
              {
                     fax(p);
                     exit(0);
              }
       }

       module_signal(SIGTERM);
       signal(SIGCHLD, SIG_DFL);
       signal(SIGALRM, killit);
       alarm(5);
       wait(&waitstat);
       alarm(0);
       return (0);
}

Here is the call graph for this function:

static int read_childerrmsg ( pid_t  child_proc,
int  errfd,
int(*)(int, char *, void *)  errhandler,
void *  errhandler_arg 
) [static]

Definition at line 146 of file courierfax.c.

{
       char errbuf[BUFSIZ];
       int errptr=0;
       int waitstat;
       pid_t p;

       for (;;)
       {
              int n, i;

              if (errptr >= BUFSIZ/4 * 3)
              {
                     /* Read errmsg too big, strip it */

                     i=BUFSIZ/2;

                     for (n=BUFSIZ/2; n<errptr; n++)
                            if (errbuf[n] == '\n')
                            {
                                   i= n+1;
                                   break;
                            }

                     strcpy(errbuf, "...\n");
                     n=4;
                     while (i < errptr)
                            errbuf[n++] = errbuf[i++];
                     errptr=n;
              }

              n=read(errfd, errbuf+errptr, BUFSIZ-1-errptr);
              if (n <= 0)
                     break;
              errptr += n;
       }
       errbuf[errptr]=0;
       close(errfd);

       while ((p=wait(&waitstat)) != child_proc)
       {
              if (p < 0 && errno != EINTR)
                     break;
       }

       if (p == child_proc && WIFEXITED(waitstat) &&
           WEXITSTATUS(waitstat) == 0)
              return (0);

       if (errptr == 0)
              strcpy(errbuf, "courierfax: child process crashed.");

       return (*errhandler)(p == child_proc && WIFEXITED(waitstat)
                          ? WEXITSTATUS(waitstat):1,
                          errbuf, errhandler_arg);
}

Here is the caller graph for this function: