2 * A simple SMTP mail sender
4 * Copyright (C) 2010-2016 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
20 #define LNLEN (1 << 12)
21 #define HDLEN (1 << 15)
22 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
23 #define MIN(a, b) ((a) < (b) ? (a) : (b))
25 static char buf
[LNLEN
]; /* SMTP reply buffer */
28 static char mail
[HDLEN
]; /* the first HDLEN bytes of the mail */
30 static struct conn
*conn
; /* the SMTP connection */
32 static char *b64_chr
=
33 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
35 /* encode 3 bytes in base64 */
36 static void b64_word(char *s
, unsigned num
)
38 s
[3] = b64_chr
[num
& 0x3f];
40 s
[2] = b64_chr
[num
& 0x3f];
42 s
[1] = b64_chr
[num
& 0x3f];
44 s
[0] = b64_chr
[num
& 0x3f];
47 /* base64 encoding; returns a static buffer */
48 static char *b64(char *src
, int len
)
50 static char dst
[LNLEN
];
51 int n
= (len
+ 2) / 3;
53 for (i
= 0; i
< n
; i
++) {
54 char *s
= src
+ 3 * i
;
55 unsigned c0
= (unsigned char) s
[0];
56 unsigned c1
= s
+ 1 < src
+ len
? (unsigned char) s
[1] : 0;
57 unsigned c2
= s
+ 2 < src
+ len
? (unsigned char) s
[2] : 0;
58 b64_word(dst
+ 4 * i
, (c0
<< 16) | (c1
<< 8) | c2
);
68 /* copy the address in s to dst */
69 static char *cutaddr(char *dst
, char *s
, int len
)
71 static char *addrseps
= "<>()%!~* \t\r\n,\"'%";
73 while (s
< end
&& *s
&& strchr(addrseps
, *s
))
75 while (s
< end
&& *s
&& !strchr(addrseps
, *s
))
81 static char *hdr_val(char *hdr
)
84 int len
= strlen(hdr
);
85 char *end
= mail
+ mail_len
- len
;
86 while (s
< end
&& strncasecmp(s
, hdr
, len
)) {
87 char *r
= memchr(s
, '\n', end
- s
);
92 return s
< end
? s
: NULL
;
95 static int hdr_len(char *hdr
)
99 char *r
= strchr(s
, '\n');
101 return strchr(s
, '\0') - hdr
;
109 static int xread(int fd
, char *buf
, int len
)
113 int ret
= read(fd
, buf
+ nr
, len
- nr
);
114 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
123 /* read one character from the SMTP server */
124 static int smtp_read(void)
126 if (buf_pos
== buf_len
) {
127 buf_len
= conn_read(conn
, buf
, sizeof(buf
));
130 return buf_pos
< buf_len
? (unsigned char) buf
[buf_pos
++] : -1;
133 /* read a line from the SMTP server; returns a static buffer */
134 static char *smtp_line(void)
136 static char dst
[LNLEN
];
139 while (i
< sizeof(dst
)) {
152 /* check the error code of an SMTP reply */
153 static int smtp_ok(char *s
)
155 return s
&& atoi(s
) < 400;
158 /* check if s is the last line of an SMTP reply */
159 static int smtp_eoc(char *s
)
161 return isdigit((unsigned char) s
[0]) && isdigit((unsigned char) s
[1]) &&
162 isdigit((unsigned char) s
[2]) && isspace((unsigned char) s
[3]);
165 static int smtp_xwrite(char *buf
, int len
)
169 int ret
= conn_write(conn
, buf
+ nw
, len
- nw
);
170 if (ret
< 0 && (errno
== EAGAIN
|| errno
== EINTR
))
174 DPRINT(buf
+ nw
, ret
);
180 /* send a command to the SMTP server */
181 static void smtp_cmd(char *cmd
)
183 conn_write(conn
, cmd
, strlen(cmd
));
184 DPRINT(cmd
, strlen(cmd
));
187 static int ehlo(void)
190 if (!smtp_ok(smtp_line()))
192 smtp_cmd("EHLO " HOSTNAME
"\r\n");
195 } while (ret
&& !smtp_eoc(ret
));
196 return !smtp_ok(ret
);
199 static int login(char *user
, char *pass
)
202 smtp_cmd("AUTH LOGIN\r\n");
203 if (!smtp_ok(smtp_line()))
205 sprintf(cmd
, "%s\r\n", b64(user
, strlen(user
)));
207 if (!smtp_ok(smtp_line()))
209 sprintf(cmd
, "%s\r\n", b64(pass
, strlen(pass
)));
211 return !smtp_ok(smtp_line());
214 static int mail_data(struct account
*account
)
218 char *to_hdrs
[] = {"to:", "cc:", "bcc:"};
220 sprintf(cmd
, "MAIL FROM:<%s>\r\n", account
->from
);
222 if (!smtp_ok(smtp_line()))
224 for (i
= 0; i
< ARRAY_SIZE(to_hdrs
); i
++) {
227 if (!(hdr
= hdr_val(to_hdrs
[i
])))
229 end
= hdr
+ hdr_len(hdr
);
231 while ((s
= cutaddr(addr
, s
, end
- s
)) && s
< end
) {
232 char *at
= strchr(addr
, '@');
233 if (at
&& at
> addr
&& *(at
+ 1)) {
234 sprintf(cmd
, "RCPT TO:<%s>\r\n", addr
);
236 if (!smtp_ok(smtp_line()))
241 smtp_cmd("DATA\r\n");
242 if (!smtp_ok(smtp_line()))
244 memcpy(buf
, mail
, mail_len
);
247 if (smtp_xwrite(buf
, buflen
) != buflen
)
249 } while ((buflen
= xread(0, buf
, sizeof(buf
))) > 0);
250 smtp_cmd("\r\n.\r\n");
251 if (!smtp_ok(smtp_line()))
253 smtp_cmd("QUIT\r\n");
258 static struct account
*choose_account(void)
260 char *from
= hdr_val("from:");
261 char *end
= from
+ hdr_len(from
);
263 for (i
= 0; i
< ARRAY_SIZE(accounts
) && from
; i
++) {
264 char *pat
= accounts
[i
].from
;
265 int len
= strlen(pat
);
267 while (s
+ len
< end
&& (s
= memchr(s
, pat
[0], end
- s
)))
268 if (!strncmp(pat
, s
++, len
))
274 int main(int argc
, char *argv
[])
276 struct account
*account
;
277 mail_len
= xread(0, mail
, sizeof(mail
));
280 account
= choose_account();
283 conn
= conn_connect(account
->server
, account
->port
, account
->cert
);
288 if (login(account
->user
, account
->pass
))
290 if (mail_data(account
))