pg: -b specifies the mbox and -i the message number
[neatmail.git] / pg.c
blob49d1c952e2a9be88a553fbe01f34c19749b54a8d
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 (git://repo.or.cz/neatmail.git)"
12 #define MBOUNDARY "neatmail-boundary"
13 #define NPARTS 16
15 static char *parts[NPARTS];
16 static int parts_n;
18 static void msg_new(char **msg, long *msglen);
19 static int msg_reply(char *msg, long msglen, char **mod, long *modlen);
20 static int msg_forward(char *msg, long msglen, char **mod, long *modlen);
22 static char *segment(char *d, char *s, int m)
24 char *r = strchr(s, m);
25 char *e = r ? r + 1 : strchr(s, '\0');
26 memcpy(d, s, e - s);
27 d[e - s] = '\0';
28 return e;
31 static int msg_filter(char *msg, long msglen, char **mod, long *modlen, char *hdrs)
33 struct sbuf *sb = sbuf_make();
34 char *hdr = malloc(strlen(hdrs) + 1);
35 char *s = msg;
36 char *e = msg + msglen;
37 while ((hdrs = segment(hdr, hdrs, ':')) && hdr[0]) {
38 char *val = msg_get(msg, msglen, hdr);
39 if (val)
40 sbuf_mem(sb, val, hdrlen(val, msg + msglen - val));
42 free(hdr);
43 while (s + 1 < e && (s[0] != '\n' || s[1] != '\n'))
44 s++;
45 s++;
46 sbuf_mem(sb, s, e - s);
47 *modlen = sbuf_len(sb);
48 *mod = sbuf_done(sb);
49 return 0;
52 /* obtain a message from an mbox by its message id */
53 static int mbox_mid(char *path, char *mid)
55 struct mbox *mbox = mbox_open(path);
56 int ret = -1;
57 int i;
58 if (!mbox)
59 return -1;
60 for (i = 0; i < mbox_len(mbox); i++) {
61 char *id_hdr;
62 char *msg = NULL;
63 long msglen;
64 if (mbox_get(mbox, i, &msg, &msglen))
65 continue;
66 id_hdr = msg_get(msg, msglen, "Message-ID:");
67 if (id_hdr) {
68 int len = hdrlen(id_hdr, (msg + msglen) - id_hdr);
69 char *beg = memchr(id_hdr, '<', len);
70 char *end = beg ? memchr(id_hdr, '>', len) : NULL;
71 if (!beg || !end || beg > end)
72 continue;
73 beg++;
74 if (strlen(mid) == end - beg &&
75 !memcmp(mid, beg, end - beg))
76 ret = i;
79 mbox_free(mbox);
80 return ret;
83 static char *usage =
84 "usage: neatmail pg [options]\n\n"
85 "options:\n"
86 " -b path \tmbox path\n"
87 " -i msg \tmsg number or message id (=msg_id)\n"
88 " -h hdrs \tthe list of headers to include\n"
89 " -m \tdecode mime message\n"
90 " -r \tgenerate a reply\n"
91 " -f \tgenerate a forward\n"
92 " -n \tgenerate a new message\n"
93 " -a file \tadd an attachment\n";
95 int pg(char *argv[])
97 char *msg, *mod;
98 char *hdrs = NULL;
99 char *path = NULL;
100 char *msgnum = NULL;
101 long msglen, modlen;
102 int demime = 0;
103 int reply = 0;
104 int forward = 0;
105 int newmsg = 0;
106 int addr;
107 int i;
108 for (i = 0; argv[i] && argv[i][0] == '-'; i++) {
109 if (argv[i][1] == 'm')
110 demime = 1;
111 if (argv[i][1] == 'r')
112 reply = 1;
113 if (argv[i][1] == 'n')
114 newmsg = 1;
115 if (argv[i][1] == 'f')
116 forward = 1;
117 if (argv[i][1] == 'b') {
118 path = argv[i][2] ? argv[i] + 2 : argv[++i];
119 continue;
121 if (argv[i][1] == 'i') {
122 msgnum = argv[i][2] ? argv[i] + 2 : argv[++i];
123 continue;
125 if (argv[i][1] == 'h') {
126 hdrs = argv[i][2] ? argv[i] + 2 : argv[++i];
127 continue;
129 if (argv[i][1] == 'a') {
130 if (parts_n < NPARTS)
131 parts[parts_n++] = argv[i][2] ? argv[i] + 2 : argv[++i];
132 continue;
135 if (newmsg) {
136 msg_new(&msg, &msglen);
137 xwrite(1, msg, msglen);
138 free(msg);
139 return 0;
141 if (!path && argv[i])
142 path = argv[i++];
143 if (!msgnum && argv[i])
144 msgnum = argv[i++];
145 if (!path || !msgnum) {
146 printf("%s", usage);
147 return 1;
149 if (msgnum[0] == '=')
150 addr = mbox_mid(path, msgnum + 1);
151 else
152 addr = atoi(msgnum);
153 if (addr >= 0 && !mbox_ith(path, addr, &msg, &msglen)) {
154 if (demime && !msg_demime(msg, msglen, &mod, &modlen)) {
155 free(msg);
156 msg = mod;
157 msglen = modlen;
159 if (reply && !msg_reply(msg, msglen, &mod, &modlen)) {
160 free(msg);
161 msg = mod;
162 msglen = modlen;
164 if (hdrs && !msg_filter(msg, msglen, &mod, &modlen, hdrs)) {
165 free(msg);
166 msg = mod;
167 msglen = modlen;
169 if (forward && !msg_forward(msg, msglen, &mod, &modlen)) {
170 free(msg);
171 msg = mod;
172 msglen = modlen;
174 xwrite(1, msg, msglen);
175 free(msg);
177 return addr < 0;
180 static void put_from_(struct sbuf *sb)
182 char buf[128];
183 time_t t;
184 time(&t);
185 strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y", localtime(&t));
186 sbuf_printf(sb, "From %s %s\n",
187 getenv("LOGNAME") ? getenv("LOGNAME") : "me", buf);
190 static void put_date(struct sbuf *sb)
192 char buf[128];
193 time_t t;
194 time(&t);
195 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
196 sbuf_printf(sb, "Date: %s\n", buf);
199 static void put_id(struct sbuf *sb)
201 char buf[128];
202 char host[32] = "neatmail.host";
203 time_t t;
204 time(&t);
205 strftime(buf, sizeof(buf), "%Y%d%m%H%M%S", localtime(&t));
206 sbuf_printf(sb, "Message-ID: <%s@%s>\n", buf, host);
209 static void put_agent(struct sbuf *sb)
211 sbuf_printf(sb, "User-Agent: " USERAGENT "\n");
214 static char *fileread(char *path, int *len)
216 int fd = open(path, O_RDONLY);
217 char buf[4096];
218 struct sbuf *sb;
219 if (fd < 0)
220 return NULL;
221 sb = sbuf_make();
222 while (1) {
223 int ret = read(fd, buf, sizeof(buf));
224 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
225 continue;
226 if (ret <= 0)
227 break;
228 sbuf_mem(sb, buf, ret);
230 close(fd);
231 *len = sbuf_len(sb);
232 return sbuf_done(sb);
235 /* just in case basename() is not available */
236 static char *filename(char *path)
238 char *sl = strrchr(path, '/');
239 return sl ? sl + 1 : path;
242 static void put_body(struct sbuf *sb, char *body)
244 sbuf_printf(sb, "MIME-Version: 1.0\n");
245 if (!parts_n) {
246 sbuf_printf(sb, "Content-Type: text/plain; charset=utf-8\n");
247 sbuf_printf(sb, "Content-Transfer-Encoding: 8bit\n");
248 sbuf_printf(sb, "\n");
249 sbuf_str(sb, body);
250 } else {
251 int i;
252 sbuf_printf(sb, "Content-Type: multipart/mixed; boundary=%s\n", MBOUNDARY);
253 sbuf_printf(sb, "\n\n");
254 sbuf_printf(sb, "--%s\n", MBOUNDARY);
255 sbuf_printf(sb, "Content-Type: text/plain; charset=utf-8\n");
256 sbuf_printf(sb, "Content-Transfer-Encoding: 8bit\n");
257 sbuf_printf(sb, "\n");
258 sbuf_str(sb, body);
259 for (i = 0; i < parts_n; i++) {
260 char *cont;
261 int cont_len = 0;
262 sbuf_printf(sb, "--%s\n", MBOUNDARY);
263 sbuf_printf(sb, "Content-Type: application/octet-stream\n");
264 sbuf_printf(sb, "Content-Disposition: attachment; filename=%s;\n",
265 filename(parts[i]));
266 sbuf_printf(sb, "Content-Transfer-Encoding: base64\n");
267 sbuf_printf(sb, "\n");
268 cont = fileread(parts[i], &cont_len);
269 if (cont) {
270 char *b64 = base64(cont, cont_len);
271 sbuf_mem(sb, b64, strlen(b64));
272 free(b64);
274 free(cont);
276 sbuf_printf(sb, "--%s--\n", MBOUNDARY);
280 static void msg_new(char **msg, long *msglen)
282 struct sbuf *sb = sbuf_make();
283 put_from_(sb);
284 sbuf_printf(sb, "From: \n");
285 sbuf_printf(sb, "To: \n");
286 sbuf_printf(sb, "Subject: \n");
287 put_id(sb);
288 put_date(sb);
289 put_agent(sb);
290 put_body(sb, "MAIL BODY...\n");
291 sbuf_chr(sb, '\n');
292 *msglen = sbuf_len(sb);
293 *msg = sbuf_done(sb);
296 static char *hdr_val(char *hdr)
298 hdr = strchr(hdr, ':') + 1;
299 while (isspace(*hdr))
300 hdr++;
301 return hdr;
304 static int hdr_len(char *hdr)
306 int l = hdrlen(hdr, 1024);
307 while (l > 0 && strchr(" \r\n", (unsigned char) hdr[l - 1]))
308 l--;
309 return l;
312 static void put_subjreply(struct sbuf *sb, char *subj)
314 subj = hdr_val(subj);
315 sbuf_str(sb, "Subject: ");
316 if (tolower(subj[0]) != 'r' || tolower(subj[1]) != 'e')
317 sbuf_str(sb, "Re: ");
318 sbuf_mem(sb, subj, hdr_len(subj));
319 sbuf_str(sb, "\n");
322 static void put_subjfwd(struct sbuf *sb, char *subj)
324 subj = hdr_val(subj);
325 sbuf_str(sb, "Subject: ");
326 sbuf_str(sb, "Fwd: ");
327 sbuf_mem(sb, subj, hdr_len(subj));
328 sbuf_str(sb, "\n");
331 static void put_replyto(struct sbuf *sb, char *id, char *ref)
333 id = hdr_val(id);
334 sbuf_str(sb, "In-Reply-To: ");
335 sbuf_mem(sb, id, hdr_len(id));
336 sbuf_str(sb, "\n");
337 sbuf_str(sb, "References: ");
338 if (ref) {
339 ref = hdr_val(ref);
340 sbuf_mem(sb, ref, hdr_len(ref));
341 sbuf_str(sb, "\n\t");
343 sbuf_mem(sb, id, hdr_len(id));
344 sbuf_str(sb, "\n");
347 static void put_reply(struct sbuf *sb, char *from, char *to, char *cc, char *rply)
349 if (from || rply) {
350 char *hdr = rply ? rply : from;
351 hdr = hdr_val(hdr);
352 sbuf_str(sb, "To: ");
353 sbuf_mem(sb, hdr, hdr_len(hdr));
354 sbuf_str(sb, "\n");
356 if (to || cc || (rply && from)) {
357 int n = 0;
358 sbuf_str(sb, "Cc: ");
359 if (to) {
360 to = hdr_val(to);
361 if (n++)
362 sbuf_str(sb, ",\n\t");
363 sbuf_mem(sb, to, hdr_len(to));
365 if (rply && from) {
366 from = hdr_val(from);
367 if (n++)
368 sbuf_str(sb, ",\n\t");
369 sbuf_mem(sb, from, hdr_len(from));
371 if (cc) {
372 cc = hdr_val(cc);
373 if (n++)
374 sbuf_str(sb, ",\n\t");
375 sbuf_mem(sb, cc, hdr_len(cc));
377 sbuf_str(sb, "\n");
381 static char *quote_body(char *msg, long msglen)
383 struct sbuf *sb = sbuf_make();
384 char *from = msg_get(msg, msglen, "From:");
385 char *s = msg;
386 char *e = msg + msglen;
387 while (s + 1 < e && (s[0] != '\n' || s[1] != '\n'))
388 s++;
389 s += 2;
390 sbuf_chr(sb, '\n');
391 if (from) {
392 from = hdr_val(from);
393 sbuf_mem(sb, from, hdr_len(from));
394 sbuf_str(sb, " wrote:\n");
396 while (s < e) {
397 char *r = memchr(s, '\n', e - s);
398 if (!r)
399 break;
400 sbuf_str(sb, "> ");
401 sbuf_mem(sb, s, r - s + 1);
402 s = r + 1;
404 return sbuf_done(sb);
407 static int msg_reply(char *msg, long msglen, char **mod, long *modlen)
409 struct sbuf *sb = sbuf_make();
410 char *id_hdr = msg_get(msg, msglen, "Message-ID:");
411 char *ref_hdr = msg_get(msg, msglen, "References:");
412 char *from_hdr = msg_get(msg, msglen, "From:");
413 char *subj_hdr = msg_get(msg, msglen, "Subject:");
414 char *to_hdr = msg_get(msg, msglen, "To:");
415 char *cc_hdr = msg_get(msg, msglen, "CC:");
416 char *rply_hdr = msg_get(msg, msglen, "Reply-To:");
417 char *body;
418 put_from_(sb);
419 put_date(sb);
420 sbuf_printf(sb, "From: \n");
421 put_reply(sb, from_hdr, to_hdr, cc_hdr, rply_hdr);
422 if (subj_hdr)
423 put_subjreply(sb, subj_hdr);
424 put_id(sb);
425 if (id_hdr)
426 put_replyto(sb, id_hdr, ref_hdr);
427 put_agent(sb);
428 body = quote_body(msg, msglen);
429 put_body(sb, body);
430 free(body);
431 *modlen = sbuf_len(sb);
432 *mod = sbuf_done(sb);
433 return 0;
436 static int msg_forward(char *msg, long msglen, char **mod, long *modlen)
438 struct sbuf *sb = sbuf_make();
439 struct sbuf *sb_body = sbuf_make();
440 char *subj_hdr = msg_get(msg, msglen, "Subject:");
441 put_from_(sb);
442 put_date(sb);
443 sbuf_printf(sb, "From: \n");
444 sbuf_printf(sb, "To: \n");
445 put_subjfwd(sb, subj_hdr);
446 put_id(sb);
447 put_agent(sb);
448 sbuf_str(sb_body, "\n-------- Original Message --------\n");
449 sbuf_mem(sb_body, msg, msglen);
450 put_body(sb, sbuf_buf(sb_body));
451 sbuf_free(sb_body);
452 *modlen = sbuf_len(sb);
453 *mod = sbuf_done(sb);
454 return 0;