1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Dig message objects. TODO Very very restricted (especially non-compose)
3 *@ Protocol change: adjust mx-config.h:mx_DIG_MSG_PLUMBING_VERSION + `~^' man.
4 *@ TODO - a_dmsg_cmd() should generate string lists, not perform real I/O.
5 *@ TODO I.e., drop FILE* arg, generate stringlist; up to callers...
6 *@ TODO - With our own I/O there should then be a StringListDevice as the
7 *@ TODO owner and I/O overlay provider: NO temporary file (sic)!
8 *@ XXX - Multiple objects per message could be possible (a_dmsg_find()),
9 *@ XXX except in compose mode
10 *
11 * Copyright (c) 2016 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
12 * SPDX-License-Identifier: ISC
13 *
14 * Permission to use, copy, modify, and/or distribute this software for any
15 * purpose with or without fee is hereby granted, provided that the above
16 * copyright notice and this permission notice appear in all copies.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 */
26 #undef su_FILE
27 #define su_FILE dig_msg
28 #define mx_SOURCE
29 #define mx_SOURCE_DIG_MSG
30
31 #ifndef mx_HAVE_AMALGAMATION
32 # include "mx/nail.h"
33 #endif
34
35 #include <su/cs.h>
36 #include <su/icodec.h>
37 #include <su/mem.h>
38
39 #include "mx/attachments.h"
40 #include "mx/cmd.h"
41 #include "mx/file-streams.h"
42 #include "mx/mime-type.h"
43 #include "mx/names.h"
44
45 #include "mx/dig-msg.h"
46 #include "su/code-in.h"
47
48 #define a_DMSG_QUOTE(S) n_shexp_quote_cp(S, FAL0)
49
50 struct mx_dig_msg_ctx *mx_dig_msg_read_overlay; /* XXX HACK */
51 struct mx_dig_msg_ctx *mx_dig_msg_compose_ctx; /* Or NIL XXX HACK*/
52
53 /* Try to convert cp into an unsigned number that corresponds to an existing
54 * message number (or ERR_INVAL), search for an existing object (ERR_EXIST if
55 * oexcl and exists; ERR_NOENT if not oexcl and does not exist).
56 * On oexcl success *dmcp will be n_alloc()ated with .dmc_msgno and .dmc_mp
57 * etc. set; but not linked into mb.mb_digmsg and .dmc_fp not created etc. */
58 static s32 a_dmsg_find(char const *cp, struct mx_dig_msg_ctx **dmcpp,
59 boole oexcl);
60
61 /* Subcommand drivers */
62 static boole a_dmsg_cmd(FILE *fp, struct mx_dig_msg_ctx *dmcp,
63 struct mx_cmd_arg *cmd, struct mx_cmd_arg *args);
64
65 static boole a_dmsg__header(FILE *fp, struct mx_dig_msg_ctx *dmcp,
66 struct mx_cmd_arg *args);
67 static boole a_dmsg__attach(FILE *fp, struct mx_dig_msg_ctx *dmcp,
68 struct mx_cmd_arg *args);
69
70 static s32
a_dmsg_find(char const * cp,struct mx_dig_msg_ctx ** dmcpp,boole oexcl)71 a_dmsg_find(char const *cp, struct mx_dig_msg_ctx **dmcpp, boole oexcl){
72 struct mx_dig_msg_ctx *dmcp;
73 s32 rv;
74 u32 msgno;
75 NYD2_IN;
76
77 if(cp[0] == '-' && cp[1] == '\0'){
78 if((dmcp = mx_dig_msg_compose_ctx) != NIL){
79 *dmcpp = dmcp;
80 if(dmcp->dmc_flags & mx_DIG_MSG_COMPOSE_DIGGED)
81 rv = oexcl ? su_ERR_EXIST : su_ERR_NONE;
82 else
83 rv = oexcl ? su_ERR_NONE : su_ERR_NOENT;
84 }else
85 rv = su_ERR_INVAL;
86 goto jleave;
87 }
88
89 if((su_idec_u32_cp(&msgno, cp, 0, NIL
90 ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
91 ) != su_IDEC_STATE_CONSUMED ||
92 msgno == 0 || UCMP(z, msgno, >, msgCount)){
93 rv = su_ERR_INVAL;
94 goto jleave;
95 }
96
97 for(dmcp = mb.mb_digmsg; dmcp != NIL; dmcp = dmcp->dmc_next)
98 if(dmcp->dmc_msgno == msgno){
99 *dmcpp = dmcp;
100 rv = oexcl ? su_ERR_EXIST : su_ERR_NONE;
101 goto jleave;
102 }
103 if(!oexcl){
104 rv = su_ERR_NOENT;
105 goto jleave;
106 }
107
108 *dmcpp = dmcp = n_calloc(1, Z_ALIGN(sizeof *dmcp) + sizeof(struct header));
109 dmcp->dmc_mp = &message[msgno - 1];
110 dmcp->dmc_flags = mx_DIG_MSG_OWN_MEMBAG |
111 ((TRU1/*TODO*/ || !(mb.mb_perm & MB_DELE))
112 ? mx_DIG_MSG_RDONLY : mx_DIG_MSG_NONE);
113 dmcp->dmc_msgno = msgno;
114 dmcp->dmc_hp = R(struct header*,P2UZ(&dmcp[1]));
115 dmcp->dmc_membag = su_mem_bag_create(&dmcp->dmc__membag_buf[0], 0);
116 /* Rest done by caller */
117 rv = su_ERR_NONE;
118 jleave:
119 NYD2_OU;
120 return rv;
121 }
122
123 static boole
a_dmsg_cmd(FILE * fp,struct mx_dig_msg_ctx * dmcp,struct mx_cmd_arg * cmd,struct mx_cmd_arg * args)124 a_dmsg_cmd(FILE *fp, struct mx_dig_msg_ctx *dmcp, struct mx_cmd_arg *cmd,
125 struct mx_cmd_arg *args){
126 union {struct mx_cmd_arg *ca; char *c; struct str const *s; boole rv;} p;
127 NYD2_IN;
128
129 if(cmd == NIL)
130 goto jecmd;
131
132 p.s = &cmd->ca_arg.ca_str;
133 if(su_cs_starts_with_case_n("header", p.s->s, p.s->l))
134 p.rv = a_dmsg__header(fp, dmcp, args);
135 else if(su_cs_starts_with_case_n("attachment", p.s->s, p.s->l)){
136 if(!(dmcp->dmc_flags & mx_DIG_MSG_COMPOSE)) /* TODO attachment support */
137 p.rv = (fprintf(fp,
138 "505 `digmsg attachment' only in compose mode (yet)\n") > 0);
139 else
140 p.rv = a_dmsg__attach(fp, dmcp, args);
141 }else if(su_cs_starts_with_case_n("version", p.s->s, p.s->l)){
142 if(args != NIL)
143 goto jecmd;
144 p.rv = (fputs("210 " mx_DIG_MSG_PLUMBING_VERSION "\n", fp) != EOF);
145 }else if((p.s->l == 1 && p.s->s[0] == '?') ||
146 su_cs_starts_with_case_n("help", p.s->s, p.s->l)){
147 if(args != NIL)
148 goto jecmd;
149 p.rv = (fputs(_("211 (Arguments undergo shell-style evaluation)\n"),
150 fp) != EOF &&
151 #ifdef mx_HAVE_UISTRINGS
152 fputs(_(
153 "attachment:\n"
154 " attribute name (212; 501)\n"
155 " attribute-at position\n"
156 " attribute-set name key value (210; 505/501)\n"
157 " attribute-set-at position key value\n"
158 " insert file[=input-charset[#output-charset]] "
159 "(210; 501/505/506)\n"
160 " insert #message-number\n"
161 " list (212; 501)\n"
162 " remove name (210; 501/506)\n"
163 " remove-at position (210; 501/505)\n"), fp) != EOF &&
164 fputs(_(
165 "header\n"
166 " insert field content (210; 501/505/506)\n"
167 " list [field] (210; [501]);\n"
168 " remove field (210; 501/505)\n"
169 " remove-at field position (210; 501/505)\n"
170 " show field (211/212; 501)\n"
171 "help (211)\n"
172 "version (210)\n"), fp) != EOF &&
173 #endif
174 putc('\n', fp) != EOF);
175 }else{
176 jecmd:
177 fputs("500\n", fp);
178 p.rv = FAL0;
179 }
180 fflush(fp);
181
182 NYD2_OU;
183 return p.rv;
184 }
185
186 static boole
a_dmsg__header(FILE * fp,struct mx_dig_msg_ctx * dmcp,struct mx_cmd_arg * args)187 a_dmsg__header(FILE *fp, struct mx_dig_msg_ctx *dmcp,
188 struct mx_cmd_arg *args){
189 struct n_header_field *hfp;
190 struct mx_name *np, **npp;
191 uz i;
192 struct mx_cmd_arg *a3p;
193 char const *cp;
194 struct header *hp;
195 NYD2_IN;
196
197 hp = dmcp->dmc_hp;
198 UNINIT(a3p, NIL);
199
200 if(args == NIL){
201 cp = su_empty; /* xxx not NIL anyway */
202 goto jdefault;
203 }
204
205 cp = args->ca_arg.ca_str.s;
206 args = args->ca_next;
207
208 /* Strip the optional colon from header names */
209 if((a3p = args) != NIL){
210 char *xp;
211
212 a3p = a3p->ca_next;
213
214 for(xp = args->ca_arg.ca_str.s;; ++xp)
215 if(*xp == '\0')
216 break;
217 else if(*xp == ':'){
218 *xp = '\0';
219 break;
220 }
221 }
222
223 /* TODO ERR_2BIG should happen on the cmd_arg parser side */
224 if(a3p != NIL && a3p->ca_next != NIL)
225 goto jecmd;
226
227 if(su_cs_starts_with_case("insert", cp)){ /* TODO LOGIC BELONGS head.c
228 * TODO That is: Header::factory(string) -> object (blahblah).
229 * TODO I.e., as long as we don't have regular RFC compliant parsers
230 * TODO which differentiate in between structured and unstructured
231 * TODO header fields etc., a little workaround */
232 struct mx_name *xnp;
233 s8 aerr;
234 char const *mod_suff;
235 enum expand_addr_check_mode eacm;
236 enum gfield ntype;
237 boole mult_ok;
238
239 if(args == NIL || a3p == NIL)
240 goto jecmd;
241 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
242 goto j505r;
243
244 /* Strip [\r\n] which would render a body invalid XXX all controls? */
245 /* C99 */{
246 char c;
247
248 for(cp = a3p->ca_arg.ca_str.s; (c = *cp) != '\0'; ++cp)
249 if(c == '\n' || c == '\r')
250 *UNCONST(char*,cp) = ' ';
251 }
252
253 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "Subject")){
254 if(a3p->ca_arg.ca_str.l == 0)
255 goto j501cp;
256
257 if(hp->h_subject != NIL)
258 hp->h_subject = savecatsep(hp->h_subject, ' ',
259 a3p->ca_arg.ca_str.s);
260 else
261 hp->h_subject = a3p->ca_arg.ca_str.s;
262 if(fprintf(fp, "210 %s 1\n", cp) < 0)
263 cp = NIL;
264 goto jleave;
265 }
266
267 mult_ok = TRU1;
268 ntype = GEXTRA | GFULL | GFULLEXTRA;
269 eacm = EACM_STRICT;
270 mod_suff = NIL;
271
272 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "From")){
273 npp = &hp->h_from;
274 jins:
275 aerr = 0;
276 /* todo As said above, this should be table driven etc., but.. */
277 if(ntype & GBCC_IS_FCC){
278 np = nalloc_fcc(a3p->ca_arg.ca_str.s);
279 if(is_addr_invalid(np, eacm))
280 goto jins_505;
281 }else{
282 if((np = (mult_ok > FAL0 ? lextract : n_extract_single
283 )(a3p->ca_arg.ca_str.s, ntype | GNULL_OK)) == NIL)
284 goto j501cp;
285
286 if((np = checkaddrs(np, eacm, &aerr), aerr != 0)){
287 jins_505:
288 if(fprintf(fp, "505 %s\n", cp) < 0)
289 cp = NIL;
290 goto jleave;
291 }
292 }
293
294 /* Go to the end of the list, track whether it contains any
295 * non-deleted entries */
296 i = 0;
297 if((xnp = *npp) != NIL)
298 for(;; xnp = xnp->n_flink){
299 if(!(xnp->n_type & GDEL))
300 ++i;
301 if(xnp->n_flink == NIL)
302 break;
303 }
304
305 if(!mult_ok && (i != 0 || np->n_flink != NIL)){
306 if(fprintf(fp, "506 %s\n", cp) < 0)
307 cp = NIL;
308 }else{
309 if(xnp == NIL)
310 *npp = np;
311 else
312 xnp->n_flink = np;
313 np->n_blink = xnp;
314 if(fprintf(fp, "210 %s %" PRIuZ "\n", cp, ++i) < 0)
315 cp = NIL;
316 }
317 goto jleave;
318 }
319
320 #undef a_X
321 #define a_X(F,H,INS) \
322 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = F)) \
323 {npp = &hp->H; INS; goto jins;}
324
325 if((cp = su_cs_find_c(args->ca_arg.ca_str.s, '?')) != NIL){
326 mod_suff = cp;
327 args->ca_arg.ca_str.s[P2UZ(cp - args->ca_arg.ca_str.s)] = '\0';
328 if(*++cp != '\0' && !su_cs_starts_with_case("single", cp)){
329 cp = mod_suff;
330 goto j501cp;
331 }
332 mult_ok = TRUM1;
333 }
334
335 /* Just like with ~t,~c,~b, immediately test *expandaddr* compliance */
336 a_X("To", h_to, ntype = GTO|GFULL su_COMMA eacm = EACM_NORMAL);
337 a_X("Cc", h_cc, ntype = GCC|GFULL su_COMMA eacm = EACM_NORMAL);
338 a_X("Bcc", h_bcc, ntype = GBCC|GFULL su_COMMA eacm = EACM_NORMAL);
339
340 if((cp = mod_suff) != NIL)
341 goto j501cp;
342
343 /* Not | EAF_FILE, depend on *expandaddr*! */
344 a_X("Fcc", h_fcc, ntype = GBCC|GBCC_IS_FCC su_COMMA eacm = EACM_NORMAL);
345 a_X("Sender", h_sender, mult_ok = FAL0);
346 a_X("Reply-To", h_reply_to, eacm = EACM_NONAME);
347 a_X("Mail-Followup-To", h_mft, eacm = EACM_NONAME);
348 a_X("Message-ID", h_message_id,
349 mult_ok = FAL0 su_COMMA ntype = GREF su_COMMA eacm = EACM_NONAME);
350 a_X("References", h_ref, ntype = GREF su_COMMA eacm = EACM_NONAME);
351 a_X("In-Reply-To", h_in_reply_to, ntype = GREF su_COMMA
352 eacm = EACM_NONAME);
353
354 #undef a_X
355
356 if((cp = n_header_is_known(args->ca_arg.ca_str.s, UZ_MAX)) != NIL)
357 goto j505r;
358
359 /* Free-form header fields */
360 /* C99 */{
361 uz nl, bl;
362 struct n_header_field **hfpp;
363
364 for(cp = args->ca_arg.ca_str.s; *cp != '\0'; ++cp)
365 if(!fieldnamechar(*cp)){
366 cp = args->ca_arg.ca_str.s;
367 goto j501cp;
368 }
369
370 for(i = 0, hfpp = &hp->h_user_headers; *hfpp != NIL; ++i)
371 hfpp = &(*hfpp)->hf_next;
372
373 nl = su_cs_len(cp = args->ca_arg.ca_str.s) +1;
374 bl = su_cs_len(a3p->ca_arg.ca_str.s) +1;
375 *hfpp = hfp = n_autorec_alloc(VSTRUCT_SIZEOF(struct n_header_field,
376 hf_dat) + nl + bl);
377 hfp->hf_next = NIL;
378 hfp->hf_nl = nl - 1;
379 hfp->hf_bl = bl - 1;
380 su_mem_copy(&hfp->hf_dat[0], cp, nl);
381 su_mem_copy(&hfp->hf_dat[nl], a3p->ca_arg.ca_str.s, bl);
382 if(fprintf(fp, "210 %s %" PRIuZ "\n", &hfp->hf_dat[0], ++i) < 0)
383 cp = NIL;
384 }
385 }else if(su_cs_starts_with_case("list", cp)){
386 jdefault:
387 if(args == NIL){
388 if(fputs("210", fp) == EOF){
389 cp = NIL;
390 goto jleave;
391 }
392
393 #undef a_X
394 #define a_X(F,S) \
395 if(su_CONCAT(hp->h_, F) != NIL && fputs(" " su_STRING(S), fp) == EOF){\
396 cp = NIL;\
397 goto jleave;\
398 }
399
400 a_X(subject, Subject);
401 a_X(from, From);
402 a_X(sender, Sender);
403 a_X(to, To);
404 a_X(cc, Cc);
405 a_X(bcc, Bcc);
406 a_X(fcc, Fcc);
407 a_X(reply_to, Reply-To);
408 a_X(mft, Mail-Followup-To);
409 a_X(message_id, Message-ID);
410 a_X(ref, References);
411 a_X(in_reply_to, In-Reply-To);
412
413 a_X(mailx_command, Mailx-Command);
414 a_X(mailx_raw_to, Mailx-Raw-To);
415 a_X(mailx_raw_cc, Mailx-Raw-Cc);
416 a_X(mailx_raw_bcc, Mailx-Raw-Bcc);
417 a_X(mailx_orig_sender, Mailx-Orig-Sender);
418 a_X(mailx_orig_from, Mailx-Orig-From);
419 a_X(mailx_orig_to, Mailx-Orig-To);
420 a_X(mailx_orig_cc, Mailx-Orig-Cc);
421 a_X(mailx_orig_bcc, Mailx-Orig-Bcc);
422
423 #undef a_X
424
425 /* Print only one instance of each free-form header */
426 for(hfp = hp->h_user_headers; hfp != NIL; hfp = hfp->hf_next){
427 struct n_header_field *hfpx;
428
429 for(hfpx = hp->h_user_headers;; hfpx = hfpx->hf_next)
430 if(hfpx == hfp){
431 putc(' ', fp);
432 fputs(&hfp->hf_dat[0], fp);
433 break;
434 }else if(!su_cs_cmp_case(&hfpx->hf_dat[0], &hfp->hf_dat[0]))
435 break;
436 }
437 if(putc('\n', fp) == EOF)
438 cp = NIL;
439 goto jleave;
440 }
441
442 if(a3p != NIL)
443 goto jecmd;
444
445 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "Subject")){
446 np = (hp->h_subject != NIL) ? R(struct mx_name*,-1) : NIL;
447 goto jlist;
448 }
449 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "From")){
450 np = hp->h_from;
451 jlist:
452 fprintf(fp, "%s %s\n", (np == NIL ? "501" : "210"), cp);
453 goto jleave;
454 }
455
456 #undef a_X
457 #define a_X(F,H) \
458 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = su_STRING(F))){\
459 np = hp->su_CONCAT(h_,H);\
460 goto jlist;\
461 }
462
463 a_X(Sender, sender);
464 a_X(To, to);
465 a_X(Cc, cc);
466 a_X(Bcc, bcc);
467 a_X(Fcc, fcc);
468 a_X(Reply-To, reply_to);
469 a_X(Mail-Followup-To, mft);
470 a_X(Message-ID, message_id);
471 a_X(References, ref);
472 a_X(In-Reply-To, in_reply_to);
473
474 a_X(Mailx-Raw-To, mailx_raw_to);
475 a_X(Mailx-Raw-Cc, mailx_raw_cc);
476 a_X(Mailx-Raw-Bcc, mailx_raw_bcc);
477 a_X(Mailx-Orig-Sender, mailx_orig_sender);
478 a_X(Mailx-Orig-From, mailx_orig_from);
479 a_X(Mailx-Orig-To, mailx_orig_to);
480 a_X(Mailx-Orig-Cc, mailx_orig_cc);
481 a_X(Mailx-Orig-Bcc, mailx_orig_bcc);
482
483 #undef a_X
484
485 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "Mailx-Command")){
486 np = (hp->h_mailx_command != NIL) ? R(struct mx_name*,-1) : NIL;
487 goto jlist;
488 }
489
490 /* Free-form header fields */
491 for(cp = args->ca_arg.ca_str.s; *cp != '\0'; ++cp)
492 if(!fieldnamechar(*cp)){
493 cp = args->ca_arg.ca_str.s;
494 goto j501cp;
495 }
496
497 cp = args->ca_arg.ca_str.s;
498 for(hfp = hp->h_user_headers;; hfp = hfp->hf_next){
499 if(hfp == NIL)
500 goto j501cp;
501 else if(!su_cs_cmp_case(cp, &hfp->hf_dat[0])){
502 if(fprintf(fp, "210 %s\n", &hfp->hf_dat[0]) < 0)
503 cp = NIL;
504 break;
505 }
506 }
507 }else if(su_cs_starts_with_case("remove", cp)){
508 if(args == NIL || a3p != NIL)
509 goto jecmd;
510 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
511 goto j505r;
512
513 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "Subject")){
514 if(hp->h_subject == NIL)
515 goto j501cp;
516
517 hp->h_subject = NIL;
518 if(fprintf(fp, "210 %s\n", cp) < 0)
519 cp = NIL;
520 goto jleave;
521 }
522
523 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "From")){
524 npp = &hp->h_from;
525 jrem:
526 if(*npp != NIL){
527 *npp = NIL;
528 if(fprintf(fp, "210 %s\n", cp) < 0)
529 cp = NIL;
530 goto jleave;
531 }else
532 goto j501cp;
533 }
534
535 #undef a_X
536 #define a_X(F,H) \
537 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = su_STRING(F))){\
538 npp = &hp->su_CONCAT(h_,H);\
539 goto jrem;\
540 }
541
542 a_X(Sender, sender);
543 a_X(To, to);
544 a_X(Cc, cc);
545 a_X(Bcc, bcc);
546 a_X(Fcc, fcc);
547 a_X(Reply-To, reply_to);
548 a_X(Mail-Followup-To, mft);
549 a_X(Message-ID, message_id);
550 a_X(References, ref);
551 a_X(In-Reply-To, in_reply_to);
552
553 #undef a_X
554
555 if((cp = n_header_is_known(args->ca_arg.ca_str.s, UZ_MAX)) != NIL)
556 goto j505r;
557
558 /* Free-form header fields (note j501cp may print non-normalized name) */
559 /* C99 */{
560 struct n_header_field **hfpp;
561 boole any;
562
563 for(cp = args->ca_arg.ca_str.s; *cp != '\0'; ++cp)
564 if(!fieldnamechar(*cp)){
565 cp = args->ca_arg.ca_str.s;
566 goto j501cp;
567 }
568 cp = args->ca_arg.ca_str.s;
569
570 for(any = FAL0, hfpp = &hp->h_user_headers; (hfp = *hfpp) != NIL;){
571 if(!su_cs_cmp_case(cp, &hfp->hf_dat[0])){
572 *hfpp = hfp->hf_next;
573 if(!any){
574 if(fprintf(fp, "210 %s\n", &hfp->hf_dat[0]) < 0){
575 cp = NIL;
576 goto jleave;
577 }
578 }
579 any = TRU1;
580 }else
581 hfpp = &hfp->hf_next;
582 }
583 if(!any)
584 goto j501cp;
585 }
586 }else if(su_cs_starts_with_case("remove-at", cp)){
587 if(args == NIL || a3p == NIL)
588 goto jecmd;
589 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
590 goto j505r;
591
592 if((su_idec_uz_cp(&i, a3p->ca_arg.ca_str.s, 0, NIL
593 ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
594 ) != su_IDEC_STATE_CONSUMED || i == 0){
595 if(fprintf(fp, "505 invalid position: %s\n",
596 a3p->ca_arg.ca_str.s) < 0)
597 cp = NIL;
598 goto jleave;
599 }
600
601 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "Subject")){
602 if(hp->h_subject != NIL && i == 1){
603 hp->h_subject = NIL;
604 if(fprintf(fp, "210 %s 1\n", cp) < 0)
605 cp = NIL;
606 goto jleave;
607 }else
608 goto j501cp;
609 }
610
611 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "From")){
612 npp = &hp->h_from;
613 jremat:
614 if((np = *npp) == NIL)
615 goto j501cp;
616 while(--i != 0 && np != NIL)
617 np = np->n_flink;
618 if(np == NIL)
619 goto j501cp;
620
621 if(np->n_blink != NIL)
622 np->n_blink->n_flink = np->n_flink;
623 else
624 *npp = np->n_flink;
625 if(np->n_flink != NIL)
626 np->n_flink->n_blink = np->n_blink;
627
628 if(fprintf(fp, "210 %s\n", cp) < 0)
629 cp = NIL;
630 goto jleave;
631 }
632
633 #undef a_X
634 #define a_X(F,H) \
635 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = su_STRING(F))){\
636 npp = &hp->su_CONCAT(h_,H);\
637 goto jremat;\
638 }
639
640 a_X(Sender, sender);
641 a_X(To, to);
642 a_X(Cc, cc);
643 a_X(Bcc, bcc);
644 a_X(Fcc, fcc);
645 a_X(Reply-To, reply_to);
646 a_X(Mail-Followup-To, mft);
647 a_X(Message-ID, message_id);
648 a_X(References, ref);
649 a_X(In-Reply-To, in_reply_to);
650
651 #undef a_X
652
653 if((cp = n_header_is_known(args->ca_arg.ca_str.s, UZ_MAX)) != NIL)
654 goto j505r;
655
656 /* Free-form header fields */
657 /* C99 */{
658 struct n_header_field **hfpp;
659
660 for(cp = args->ca_arg.ca_str.s; *cp != '\0'; ++cp)
661 if(!fieldnamechar(*cp)){
662 cp = args->ca_arg.ca_str.s;
663 goto j501cp;
664 }
665 cp = args->ca_arg.ca_str.s;
666
667 for(hfpp = &hp->h_user_headers; (hfp = *hfpp) != NIL;){
668 if(--i == 0){
669 *hfpp = hfp->hf_next;
670 if(fprintf(fp, "210 %s %" PRIuZ "\n", &hfp->hf_dat[0], i) < 0){
671 cp = NIL;
672 goto jleave;
673 }
674 break;
675 }else
676 hfpp = &hfp->hf_next;
677 }
678 if(hfp == NIL)
679 goto j501cp;
680 }
681 }else if(su_cs_starts_with_case("show", cp)){
682 if(args == NIL || a3p != NIL)
683 goto jecmd;
684
685 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "Subject")){
686 if(hp->h_subject == NIL)
687 goto j501cp;
688 if(fprintf(fp, "212 %s\n%s\n\n", cp, a_DMSG_QUOTE(hp->h_subject)) < 0)
689 cp = NIL;
690 goto jleave;
691 }
692
693 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "From")){
694 np = hp->h_from;
695 jshow:
696 if(np == NIL)
697 goto j501cp;
698
699 fprintf(fp, "211 %s\n", cp);
700 do if(!(np->n_type & GDEL)){
701 switch(np->n_flags & mx_NAME_ADDRSPEC_ISMASK){
702 case mx_NAME_ADDRSPEC_ISFILE: cp = n_hy; break;
703 case mx_NAME_ADDRSPEC_ISPIPE: cp = "|"; break;
704 case mx_NAME_ADDRSPEC_ISNAME: cp = n_ns; break;
705 default: cp = np->n_name; break;
706 }
707 fprintf(fp, "%s %s\n", cp, a_DMSG_QUOTE(np->n_fullname));
708 }while((np = np->n_flink) != NIL);
709 if(putc('\n', fp) == EOF)
710 cp = NIL;
711 goto jleave;
712 }
713
714 #undef a_X
715 #define a_X(F,H) \
716 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = su_STRING(F))){\
717 np = hp->su_CONCAT(h_,H);\
718 goto jshow;\
719 }
720
721 a_X(Sender, sender);
722 a_X(To, to);
723 a_X(Cc, cc);
724 a_X(Bcc, bcc);
725 a_X(Fcc, fcc);
726 a_X(Reply-To, reply_to);
727 a_X(Mail-Followup-To, mft);
728 a_X(Message-ID, message_id);
729 a_X(References, ref);
730 a_X(In-Reply-To, in_reply_to);
731
732 a_X(Mailx-Raw-To, mailx_raw_to);
733 a_X(Mailx-Raw-Cc, mailx_raw_cc);
734 a_X(Mailx-Raw-Bcc, mailx_raw_bcc);
735 a_X(Mailx-Orig-Sender, mailx_orig_sender);
736 a_X(Mailx-Orig-From, mailx_orig_from);
737 a_X(Mailx-Orig-To, mailx_orig_to);
738 a_X(Mailx-Orig-Cc, mailx_orig_cc);
739 a_X(Mailx-Orig-Bcc, mailx_orig_bcc);
740
741 #undef a_X
742
743 if(!su_cs_cmp_case(args->ca_arg.ca_str.s, cp = "Mailx-Command")){
744 if(hp->h_mailx_command == NIL)
745 goto j501cp;
746 if(fprintf(fp, "212 %s\n%s\n\n", cp, hp->h_mailx_command) < 0)
747 cp = NIL;
748 goto jleave;
749 }
750
751 /* Free-form header fields */
752 /* C99 */{
753 boole any;
754
755 for(cp = args->ca_arg.ca_str.s; *cp != '\0'; ++cp)
756 if(!fieldnamechar(*cp)){
757 cp = args->ca_arg.ca_str.s;
758 goto j501cp;
759 }
760 cp = args->ca_arg.ca_str.s;
761
762 for(any = FAL0, hfp = hp->h_user_headers; hfp != NIL;
763 hfp = hfp->hf_next){
764 if(!su_cs_cmp_case(cp, &hfp->hf_dat[0])){
765 if(!any)
766 fprintf(fp, "212 %s\n", &hfp->hf_dat[0]);
767 any = TRU1;
768 fprintf(fp, "%s\n", a_DMSG_QUOTE(&hfp->hf_dat[hfp->hf_nl +1]));
769 }
770 }
771 if(!any)
772 goto j501cp;
773 if(putc('\n', fp) == EOF)
774 cp = NIL;
775 }
776 }else
777 goto jecmd;
778
779 jleave:
780 NYD2_OU;
781 return (cp != NIL);
782
783 jecmd:
784 if(fputs("500\n", fp) == EOF)
785 cp = NIL;
786 cp = NIL;
787 goto jleave;
788 j505r:
789 if(fprintf(fp, "505 read-only: %s\n", cp) < 0)
790 cp = NIL;
791 goto jleave;
792 j501cp:
793 if(fprintf(fp, "501 %s\n", cp) < 0)
794 cp = NIL;
795 goto jleave;
796 }
797
798 static boole
a_dmsg__attach(FILE * fp,struct mx_dig_msg_ctx * dmcp,struct mx_cmd_arg * args)799 a_dmsg__attach(FILE *fp, struct mx_dig_msg_ctx *dmcp,
800 struct mx_cmd_arg *args){
801 boole status;
802 struct mx_attachment *ap;
803 char const *cp;
804 struct header *hp;
805 NYD2_IN;
806
807 hp = dmcp->dmc_hp;
808
809 if(args == NIL){
810 cp = su_empty; /* xxx not NIL anyway */
811 goto jdefault;
812 }
813
814 cp = args->ca_arg.ca_str.s;
815 args = args->ca_next;
816
817 if(su_cs_starts_with_case("attribute", cp)){
818 if(args == NIL || args->ca_next != NIL)
819 goto jecmd;
820
821 cp = args->ca_arg.ca_str.s;
822 if((ap = mx_attachments_find(hp->h_attach, cp, NIL)) == NIL)
823 goto j501;
824
825 jatt_att:
826 fprintf(fp, "212 %s\n", a_DMSG_QUOTE(cp));
827 if(ap->a_msgno > 0){
828 if(fprintf(fp, "message-number %d\n\n", ap->a_msgno) < 0)
829 cp = NIL;
830 }else{
831 fprintf(fp, "creation-name %s\nopen-path %s\nfilename %s\n",
832 a_DMSG_QUOTE(ap->a_path_user), a_DMSG_QUOTE(ap->a_path),
833 a_DMSG_QUOTE(ap->a_name));
834 if((cp = ap->a_content_description) != NIL)
835 fprintf(fp, "content-description %s\n", a_DMSG_QUOTE(cp));
836 if(ap->a_content_id != NIL)
837 fprintf(fp, "content-id %s\n", ap->a_content_id->n_name);
838 if((cp = ap->a_content_type) != NIL)
839 fprintf(fp, "content-type %s\n", a_DMSG_QUOTE(cp));
840 if((cp = ap->a_content_disposition) != NIL)
841 fprintf(fp, "content-disposition %s\n", a_DMSG_QUOTE(cp));
842 cp = (putc('\n', fp) != EOF) ? su_empty : NIL;
843 }
844 }else if(su_cs_starts_with_case("attribute-at", cp)){
845 uz i;
846
847 if(args == NIL || args->ca_next != NIL)
848 goto jecmd;
849
850 if((su_idec_uz_cp(&i, cp = args->ca_arg.ca_str.s, 0, NIL
851 ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
852 ) != su_IDEC_STATE_CONSUMED || i == 0)
853 goto j505invpos;
854
855 for(ap = hp->h_attach; ap != NIL && --i != 0; ap = ap->a_flink)
856 ;
857 if(ap != NIL)
858 goto jatt_att;
859 goto j501;
860 }else if(su_cs_starts_with_case("attribute-set", cp)){
861 /* ATT-ID KEYWORD VALUE */
862 if(args == NIL)
863 goto jecmd;
864
865 cp = args->ca_arg.ca_str.s;
866 args = args->ca_next;
867
868 if(args == NIL || args->ca_next == NIL || args->ca_next->ca_next != NIL)
869 goto jecmd;
870 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
871 goto j505r;
872
873 if((ap = mx_attachments_find(hp->h_attach, cp, NIL)) == NIL)
874 goto j501;
875
876 jatt_attset:
877 if(ap->a_msgno > 0){
878 if(fprintf(fp, "505 RFC822 message attachment: %s\n", cp) < 0)
879 cp = NIL;
880 }else{
881 char c;
882 char const *keyw, *xcp;
883
884 keyw = args->ca_arg.ca_str.s;
885 cp = args->ca_next->ca_arg.ca_str.s;
886
887 for(xcp = cp; (c = *xcp) != '\0'; ++xcp)
888 if(su_cs_is_cntrl(c))
889 goto j505;
890 c = *cp;
891
892 if(!su_cs_cmp_case(keyw, "filename"))
893 ap->a_name = (c == '\0') ? ap->a_path_bname : cp;
894 else if(!su_cs_cmp_case(keyw, "content-description"))
895 ap->a_content_description = (c == '\0') ? NIL : cp;
896 else if(!su_cs_cmp_case(keyw, "content-id")){
897 ap->a_content_id = NIL;
898
899 if(c != '\0'){
900 struct mx_name *np;
901
902 /* XXX lextract->extract_single() */
903 np = checkaddrs(lextract(cp, GREF),
904 /*EACM_STRICT | TODO '/' valid!! */ EACM_NOLOG |
905 EACM_NONAME, NIL);
906 if(np != NIL && np->n_flink == NIL)
907 ap->a_content_id = np;
908 else
909 cp = NIL;
910 }
911 }else if(!su_cs_cmp_case(keyw, "content-type")){
912 if((ap->a_content_type = (c == '\0') ? NIL : cp) != NIL){
913 char *cp2;
914
915 for(cp2 = UNCONST(char*,cp); (c = *cp++) != '\0';)
916 *cp2++ = su_cs_to_lower(c);
917
918 if(!mx_mimetype_is_valid(ap->a_content_type, TRU1, FAL0)){
919 ap->a_content_type = NIL;
920 goto j505;
921 }
922 }
923 }else if(!su_cs_cmp_case(keyw, "content-disposition"))
924 ap->a_content_disposition = (c == '\0') ? NIL : cp;
925 else
926 cp = NIL;
927
928 if(cp != NIL){
929 uz i;
930
931 for(i = 0; ap != NIL; ++i, ap = ap->a_blink)
932 ;
933 if(fprintf(fp, "210 %" PRIuZ "\n", i) < 0)
934 cp = NIL;
935 }else{
936 cp = xcp;
937 goto j505; /* xxx jecmd; */
938 }
939 }
940 }else if(su_cs_starts_with_case("attribute-set-at", cp)){
941 uz i;
942
943 cp = args->ca_arg.ca_str.s;
944 args = args->ca_next;
945
946 if(args == NIL || args->ca_next == NIL || args->ca_next->ca_next != NIL)
947 goto jecmd;
948 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
949 goto j505r;
950
951 if((su_idec_uz_cp(&i, cp, 0, NIL
952 ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
953 ) != su_IDEC_STATE_CONSUMED || i == 0)
954 goto j505invpos;
955
956 for(ap = hp->h_attach; ap != NIL && --i != 0; ap = ap->a_flink)
957 ;
958 if(ap != NIL)
959 goto jatt_attset;
960 goto j501;
961 }else if(su_cs_starts_with_case("insert", cp)){
962 BITENUM_IS(u32,mx_attach_error) aerr;
963
964 if(args == NIL || args->ca_next != NIL)
965 goto jecmd;
966 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
967 goto j505r;
968
969 hp->h_attach = mx_attachments_append(hp->h_attach, args->ca_arg.ca_str.s,
970 &aerr, &ap);
971 switch(aerr){
972 case mx_ATTACHMENTS_ERR_FILE_OPEN: cp = "505"; goto jatt__ins;
973 case mx_ATTACHMENTS_ERR_ICONV_FAILED: cp = "506"; goto jatt__ins;
974 case mx_ATTACHMENTS_ERR_ICONV_NAVAIL: /* FALLTHRU */
975 case mx_ATTACHMENTS_ERR_OTHER: /* FALLTHRU */
976 default:
977 cp = "501";
978 jatt__ins:
979 if(fprintf(fp, "%s %s\n", cp, a_DMSG_QUOTE(args->ca_arg.ca_str.s)
980 ) < 0)
981 cp = NIL;
982 break;
983 case mx_ATTACHMENTS_ERR_NONE:{
984 uz i;
985
986 for(i = 0; ap != NIL; ++i, ap = ap->a_blink)
987 ;
988 if(fprintf(fp, "210 %" PRIuZ "\n", i) < 0)
989 cp = NIL;
990 }break;
991 }
992 }else if(su_cs_starts_with_case("list", cp)){
993 jdefault:
994 if(args != NIL)
995 goto jecmd;
996
997 if((ap = hp->h_attach) == NIL)
998 goto j501;
999
1000 fputs("212\n", fp);
1001 do
1002 fprintf(fp, "%s\n", a_DMSG_QUOTE(ap->a_path_user));
1003 while((ap = ap->a_flink) != NIL);
1004 if(putc('\n', fp) == EOF)
1005 cp = NIL;
1006 }else if(su_cs_starts_with_case("remove", cp)){
1007 if(args == NIL || args->ca_next != NIL)
1008 goto jecmd;
1009 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
1010 goto j505r;
1011
1012 cp = args->ca_arg.ca_str.s;
1013 if((ap = mx_attachments_find(hp->h_attach, cp, &status)) == NIL)
1014 goto j501;
1015 if(status == TRUM1)
1016 goto j506;
1017
1018 hp->h_attach = mx_attachments_remove(hp->h_attach, ap);
1019 if(fprintf(fp, "210 %s\n", a_DMSG_QUOTE(cp)) < 0)
1020 cp = NIL;
1021 }else if(su_cs_starts_with_case("remove-at", cp)){
1022 uz i;
1023
1024 if(args == NIL || args->ca_next != NIL)
1025 goto jecmd;
1026 if(dmcp->dmc_flags & mx_DIG_MSG_RDONLY)
1027 goto j505r;
1028
1029 if((su_idec_uz_cp(&i, cp = args->ca_arg.ca_str.s, 0, NIL
1030 ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
1031 ) != su_IDEC_STATE_CONSUMED || i == 0)
1032 goto j505invpos;
1033
1034 for(ap = hp->h_attach; ap != NIL && --i != 0; ap = ap->a_flink)
1035 ;
1036 if(ap != NIL){
1037 hp->h_attach = mx_attachments_remove(hp->h_attach, ap);
1038 if(fprintf(fp, "210 %s\n", cp) < 0)
1039 cp = NIL;
1040 }else
1041 goto j501;
1042 }else
1043 goto jecmd;
1044
1045 jleave:
1046 NYD2_OU;
1047 return (cp != NIL);
1048
1049 jecmd:
1050 if(fputs("500\n", fp) == EOF)
1051 cp = NIL;
1052 cp = NIL;
1053 goto jleave;
1054 j501:
1055 if(fputs("501\n", fp) == EOF)
1056 cp = NIL;
1057 goto jleave;
1058 j505:
1059 if(fputs("505\n", fp) == EOF)
1060 cp = NIL;
1061 goto jleave;
1062 j505r:
1063 if(fprintf(fp, "505 read-only: %s\n", cp) < 0)
1064 cp = NIL;
1065 goto jleave;
1066 j505invpos:
1067 if(fprintf(fp, "505 invalid position: %s\n", cp) < 0)
1068 cp = NIL;
1069 goto jleave;
1070 j506:
1071 if(fputs("506\n", fp) == EOF)
1072 cp = NIL;
1073 goto jleave;
1074 }
1075
1076 void
mx_dig_msg_on_mailbox_close(struct mailbox * mbp)1077 mx_dig_msg_on_mailbox_close(struct mailbox *mbp){ /* XXX HACK <- event! */
1078 struct mx_dig_msg_ctx *dmcp;
1079 NYD_IN;
1080
1081 while((dmcp = mbp->mb_digmsg) != NIL){
1082 mbp->mb_digmsg = dmcp->dmc_next;
1083 if(dmcp->dmc_flags & mx_DIG_MSG_FCLOSE)
1084 fclose(dmcp->dmc_fp);
1085 if(dmcp->dmc_flags & mx_DIG_MSG_OWN_MEMBAG)
1086 su_mem_bag_gut(dmcp->dmc_membag);
1087 n_free(dmcp);
1088 }
1089 NYD_OU;
1090 }
1091
1092 int
c_digmsg(void * vp)1093 c_digmsg(void *vp){
1094 char const *cp, *emsg;
1095 struct mx_dig_msg_ctx *dmcp;
1096 struct mx_cmd_arg *cap;
1097 struct mx_cmd_arg_ctx *cacp;
1098 NYD_IN;
1099
1100 n_pstate_err_no = su_ERR_NONE;
1101 cacp = vp;
1102 cap = cacp->cac_arg;
1103
1104 if(su_cs_starts_with_case("create", cp = cap->ca_arg.ca_str.s)){
1105 if(cacp->cac_no < 2 || cacp->cac_no > 3) /* XXX argparse is stupid */
1106 goto jesynopsis;
1107 cap = cap->ca_next;
1108
1109 /* Request to use STDOUT? */
1110 if(cacp->cac_no == 3){
1111 cp = cap->ca_next->ca_arg.ca_str.s;
1112 if(*cp != '-' || cp[1] != '\0'){
1113 emsg = N_("digmsg: create: invalid I/O channel: %s\n");
1114 goto jeinval_quote;
1115 }
1116 }
1117
1118 /* First of all, our context object */
1119 switch(a_dmsg_find(cp = cap->ca_arg.ca_str.s, &dmcp, TRU1)){
1120 case su_ERR_INVAL:
1121 emsg = N_("digmsg: create: message number invalid: %s\n");
1122 goto jeinval_quote;
1123 case su_ERR_EXIST:
1124 emsg = N_("digmsg: create: message object already exists: %s\n");
1125 goto jeinval_quote;
1126 default:
1127 break;
1128 }
1129
1130 if(dmcp->dmc_flags & mx_DIG_MSG_COMPOSE)
1131 dmcp->dmc_flags = mx_DIG_MSG_COMPOSE | mx_DIG_MSG_COMPOSE_DIGGED;
1132 else{
1133 FILE *fp;
1134
1135 if((fp = setinput(&mb, dmcp->dmc_mp, NEED_HEADER)) == NIL){
1136 /* XXX Should have panicked before.. */
1137 n_free(dmcp);
1138 emsg = N_("digmsg: create: mailbox I/O error for message: %s\n");
1139 goto jeinval_quote;
1140 }
1141
1142 su_mem_bag_push(n_go_data->gdc_membag, dmcp->dmc_membag);
1143 /* XXX n_header_extract error!! */
1144 n_header_extract((n_HEADER_EXTRACT_FULL |
1145 n_HEADER_EXTRACT_PREFILL_RECEIVERS |
1146 n_HEADER_EXTRACT_IGNORE_FROM_), fp, dmcp->dmc_hp, NIL);
1147 su_mem_bag_pop(n_go_data->gdc_membag, dmcp->dmc_membag);
1148 }
1149
1150 if(cacp->cac_no == 3)
1151 dmcp->dmc_fp = n_stdout;
1152 /* For compose mode simply use FS_O_REGISTER, the number of dangling
1153 * deleted files with open descriptors until next fs_close_all()
1154 * should be very small; if this paradigm is changed
1155 * DIG_MSG_COMPOSE_GUT() needs to be adjusted */
1156 else if((dmcp->dmc_fp = mx_fs_tmp_open("digmsg", (mx_FS_O_RDWR |
1157 mx_FS_O_UNLINK | (dmcp->dmc_flags & mx_DIG_MSG_COMPOSE
1158 ? mx_FS_O_REGISTER : 0)),
1159 NIL)) != NIL)
1160 dmcp->dmc_flags |= mx_DIG_MSG_HAVE_FP |
1161 (dmcp->dmc_flags & mx_DIG_MSG_COMPOSE ? 0 : mx_DIG_MSG_FCLOSE);
1162 else{
1163 n_err(_("digmsg: create: cannot create temporary file: %s\n"),
1164 su_err_doc(n_pstate_err_no = su_err_no()));
1165 vp = NIL;
1166 goto jeremove;
1167 }
1168
1169 if(!(dmcp->dmc_flags & mx_DIG_MSG_COMPOSE)){
1170 dmcp->dmc_last = NIL;
1171 if((dmcp->dmc_next = mb.mb_digmsg) != NIL)
1172 dmcp->dmc_next->dmc_last = dmcp;
1173 mb.mb_digmsg = dmcp;
1174 }
1175 }else if(su_cs_starts_with_case("remove", cp)){
1176 if(cacp->cac_no != 2)
1177 goto jesynopsis;
1178 cap = cap->ca_next;
1179
1180 switch(a_dmsg_find(cp = cap->ca_arg.ca_str.s, &dmcp, FAL0)){
1181 case su_ERR_INVAL:
1182 emsg = N_("digmsg: remove: message number invalid: %s\n");
1183 goto jeinval_quote;
1184 default:
1185 if(!(dmcp->dmc_flags & mx_DIG_MSG_COMPOSE) ||
1186 (dmcp->dmc_flags & mx_DIG_MSG_COMPOSE_DIGGED))
1187 break;
1188 /* FALLTHRU */
1189 case su_ERR_NOENT:
1190 emsg = N_("digmsg: remove: no such message object: %s\n");
1191 goto jeinval_quote;
1192 }
1193
1194 if(!(dmcp->dmc_flags & mx_DIG_MSG_COMPOSE)){
1195 if(dmcp->dmc_last != NIL)
1196 dmcp->dmc_last->dmc_next = dmcp->dmc_next;
1197 else{
1198 ASSERT(dmcp == mb.mb_digmsg);
1199 mb.mb_digmsg = dmcp->dmc_next;
1200 }
1201 if(dmcp->dmc_next != NIL)
1202 dmcp->dmc_next->dmc_last = dmcp->dmc_last;
1203 }
1204
1205 if((dmcp->dmc_flags & mx_DIG_MSG_HAVE_FP) &&
1206 mx_dig_msg_read_overlay == dmcp)
1207 mx_dig_msg_read_overlay = NIL;
1208
1209 if(dmcp->dmc_flags & mx_DIG_MSG_FCLOSE)
1210 fclose(dmcp->dmc_fp);
1211 jeremove:
1212 if(dmcp->dmc_flags & mx_DIG_MSG_OWN_MEMBAG)
1213 su_mem_bag_gut(dmcp->dmc_membag);
1214
1215 if(dmcp->dmc_flags & mx_DIG_MSG_COMPOSE)
1216 dmcp->dmc_flags = mx_DIG_MSG_COMPOSE;
1217 else
1218 n_free(dmcp);
1219 }else{
1220 switch(a_dmsg_find(cp, &dmcp, FAL0)){
1221 case su_ERR_INVAL:
1222 emsg = N_("digmsg: message number invalid: %s\n");
1223 goto jeinval_quote;
1224 case su_ERR_NOENT:
1225 emsg = N_("digmsg: no such message object: %s\n");
1226 goto jeinval_quote;
1227 default:
1228 break;
1229 }
1230 cap = cap->ca_next;
1231
1232 if(dmcp->dmc_flags & mx_DIG_MSG_HAVE_FP){
1233 rewind(dmcp->dmc_fp);
1234 ftruncate(fileno(dmcp->dmc_fp), 0);
1235 }
1236
1237 su_mem_bag_push(n_go_data->gdc_membag, dmcp->dmc_membag);
1238 if(!a_dmsg_cmd(dmcp->dmc_fp, dmcp, cap,
1239 ((cap != NIL) ? cap->ca_next : NIL)))
1240 vp = NIL;
1241 su_mem_bag_pop(n_go_data->gdc_membag, dmcp->dmc_membag);
1242
1243 if(dmcp->dmc_flags & mx_DIG_MSG_HAVE_FP){
1244 rewind(dmcp->dmc_fp);
1245 /* This will be reset by go_input() _if_ we read to EOF */
1246 mx_dig_msg_read_overlay = dmcp;
1247 }
1248 }
1249
1250 jleave:
1251 NYD_OU;
1252 return (vp == NIL);
1253
1254 jesynopsis:
1255 mx_cmd_print_synopsis(mx_cmd_firstfit("digmsg"), NIL);
1256 goto jeinval;
1257 jeinval_quote:
1258 emsg = V_(emsg);
1259 n_err(emsg, n_shexp_quote_cp(cp, FAL0));
1260 jeinval:
1261 n_pstate_err_no = su_ERR_INVAL;
1262 vp = NIL;
1263 goto jleave;
1264 }
1265
1266 boole
mx_dig_msg_circumflex(struct mx_dig_msg_ctx * dmcp,FILE * fp,char const * cmd)1267 mx_dig_msg_circumflex(struct mx_dig_msg_ctx *dmcp, FILE *fp, char const *cmd){
1268 /* Identical to (subset of) c_digmsg() cmd-tab */
1269 mx_CMD_ARG_DESC_SUBCLASS_DEF_NAME(dm, "digmsg", 5, pseudo_cad){
1270 {mx_CMD_ARG_DESC_SHEXP | mx_CMD_ARG_DESC_HONOUR_STOP,
1271 n_SHEXP_PARSE_IGNORE_EMPTY | n_SHEXP_PARSE_TRIM_IFSSPACE},
1272 {mx_CMD_ARG_DESC_SHEXP | mx_CMD_ARG_DESC_OPTION |
1273 mx_CMD_ARG_DESC_HONOUR_STOP,
1274 n_SHEXP_PARSE_TRIM_IFSSPACE}, /* arg1 */
1275 {mx_CMD_ARG_DESC_SHEXP | mx_CMD_ARG_DESC_OPTION |
1276 mx_CMD_ARG_DESC_HONOUR_STOP,
1277 n_SHEXP_PARSE_TRIM_IFSSPACE}, /* arg2 */
1278 {mx_CMD_ARG_DESC_SHEXP | mx_CMD_ARG_DESC_OPTION |
1279 mx_CMD_ARG_DESC_HONOUR_STOP,
1280 n_SHEXP_PARSE_TRIM_IFSSPACE}, /* arg3 */
1281 {mx_CMD_ARG_DESC_SHEXP | mx_CMD_ARG_DESC_OPTION |
1282 mx_CMD_ARG_DESC_HONOUR_STOP |
1283 mx_CMD_ARG_DESC_GREEDY | mx_CMD_ARG_DESC_GREEDY_JOIN,
1284 n_SHEXP_PARSE_TRIM_IFSSPACE} /* arg4 */
1285 }mx_CMD_ARG_DESC_SUBCLASS_DEF_END;
1286
1287 struct mx_cmd_arg_ctx cac;
1288 boole rv;
1289 NYD_IN;
1290
1291 cac.cac_desc = mx_CMD_ARG_DESC_SUBCLASS_CAST(&pseudo_cad);
1292 cac.cac_indat = cmd;
1293 cac.cac_inlen = UZ_MAX;
1294 cac.cac_msgflag = cac.cac_msgmask = 0;
1295
1296 if((rv = mx_cmd_arg_parse(&cac)))
1297 rv = a_dmsg_cmd(fp, dmcp, cac.cac_arg, cac.cac_arg->ca_next);
1298
1299 NYD_OU;
1300 return rv;
1301 }
1302
1303 #include "su/code-ou.h"
1304 /* s-it-mode */
1305