1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20    Derived from smtp-orig.c
21 
22    AUTHOR(S)
23      W.Z. Venema
24      Eindhoven University of Technology
25      Department of Mathematics and Computer Science
26      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
27    CREATION DATE
28      Wed Dec 1 14:51:13 MET 1993
29    LAST UPDATE
30      Fri Aug 11 12:29:23 MET DST 1995
31    COPYRIGHT
32      None specified.
33 
34    Kern Sibbald, July 2001
35 
36      Note, the original W.Z. Venema smtp.c had no license and no
37      copyright.
38      See:
39         http://archives.neohapsis.com/archives/postfix/2000-05/1520.html
40 
41     In previous versions, I mistakenly believed that this code came from
42      Ralf S. Engelshall's smtpclient_main.c, but in fact 99% was
43      Wietse Venema's code, and the rest was mine.
44  */
45 
46 #include "bacula.h"
47 #include "jcr.h"
48 #define MY_NAME "bsmtp"
49 
50 #if defined(HAVE_WIN32)
51 #include <lmcons.h>
52 #endif
53 
54 #ifndef MAXSTRING
55 #define MAXSTRING 254
56 #endif
57 
58 enum resolv_type {
59    RESOLV_PROTO_ANY,
60    RESOLV_PROTO_IPV4,
61    RESOLV_PROTO_IPV6
62 };
63 
64 static FILE *sfp;
65 static FILE *rfp;
66 
67 static char *from_addr = NULL;
68 static char *cc_addr = NULL;
69 static char *subject = NULL;
70 static char *err_addr = NULL;
71 static const char *mailhost = NULL;
72 static char *reply_addr = NULL;
73 static char *sender_addr = NULL;
74 static char *custom_headers[10];
75 static int mailport = 25;
76 static char my_hostname[MAXSTRING];
77 static bool content_utf8 = false;
78 static resolv_type default_resolv_type = RESOLV_PROTO_IPV4;
79 
80 /*
81  * Take input that may have names and other stuff and strip
82  *  it down to the mail box address ... i.e. what is enclosed
83  *  in < >.  Otherwise add < >.
84  */
cleanup_addr(char * addr,char * buf,int buf_len)85 static char *cleanup_addr(char *addr, char *buf, int buf_len)
86 {
87    char *p, *q;
88 
89    if ((p = strchr(addr, '<')) == NULL) {
90       snprintf(buf, buf_len, "<%s>", addr);
91    } else {
92       /* Copy <addr> */
93       for (q=buf; *p && *p!='>'; ) {
94          *q++ = *p++;
95       }
96       if (*p) {
97          *q++ = *p;
98       }
99       *q = 0;
100    }
101    Dmsg2(100, "cleanup in=%s out=%s\n", addr, buf);
102    return buf;
103 }
104 
105 /*
106  * get_response - examine message from server
107  */
get_response(void)108 static void get_response(void)
109 {
110    char buf[1000];
111 
112    Dmsg0(50, "Calling fgets on read socket rfp.\n");
113    buf[3] = 0;
114    while (fgets(buf, sizeof(buf), rfp)) {
115       int len = strlen(buf);
116       if (len > 0) {
117          buf[len-1] = 0;
118       }
119       if (debug_level >= 10) {
120          fprintf(stderr, "%s <-- %s\n", mailhost, buf);
121       }
122       Dmsg2(10, "%s --> %s\n", mailhost, buf);
123       if (!isdigit((int)buf[0]) || buf[0] > '3') {
124          Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
125          exit(1);
126       }
127       if (buf[3] != '-') {
128          break;
129       }
130    }
131    if (ferror(rfp)) {
132       fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
133    }
134    return;
135 }
136 
137 /*
138  * chat - say something to server and check the response
139  */
chat(const char * fmt,...)140 static void chat(const char *fmt, ...)
141 {
142    va_list ap;
143 
144    va_start(ap, fmt);
145    vfprintf(sfp, fmt, ap);
146    va_end(ap);
147    if (debug_level >= 10) {
148       fprintf(stdout, "%s --> ", my_hostname);
149       va_start(ap, fmt);
150       vfprintf(stdout, fmt, ap);
151       va_end(ap);
152    }
153 
154    /* Send message to server and parse its response. */
155    fflush(sfp);
156    if (debug_level >= 10) {
157       fflush(stdout);
158    }
159    get_response();
160 }
161 
162 /*
163  * usage - explain and bail out
164  */
usage()165 static void usage()
166 {
167    fprintf(stderr,
168 _("\n"
169 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
170 "       -4          forces bsmtp to use IPv4 addresses only.\n"
171 #ifdef HAVE_IPV6
172 "       -6          forces bsmtp to use IPv6 addresses only.\n"
173 #endif
174 "       -8          set charset to UTF-8\n"
175 "       -a          use any ip protocol for address resolution\n"
176 "       -c          set the Cc: field\n"
177 "       -d <nn>     set debug level to <nn>\n"
178 "       -dt         print a timestamp in debug output\n"
179 "       -f          set the From: field\n"
180 "       -h          use mailhost:port as the SMTP server\n"
181 "       -s          set the Subject: field\n"
182 "       -r          set the Reply-To: field\n"
183 "       -S          set the Sender: field\n"
184 "       -H          add a custom line to the mail header\n"
185 "       -l          set the maximum number of lines to send (default: unlimited)\n"
186 "       -?          print this message.\n"
187 "\n"), MY_NAME);
188 
189    exit(1);
190 }
191 
192 /*
193  * Return the offset west from localtime to UTC in minutes
194  * Same as timezone.tz_minuteswest
195  *   Unix tz_offset coded by:  Attila Fülöp
196  */
tz_offset(time_t lnow,struct tm & tm)197 static long tz_offset(time_t lnow, struct tm &tm)
198 {
199 #if defined(HAVE_WIN32)
200 #if defined(HAVE_MINGW)
201 __MINGW_IMPORT long     _dstbias;
202 #endif
203 #if defined(MINGW64)
204 # define _tzset tzset
205 #endif
206    /* Win32 code */
207    long offset;
208    _tzset();
209    offset = _timezone;
210    if (tm.tm_isdst) {
211       offset += _dstbias;
212    }
213    return offset /= 60;
214 #else
215 
216    /* Unix/Linux code */
217    struct tm tm_utc;
218    time_t now = lnow;
219 
220    (void)gmtime_r(&now, &tm_utc);
221    tm_utc.tm_isdst = tm.tm_isdst;
222    return (long)difftime(mktime(&tm_utc), now) / 60;
223 #endif
224 }
225 
get_date_string(char * buf,int buf_len)226 static void get_date_string(char *buf, int buf_len)
227 {
228    time_t now = time(NULL);
229    struct tm tm;
230    char tzbuf[MAXSTRING];
231    long my_timezone;
232 
233    /* Add RFC822 date */
234    (void)localtime_r(&now, &tm);
235 
236    my_timezone = tz_offset(now, tm);
237    strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
238    snprintf(tzbuf, sizeof(tzbuf), " %+2.2ld%2.2u", -my_timezone / 60, (unsigned int)abs(my_timezone) % 60);
239    strcat(buf, tzbuf);              /* add +0100 */
240    strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
241    strcat(buf, tzbuf);              /* add (CEST) */
242 }
243 
244 /*********************************************************************
245  *
246  *  Program to send email
247  */
main(int argc,char * argv[])248 int main (int argc, char *argv[])
249 {
250    char buf[1000];
251    int i, ch;
252    unsigned long maxlines, lines;
253 #if defined(HAVE_WIN32)
254    SOCKET s;
255 #else
256    int s, r;
257    struct passwd *pwd;
258 #endif
259    char *cp, *p;
260 #ifdef HAVE_GETADDRINFO
261    int res;
262    struct addrinfo hints;
263    struct addrinfo *ai, *rp;
264    char mail_port[10];
265 #else
266    struct hostent *hp;
267    struct sockaddr_in sin;
268 #endif
269 #ifdef HAVE_IPV6
270    const char *options = "468ac:d:f:h:r:s:l:S:H:?";
271 #else
272    const char *options = "48ac:d:f:h:r:s:l:S:H:?";
273 #endif
274 
275    int custom_header_count = 0;
276    int custom_header_max = (sizeof(custom_headers) / sizeof(char *));
277 
278    setlocale(LC_ALL, "en_US");
279    bindtextdomain("bacula", LOCALEDIR);
280    textdomain("bacula");
281 
282    my_name_is(argc, argv, "bsmtp");
283    maxlines = 0;
284 
285    while ((ch = getopt(argc, argv, options)) != -1) {
286       switch (ch) {
287       case '4':
288          default_resolv_type = RESOLV_PROTO_IPV4;
289          break;
290 
291 #ifdef HAVE_IPV6
292       case '6':
293          default_resolv_type = RESOLV_PROTO_IPV6;
294          break;
295 #endif
296 
297       case '8':
298          content_utf8 = true;
299          break;
300 
301       case 'a':
302          default_resolv_type = RESOLV_PROTO_ANY;
303          break;
304 
305       case 'c':
306          Dmsg1(20, "cc=%s\n", optarg);
307          cc_addr = optarg;
308          break;
309 
310       case 'd':                    /* set debug level */
311          if (*optarg == 't') {
312             dbg_timestamp = true;
313          } else {
314             debug_level = atoi(optarg);
315             if (debug_level <= 0) {
316                debug_level = 1;
317             }
318          }
319          Dmsg1(20, "Debug level = %d\n", debug_level);
320          break;
321 
322       case 'f':                    /* from */
323          from_addr = optarg;
324          break;
325 
326       case 'h':                    /* smtp host */
327          Dmsg1(20, "host=%s\n", optarg);
328          p = strchr(optarg, ':');
329          if (p) {
330             *p++ = 0;
331             mailport = atoi(p);
332          }
333          mailhost = optarg;
334          break;
335 
336       case 's':                    /* subject */
337          Dmsg1(20, "subject=%s\n", optarg);
338          subject = optarg;
339          break;
340 
341       case 'r':                    /* reply address */
342          reply_addr = optarg;
343          break;
344 
345       case 'S':                    /* sender address */
346          sender_addr = optarg;
347          break;
348 
349       case 'H':                    /* custom header "XXXX: YYYYYYY" */
350          if (custom_header_count<custom_header_max) {
351             custom_headers[custom_header_count++] = optarg;
352          } else {
353             fprintf(stderr, "Too many custom header, ignored\n");
354          }
355          break;
356 
357       case 'l':
358          Dmsg1(20, "maxlines=%s\n", optarg);
359          maxlines = (unsigned long) atol(optarg);
360          break;
361 
362       case '?':
363       default:
364          usage();
365 
366       }
367    }
368    argc -= optind;
369    argv += optind;
370 
371    if (argc < 1) {
372       Pmsg0(0, _("Fatal error: no recipient given.\n"));
373       usage();
374       exit(1);
375    }
376 
377    /*
378     *  Determine SMTP server
379     */
380    if (mailhost == NULL) {
381       if ((cp = getenv("SMTPSERVER")) != NULL) {
382          mailhost = cp;
383       } else {
384          mailhost = "localhost";
385       }
386    }
387 
388 #if defined(HAVE_WIN32)
389    WSADATA  wsaData;
390 
391    _setmode(0, _O_BINARY);
392    WSAStartup(MAKEWORD(2,2), &wsaData);
393 #endif
394 
395    /*
396     *  Find out my own host name for HELO;
397     *  if possible, get the FQDN - fully qualified domain name
398     */
399    if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
400       Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
401       exit(1);
402    }
403 #ifdef HAVE_GETADDRINFO
404    memset(&hints, 0, sizeof(struct addrinfo));
405    hints.ai_family = AF_UNSPEC;
406    hints.ai_socktype = 0;
407    hints.ai_protocol = 0;
408    hints.ai_flags = AI_CANONNAME;
409 
410    if ((res = getaddrinfo(my_hostname, NULL, &hints, &ai)) != 0) {
411       Pmsg2(0, _("Fatal getaddrinfo for myself failed \"%s\": ERR=%s\n"),
412             my_hostname, gai_strerror(res));
413       exit(1);
414    }
415    bstrncpy(my_hostname, ai->ai_canonname, sizeof(my_hostname));
416    freeaddrinfo(ai);
417 #else
418    if ((hp = gethostbyname(my_hostname)) == NULL) {
419       Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"),
420             my_hostname, strerror(errno));
421       exit(1);
422    }
423    bstrncpy(my_hostname, hp->h_name, sizeof(my_hostname));
424 #endif
425    Dmsg1(20, "My hostname is: %s\n", my_hostname);
426 
427    /*
428     *  Determine from address.
429     */
430    if (from_addr == NULL) {
431 #if defined(HAVE_WIN32)
432       DWORD dwSize = UNLEN + 1;
433       LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
434 
435       if (GetUserName(lpszBuffer, &dwSize)) {
436          snprintf(buf, sizeof(buf), "%s@%s", lpszBuffer, my_hostname);
437       } else {
438          snprintf(buf, sizeof(buf), "unknown-user@%s", my_hostname);
439       }
440 #else
441       if ((pwd = getpwuid(getuid())) == 0) {
442          snprintf(buf, sizeof(buf), "userid-%d@%s", (int)getuid(), my_hostname);
443       } else {
444          snprintf(buf, sizeof(buf), "%s@%s", pwd->pw_name, my_hostname);
445       }
446 #endif
447       from_addr = bstrdup(buf);
448    }
449    Dmsg1(20, "From addr=%s\n", from_addr);
450 
451    /*
452     *  Connect to smtp daemon on mailhost.
453     */
454 lookup_host:
455 #ifdef HAVE_GETADDRINFO
456    memset(&hints, 0, sizeof(struct addrinfo));
457    switch (default_resolv_type) {
458    case RESOLV_PROTO_ANY:
459       hints.ai_family = AF_UNSPEC;
460       break;
461    case RESOLV_PROTO_IPV4:
462       hints.ai_family = AF_INET;
463       break;
464 #ifdef HAVE_IPV6
465    case RESOLV_PROTO_IPV6:
466       hints.ai_family = AF_INET6;
467       break;
468 #endif
469    default:
470       hints.ai_family = AF_UNSPEC;
471       break;
472    }
473    hints.ai_socktype = SOCK_STREAM;
474    hints.ai_protocol = 0;
475    hints.ai_flags = 0;
476    snprintf(mail_port, sizeof(mail_port), "%d", mailport);
477 
478    if ((res = getaddrinfo(mailhost, mail_port, &hints, &ai)) != 0) {
479       Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"),
480             mailhost, gai_strerror(res));
481       if (strcasecmp(mailhost, "localhost")) {
482          Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
483          mailhost = "localhost";
484          goto lookup_host;
485       }
486       exit(1);
487    }
488 
489    for (rp = ai; rp != NULL; rp = rp->ai_next) {
490 #if defined(HAVE_WIN32)
491       s = WSASocket(rp->ai_family, rp->ai_socktype, rp->ai_protocol, NULL, 0, 0);
492 #else
493       s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
494 #endif
495       if (s < 0) {
496          continue;
497       }
498 
499       if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) {
500          break;
501       }
502 
503       close(s);
504    }
505 
506    if (!rp) {
507       Pmsg1(0, _("Failed to connect to mailhost %s\n"), mailhost);
508       exit(1);
509    }
510 
511    freeaddrinfo(ai);
512 #else
513    if ((hp = gethostbyname(mailhost)) == NULL) {
514       Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
515          strerror(errno));
516       if (strcasecmp(mailhost, "localhost") != 0) {
517          Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
518          mailhost = "localhost";
519          goto lookup_host;
520       }
521       exit(1);
522    }
523 
524    if (hp->h_addrtype != AF_INET) {
525       Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
526       exit(1);
527    }
528    memset((char *)&sin, 0, sizeof(sin));
529    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
530    sin.sin_family = hp->h_addrtype;
531    sin.sin_port = htons(mailport);
532 #if defined(HAVE_WIN32)
533    if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
534       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
535       exit(1);
536    }
537 #else
538    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
539       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
540       exit(1);
541    }
542 #endif
543    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
544       Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
545       exit(1);
546    }
547    Dmsg0(20, "Connected\n");
548 #endif
549 
550 #if defined(HAVE_WIN32)
551    int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
552    if (fdSocket == -1) {
553       Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
554       exit(1);
555    }
556 
557    int fdSocket2 = dup(fdSocket);
558 
559    if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
560       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
561       exit(1);
562    }
563    if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
564       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
565       exit(1);
566    }
567 #else
568    if ((r = dup(s)) < 0) {
569       Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
570       exit(1);
571    }
572    if ((sfp = fdopen(s, "w")) == 0) {
573       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
574       exit(1);
575    }
576    if ((rfp = fdopen(r, "r")) == 0) {
577       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
578       exit(1);
579    }
580 #endif
581 
582    /*
583     * Send SMTP headers.  Note, if any of the strings have a <
584     *  in them already, we do not enclose the string in < >, otherwise
585     *  we do.
586     */
587    get_response(); /* banner */
588    chat("HELO %s\r\n", my_hostname);
589    chat("MAIL FROM:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
590 
591    for (i = 0; i < argc; i++) {
592       Dmsg1(20, "rcpt to: %s\n", argv[i]);
593       chat("RCPT TO:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
594    }
595 
596    if (cc_addr) {
597       chat("RCPT TO:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
598    }
599    Dmsg0(20, "Data\n");
600    chat("DATA\r\n");
601 
602    /*
603     * Send header
604     */
605    fprintf(sfp, "From: %s\r\n", from_addr);
606    Dmsg1(10, "From: %s\r\n", from_addr);
607    if (subject) {
608       fprintf(sfp, "Subject: %s\r\n", subject);
609       Dmsg1(10, "Subject: %s\r\n", subject);
610    }
611    if (reply_addr) {
612       fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
613       Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
614    }
615    if (err_addr) {
616       fprintf(sfp, "Errors-To: %s\r\n", err_addr);
617       Dmsg1(10, "Errors-To: %s\r\n", err_addr);
618    }
619 
620    if (sender_addr) {
621       fprintf(sfp, "Sender: %s\r\n", sender_addr);
622       Dmsg1(10, "Sender: %s\r\n", sender_addr);
623    } else {
624 
625 #if defined(HAVE_WIN32)
626       DWORD dwSize = UNLEN + 1;
627       LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
628 
629       if (GetUserName(lpszBuffer, &dwSize)) {
630          fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
631          Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
632       } else {
633          fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
634          Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
635       }
636 #else
637       if ((pwd = getpwuid(getuid())) == 0) {
638          fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
639          Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
640       } else {
641          fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
642          Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
643       }
644 #endif
645    }
646    /* Add custom header, if any */
647    for (i=0; i<custom_header_count; i++) {
648       fprintf(sfp, "%s\n", custom_headers[i]);
649    }
650 
651    fprintf(sfp, "To: %s", argv[0]);
652    Dmsg1(10, "To: %s", argv[0]);
653    for (i = 1; i < argc; i++) {
654       fprintf(sfp, ",%s", argv[i]);
655       Dmsg1(10, ",%s", argv[i]);
656    }
657 
658    fprintf(sfp, "\r\n");
659    Dmsg0(10, "\r\n");
660    if (cc_addr) {
661       fprintf(sfp, "Cc: %s\r\n", cc_addr);
662       Dmsg1(10, "Cc: %s\r\n", cc_addr);
663    }
664 
665    if (content_utf8) {
666       fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
667       Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
668    }
669 
670    get_date_string(buf, sizeof(buf));
671    fprintf(sfp, "Date: %s\r\n", buf);
672    Dmsg1(10, "Date: %s\r\n", buf);
673 
674    fprintf(sfp, "\r\n");
675 
676    /*
677     * Send message body
678     */
679    lines = 0;
680    while (fgets(buf, sizeof(buf), stdin)) {
681       if (maxlines > 0 && ++lines > maxlines) {
682          Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
683          while (fgets(buf, sizeof(buf), stdin)) {
684             ++lines;
685          }
686          break;
687       }
688       buf[sizeof(buf)-1] = '\0';
689       buf[strlen(buf)-1] = '\0';
690       if (buf[0] == '.') {         /* add extra . see RFC 2821 4.5.2 */
691          fputs(".", sfp);
692       }
693       fputs(buf, sfp);
694       fputs("\r\n", sfp);
695    }
696 
697    if (lines > maxlines) {
698       Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
699       fprintf(sfp, "\r\n\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
700    }
701 
702    /*
703     *  Send SMTP quit command
704     */
705    chat(".\r\n");
706    chat("QUIT\r\n");
707    exit(0);
708 }
709