11 #define USERAGENT "neatmail (git://repo.or.cz/neatmail.git)"
12 #define MBOUNDARY "neatmail-boundary"
15 static char *parts
[NPARTS
];
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');
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);
36 char *e
= msg
+ msglen
;
37 while ((hdrs
= segment(hdr
, hdrs
, ':')) && hdr
[0]) {
38 char *val
= msg_get(msg
, msglen
, hdr
);
40 sbuf_mem(sb
, val
, hdrlen(val
, msg
+ msglen
- val
));
43 while (s
+ 1 < e
&& (s
[0] != '\n' || s
[1] != '\n'))
46 sbuf_mem(sb
, s
, e
- s
);
47 *modlen
= sbuf_len(sb
);
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
);
60 for (i
= 0; i
< mbox_len(mbox
); i
++) {
64 if (mbox_get(mbox
, i
, &msg
, &msglen
))
66 id_hdr
= msg_get(msg
, msglen
, "Message-ID:");
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
)
74 if (strlen(mid
) == end
- beg
&&
75 !memcmp(mid
, beg
, end
- beg
))
84 "usage: neatmail pg [options]\n\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";
108 for (i
= 0; argv
[i
] && argv
[i
][0] == '-'; i
++) {
109 if (argv
[i
][1] == 'm')
111 if (argv
[i
][1] == 'r')
113 if (argv
[i
][1] == 'n')
115 if (argv
[i
][1] == 'f')
117 if (argv
[i
][1] == 'b') {
118 path
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
121 if (argv
[i
][1] == 'i') {
122 msgnum
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
125 if (argv
[i
][1] == 'h') {
126 hdrs
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
129 if (argv
[i
][1] == 'a') {
130 if (parts_n
< NPARTS
)
131 parts
[parts_n
++] = argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
136 msg_new(&msg
, &msglen
);
137 xwrite(1, msg
, msglen
);
141 if (!path
&& argv
[i
])
143 if (!msgnum
&& argv
[i
])
145 if (!path
|| !msgnum
) {
149 if (msgnum
[0] == '=')
150 addr
= mbox_mid(path
, msgnum
+ 1);
153 if (addr
>= 0 && !mbox_ith(path
, addr
, &msg
, &msglen
)) {
154 if (demime
&& !msg_demime(msg
, msglen
, &mod
, &modlen
)) {
159 if (reply
&& !msg_reply(msg
, msglen
, &mod
, &modlen
)) {
164 if (hdrs
&& !msg_filter(msg
, msglen
, &mod
, &modlen
, hdrs
)) {
169 if (forward
&& !msg_forward(msg
, msglen
, &mod
, &modlen
)) {
174 xwrite(1, msg
, msglen
);
180 static void put_from_(struct sbuf
*sb
)
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
)
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
)
202 char host
[32] = "neatmail.host";
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
);
223 int ret
= read(fd
, buf
, sizeof(buf
));
224 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
228 sbuf_mem(sb
, buf
, ret
);
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");
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");
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");
259 for (i
= 0; i
< parts_n
; i
++) {
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",
266 sbuf_printf(sb
, "Content-Transfer-Encoding: base64\n");
267 sbuf_printf(sb
, "\n");
268 cont
= fileread(parts
[i
], &cont_len
);
270 char *b64
= base64(cont
, cont_len
);
271 sbuf_mem(sb
, b64
, strlen(b64
));
276 sbuf_printf(sb
, "--%s--\n", MBOUNDARY
);
280 static void msg_new(char **msg
, long *msglen
)
282 struct sbuf
*sb
= sbuf_make();
284 sbuf_printf(sb
, "From: \n");
285 sbuf_printf(sb
, "To: \n");
286 sbuf_printf(sb
, "Subject: \n");
290 put_body(sb
, "MAIL BODY...\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
))
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]))
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
));
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
));
331 static void put_replyto(struct sbuf
*sb
, char *id
, char *ref
)
334 sbuf_str(sb
, "In-Reply-To: ");
335 sbuf_mem(sb
, id
, hdr_len(id
));
337 sbuf_str(sb
, "References: ");
340 sbuf_mem(sb
, ref
, hdr_len(ref
));
341 sbuf_str(sb
, "\n\t");
343 sbuf_mem(sb
, id
, hdr_len(id
));
347 static void put_reply(struct sbuf
*sb
, char *from
, char *to
, char *cc
, char *rply
)
350 char *hdr
= rply
? rply
: from
;
352 sbuf_str(sb
, "To: ");
353 sbuf_mem(sb
, hdr
, hdr_len(hdr
));
356 if (to
|| cc
|| (rply
&& from
)) {
358 sbuf_str(sb
, "Cc: ");
362 sbuf_str(sb
, ",\n\t");
363 sbuf_mem(sb
, to
, hdr_len(to
));
366 from
= hdr_val(from
);
368 sbuf_str(sb
, ",\n\t");
369 sbuf_mem(sb
, from
, hdr_len(from
));
374 sbuf_str(sb
, ",\n\t");
375 sbuf_mem(sb
, cc
, hdr_len(cc
));
381 static char *quote_body(char *msg
, long msglen
)
383 struct sbuf
*sb
= sbuf_make();
384 char *from
= msg_get(msg
, msglen
, "From:");
386 char *e
= msg
+ msglen
;
387 while (s
+ 1 < e
&& (s
[0] != '\n' || s
[1] != '\n'))
392 from
= hdr_val(from
);
393 sbuf_mem(sb
, from
, hdr_len(from
));
394 sbuf_str(sb
, " wrote:\n");
397 char *r
= memchr(s
, '\n', e
- s
);
401 sbuf_mem(sb
, s
, r
- s
+ 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:");
420 sbuf_printf(sb
, "From: \n");
421 put_reply(sb
, from_hdr
, to_hdr
, cc_hdr
, rply_hdr
);
423 put_subjreply(sb
, subj_hdr
);
426 put_replyto(sb
, id_hdr
, ref_hdr
);
428 body
= quote_body(msg
, msglen
);
431 *modlen
= sbuf_len(sb
);
432 *mod
= sbuf_done(sb
);
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:");
443 sbuf_printf(sb
, "From: \n");
444 sbuf_printf(sb
, "To: \n");
445 put_subjfwd(sb
, subj_hdr
);
448 sbuf_str(sb_body
, "\n-------- Original Message --------\n");
449 sbuf_mem(sb_body
, msg
, msglen
);
450 put_body(sb
, sbuf_buf(sb_body
));
452 *modlen
= sbuf_len(sb
);
453 *mod
= sbuf_done(sb
);