11 #define USERAGENT "neatmail (git://repo.or.cz/neatmail.git)"
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] [msg@path]\n\n"
77 " -b path \tmbox path\n"
78 " -i msg \tmsg number or message id (=msg_id)\n"
79 " -h hdr \tspecify headers to include\n"
80 " -m \tdecode mime message\n"
81 " -r \tgenerate a reply\n"
82 " -f \tgenerate a forward\n"
83 " -n \tgenerate a new message\n"
84 " -a file \tadd an attachment\n"
85 " -s \tfollow neatmail-source: header\n";
90 char *hdrs
[HDRS_N
] = {NULL
};
91 char *path
[16] = {NULL
};
103 for (i
= 0; argv
[i
] && argv
[i
][0] == '-'; i
++) {
104 if (argv
[i
][1] == 'm')
106 if (argv
[i
][1] == 'r')
108 if (argv
[i
][1] == 'n')
110 if (argv
[i
][1] == 'f')
112 if (argv
[i
][1] == 's')
114 if (argv
[i
][1] == 'b') {
115 path
[path_n
++] = argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
118 if (argv
[i
][1] == 'i') {
119 msgnum
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
122 if (argv
[i
][1] == 'h') {
123 char *hdr
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
124 if (hdrs_n
+ 1 < HDRS_N
)
125 hdrs
[hdrs_n
++] = hdr
;
128 if (argv
[i
][1] == 'a') {
129 if (parts_n
< PARTS_N
)
130 parts
[parts_n
++] = argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
135 msg_new(&msg
, &msglen
);
136 xwrite(1, msg
, msglen
);
140 if (!path
[0] && argv
[i
])
142 if (!msgnum
&& argv
[i
])
144 if (msgnum
&& msgnum
[0] != '=' && strchr(msgnum
, '@') != NULL
) {
145 path
[0] = strchr(msgnum
, '@') + 1;
148 if (!path
[0] || !msgnum
) {
152 if (msgnum
[0] == '=')
153 addr
= mbox_mid(path
, msgnum
+ 1);
156 if (addr
>= 0 && !mbox_ith(path
[0], addr
, &msg
, &msglen
)) {
161 if (source
&& (hdr
= msg_get(msg
, msglen
, "Neatmail-Source:")) != NULL
) {
162 while (*hdr
&& *hdr
!= ' ')
165 if (sscanf(hdr
, "%d@%s %ld %ld", &num
, box
, &beg
, &end
) != 4)
167 if (mbox_off(box
, beg
, end
, &msg
, &msglen
) != 0)
170 if (demime
&& !msg_demime(msg
, msglen
, &mod
, &modlen
)) {
175 if (reply
&& !msg_reply(msg
, msglen
, &mod
, &modlen
)) {
180 if (hdrs_n
&& !msg_filter(msg
, msglen
, &mod
, &modlen
, hdrs
)) {
185 if (forward
&& !msg_forward(msg
, msglen
, &mod
, &modlen
)) {
190 xwrite(1, msg
, msglen
);
196 static void put_from_(struct sbuf
*sb
)
201 strftime(buf
, sizeof(buf
), "%a %b %d %H:%M:%S %Y", localtime(&t
));
202 sbuf_printf(sb
, "From %s %s\n",
203 getenv("LOGNAME") ? getenv("LOGNAME") : "me", buf
);
206 static void put_date(struct sbuf
*sb
)
211 strftime(buf
, sizeof(buf
), "%a, %d %b %Y %H:%M:%S %z", localtime(&t
));
212 sbuf_printf(sb
, "Date: %s\n", buf
);
215 static void put_id(struct sbuf
*sb
)
218 char host
[32] = "neatmail.host";
221 strftime(buf
, sizeof(buf
), "%Y%d%m%H%M%S", localtime(&t
));
222 sbuf_printf(sb
, "Message-ID: <%s@%s>\n", buf
, host
);
225 static void put_agent(struct sbuf
*sb
)
227 sbuf_printf(sb
, "User-Agent: " USERAGENT
"\n");
230 static char *fileread(char *path
, int *len
)
232 int fd
= open(path
, O_RDONLY
);
239 int ret
= read(fd
, buf
, sizeof(buf
));
240 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
244 sbuf_mem(sb
, buf
, ret
);
248 return sbuf_done(sb
);
251 static char *filename(char *path
)
253 char *sl
= strrchr(path
, '/');
254 return sl
? sl
+ 1 : path
;
257 static void put_body(struct sbuf
*sb
, char *body
)
259 sbuf_printf(sb
, "MIME-Version: 1.0\n");
261 sbuf_printf(sb
, "Content-Type: text/plain; charset=utf-8\n");
262 sbuf_printf(sb
, "Content-Transfer-Encoding: 8bit\n");
263 sbuf_printf(sb
, "\n");
267 sbuf_printf(sb
, "Content-Type: multipart/mixed; boundary=%s\n", MBOUNDARY
);
268 sbuf_printf(sb
, "\nMulti-part MIME message.\n");
269 sbuf_printf(sb
, "--%s\n", MBOUNDARY
);
270 sbuf_printf(sb
, "Content-Type: text/plain; charset=utf-8\n");
271 sbuf_printf(sb
, "Content-Transfer-Encoding: 8bit\n");
272 sbuf_printf(sb
, "\n");
274 for (i
= 0; i
< parts_n
; i
++) {
277 sbuf_printf(sb
, "--%s\n", MBOUNDARY
);
278 sbuf_printf(sb
, "Content-Type: application/octet-stream\n");
279 sbuf_printf(sb
, "Content-Disposition: attachment; filename=\"%s\"\n",
281 sbuf_printf(sb
, "Content-Transfer-Encoding: base64\n");
282 sbuf_printf(sb
, "\n");
283 cont
= fileread(parts
[i
], &cont_len
);
285 char *b64
= base64(cont
, cont_len
);
286 sbuf_mem(sb
, b64
, strlen(b64
));
291 sbuf_printf(sb
, "--%s--\n", MBOUNDARY
);
295 static void msg_new(char **msg
, long *msglen
)
297 struct sbuf
*sb
= sbuf_make();
299 sbuf_printf(sb
, "From: \n");
300 sbuf_printf(sb
, "To: \n");
301 sbuf_printf(sb
, "Subject: \n");
305 put_body(sb
, "MAIL BODY...\n");
307 *msglen
= sbuf_len(sb
);
308 *msg
= sbuf_done(sb
);
311 static char *hdr_val(char *hdr
)
313 hdr
= strchr(hdr
, ':') + 1;
314 while (isspace(*hdr
))
319 static int hdr_len(char *hdr
)
321 int l
= hdrlen(hdr
, 1024);
322 while (l
> 0 && strchr(" \r\n", (unsigned char) hdr
[l
- 1]))
327 static void put_subjreply(struct sbuf
*sb
, char *subj
)
329 subj
= hdr_val(subj
);
330 sbuf_str(sb
, "Subject: ");
331 if (tolower(subj
[0]) != 'r' || tolower(subj
[1]) != 'e')
332 sbuf_str(sb
, "Re: ");
333 sbuf_mem(sb
, subj
, hdr_len(subj
));
337 static void put_subjfwd(struct sbuf
*sb
, char *subj
)
339 subj
= hdr_val(subj
);
340 sbuf_str(sb
, "Subject: ");
341 sbuf_str(sb
, "Fwd: ");
342 sbuf_mem(sb
, subj
, hdr_len(subj
));
346 static void put_replyto(struct sbuf
*sb
, char *id
, char *ref
)
349 sbuf_str(sb
, "In-Reply-To: ");
350 sbuf_mem(sb
, id
, hdr_len(id
));
352 sbuf_str(sb
, "References: ");
355 sbuf_mem(sb
, ref
, hdr_len(ref
));
356 sbuf_str(sb
, "\n\t");
358 sbuf_mem(sb
, id
, hdr_len(id
));
362 static void put_reply(struct sbuf
*sb
, char *from
, char *to
, char *cc
, char *rply
)
365 char *hdr
= rply
? rply
: from
;
367 sbuf_str(sb
, "To: ");
368 sbuf_mem(sb
, hdr
, hdr_len(hdr
));
371 if (to
|| cc
|| (rply
&& from
)) {
373 sbuf_str(sb
, "Cc: ");
377 sbuf_str(sb
, ",\n\t");
378 sbuf_mem(sb
, to
, hdr_len(to
));
381 from
= hdr_val(from
);
383 sbuf_str(sb
, ",\n\t");
384 sbuf_mem(sb
, from
, hdr_len(from
));
389 sbuf_str(sb
, ",\n\t");
390 sbuf_mem(sb
, cc
, hdr_len(cc
));
396 static char *quote_body(char *msg
, long msglen
)
398 struct sbuf
*sb
= sbuf_make();
399 char *from
= msg_get(msg
, msglen
, "From:");
401 char *e
= msg
+ msglen
;
402 while (s
+ 1 < e
&& (s
[0] != '\n' || s
[1] != '\n'))
407 from
= hdr_val(from
);
408 sbuf_mem(sb
, from
, hdr_len(from
));
409 sbuf_str(sb
, " wrote:\n");
412 char *r
= memchr(s
, '\n', e
- s
);
416 sbuf_mem(sb
, s
, r
- s
+ 1);
419 return sbuf_done(sb
);
422 static int msg_reply(char *msg
, long msglen
, char **mod
, long *modlen
)
424 struct sbuf
*sb
= sbuf_make();
425 char *id_hdr
= msg_get(msg
, msglen
, "Message-ID:");
426 char *ref_hdr
= msg_get(msg
, msglen
, "References:");
427 char *from_hdr
= msg_get(msg
, msglen
, "From:");
428 char *subj_hdr
= msg_get(msg
, msglen
, "Subject:");
429 char *to_hdr
= msg_get(msg
, msglen
, "To:");
430 char *cc_hdr
= msg_get(msg
, msglen
, "CC:");
431 char *rply_hdr
= msg_get(msg
, msglen
, "Reply-To:");
435 sbuf_printf(sb
, "From: \n");
436 put_reply(sb
, from_hdr
, to_hdr
, cc_hdr
, rply_hdr
);
438 put_subjreply(sb
, subj_hdr
);
441 put_replyto(sb
, id_hdr
, ref_hdr
);
443 body
= quote_body(msg
, msglen
);
446 *modlen
= sbuf_len(sb
);
447 *mod
= sbuf_done(sb
);
451 static int msg_forward(char *msg
, long msglen
, char **mod
, long *modlen
)
453 struct sbuf
*sb
= sbuf_make();
454 struct sbuf
*sb_body
= sbuf_make();
455 char *subj_hdr
= msg_get(msg
, msglen
, "Subject:");
458 sbuf_printf(sb
, "From: \n");
459 sbuf_printf(sb
, "To: \n");
460 put_subjfwd(sb
, subj_hdr
);
463 sbuf_str(sb_body
, "\n-------- Original Message --------\n");
464 sbuf_mem(sb_body
, msg
, msglen
);
465 put_body(sb
, sbuf_buf(sb_body
));
467 *modlen
= sbuf_len(sb
);
468 *mod
= sbuf_done(sb
);