regex: updates from neatvi
[neatmail.git] / pg.c
blob392dd0987067321c9782ef4c57d6da80b1c78175
1 #include <ctype.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <time.h>
9 #include "mail.h"
11 #define USERAGENT "Neatmail/1.1 (https://github.com/aligrudi/neatmail)"
12 #define MBOUNDARY "neatmail-boundary"
13 #define PARTS_N 16
14 #define HDRS_N 128
16 static char *parts[PARTS_N];
17 static int parts_n;
19 static void msg_new(char **msg, long *msglen);
20 static int msg_reply(char *msg, long msglen, char **mod, long *modlen);
21 static int msg_forward(char *msg, long msglen, char **mod, long *modlen);
23 static int msg_filter(char *msg, long msglen, char **mod, long *modlen, char *hdrs[])
25 struct sbuf *sb = sbuf_make();
26 char *s = msg;
27 char *e = msg + msglen;
28 int i;
29 for (i = 0; hdrs[i]; i++) {
30 char *val = msg_get(msg, msglen, hdrs[i]);
31 if (val)
32 sbuf_mem(sb, val, hdrlen(val, msg + msglen - val));
34 while (s + 1 < e && (s[0] != '\n' || s[1] != '\n'))
35 s++;
36 s++;
37 sbuf_mem(sb, s, e - s);
38 *modlen = sbuf_len(sb);
39 *mod = sbuf_done(sb);
40 return 0;
43 /* obtain a message from an mbox by its message id */
44 static int mbox_mid(char **path, char *mid)
46 struct mbox *mbox = mbox_open(path);
47 int ret = -1;
48 int i;
49 if (!mbox)
50 return -1;
51 for (i = 0; i < mbox_len(mbox); i++) {
52 char *id_hdr;
53 char *msg = NULL;
54 long msglen;
55 if (mbox_get(mbox, i, &msg, &msglen))
56 continue;
57 id_hdr = msg_get(msg, msglen, "Message-ID:");
58 if (id_hdr) {
59 int len = hdrlen(id_hdr, (msg + msglen) - id_hdr);
60 char *beg = memchr(id_hdr, '<', len);
61 char *end = beg ? memchr(id_hdr, '>', len) : NULL;
62 if (!beg || !end || beg > end)
63 continue;
64 beg++;
65 if (strlen(mid) == end - beg &&
66 !memcmp(mid, beg, end - beg))
67 ret = i;
70 mbox_free(mbox);
71 return ret;
74 static char *usage =
75 "usage: neatmail pg [options] [address]\n\n"
76 "options:\n"
77 " -h hdr \tspecify headers to include\n"
78 " -m \tdecode mime message\n"
79 " -r \tgenerate a reply\n"
80 " -f \tgenerate a forward\n"
81 " -n \tgenerate a new message\n"
82 " -a file \tadd an attachment\n"
83 " -s \tfollow neatmail-source: header\n\n"
84 "address:\n"
85 " mbox msgnum \tpage msgnum-th message in mbox\n"
86 " mbox =msgid \tpage the message with the given message-id\n"
87 " msgnum@mbox \tsame as mbox msgnum\n";
89 int pg(char *argv[])
91 char *msg, *mod;
92 char *hdrs[HDRS_N] = {NULL};
93 char *path[16] = {NULL};
94 char *msgnum = NULL;
95 long msglen, modlen;
96 int demime = 0;
97 int reply = 0;
98 int forward = 0;
99 int source = 0;
100 int newmsg = 0;
101 int hdrs_n = 0;
102 int path_n = 0;
103 int addr;
104 int i;
105 for (i = 0; argv[i] && argv[i][0] == '-'; i++) {
106 if (argv[i][1] == 'm')
107 demime = 1;
108 if (argv[i][1] == 'r')
109 reply = 1;
110 if (argv[i][1] == 'n')
111 newmsg = 1;
112 if (argv[i][1] == 'f')
113 forward = 1;
114 if (argv[i][1] == 's')
115 source = 1;
116 if (argv[i][1] == 'b') {
117 path[path_n++] = argv[i][2] ? argv[i] + 2 : argv[++i];
118 continue;
120 if (argv[i][1] == 'i') {
121 msgnum = argv[i][2] ? argv[i] + 2 : argv[++i];
122 continue;
124 if (argv[i][1] == 'h') {
125 char *hdr = argv[i][2] ? argv[i] + 2 : argv[++i];
126 if (hdrs_n + 1 < HDRS_N)
127 hdrs[hdrs_n++] = hdr;
128 continue;
130 if (argv[i][1] == 'a') {
131 if (parts_n < PARTS_N)
132 parts[parts_n++] = argv[i][2] ? argv[i] + 2 : argv[++i];
133 continue;
136 if (newmsg) {
137 msg_new(&msg, &msglen);
138 xwrite(1, msg, msglen);
139 free(msg);
140 return 0;
142 if (!path[0] && argv[i])
143 path[0] = argv[i++];
144 if (!msgnum && argv[i])
145 msgnum = argv[i++];
146 if (msgnum && msgnum[0] != '=' && strchr(msgnum, '@') != NULL) {
147 path[0] = strchr(msgnum, '@') + 1;
148 path[0][-1] = '\0';
150 if (!path[0] || !msgnum) {
151 printf("%s", usage);
152 return 1;
154 if (msgnum[0] == '=')
155 addr = mbox_mid(path, msgnum + 1);
156 else
157 addr = atoi(msgnum);
158 if (addr >= 0 && !mbox_ith(path[0], addr, &msg, &msglen)) {
159 char *hdr;
160 char box[512];
161 long beg, end;
162 int num;
163 if (source && (hdr = msg_get(msg, msglen, "Neatmail-Source:")) != NULL) {
164 while (*hdr && *hdr != ' ')
165 hdr++;
166 if (sscanf(hdr, "%d@%s %ld %ld", &num, box, &beg, &end) != 4)
167 return 1;
168 free(msg);
169 if (mbox_off(box, beg, end, &msg, &msglen) != 0)
170 return 1;
172 if (demime && !msg_demime(msg, msglen, &mod, &modlen)) {
173 free(msg);
174 msg = mod;
175 msglen = modlen;
177 if (reply && !msg_reply(msg, msglen, &mod, &modlen)) {
178 free(msg);
179 msg = mod;
180 msglen = modlen;
182 if (hdrs_n && !msg_filter(msg, msglen, &mod, &modlen, hdrs)) {
183 free(msg);
184 msg = mod;
185 msglen = modlen;
187 if (forward && !msg_forward(msg, msglen, &mod, &modlen)) {
188 free(msg);
189 msg = mod;
190 msglen = modlen;
192 xwrite(1, msg, msglen);
193 free(msg);
195 return addr < 0;
198 static void put_from_(struct sbuf *sb)
200 char buf[128];
201 time_t t;
202 time(&t);
203 strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y", localtime(&t));
204 sbuf_printf(sb, "From %s %s\n",
205 getenv("LOGNAME") ? getenv("LOGNAME") : "me", buf);
208 static void put_date(struct sbuf *sb)
210 char buf[128];
211 time_t t;
212 time(&t);
213 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
214 sbuf_printf(sb, "Date: %s\n", buf);
217 static void put_id(struct sbuf *sb)
219 char buf[128];
220 char host[32] = "neatmail.host";
221 time_t t;
222 time(&t);
223 strftime(buf, sizeof(buf), "%Y%d%m%H%M%S", localtime(&t));
224 sbuf_printf(sb, "Message-ID: <%s@%s>\n", buf, host);
227 static void put_agent(struct sbuf *sb)
229 sbuf_printf(sb, "User-Agent: " USERAGENT "\n");
232 static char *fileread(char *path, int *len)
234 int fd = open(path, O_RDONLY);
235 char buf[4096];
236 struct sbuf *sb;
237 if (fd < 0)
238 return NULL;
239 sb = sbuf_make();
240 while (1) {
241 int ret = read(fd, buf, sizeof(buf));
242 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
243 continue;
244 if (ret <= 0)
245 break;
246 sbuf_mem(sb, buf, ret);
248 close(fd);
249 *len = sbuf_len(sb);
250 return sbuf_done(sb);
253 static char *filename(char *path)
255 char *sl = strrchr(path, '/');
256 return sl ? sl + 1 : path;
259 static void put_body(struct sbuf *sb, char *body)
261 sbuf_printf(sb, "MIME-Version: 1.0\n");
262 if (!parts_n) {
263 sbuf_printf(sb, "Content-Type: text/plain; charset=utf-8\n");
264 sbuf_printf(sb, "Content-Transfer-Encoding: 8bit\n");
265 sbuf_printf(sb, "\n");
266 sbuf_str(sb, body);
267 } else {
268 int i;
269 sbuf_printf(sb, "Content-Type: multipart/mixed; boundary=%s\n", MBOUNDARY);
270 sbuf_printf(sb, "\nMulti-part MIME message.\n");
271 sbuf_printf(sb, "--%s\n", MBOUNDARY);
272 sbuf_printf(sb, "Content-Type: text/plain; charset=utf-8\n");
273 sbuf_printf(sb, "Content-Transfer-Encoding: 8bit\n");
274 sbuf_printf(sb, "\n");
275 sbuf_str(sb, body);
276 for (i = 0; i < parts_n; i++) {
277 char *cont;
278 int cont_len = 0;
279 sbuf_printf(sb, "--%s\n", MBOUNDARY);
280 sbuf_printf(sb, "Content-Type: application/octet-stream\n");
281 sbuf_printf(sb, "Content-Disposition: attachment; filename=\"%s\"\n",
282 filename(parts[i]));
283 sbuf_printf(sb, "Content-Transfer-Encoding: base64\n");
284 sbuf_printf(sb, "\n");
285 cont = fileread(parts[i], &cont_len);
286 if (cont) {
287 char *b64 = base64(cont, cont_len);
288 sbuf_mem(sb, b64, strlen(b64));
289 free(b64);
291 free(cont);
293 sbuf_printf(sb, "--%s--\n", MBOUNDARY);
297 static void msg_new(char **msg, long *msglen)
299 struct sbuf *sb = sbuf_make();
300 put_from_(sb);
301 sbuf_printf(sb, "From: \n");
302 sbuf_printf(sb, "To: \n");
303 sbuf_printf(sb, "Subject: \n");
304 put_id(sb);
305 put_date(sb);
306 put_agent(sb);
307 put_body(sb, "MAIL BODY...\n");
308 sbuf_chr(sb, '\n');
309 *msglen = sbuf_len(sb);
310 *msg = sbuf_done(sb);
313 static char *hdr_val(char *hdr)
315 hdr = strchr(hdr, ':') + 1;
316 while (isspace(*hdr))
317 hdr++;
318 return hdr;
321 static int hdr_len(char *hdr)
323 int l = hdrlen(hdr, 1024);
324 while (l > 0 && strchr(" \r\n", (unsigned char) hdr[l - 1]))
325 l--;
326 return l;
329 static void put_subjreply(struct sbuf *sb, char *subj)
331 subj = hdr_val(subj);
332 sbuf_str(sb, "Subject: ");
333 if (tolower(subj[0]) != 'r' || tolower(subj[1]) != 'e')
334 sbuf_str(sb, "Re: ");
335 sbuf_mem(sb, subj, hdr_len(subj));
336 sbuf_str(sb, "\n");
339 static void put_subjfwd(struct sbuf *sb, char *subj)
341 subj = hdr_val(subj);
342 sbuf_str(sb, "Subject: ");
343 sbuf_str(sb, "Fwd: ");
344 sbuf_mem(sb, subj, hdr_len(subj));
345 sbuf_str(sb, "\n");
348 static void put_replyto(struct sbuf *sb, char *id, char *ref)
350 id = hdr_val(id);
351 sbuf_str(sb, "In-Reply-To: ");
352 sbuf_mem(sb, id, hdr_len(id));
353 sbuf_str(sb, "\n");
354 sbuf_str(sb, "References: ");
355 if (ref) {
356 ref = hdr_val(ref);
357 sbuf_mem(sb, ref, hdr_len(ref));
358 sbuf_str(sb, "\n\t");
360 sbuf_mem(sb, id, hdr_len(id));
361 sbuf_str(sb, "\n");
364 static void put_reply(struct sbuf *sb, char *from, char *to, char *cc, char *rply)
366 if (from || rply) {
367 char *hdr = rply ? rply : from;
368 hdr = hdr_val(hdr);
369 sbuf_str(sb, "To: ");
370 sbuf_mem(sb, hdr, hdr_len(hdr));
371 sbuf_str(sb, "\n");
373 if (to || cc || (rply && from)) {
374 int n = 0;
375 sbuf_str(sb, "Cc: ");
376 if (to) {
377 to = hdr_val(to);
378 if (n++)
379 sbuf_str(sb, ",\n\t");
380 sbuf_mem(sb, to, hdr_len(to));
382 if (rply && from) {
383 from = hdr_val(from);
384 if (n++)
385 sbuf_str(sb, ",\n\t");
386 sbuf_mem(sb, from, hdr_len(from));
388 if (cc) {
389 cc = hdr_val(cc);
390 if (n++)
391 sbuf_str(sb, ",\n\t");
392 sbuf_mem(sb, cc, hdr_len(cc));
394 sbuf_str(sb, "\n");
398 static char *quote_body(char *msg, long msglen)
400 struct sbuf *sb = sbuf_make();
401 char *from = msg_get(msg, msglen, "From:");
402 char *s = msg;
403 char *e = msg + msglen;
404 while (s + 1 < e && (s[0] != '\n' || s[1] != '\n'))
405 s++;
406 s += 2;
407 sbuf_chr(sb, '\n');
408 if (from) {
409 from = hdr_val(from);
410 sbuf_mem(sb, from, hdr_len(from));
411 sbuf_str(sb, " wrote:\n");
413 while (s < e) {
414 char *r = memchr(s, '\n', e - s);
415 if (!r)
416 break;
417 sbuf_str(sb, "> ");
418 sbuf_mem(sb, s, r - s + 1);
419 s = r + 1;
421 return sbuf_done(sb);
424 static int msg_reply(char *msg, long msglen, char **mod, long *modlen)
426 struct sbuf *sb = sbuf_make();
427 char *id_hdr = msg_get(msg, msglen, "Message-ID:");
428 char *ref_hdr = msg_get(msg, msglen, "References:");
429 char *from_hdr = msg_get(msg, msglen, "From:");
430 char *subj_hdr = msg_get(msg, msglen, "Subject:");
431 char *to_hdr = msg_get(msg, msglen, "To:");
432 char *cc_hdr = msg_get(msg, msglen, "CC:");
433 char *rply_hdr = msg_get(msg, msglen, "Reply-To:");
434 char *body;
435 put_from_(sb);
436 put_date(sb);
437 sbuf_printf(sb, "From: \n");
438 put_reply(sb, from_hdr, to_hdr, cc_hdr, rply_hdr);
439 if (subj_hdr)
440 put_subjreply(sb, subj_hdr);
441 put_id(sb);
442 if (id_hdr)
443 put_replyto(sb, id_hdr, ref_hdr);
444 put_agent(sb);
445 body = quote_body(msg, msglen);
446 put_body(sb, body);
447 free(body);
448 *modlen = sbuf_len(sb);
449 *mod = sbuf_done(sb);
450 return 0;
453 static int msg_forward(char *msg, long msglen, char **mod, long *modlen)
455 struct sbuf *sb = sbuf_make();
456 struct sbuf *sb_body = sbuf_make();
457 char *subj_hdr = msg_get(msg, msglen, "Subject:");
458 put_from_(sb);
459 put_date(sb);
460 sbuf_printf(sb, "From: \n");
461 sbuf_printf(sb, "To: \n");
462 put_subjfwd(sb, subj_hdr);
463 put_id(sb);
464 put_agent(sb);
465 sbuf_str(sb_body, "\n-------- Original Message --------\n");
466 sbuf_mem(sb_body, msg, msglen);
467 put_body(sb, sbuf_buf(sb_body));
468 sbuf_free(sb_body);
469 *modlen = sbuf_len(sb);
470 *mod = sbuf_done(sb);
471 return 0;