11 #define USERAGENT "Neatmail/1.1 (https://github.com/aligrudi/neatmail)"
12 #define MBOUNDARY "neatmail-boundary"
16 static char *parts
[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();
27 char *e
= msg
+ msglen
;
29 for (i
= 0; hdrs
[i
]; i
++) {
30 char *val
= msg_get(msg
, msglen
, hdrs
[i
]);
32 sbuf_mem(sb
, val
, hdrlen(val
, msg
+ msglen
- val
));
34 while (s
+ 1 < e
&& (s
[0] != '\n' || s
[1] != '\n'))
37 sbuf_mem(sb
, s
, e
- s
);
38 *modlen
= sbuf_len(sb
);
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
);
51 for (i
= 0; i
< mbox_len(mbox
); i
++) {
55 if (mbox_get(mbox
, i
, &msg
, &msglen
))
57 id_hdr
= msg_get(msg
, msglen
, "Message-ID:");
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
)
65 if (strlen(mid
) == end
- beg
&&
66 !memcmp(mid
, beg
, end
- beg
))
75 "usage: neatmail pg [options] [address]\n\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"
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";
92 char *hdrs
[HDRS_N
] = {NULL
};
93 char *path
[16] = {NULL
};
105 for (i
= 0; argv
[i
] && argv
[i
][0] == '-'; i
++) {
106 if (argv
[i
][1] == 'm')
108 if (argv
[i
][1] == 'r')
110 if (argv
[i
][1] == 'n')
112 if (argv
[i
][1] == 'f')
114 if (argv
[i
][1] == 's')
116 if (argv
[i
][1] == 'b') {
117 path
[path_n
++] = argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
120 if (argv
[i
][1] == 'i') {
121 msgnum
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
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
;
130 if (argv
[i
][1] == 'a') {
131 if (parts_n
< PARTS_N
)
132 parts
[parts_n
++] = argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
137 msg_new(&msg
, &msglen
);
138 xwrite(1, msg
, msglen
);
142 if (!path
[0] && argv
[i
])
144 if (!msgnum
&& argv
[i
])
146 if (msgnum
&& msgnum
[0] != '=' && strchr(msgnum
, '@') != NULL
) {
147 path
[0] = strchr(msgnum
, '@') + 1;
150 if (!path
[0] || !msgnum
) {
154 if (msgnum
[0] == '=')
155 addr
= mbox_mid(path
, msgnum
+ 1);
158 if (addr
>= 0 && !mbox_ith(path
[0], addr
, &msg
, &msglen
)) {
163 if (source
&& (hdr
= msg_get(msg
, msglen
, "Neatmail-Source:")) != NULL
) {
164 while (*hdr
&& *hdr
!= ' ')
166 if (sscanf(hdr
, "%d@%s %ld %ld", &num
, box
, &beg
, &end
) != 4)
169 if (mbox_off(box
, beg
, end
, &msg
, &msglen
) != 0)
172 if (demime
&& !msg_demime(msg
, msglen
, &mod
, &modlen
)) {
177 if (reply
&& !msg_reply(msg
, msglen
, &mod
, &modlen
)) {
182 if (hdrs_n
&& !msg_filter(msg
, msglen
, &mod
, &modlen
, hdrs
)) {
187 if (forward
&& !msg_forward(msg
, msglen
, &mod
, &modlen
)) {
192 xwrite(1, msg
, msglen
);
198 static void put_from_(struct sbuf
*sb
)
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
)
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
)
220 char host
[32] = "neatmail.host";
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
);
241 int ret
= read(fd
, buf
, sizeof(buf
));
242 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
246 sbuf_mem(sb
, buf
, ret
);
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");
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");
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");
276 for (i
= 0; i
< parts_n
; i
++) {
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",
283 sbuf_printf(sb
, "Content-Transfer-Encoding: base64\n");
284 sbuf_printf(sb
, "\n");
285 cont
= fileread(parts
[i
], &cont_len
);
287 char *b64
= base64(cont
, cont_len
);
288 sbuf_mem(sb
, b64
, strlen(b64
));
293 sbuf_printf(sb
, "--%s--\n", MBOUNDARY
);
297 static void msg_new(char **msg
, long *msglen
)
299 struct sbuf
*sb
= sbuf_make();
301 sbuf_printf(sb
, "From: \n");
302 sbuf_printf(sb
, "To: \n");
303 sbuf_printf(sb
, "Subject: \n");
307 put_body(sb
, "MAIL BODY...\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
))
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]))
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
));
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
));
348 static void put_replyto(struct sbuf
*sb
, char *id
, char *ref
)
351 sbuf_str(sb
, "In-Reply-To: ");
352 sbuf_mem(sb
, id
, hdr_len(id
));
354 sbuf_str(sb
, "References: ");
357 sbuf_mem(sb
, ref
, hdr_len(ref
));
358 sbuf_str(sb
, "\n\t");
360 sbuf_mem(sb
, id
, hdr_len(id
));
364 static void put_reply(struct sbuf
*sb
, char *from
, char *to
, char *cc
, char *rply
)
367 char *hdr
= rply
? rply
: from
;
369 sbuf_str(sb
, "To: ");
370 sbuf_mem(sb
, hdr
, hdr_len(hdr
));
373 if (to
|| cc
|| (rply
&& from
)) {
375 sbuf_str(sb
, "Cc: ");
379 sbuf_str(sb
, ",\n\t");
380 sbuf_mem(sb
, to
, hdr_len(to
));
383 from
= hdr_val(from
);
385 sbuf_str(sb
, ",\n\t");
386 sbuf_mem(sb
, from
, hdr_len(from
));
391 sbuf_str(sb
, ",\n\t");
392 sbuf_mem(sb
, cc
, hdr_len(cc
));
398 static char *quote_body(char *msg
, long msglen
)
400 struct sbuf
*sb
= sbuf_make();
401 char *from
= msg_get(msg
, msglen
, "From:");
403 char *e
= msg
+ msglen
;
404 while (s
+ 1 < e
&& (s
[0] != '\n' || s
[1] != '\n'))
409 from
= hdr_val(from
);
410 sbuf_mem(sb
, from
, hdr_len(from
));
411 sbuf_str(sb
, " wrote:\n");
414 char *r
= memchr(s
, '\n', e
- s
);
418 sbuf_mem(sb
, s
, r
- s
+ 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:");
437 sbuf_printf(sb
, "From: \n");
438 put_reply(sb
, from_hdr
, to_hdr
, cc_hdr
, rply_hdr
);
440 put_subjreply(sb
, subj_hdr
);
443 put_replyto(sb
, id_hdr
, ref_hdr
);
445 body
= quote_body(msg
, msglen
);
448 *modlen
= sbuf_len(sb
);
449 *mod
= sbuf_done(sb
);
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:");
460 sbuf_printf(sb
, "From: \n");
461 sbuf_printf(sb
, "To: \n");
462 put_subjfwd(sb
, subj_hdr
);
465 sbuf_str(sb_body
, "\n-------- Original Message --------\n");
466 sbuf_mem(sb_body
, msg
, msglen
);
467 put_body(sb
, sbuf_buf(sb_body
));
469 *modlen
= sbuf_len(sb
);
470 *mod
= sbuf_done(sb
);