Back to index

courier  0.68.2
comctlfile.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2012 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #include      "courier.h"
00007 #include      "comctlfile.h"
00008 #include      "comqueuename.h"
00009 #include      "comstrtimestamp.h"
00010 #include      "comtrack.h"
00011 #include      "maxlongsize.h"
00012 #include      <sys/types.h>
00013 #include      <sys/uio.h>
00014 #if    HAVE_SYS_STAT_H
00015 #include      <sys/stat.h>
00016 #endif
00017 #if    HAVE_UNISTD_H
00018 #include      <unistd.h>
00019 #endif
00020 #if    HAVE_FCNTL_H
00021 #include      <fcntl.h>
00022 #endif
00023 #include      <stdlib.h>
00024 #include      <string.h>
00025 #include      <errno.h>
00026 
00027 
00028 int ctlfile_openi(ino_t i, struct ctlfile *p, int ro)
00029 {
00030        return (ctlfile_openfn(qmsgsctlname(i), p, ro, 1));
00031 }
00032 
00033 int ctlfile_openit(ino_t i, time_t t, struct ctlfile *p, int ro)
00034 {
00035        return (ctlfile_openfn(qmsgqname(i, t), p, ro, 1));
00036 }
00037 
00038 int ctlfile_openfn(const char *n, struct ctlfile *p, int ro, int chk)
00039 {
00040 struct stat   stat_buf;
00041 unsigned nlines=2, nreceipients=1;
00042 char   *cp;
00043  unsigned i, j, rcnt, orcnt, dsncnt;
00044 char   *q;
00045 static char deferred_status[2]={COMCTLFILE_DELDEFERRED, 0};
00046 
00047        p->cancelled=0;
00048        if ((p->fd=open(n, ro ? O_RDONLY:O_RDWR | O_APPEND)) < 0)
00049               return (-1);
00050 
00051        p->msgsize=0;
00052 
00053 #ifdef FD_CLOEXEC
00054        fcntl(p->fd, F_SETFD, FD_CLOEXEC);
00055 #endif
00056        if (fstat(p->fd, &stat_buf) < 0)
00057        {
00058               close (p->fd);
00059               return (-1);
00060        }
00061 
00062        if (chk && stat_buf.st_nlink != 2)
00063        {
00064               if (ro)
00065               {
00066                      close(p->fd);
00067                      errno=0;
00068                      return (-1);
00069               }
00070 
00071               if (stat_buf.st_nlink == 1)
00072                      /*
00073                      ** Happens all the time - delivery complete, hanging
00074                      ** link
00075                      */
00076               {
00077                      unlink(n);
00078                      close(p->fd);
00079                      errno=0;
00080                      return (-1);
00081               }
00082               clog_msg_start_err();
00083               clog_msg_str("Invalid number of links for ");
00084               clog_msg_str(n);
00085               clog_msg_send();
00086               close(p->fd);
00087               errno=EINVAL;
00088               return (-1);
00089        }
00090        p->contents=q=courier_malloc(stat_buf.st_size+1);
00091 
00092        i=stat_buf.st_size;
00093        while (i)
00094        {
00095               j=read(p->fd, q, i);
00096               if (j <= 0)
00097               {
00098                      close(p->fd);
00099                      free(p->contents);
00100                      return (-1);
00101               }
00102               q += j;
00103               i -= j;
00104        }
00105        *q=0;
00106 
00107        p->n=stat_buf.st_ino;
00108        p->mtime=stat_buf.st_mtime;
00109        p->starttime=0;
00110        for (cp=p->contents; *cp; cp++)
00111        {
00112               if (*cp == '\r')
00113                      *cp=' ';
00114               if (*cp == '\n')     ++nlines;
00115        }
00116        p->lines=(char **)courier_malloc(nlines * sizeof(char *));
00117 
00118        for (nlines=0, cp=p->contents; (cp=strtok(cp, "\n")) != 0; cp=0)
00119        {
00120               p->lines[nlines++]=cp;
00121               if (*cp == COMCTLFILE_RECEIPIENT)  ++nreceipients;
00122               if (*cp == COMCTLFILE_CANCEL_MSG)  p->cancelled=1;
00123        }
00124        p->lines[nlines]=0;
00125 
00126        p->sender="";
00127        p->receipients=(char **)courier_malloc(nreceipients*sizeof(char *));
00128        p->oreceipients=(char **)courier_malloc(nreceipients*sizeof(char *));
00129        p->dsnreceipients=(char **)courier_malloc(nreceipients*sizeof(char *));
00130        p->delstatus=(char **)courier_malloc(nreceipients*sizeof(char *));
00131 
00132        for (i=0; i<nreceipients; i++)
00133        {
00134               p->receipients[i]=p->oreceipients[i]=p->dsnreceipients[i]=0;
00135               p->delstatus[i]=deferred_status;
00136        }
00137 
00138        for (i=0, rcnt=0, orcnt=0, dsncnt=0; p->lines[i]; i++)
00139        {
00140               switch (p->lines[i][0])     {
00141               case COMCTLFILE_SENDER:
00142                      p->sender=p->lines[i]+1;
00143                      break;
00144               case COMCTLFILE_RECEIPIENT:
00145                      p->receipients[rcnt++]=p->lines[i]+1;
00146                      break;
00147               case COMCTLFILE_ORECEIPIENT:
00148                      if (orcnt < nreceipients)
00149                             p->oreceipients[orcnt++]=p->lines[i]+1;
00150                      break;
00151               case COMCTLFILE_DSN:
00152                      if (dsncnt < nreceipients)
00153                             p->dsnreceipients[dsncnt++]=p->lines[i]+1;
00154                      break;
00155               case COMCTLFILE_DELSUCCESS:
00156               case COMCTLFILE_DELFAIL:
00157                      j=atoi(p->lines[i]+1);
00158                      if (j < nreceipients)
00159                             p->delstatus[j]=p->lines[i];
00160                      break;
00161               }
00162        }
00163        p->nreceipients=rcnt;
00164 
00165        return (0);
00166 }
00167 
00168 void ctlfile_close(struct ctlfile *p)
00169 {
00170        free( p->oreceipients );
00171        free( p->dsnreceipients );
00172        free( p->receipients );
00173        free( p->lines );
00174        free( p->contents );
00175        free( p->delstatus );
00176 
00177 #if    EXPLICITSYNC
00178        fsync(p->fd);
00179 #endif
00180        close(p->fd);
00181 }
00182 
00183 int ctlfile_searchfirst(struct ctlfile *p, char c)
00184 {
00185 int    i;
00186 
00187        for (i=0; p->lines[i]; i++)
00188               if (p->lines[i][0] == c)    return (i);
00189        return (-1);
00190 }
00191 
00192 static void notatomic()
00193 {
00194        clog_msg_start_err();
00195        clog_msg_str("Write to control file FAILED - not atomic.");
00196        clog_msg_send();
00197        exit(1);
00198 }
00199 
00200 void ctlfile_append(struct ctlfile *p, const char *q)
00201 {
00202 int    l=strlen(q);
00203 
00204        if (write(p->fd, q, l) != l)       notatomic();
00205 }
00206 
00207 void ctlfile_appendv(struct ctlfile *p, const struct iovec *iov, int iovcnt)
00208 {
00209        ctlfile_appendvfd(p->fd, iov, iovcnt);
00210 }
00211 
00212 void ctlfile_appendvfd(int fd, const struct iovec *iov, int iovcnt)
00213 {
00214 int    l=0;
00215 int    i;
00216 
00217        for (i=0; i<iovcnt; i++)
00218               l += iov[i].iov_len;
00219 
00220        if (writev(fd, iov, iovcnt) != l)  notatomic();
00221 }
00222 
00223 void ctlfile_append_info(struct ctlfile *cf, unsigned rcptnum, const char *info)
00224 {
00225        ctlfile_append_connectioninfo(cf, rcptnum, COMCTLFILE_DELINFO_REPLY,
00226               info);
00227 }
00228 
00229 void ctlfile_append_connectioninfo(struct ctlfile *cf,
00230        unsigned rcptnum, char infotype, const char *info)
00231 {
00232        if (infotype == COMCTLFILE_DELINFO_CONNECTIONERROR
00233               || infotype == COMCTLFILE_DELINFO_REPLY)
00234        {
00235               clog_msg_start_info();
00236               clog_msg_str("id=");
00237               clog_msg_msgid(cf);
00238               clog_msg_str(",from=<");
00239               clog_msg_str(cf->sender);
00240               clog_msg_str(">,addr=<");
00241               clog_msg_str(cf->receipients[rcptnum]);
00242               clog_msg_str(">: ");
00243               clog_msg_str(info);
00244               clog_msg_send();
00245        }
00246        ctlfile_append_connectioninfofd(cf->fd, rcptnum, infotype, info);
00247 }
00248 
00249 void ctlfile_append_infofd(int fd, unsigned rcptnum, const char *info)
00250 {
00251        ctlfile_append_connectioninfofd(fd, rcptnum, COMCTLFILE_DELINFO_REPLY,
00252               info);
00253 }
00254 
00255 void ctlfile_append_connectioninfofd(int fd, unsigned rcptnum,
00256               char infotype, const char *info)
00257 {
00258 char   fmtbuf[MAXLONGSIZE+10];
00259 struct iovec iov[4];
00260 char   buf2[2];
00261 
00262        sprintf(fmtbuf, "%c%u ", COMCTLFILE_DELINFO, rcptnum);
00263        iov[0].iov_base=(caddr_t)fmtbuf;
00264        iov[0].iov_len=strlen(fmtbuf);
00265 
00266        buf2[0]=infotype;
00267        buf2[1]=' ';
00268        iov[1].iov_base=(caddr_t)buf2;
00269        iov[1].iov_len=2;
00270        iov[3].iov_base=(caddr_t)"\n";
00271        iov[3].iov_len=1;
00272 
00273        while (info && *info)
00274        {
00275        unsigned      i;
00276 
00277               for (i=0; info[i] && info[i] != '\n'; i++)
00278                      ;
00279 
00280               iov[2].iov_base=(caddr_t)info;
00281               iov[2].iov_len=i;
00282 
00283               if (i && info[i-1] == '\r') --iov[2].iov_len;
00284               ctlfile_appendvfd(fd, iov, 4);
00285               info += i;
00286               if (*info)    ++info;
00287        }
00288 }
00289 
00290 /* Record delivery completion status into the log file */
00291 
00292 static void ctlfile_append_msgsize(struct ctlfile *);
00293 
00294 void ctlfile_append_reply(struct ctlfile *cf, unsigned rcptnum,
00295        const char *errmsg, char type, const char *delextra)
00296 {
00297        char type2;
00298 
00299        if (errmsg && *errmsg)
00300        {
00301               clog_msg_start_info();
00302               clog_msg_str("id=");
00303               clog_msg_msgid(cf);
00304               clog_msg_str(",from=<");
00305               clog_msg_str(cf->sender);
00306               clog_msg_str(">,addr=<");
00307               clog_msg_str(cf->receipients[rcptnum]);
00308               clog_msg_str(">");
00309               if (type == COMCTLFILE_DELSUCCESS
00310                   || type == COMCTLFILE_DELSUCCESS_NOLOG)
00311               {
00312                      if (cf->msgsize != 0)
00313                             ctlfile_append_msgsize(cf);
00314                      clog_msg_str(",success");
00315               }
00316               clog_msg_str(": ");
00317               clog_msg_str(errmsg);
00318               clog_msg_send();
00319               if (type == COMCTLFILE_DELSUCCESS)
00320                      goto ugly_hack;
00321        }
00322        clog_msg_start_info();
00323        clog_msg_str("id=");
00324        clog_msg_msgid(cf);
00325        clog_msg_str(",from=<");
00326        clog_msg_str(cf->sender);
00327        clog_msg_str(">,addr=<");
00328        clog_msg_str(cf->receipients[rcptnum]);
00329        clog_msg_str(">");
00330 
00331        if ((type == COMCTLFILE_DELSUCCESS
00332             || type == COMCTLFILE_DELSUCCESS_NOLOG)
00333            && cf->msgsize != 0)
00334               ctlfile_append_msgsize(cf);
00335 
00336        clog_msg_str(type == COMCTLFILE_DELSUCCESS ||
00337                    type == COMCTLFILE_DELSUCCESS_NOLOG
00338                    ? ",status: success":
00339                    type == COMCTLFILE_DELFAIL ||
00340                    type == COMCTLFILE_DELFAIL_NOTRACK ? ",status: failure":
00341                    ",status: deferred");
00342        clog_msg_send();
00343 
00344 ugly_hack:
00345 
00346        if (type == COMCTLFILE_DELSUCCESS_NOLOG)
00347        {
00348               type=COMCTLFILE_DELSUCCESS;
00349               errmsg=0;
00350        }
00351 
00352        type2=type;
00353 
00354        if (type2 == COMCTLFILE_DELFAIL_NOTRACK)
00355               type2=COMCTLFILE_DELFAIL;
00356 
00357        ctlfile_append_replyfd(cf->fd, rcptnum, errmsg, type2, delextra);
00358 
00359        if (rcptnum < cf->nreceipients && /* Sanity check */
00360            ctlfile_searchfirst(cf, COMCTLFILE_TRACK) >= 0)
00361        {
00362               time_t timestamp;
00363               int results;
00364 
00365               results=track_find(cf->receipients[rcptnum], &timestamp);
00366 
00367               switch (type) {
00368               case COMCTLFILE_DELFAIL_NOTRACK:
00369                      break;
00370 
00371               case COMCTLFILE_DELSUCCESS:
00372                      if (results == TRACK_ADDRDEFERRED ||
00373                          results == TRACK_ADDRFAILED)
00374                             track_save(cf->receipients[rcptnum],
00375                                       TRACK_ADDRACCEPTED);
00376                      break;
00377 
00378               case COMCTLFILE_DELFAIL:
00379               case COMCTLFILE_DELDEFERRED:
00380 
00381                      if (results == TRACK_ADDRDEFERRED ||
00382                          results == TRACK_ADDRFAILED)
00383                      {
00384                             if (timestamp >= time(NULL) -
00385                                 (TRACK_NHOURS * 60 * 60)/2)
00386                                    break;
00387                             /* If already had a failed attempt within
00388                             ** half the tracking window, no need to
00389                             ** add another record.
00390                             */
00391                      }
00392                      track_save(cf->receipients[rcptnum],
00393                                type == COMCTLFILE_DELFAIL ?
00394                                TRACK_ADDRFAILED:
00395                                TRACK_ADDRDEFERRED);
00396                      break;
00397               default:
00398                      break;
00399               }
00400        }
00401 }
00402 
00403 static void ctlfile_append_msgsize(struct ctlfile *cf)
00404 {
00405        clog_msg_str(",size=");
00406        clog_msg_ulong(cf->msgsize);
00407 }
00408 
00409 void ctlfile_append_replyfd(int fd, unsigned rcptnum,
00410        const char *errmsg, char type, const char *delextra)
00411 {
00412 const char *cp;
00413 time_t timestamp;
00414 char   numbuf[1+MAXLONGSIZE];
00415 int    numbuflen;
00416 struct iovec iov[12];
00417 int    n;
00418 static const char delinfo=COMCTLFILE_DELINFO;
00419 char   replybuf[2];
00420 
00421        time(&timestamp);
00422 
00423        sprintf(numbuf, "%u ", rcptnum);
00424        numbuflen=strlen(numbuf);
00425 
00426        replybuf[0]=COMCTLFILE_DELINFO_REPLY;
00427        replybuf[1]=' ';
00428 
00429        /*
00430        ** errmsg can a multiline response.  Each line is logged.  The
00431        ** last line of the response is logged together with the final
00432        ** success/fail/deferred entry, so in most cases where there's
00433        ** a one-line response, this results in only one write call.
00434        */
00435 
00436        n=0;
00437 
00438        while (errmsg && *errmsg)
00439        {
00440               for (cp=errmsg; *cp && *cp != '\n'; cp++)
00441                      ;
00442 
00443               iov[0].iov_base=(caddr_t)&delinfo;
00444               iov[0].iov_len=1;
00445               iov[1].iov_base=(caddr_t)numbuf;
00446               iov[1].iov_len=numbuflen;
00447 
00448               iov[2].iov_base=(caddr_t)replybuf;
00449               iov[2].iov_len=2;
00450 
00451               iov[3].iov_base=(caddr_t)errmsg;
00452 
00453               /* Fix cruft like CRLF, or messages without trailing LFs */
00454 
00455               if (cp > errmsg && cp[-1] == '\r')
00456               {
00457                      iov[3].iov_len= cp - errmsg - 1;
00458                      iov[4].iov_base=(caddr_t)"\n";
00459                      iov[4].iov_len=1;
00460                      n=5;
00461               }
00462               else if (*cp == '\n')
00463               {
00464                      iov[3].iov_len= cp - errmsg + 1;
00465                      n=4;
00466               }
00467               else
00468               {
00469                      iov[3].iov_len= cp - errmsg;
00470                      iov[4].iov_base=(caddr_t)"\n";
00471                      iov[4].iov_len=1;
00472                      n=5;
00473               }
00474               if (*cp == '\n')     ++cp;
00475               errmsg=cp;
00476               if (*errmsg == '\0')
00477                      break; /* Last line, attach it to status record */
00478 
00479               ctlfile_appendvfd(fd, iov, n);
00480               n=0;
00481        }
00482 
00483        iov[n].iov_base= (caddr_t)&type;
00484        iov[n].iov_len=1;
00485        ++n;
00486        iov[n].iov_base= (caddr_t)numbuf;
00487        iov[n].iov_len= numbuflen;
00488        ++n;
00489        cp=strtimestamp(timestamp);
00490        iov[n].iov_base=(caddr_t)cp;
00491        iov[n].iov_len=strlen(cp);
00492        ++n;
00493 
00494        if (delextra)
00495        {
00496               iov[n].iov_base=(caddr_t)delextra;
00497               iov[n].iov_len=strlen(delextra);
00498               ++n;
00499        }
00500 
00501        iov[n].iov_base=(caddr_t)"\n";
00502        iov[n].iov_len=1;
00503        ++n;
00504        ctlfile_appendvfd(fd, iov, n);
00505 }
00506 
00507 void ctlfile_nextattempt(struct ctlfile *ctf, time_t t)
00508 {
00509        ctlfile_nextattemptfd(ctf->fd, t);
00510 }
00511 
00512 
00513 void ctlfile_nextattemptfd(int fd, time_t t)
00514 {
00515 const char *cp=strtimestamp(t);
00516 struct iovec iov[3];
00517 static const char a=COMCTLFILE_NEXTATTEMPT;
00518 
00519        iov[0].iov_base=(caddr_t)&a;
00520        iov[0].iov_len=1;
00521        iov[1].iov_base=(caddr_t)cp;
00522        iov[1].iov_len=strlen(cp);
00523        iov[2].iov_base="\n";
00524        iov[2].iov_len=1;
00525 
00526        ctlfile_appendvfd(fd, iov, 3);
00527 #if    EXPLICITSYNC
00528        fsync(fd);
00529 #endif
00530 }
00531 
00532 time_t ctlfile_getnextattempt(struct ctlfile *c)
00533 {
00534 unsigned i;
00535 time_t t;
00536 const char *cp;
00537 struct stat   stat_buf;
00538 
00539        for (i=0; c->lines[i]; i++)
00540               ;
00541 
00542        while (i)
00543        {
00544               if (c->lines[--i][0] != COMCTLFILE_NEXTATTEMPT)
00545                      continue;
00546               for (t=0, cp=c->lines[i]+1; *cp >= '0' && *cp <= '9'; ++cp)
00547                      t=t*10 + (*cp-'0');
00548               if (stat(qmsgqname(c->n, t), &stat_buf) == 0)
00549                      return (t);
00550        }
00551        return (0);
00552 }
00553 
00554 int ctlfile_setvhost(struct ctlfile *c)
00555 {
00556        /* The buffer for the currently-set vhost */
00557        static char *previous_vhost_buf=0;
00558        const char *current_vhost=config_get_local_vhost();
00559 
00560        /* Set vhost to the given message's vhost */
00561 
00562        char *vhost=0;
00563        int n=ctlfile_searchfirst(c, COMCTLFILE_VHOST);
00564        int res;
00565 
00566        if (n >= 0)
00567        {
00568               const char *p=c->lines[n]+1;
00569 
00570               vhost=strcpy(courier_malloc(strlen(p)+1), p);
00571        }
00572 
00573        /* Compare new vhost against the previous one */
00574 
00575        res=strcmp((current_vhost ? current_vhost:""),
00576                  (vhost ? vhost:""));
00577 
00578        /*
00579        ** Set the current vhost from the control file, save it in the vhost
00580        ** buffer, free any previously-set vhost.
00581        */
00582 
00583        config_set_local_vhost(vhost);
00584 
00585        if (previous_vhost_buf)
00586               free(previous_vhost_buf);
00587        previous_vhost_buf=vhost;
00588 
00589        return res;
00590 }