Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 325 lines (281 sloc) 7.61 kb
9155727 @dinhviethoa import from CVS
authored
1 /*
2 * Simple Mail Submission Agent using SMTP with libEtPan!
3 * TODO: Full sendmail like interface
4 */
5
6 #ifdef HAVE_CONFIG_H
7 # include <config.h>
8 #endif
9
10 #include <libetpan/libetpan.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #ifdef WIN32
17 # include "win_etpan.h"
18 #ifdef _MSC_VER
19 # include "../src/bsd/getopt.h"
20 # define STDIN_FILENO _fileno(stdin)
21 #else
22 # include <getopt.h>
23 #endif
24 #else
25 # include <netdb.h>
26 # include <netinet/in.h>
27 # include <sys/socket.h>
28 # include <sys/mman.h>
29 # include <unistd.h>
30 # include <sys/ioctl.h>
31 # include <pwd.h>
32
33 # define _GNU_SOURCE
34 # include <getopt.h>
35 #endif
36
37 /* globals */
38 char *smtp_server;
39 uint16_t smtp_port = 25;
40 char *smtp_user;
41 char *smtp_password;
42 char *smtp_from;
43 int smtp_tls = 0;
44 int smtp_esmtp = 1;
45
46 struct mem_message {
47 char *data;
48 size_t len;
49 MMAPString *mstring;
50 };
51
52 #define BLOCKSIZE 4096
53
54 int collect(struct mem_message *message) {
55 struct stat sb;
56 int len;
57
58 memset(message, 0, sizeof(struct mem_message));
59
60 #ifndef MMAP_UNAVAILABLE
61 /* if stdin is a file whose size is known, try to mmap it */
62 if (!fstat(0, &sb) && S_ISREG(sb.st_mode) && sb.st_size >= 0) {
63 message->len = sb.st_size;
64 if ((message->data = mmap(NULL, message->len, PROT_READ, MAP_SHARED,
65 STDIN_FILENO, 0)) != MAP_FAILED)
66 return 0;
67 }
68 #endif
69
70 /* read the buffer from stdin by blocks, until EOF or error.
71 save the message in a mmap_string */
72 if ((message->mstring = mmap_string_sized_new(BLOCKSIZE)) == NULL) {
73 perror("mmap_string_new");
74 goto error;
75 }
76 message->len = 0;
77
78 while ((len = read(STDIN_FILENO,
79 message->mstring->str + message->len, BLOCKSIZE)) > 0) {
80 message->len += len;
81 /* reserve room for next block */
82 if ((mmap_string_set_size(message->mstring,
83 message->len + BLOCKSIZE)) == NULL) {
84 perror("mmap_string_set_size");
85 goto error;
86 }
87 }
88
89 if (len == 0) {
90 message->data = message->mstring->str;
91 return 0; /* OK */
92 }
93
94 perror("read");
95
96 error:
97 if (message->mstring != NULL)
98 mmap_string_free(message->mstring);
99 return -1;
100 }
101
102 char *guessfrom(void) {
103 #ifndef WIN32
104 uid_t uid;
105 struct passwd *pw;
106 char hostname[256];
107 int len;
108 char *gfrom;
109
110 if (gethostname(hostname, sizeof(hostname))) {
111 perror("gethostname");
112 return NULL;
113 }
114 hostname[sizeof(hostname) - 1] = '\0';
115
116 uid = getuid();
117 pw = getpwuid(uid);
118
119 len = ((pw != NULL) ? strlen(pw->pw_name) : 12)
120 + strlen(hostname) + 2;
121
122 if ((gfrom = malloc(len)) == NULL) {
123 perror("malloc");
124 return NULL;
125 }
126 if (pw != NULL && pw->pw_name != NULL)
127 snprintf(gfrom, len, "%s@%s", pw->pw_name, hostname);
128 else
129 snprintf(gfrom, len, "#%u@%s", uid, hostname);
130 return gfrom;
131 #else
132 return NULL;
133 #endif
134 }
135
136 void release(struct mem_message *message) {
137 if (message->mstring != NULL)
138 mmap_string_free(message->mstring);
139 #ifndef MMAP_UNAVAILABLE
140 else if (message->data != NULL)
141 munmap(message->data, message->len);
142 #endif
143 }
144
145 int send_message(char *data, size_t len, char**rcpts) {
146 int s = -1;
147 int ret;
148 char **r;
149 int esmtp = 0;
150 mailsmtp *smtp = NULL;
151
152 if ((smtp = mailsmtp_new(0, NULL)) == NULL) {
153 perror("mailsmtp_new");
154 goto error;
155 }
156
157 /* first open the stream */
158 if ((ret = mailsmtp_socket_connect(smtp,
159 (smtp_server != NULL ? smtp_server : "localhost"),
160 smtp_port)) != MAILSMTP_NO_ERROR) {
161 fprintf(stderr, "mailsmtp_socket_connect: %s\n", mailsmtp_strerror(ret));
162 goto error;
163 }
164
165 /* then introduce ourselves */
166 if (smtp_esmtp && (ret = mailesmtp_ehlo(smtp)) == MAILSMTP_NO_ERROR)
167 esmtp = 1;
168 else if (!smtp_esmtp || ret == MAILSMTP_ERROR_NOT_IMPLEMENTED)
169 ret = mailsmtp_helo(smtp);
170 if (ret != MAILSMTP_NO_ERROR) {
171 fprintf(stderr, "mailsmtp_helo: %s\n", mailsmtp_strerror(ret));
172 goto error;
173 }
174
175 if (esmtp && smtp_tls &&
176 (ret = mailsmtp_socket_starttls(smtp)) != MAILSMTP_NO_ERROR) {
177 fprintf(stderr, "mailsmtp_starttls: %s\n", mailsmtp_strerror(ret));
178 goto error;
179 }
180
247437a @dinhviethoa improved smtp sample
authored
181 if (esmtp && smtp_tls) {
182 /* introduce ourselves again */
183 if (smtp_esmtp && (ret = mailesmtp_ehlo(smtp)) == MAILSMTP_NO_ERROR)
184 esmtp = 1;
185 else if (!smtp_esmtp || ret == MAILSMTP_ERROR_NOT_IMPLEMENTED)
186 ret = mailsmtp_helo(smtp);
187 if (ret != MAILSMTP_NO_ERROR) {
188 fprintf(stderr, "mailsmtp_helo: %s\n", mailsmtp_strerror(ret));
189 goto error;
190 }
191 }
192
9155727 @dinhviethoa import from CVS
authored
193 if (esmtp && smtp_user != NULL &&
194 (ret = mailsmtp_auth(smtp, smtp_user,
195 (smtp_password != NULL) ? smtp_password : ""))
196 != MAILSMTP_NO_ERROR) {
197 fprintf(stderr, "mailsmtp_auth: %s: %s\n", smtp_user, mailsmtp_strerror(ret));
198 goto error;
199 }
200
201 /* source */
202 if ((ret = (esmtp ?
203 mailesmtp_mail(smtp, smtp_from, 1, "etPanSMTPTest") :
204 mailsmtp_mail(smtp, smtp_from))) != MAILSMTP_NO_ERROR) {
205 fprintf(stderr, "mailsmtp_mail: %s, %s\n", smtp_from, mailsmtp_strerror(ret));
206 goto error;
207 }
208
209 /* recipients */
210 for (r = rcpts; *r != NULL; r++) {
211 if ((ret = (esmtp ?
212 mailesmtp_rcpt(smtp, *r,
213 MAILSMTP_DSN_NOTIFY_FAILURE|MAILSMTP_DSN_NOTIFY_DELAY,
214 NULL) :
215 mailsmtp_rcpt(smtp, *r))) != MAILSMTP_NO_ERROR) {
216 fprintf(stderr, "mailsmtp_rcpt: %s: %s\n", *r, mailsmtp_strerror(ret));
217 goto error;
218 }
219 }
220
221 /* message */
222 if ((ret = mailsmtp_data(smtp)) != MAILSMTP_NO_ERROR) {
223 fprintf(stderr, "mailsmtp_data: %s\n", mailsmtp_strerror(ret));
224 goto error;
225 }
226 if ((ret = mailsmtp_data_message(smtp, data, len)) != MAILSMTP_NO_ERROR) {
227 fprintf(stderr, "mailsmtp_data_message: %s\n", mailsmtp_strerror(ret));
228 goto error;
229 }
230 mailsmtp_free(smtp);
231 return 0;
232
233 error:
234 if (smtp != NULL)
235 mailsmtp_free(smtp);
236 if (s >= 0)
237 close(s);
238 return -1;
239 }
240
241 int main(int argc, char **argv) {
242 struct mem_message message;
243 int r;
244
c09f9ca @Dunemaster added critical section deletion on windows
Dunemaster authored
245
9155727 @dinhviethoa import from CVS
authored
246 #if HAVE_GETOPT_LONG
247 int indx;
248 static struct option long_options[] = {
249 {"server", 1, 0, 's'},
250 {"port", 1, 0, 'p'},
251 {"user", 1, 0, 'u'},
252 {"password", 1, 0, 'v'},
253 {"from", 1, 0, 'f'},
254 {"tls", 0, 0, 'S'},
255 {"no-esmtp", 0, 0, 'E'},
256 };
257 #endif
258
259 while(1) {
260 #if HAVE_GETOPT_LONG
261 r = getopt_long(argc, argv, "s:p:u:v:f:SE", long_options, &indx);
262 #else
263 r = getopt(argc, argv, "s:p:u:v:f:SE");
264 #endif
265 if (r < 0)
266 break;
267 switch (r) {
268 case 's':
269 if (smtp_server != NULL)
270 free(smtp_server);
271 smtp_server = strdup(optarg);
272 break;
273 case 'p':
274 smtp_port = (uint16_t) strtoul(optarg, NULL, 10);
275 break;
276 case 'u':
277 if (smtp_user != NULL)
278 free(smtp_user);
279 smtp_user = strdup(optarg);
280 break;
281 case 'v':
282 if (smtp_password != NULL)
283 free(smtp_password);
284 smtp_password = strdup(optarg);
285 break;
286 case 'f':
287 if (smtp_from != NULL)
288 free(smtp_from);
289 smtp_from = strdup(optarg);
290 break;
291 case 'S':
292 smtp_tls = 1;
293 break;
294 case 'E':
295 smtp_esmtp = 0;
296 break;
297 }
298 }
299
300 argc -= optind;
301 argv += optind;
302
303 if (argc < 1) {
304 fprintf(stderr, "usage: smtpsend [-f from] [-u user] [-v password] [-s server] [-p port] [-S] <rcpts>...\n");
305 return EXIT_FAILURE;
306 }
307
308 if (smtp_from == NULL && (smtp_from = guessfrom()) == NULL) {
309 fprintf(stderr, "can't guess a valid from, please use -f option.\n");
310 return EXIT_FAILURE;
311 }
312
313 /* reads message from stdin */
314 if (collect(&message))
315 return EXIT_FAILURE;
316
317 send_message(message.data, message.len, argv);
318
319 release(&message);
c09f9ca @Dunemaster added critical section deletion on windows
Dunemaster authored
320
321 fprintf(stdout, "Sent ok.\n");
322
9155727 @dinhviethoa import from CVS
authored
323 return EXIT_SUCCESS;
324 }
Something went wrong with that request. Please try again.