1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Dig message objects. TODO Very very restricted (especially non-compose)
3 *@ On protocol change adjust config.h:n_DIG_MSG_PLUMBING_VERSION + `~^' manual
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
11 * Copyright (c) 2016 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
12 * SPDX-License-Identifier: ISC
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.
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.
27 #define n_FILE dig_msg
29 #ifndef HAVE_AMALGAMATION
33 /* Try to convert cp into an unsigned number that corresponds to an existing
34 * message number (or ERR_INVAL), search for an existing object (ERR_EXIST if
35 * oexcl and exists; ERR_NOENT if not oexcl and does not exist).
36 * On oexcl success *dmcp will be n_alloc()ated with .dmc_msgno and .dmc_mp
37 * etc. set; but not linked into mb.mb_digmsg and .dmc_fp not created etc. */
38 static si32_t
a_dmsg_find(char const *cp
, struct n_dig_msg_ctx
**dmcpp
,
41 /* Subcommand drivers */
42 static bool_t
a_dmsg_cmd(FILE *fp
, struct n_dig_msg_ctx
*dmcp
, char const *cmd
,
43 uiz_t cmdl
, char const *cp
);
45 static bool_t
a_dmsg__header(FILE *fp
, struct n_dig_msg_ctx
*dmcp
,
47 static bool_t
a_dmsg__attach(FILE *fp
, struct n_dig_msg_ctx
*dmcp
,
51 a_dmsg_find(char const *cp
, struct n_dig_msg_ctx
**dmcpp
, bool_t oexcl
){
52 struct n_dig_msg_ctx
*dmcp
;
57 if(cp
[0] == '-' && cp
[1] == '\0'){
58 if((dmcp
= n_dig_msg_compose_ctx
) != NULL
){
60 if(dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE_DIGGED
)
61 rv
= oexcl
? n_ERR_EXIST
: n_ERR_NONE
;
63 rv
= oexcl
? n_ERR_NONE
: n_ERR_NOENT
;
69 if((n_idec_ui32_cp(&msgno
, cp
, 0, NULL
70 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
71 ) != n_IDEC_STATE_CONSUMED
||
72 msgno
== 0 || UICMP(z
, msgno
, >, msgCount
)){
77 for(dmcp
= mb
.mb_digmsg
; dmcp
!= NULL
; dmcp
= dmcp
->dmc_next
)
78 if(dmcp
->dmc_msgno
== msgno
){
80 rv
= oexcl
? n_ERR_EXIST
: n_ERR_NONE
;
88 *dmcpp
= dmcp
= n_calloc(1, n_ALIGN(sizeof *dmcp
) + sizeof(struct header
));
89 dmcp
->dmc_mp
= &message
[msgno
- 1];
90 dmcp
->dmc_flags
= n_DIG_MSG_OWN_MEMPOOL
|
91 ((TRU1
/*TODO*/ || !(mb
.mb_perm
& MB_DELE
))
92 ? n_DIG_MSG_RDONLY
: n_DIG_MSG_NONE
);
93 dmcp
->dmc_msgno
= msgno
;
94 dmcp
->dmc_hp
= (struct header
*)n_ALIGN(PTR2SIZE(&dmcp
[1]));
95 dmcp
->dmc_mempool
= dmcp
->dmc_mempool_buf
;
96 /* Rest done by caller */
104 a_dmsg_cmd(FILE *fp
, struct n_dig_msg_ctx
*dmcp
, char const *cmd
, uiz_t cmdl
,
113 /* TODO trim+strlist_split(_ifs?)() */
114 for(i
= 0; i
< n_NELEM(cmda
); ++i
){
115 while(blankchar(*cp
))
120 if(i
< n_NELEM(cmda
) - 1)
121 for(cmda
[i
] = cp
++; *cp
!= '\0' && !blankchar(*cp
); ++cp
)
124 /* Last slot takes all the rest of the line, less trailing WS */
125 for(cmda
[i
] = cp
++; *cp
!= '\0'; ++cp
)
127 while(blankchar(cp
[-1]))
130 cmda
[i
] = savestrbuf(cmda
[i
], PTR2SIZE(cp
- cmda
[i
]));
135 if(is_ascncaseprefix(cmd
, "header", cmdl
))
136 rv
= a_dmsg__header(fp
, dmcp
, cmda
);
137 else if(is_ascncaseprefix(cmd
, "attachment", cmdl
)){
138 if(!(dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
)) /* TODO attachment support */
140 "505 `digmsg attachment' only in compose mode (yet)\n") > 0);
142 rv
= a_dmsg__attach(fp
, dmcp
, cmda
);
143 }else if(is_ascncaseprefix(cmd
, "version", cmdl
)){
146 rv
= (fputs("210 " n_DIG_MSG_PLUMBING_VERSION
"\n", fp
) != EOF
);
159 a_dmsg__header(FILE *fp
, struct n_dig_msg_ctx
*dmcp
, char const *cmda
[3]){
161 struct n_header_field
*hfp
;
162 struct name
*np
, **npp
;
169 if((cp
= cmda
[0]) == NULL
){
170 cp
= n_empty
; /* xxx not NULL anyway */
174 if(is_asccaseprefix(cp
, "insert")){ /* TODO LOGIC BELONGS head.c
175 * TODO That is: Header::factory(string) -> object (blahblah).
176 * TODO I.e., as long as we don't have regular RFC compliant parsers
177 * TODO which differentiate in between structured and unstructured
178 * TODO header fields etc., a little workaround */
181 enum expand_addr_check_mode eacm
;
185 if(cmda
[1] == NULL
|| cmda
[2] == NULL
)
187 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
190 /* Strip [\r\n] which would render a body invalid XXX all controls? */
194 cmda
[2] = xp
= savestr(cmda
[2]);
195 for(; (c
= *xp
) != '\0'; ++xp
)
196 if(c
== '\n' || c
== '\r')
200 if(!asccasecmp(cmda
[1], cp
= "Subject")){
201 if(cmda
[2][0] != '\0'){
202 if(hp
->h_subject
!= NULL
)
203 hp
->h_subject
= savecatsep(hp
->h_subject
, ' ', cmda
[2]);
205 hp
->h_subject
= n_UNCONST(cmda
[2]);
206 if(fprintf(fp
, "210 %s 1\n", cp
) < 0)
214 ntype
= GEXTRA
| GFULL
| GFULLEXTRA
;
217 if(!asccasecmp(cmda
[1], cp
= "From")){
221 /* todo As said above, this should be table driven etc., but.. */
222 if(ntype
& GBCC_IS_FCC
){
223 np
= nalloc_fcc(cmda
[2]);
224 if(is_addr_invalid(np
, eacm
))
227 if((np
= lextract(cmda
[2], ntype
)) == NULL
)
230 if((np
= checkaddrs(np
, eacm
, &aerr
), aerr
!= 0)){
232 if(fprintf(fp
, "505 %s\n", cp
) < 0)
238 /* Go to the end of the list, track whether it contains any
239 * non-deleted entries */
241 if((xnp
= *npp
) != NULL
)
242 for(;; xnp
= xnp
->n_flink
){
243 if(!(xnp
->n_type
& GDEL
))
245 if(xnp
->n_flink
== NULL
)
249 if(!mult_ok
&& (i
!= 0 || np
->n_flink
!= NULL
)){
250 if(fprintf(fp
, "506 %s\n", cp
) < 0)
258 if(fprintf(fp
, "210 %s %" PRIuZ
"\n", cp
, ++i
) < 0)
263 if(!asccasecmp(cmda
[1], cp
= "Sender")){
268 if(!asccasecmp(cmda
[1], cp
= "To")){
271 eacm
= EACM_NORMAL
| EAF_NAME
;
274 if(!asccasecmp(cmda
[1], cp
= "Cc")){
277 eacm
= EACM_NORMAL
| EAF_NAME
;
280 if(!asccasecmp(cmda
[1], cp
= "Bcc")){
282 ntype
= GBCC
| GFULL
;
283 eacm
= EACM_NORMAL
| EAF_NAME
;
286 if(!asccasecmp(cmda
[1], cp
= "Fcc")){
288 ntype
= GBCC
| GBCC_IS_FCC
;
289 eacm
= EACM_NORMAL
/* Not | EAF_FILE, depend on *expandaddr*! */;
292 if(!asccasecmp(cmda
[1], cp
= "Reply-To")){
293 npp
= &hp
->h_reply_to
;
297 if(!asccasecmp(cmda
[1], cp
= "Mail-Followup-To")){
302 if(!asccasecmp(cmda
[1], cp
= "Message-ID")){
304 npp
= &hp
->h_message_id
;
309 if(!asccasecmp(cmda
[1], cp
= "References")){
315 if(!asccasecmp(cmda
[1], cp
= "In-Reply-To")){
316 npp
= &hp
->h_in_reply_to
;
322 if((cp
= n_header_is_known(cmda
[1], UIZ_MAX
)) != NULL
)
325 /* Free-form header fields */
328 struct n_header_field
**hfpp
;
330 for(cp
= cmda
[1]; *cp
!= '\0'; ++cp
)
331 if(!fieldnamechar(*cp
)){
336 for(i
= 0, hfpp
= &hp
->h_user_headers
; *hfpp
!= NULL
; ++i
)
337 hfpp
= &(*hfpp
)->hf_next
;
339 nl
= strlen(cp
= cmda
[1]) +1;
340 bl
= strlen(cmda
[2]) +1;
341 *hfpp
= hfp
= n_autorec_alloc(n_VSTRUCT_SIZEOF(struct n_header_field
,
346 memcpy(&hfp
->hf_dat
[0], cp
, nl
);
347 memcpy(&hfp
->hf_dat
[nl
], cmda
[2], bl
);
348 if(fprintf(fp
, "210 %s %" PRIuZ
"\n",
349 &hfp
->hf_dat
[0], ++i
) < 0)
352 }else if(is_asccaseprefix(cp
, "list")){
356 if(hp
->h_subject
!= NULL
) fputs(" Subject", fp
);
357 if(hp
->h_from
!= NULL
) fputs(" From", fp
);
358 if(hp
->h_sender
!= NULL
) fputs(" Sender", fp
);
359 if(hp
->h_to
!= NULL
) fputs(" To", fp
);
360 if(hp
->h_cc
!= NULL
) fputs(" Cc", fp
);
361 if(hp
->h_bcc
!= NULL
) fputs(" Bcc", fp
);
362 if(hp
->h_fcc
!= NULL
) fputs(" Fcc", fp
);
363 if(hp
->h_reply_to
!= NULL
) fputs(" Reply-To", fp
);
364 if(hp
->h_mft
!= NULL
) fputs(" Mail-Followup-To", fp
);
365 if(hp
->h_message_id
!= NULL
) fputs(" Message-ID", fp
);
366 if(hp
->h_ref
!= NULL
) fputs(" References", fp
);
367 if(hp
->h_in_reply_to
!= NULL
) fputs(" In-Reply-To", fp
);
368 if(hp
->h_mailx_command
!= NULL
)
369 fputs(" Mailx-Command", fp
);
370 if(hp
->h_mailx_raw_to
!= NULL
) fputs(" Mailx-Raw-To", fp
);
371 if(hp
->h_mailx_raw_cc
!= NULL
) fputs(" Mailx-Raw-Cc", fp
);
372 if(hp
->h_mailx_raw_bcc
!= NULL
)
373 fputs(" Mailx-Raw-Bcc", fp
);
374 if(hp
->h_mailx_orig_from
!= NULL
)
375 fputs(" Mailx-Orig-From", fp
);
376 if(hp
->h_mailx_orig_to
!= NULL
)
377 fputs(" Mailx-Orig-To", fp
);
378 if(hp
->h_mailx_orig_cc
!= NULL
)
379 fputs(" Mailx-Orig-Cc", fp
);
380 if(hp
->h_mailx_orig_bcc
!= NULL
)
381 fputs(" Mailx-Orig-Bcc", fp
);
383 /* Print only one instance of each free-form header */
384 for(hfp
= hp
->h_user_headers
; hfp
!= NULL
; hfp
= hfp
->hf_next
){
385 struct n_header_field
*hfpx
;
387 for(hfpx
= hp
->h_user_headers
;; hfpx
= hfpx
->hf_next
)
390 fputs(&hfp
->hf_dat
[0], fp
);
392 }else if(!asccasecmp(&hfpx
->hf_dat
[0], &hfp
->hf_dat
[0]))
395 if(putc('\n', fp
) == EOF
)
403 if(!asccasecmp(cmda
[1], cp
= "Subject")){
404 np
= (hp
->h_subject
!= NULL
) ? (struct name
*)-1 : NULL
;
407 if(!asccasecmp(cmda
[1], cp
= "From")){
410 fprintf(fp
, "%s %s\n", (np
== NULL
? "501" : "210"), cp
);
413 if(!asccasecmp(cmda
[1], cp
= "Sender")){
417 if(!asccasecmp(cmda
[1], cp
= "To")){
421 if(!asccasecmp(cmda
[1], cp
= "Cc")){
425 if(!asccasecmp(cmda
[1], cp
= "Bcc")){
429 if(!asccasecmp(cmda
[1], cp
= "Fcc")){
433 if(!asccasecmp(cmda
[1], cp
= "Reply-To")){
437 if(!asccasecmp(cmda
[1], cp
= "Mail-Followup-To")){
441 if(!asccasecmp(cmda
[1], cp
= "Message-ID")){
442 np
= hp
->h_message_id
;
445 if(!asccasecmp(cmda
[1], cp
= "References")){
449 if(!asccasecmp(cmda
[1], cp
= "In-Reply-To")){
450 np
= hp
->h_in_reply_to
;
454 if(!asccasecmp(cmda
[1], cp
= "Mailx-Command")){
455 np
= (hp
->h_mailx_command
!= NULL
) ? (struct name
*)-1 : NULL
;
458 if(!asccasecmp(cmda
[1], cp
= "Mailx-Raw-To")){
459 np
= hp
->h_mailx_raw_to
;
462 if(!asccasecmp(cmda
[1], cp
= "Mailx-Raw-Cc")){
463 np
= hp
->h_mailx_raw_cc
;
466 if(!asccasecmp(cmda
[1], cp
= "Mailx-Raw-Bcc")){
467 np
= hp
->h_mailx_raw_bcc
;
470 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-From")){
471 np
= hp
->h_mailx_orig_from
;
474 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-To")){
475 np
= hp
->h_mailx_orig_to
;
478 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-Cc")){
479 np
= hp
->h_mailx_orig_cc
;
482 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-Bcc")){
483 np
= hp
->h_mailx_orig_bcc
;
487 /* Free-form header fields */
488 for(cp
= cmda
[1]; *cp
!= '\0'; ++cp
)
489 if(!fieldnamechar(*cp
)){
494 for(hfp
= hp
->h_user_headers
;; hfp
= hfp
->hf_next
){
497 else if(!asccasecmp(cp
, &hfp
->hf_dat
[0])){
498 if(fprintf(fp
, "210 %s\n", &hfp
->hf_dat
[0]) < 0)
503 }else if(is_asccaseprefix(cp
, "remove")){
504 if(cmda
[1] == NULL
|| cmda
[2] != NULL
)
506 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
509 if(!asccasecmp(cmda
[1], cp
= "Subject")){
510 if(hp
->h_subject
!= NULL
){
511 hp
->h_subject
= NULL
;
512 if(fprintf(fp
, "210 %s\n", cp
) < 0)
519 if(!asccasecmp(cmda
[1], cp
= "From")){
524 if(fprintf(fp
, "210 %s\n", cp
) < 0)
530 if(!asccasecmp(cmda
[1], cp
= "Sender")){
534 if(!asccasecmp(cmda
[1], cp
= "To")){
538 if(!asccasecmp(cmda
[1], cp
= "Cc")){
542 if(!asccasecmp(cmda
[1], cp
= "Bcc")){
546 if(!asccasecmp(cmda
[1], cp
= "Fcc")){
550 if(!asccasecmp(cmda
[1], cp
= "Reply-To")){
551 npp
= &hp
->h_reply_to
;
554 if(!asccasecmp(cmda
[1], cp
= "Mail-Followup-To")){
558 if(!asccasecmp(cmda
[1], cp
= "Message-ID")){
559 npp
= &hp
->h_message_id
;
562 if(!asccasecmp(cmda
[1], cp
= "References")){
566 if(!asccasecmp(cmda
[1], cp
= "In-Reply-To")){
567 npp
= &hp
->h_in_reply_to
;
571 if((cp
= n_header_is_known(cmda
[1], UIZ_MAX
)) != NULL
)
574 /* Free-form header fields (note j501cp may print non-normalized name) */
576 struct n_header_field
**hfpp
;
579 for(cp
= cmda
[1]; *cp
!= '\0'; ++cp
)
580 if(!fieldnamechar(*cp
)){
586 for(any
= FAL0
, hfpp
= &hp
->h_user_headers
; (hfp
= *hfpp
) != NULL
;){
587 if(!asccasecmp(cp
, &hfp
->hf_dat
[0])){
588 *hfpp
= hfp
->hf_next
;
590 if(fprintf(fp
, "210 %s\n", &hfp
->hf_dat
[0]) < 0){
597 hfpp
= &hfp
->hf_next
;
602 }else if(is_asccaseprefix(cp
, "remove-at")){
603 if(cmda
[1] == NULL
|| cmda
[2] == NULL
)
605 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
608 if((n_idec_uiz_cp(&i
, cmda
[2], 0, NULL
609 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
610 ) != n_IDEC_STATE_CONSUMED
|| i
== 0){
611 if(fprintf(fp
, "505 invalid position: %s\n", cmda
[2]) < 0)
616 if(!asccasecmp(cmda
[1], cp
= "Subject")){
617 if(hp
->h_subject
!= NULL
&& i
== 1){
618 hp
->h_subject
= NULL
;
619 if(fprintf(fp
, "210 %s 1\n", cp
) < 0)
626 if(!asccasecmp(cmda
[1], cp
= "From")){
629 if((np
= *npp
) == NULL
)
631 while(--i
!= 0 && np
!= NULL
)
636 if(np
->n_blink
!= NULL
)
637 np
->n_blink
->n_flink
= np
->n_flink
;
640 if(np
->n_flink
!= NULL
)
641 np
->n_flink
->n_blink
= np
->n_blink
;
643 if(fprintf(fp
, "210 %s\n", cp
) < 0)
647 if(!asccasecmp(cmda
[1], cp
= "Sender")){
651 if(!asccasecmp(cmda
[1], cp
= "To")){
655 if(!asccasecmp(cmda
[1], cp
= "Cc")){
659 if(!asccasecmp(cmda
[1], cp
= "Bcc")){
663 if(!asccasecmp(cmda
[1], cp
= "Fcc")){
667 if(!asccasecmp(cmda
[1], cp
= "Reply-To")){
668 npp
= &hp
->h_reply_to
;
671 if(!asccasecmp(cmda
[1], cp
= "Mail-Followup-To")){
675 if(!asccasecmp(cmda
[1], cp
= "Message-ID")){
676 npp
= &hp
->h_message_id
;
679 if(!asccasecmp(cmda
[1], cp
= "References")){
683 if(!asccasecmp(cmda
[1], cp
= "In-Reply-To")){
684 npp
= &hp
->h_in_reply_to
;
688 if((cp
= n_header_is_known(cmda
[1], UIZ_MAX
)) != NULL
)
691 /* Free-form header fields */
693 struct n_header_field
**hfpp
;
695 for(cp
= cmda
[1]; *cp
!= '\0'; ++cp
)
696 if(!fieldnamechar(*cp
)){
702 for(hfpp
= &hp
->h_user_headers
; (hfp
= *hfpp
) != NULL
;){
704 *hfpp
= hfp
->hf_next
;
705 if(fprintf(fp
, "210 %s %" PRIuZ
"\n",
706 &hfp
->hf_dat
[0], i
) < 0){
712 hfpp
= &hfp
->hf_next
;
717 }else if(is_asccaseprefix(cp
, "show")){
718 if(cmda
[1] == NULL
|| cmda
[2] != NULL
)
721 if(!asccasecmp(cmda
[1], cp
= "Subject")){
722 if(hp
->h_subject
== NULL
)
724 if(fprintf(fp
, "212 %s\n%s\n\n", cp
, hp
->h_subject
) < 0)
729 if(!asccasecmp(cmda
[1], cp
= "From")){
733 fprintf(fp
, "211 %s\n", cp
);
734 do if(!(np
->n_type
& GDEL
)){
735 switch(np
->n_flags
& NAME_ADDRSPEC_ISMASK
){
736 case NAME_ADDRSPEC_ISFILE
: cp
= n_hy
; break;
737 case NAME_ADDRSPEC_ISPIPE
: cp
= "|"; break;
738 case NAME_ADDRSPEC_ISNAME
: cp
= n_ns
; break;
739 default: cp
= np
->n_name
; break;
741 fprintf(fp
, "%s %s\n", cp
, np
->n_fullname
);
742 }while((np
= np
->n_flink
) != NULL
);
743 if(putc('\n', fp
) == EOF
)
749 if(!asccasecmp(cmda
[1], cp
= "Sender")){
753 if(!asccasecmp(cmda
[1], cp
= "To")){
757 if(!asccasecmp(cmda
[1], cp
= "Cc")){
761 if(!asccasecmp(cmda
[1], cp
= "Bcc")){
765 if(!asccasecmp(cmda
[1], cp
= "Fcc")){
769 if(!asccasecmp(cmda
[1], cp
= "Reply-To")){
773 if(!asccasecmp(cmda
[1], cp
= "Mail-Followup-To")){
777 if(!asccasecmp(cmda
[1], cp
= "Message-ID")){
778 np
= hp
->h_message_id
;
781 if(!asccasecmp(cmda
[1], cp
= "References")){
785 if(!asccasecmp(cmda
[1], cp
= "In-Reply-To")){
786 np
= hp
->h_in_reply_to
;
790 if(!asccasecmp(cmda
[1], cp
= "Mailx-Command")){
791 if(hp
->h_mailx_command
== NULL
)
793 if(fprintf(fp
, "212 %s\n%s\n\n",
794 cp
, hp
->h_mailx_command
) < 0)
798 if(!asccasecmp(cmda
[1], cp
= "Mailx-Raw-To")){
799 np
= hp
->h_mailx_raw_to
;
802 if(!asccasecmp(cmda
[1], cp
= "Mailx-Raw-Cc")){
803 np
= hp
->h_mailx_raw_cc
;
806 if(!asccasecmp(cmda
[1], cp
= "Mailx-Raw-Bcc")){
807 np
= hp
->h_mailx_raw_bcc
;
810 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-From")){
811 np
= hp
->h_mailx_orig_from
;
814 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-To")){
815 np
= hp
->h_mailx_orig_to
;
818 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-Cc")){
819 np
= hp
->h_mailx_orig_cc
;
822 if(!asccasecmp(cmda
[1], cp
= "Mailx-Orig-Bcc")){
823 np
= hp
->h_mailx_orig_bcc
;
827 /* Free-form header fields */
831 for(cp
= cmda
[1]; *cp
!= '\0'; ++cp
)
832 if(!fieldnamechar(*cp
)){
838 for(any
= FAL0
, hfp
= hp
->h_user_headers
; hfp
!= NULL
;
840 if(!asccasecmp(cp
, &hfp
->hf_dat
[0])){
842 fprintf(fp
, "212 %s\n", &hfp
->hf_dat
[0]);
844 fprintf(fp
, "%s\n", &hfp
->hf_dat
[hfp
->hf_nl
+1]);
848 if(putc('\n', fp
) == EOF
)
865 if(fprintf(fp
, "505 read-only: %s\n", cp
) < 0)
869 if(fprintf(fp
, "501 %s\n", cp
) < 0)
875 a_dmsg__attach(FILE *fp
, struct n_dig_msg_ctx
*dmcp
, char const *cmda
[3]){
877 struct attachment
*ap
;
884 if((cp
= cmda
[0]) == NULL
){
885 cp
= n_empty
; /* xxx not NULL anyway */
889 if(is_asccaseprefix(cp
, "attribute")){
890 if(cmda
[1] == NULL
|| cmda
[2] != NULL
)
893 if((ap
= n_attachment_find(hp
->h_attach
, cmda
[1], NULL
)) != NULL
){
895 fprintf(fp
, "212 %s\n", cmda
[1]);
897 if(fprintf(fp
, "message-number %d\n\n", ap
->a_msgno
) < 0)
901 "creation-name %s\nopen-path %s\nfilename %s\n",
902 ap
->a_path_user
, ap
->a_path
, ap
->a_name
);
903 if(ap
->a_content_description
!= NULL
)
904 fprintf(fp
, "content-description %s\n",
905 ap
->a_content_description
);
906 if(ap
->a_content_id
!= NULL
)
907 fprintf(fp
, "content-id %s\n",
908 ap
->a_content_id
->n_name
);
909 if(ap
->a_content_type
!= NULL
)
910 fprintf(fp
, "content-type %s\n", ap
->a_content_type
);
911 if(ap
->a_content_disposition
!= NULL
)
912 fprintf(fp
, "content-disposition %s\n",
913 ap
->a_content_disposition
);
914 if(putc('\n', fp
) == EOF
)
918 if(fputs("501\n", fp
) == EOF
)
921 }else if(is_asccaseprefix(cp
, "attribute-at")){
924 if(cmda
[1] == NULL
|| cmda
[2] != NULL
)
927 if((n_idec_uiz_cp(&i
, cmda
[1], 0, NULL
928 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
929 ) != n_IDEC_STATE_CONSUMED
|| i
== 0){
930 if(fprintf(fp
, "505 invalid position: %s\n", cmda
[1]) < 0)
933 for(ap
= hp
->h_attach
; ap
!= NULL
&& --i
!= 0; ap
= ap
->a_flink
)
938 if(fputs("501\n", fp
) == EOF
)
942 }else if(is_asccaseprefix(cp
, "attribute-set")){
943 if(cmda
[1] == NULL
|| cmda
[2] == NULL
)
945 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
948 if((ap
= n_attachment_find(hp
->h_attach
, cmda
[1], NULL
)) != NULL
){
951 if(fprintf(fp
, "505 RFC822 message attachment: %s\n",
958 while((c
= *cp
) != '\0' && !blankchar(c
))
960 keyw
= savestrbuf(cmda
[2], PTR2SIZE(cp
- cmda
[2]));
962 for(; (c
= *++cp
) != '\0' && blankchar(c
);)
967 /* Strip [\r\n] which would render a parameter invalid XXX
968 * XXX all controls? */
969 cp
= xp
= savestr(cp
);
970 for(; (c
= *xp
) != '\0'; ++xp
)
971 if(c
== '\n' || c
== '\r')
977 if(!asccasecmp(keyw
, "filename"))
978 ap
->a_name
= (c
== '\0') ? ap
->a_path_bname
: cp
;
979 else if(!asccasecmp(keyw
, "content-description"))
980 ap
->a_content_description
= (c
== '\0') ? NULL
: cp
;
981 else if(!asccasecmp(keyw
, "content-id")){
982 ap
->a_content_id
= NULL
;
987 np
= checkaddrs(lextract(cp
, GREF
),
988 /*EACM_STRICT | TODO '/' valid!! */ EACM_NOLOG
|
990 if(np
!= NULL
&& np
->n_flink
== NULL
)
991 ap
->a_content_id
= np
;
995 }else if(!asccasecmp(keyw
, "content-type"))
996 ap
->a_content_type
= (c
== '\0') ? NULL
: cp
;
997 else if(!asccasecmp(keyw
, "content-disposition"))
998 ap
->a_content_disposition
= (c
== '\0') ? NULL
: cp
;
1005 for(i
= 0; ap
!= NULL
; ++i
, ap
= ap
->a_blink
)
1007 if(fprintf(fp
, "210 %" PRIuZ
"\n", i
) < 0)
1010 if(fputs("505\n", fp
) == EOF
)
1015 if(fputs("501\n", fp
) == EOF
)
1018 }else if(is_asccaseprefix(cp
, "attribute-set-at")){
1021 if(cmda
[1] == NULL
|| cmda
[2] == NULL
)
1023 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
1026 if((n_idec_uiz_cp(&i
, cmda
[1], 0, NULL
1027 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1028 ) != n_IDEC_STATE_CONSUMED
|| i
== 0){
1029 if(fprintf(fp
, "505 invalid position: %s\n", cmda
[1]) < 0)
1032 for(ap
= hp
->h_attach
; ap
!= NULL
&& --i
!= 0; ap
= ap
->a_flink
)
1037 if(fputs("501\n", fp
) == EOF
)
1041 }else if(is_asccaseprefix(cmda
[0], "insert")){
1042 enum n_attach_error aerr
;
1044 if(cmda
[1] == NULL
|| cmda
[2] != NULL
)
1046 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
1049 hp
->h_attach
= n_attachment_append(hp
->h_attach
, cmda
[1], &aerr
, &ap
);
1051 case n_ATTACH_ERR_FILE_OPEN
: cp
= "505\n"; goto jatt_ins
;
1052 case n_ATTACH_ERR_ICONV_FAILED
: cp
= "506\n"; goto jatt_ins
;
1053 case n_ATTACH_ERR_ICONV_NAVAIL
:
1054 case n_ATTACH_ERR_OTHER
:
1058 if(fprintf(fp
, "%s %s\n", cp
, cmda
[1]) < 0)
1061 case n_ATTACH_ERR_NONE
:{
1064 for(i
= 0; ap
!= NULL
; ++i
, ap
= ap
->a_blink
)
1066 if(fprintf(fp
, "210 %" PRIuZ
"\n", i
) < 0)
1071 }else if(is_asccaseprefix(cp
, "list")){
1076 if((ap
= hp
->h_attach
) != NULL
){
1079 fprintf(fp
, "%s\n", ap
->a_path_user
);
1080 while((ap
= ap
->a_flink
) != NULL
);
1081 if(putc('\n', fp
) == EOF
)
1084 if(fputs("501\n", fp
) == EOF
)
1087 }else if(is_asccaseprefix(cmda
[0], "remove")){
1088 if(cmda
[1] == NULL
|| cmda
[2] != NULL
)
1090 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
1093 if((ap
= n_attachment_find(hp
->h_attach
, cmda
[1], &status
)) != NULL
){
1094 if(status
== TRUM1
){
1095 if(fputs("506\n", fp
) == EOF
)
1098 hp
->h_attach
= n_attachment_remove(hp
->h_attach
, ap
);
1099 if(fprintf(fp
, "210 %s\n", cmda
[1]) < 0)
1103 if(fputs("501\n", fp
) == EOF
)
1106 }else if(is_asccaseprefix(cp
, "remove-at")){
1109 if(cmda
[1] == NULL
|| cmda
[2] != NULL
)
1111 if(dmcp
->dmc_flags
& n_DIG_MSG_RDONLY
)
1114 if((n_idec_uiz_cp(&i
, cmda
[1], 0, NULL
1115 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1116 ) != n_IDEC_STATE_CONSUMED
|| i
== 0){
1117 if(fprintf(fp
, "505 invalid position: %s\n", cmda
[1]) < 0)
1120 for(ap
= hp
->h_attach
; ap
!= NULL
&& --i
!= 0; ap
= ap
->a_flink
)
1123 hp
->h_attach
= n_attachment_remove(hp
->h_attach
, ap
);
1124 if(fprintf(fp
, "210 %s\n", cmda
[1]) < 0)
1127 if(fputs("501\n", fp
) == EOF
)
1136 return (cp
!= NULL
);
1138 (void)fputs("500\n", fp
);
1142 if(fprintf(fp
, "505 read-only: %s\n", cp
) < 0)
1148 n_dig_msg_on_mailbox_close(struct mailbox
*mbp
){
1149 struct n_dig_msg_ctx
*dmcp
;
1152 while((dmcp
= mbp
->mb_digmsg
) != NULL
){
1153 mbp
->mb_digmsg
= dmcp
->dmc_next
;
1154 if(dmcp
->dmc_flags
& n_DIG_MSG_FCLOSE
)
1155 fclose(dmcp
->dmc_fp
);
1156 if(dmcp
->dmc_flags
& n_DIG_MSG_OWN_MEMPOOL
){
1157 n_memory_pool_push(dmcp
->dmc_mempool
, FAL0
);
1158 n_memory_pool_pop(dmcp
->dmc_mempool
, TRU1
);
1166 n_dig_msg_circumflex(struct n_dig_msg_ctx
*dmcp
, FILE *fp
, char const *cmd
){
1168 char const *cp
, *cmd_top
;
1172 while(blankchar(*cp
))
1175 for(cmd_top
= cp
; *cp
!= '\0'; cmd_top
= cp
++)
1179 rv
= a_dmsg_cmd(fp
, dmcp
, cmd
, PTR2SIZE(cmd_top
- cmd
), cp
);
1186 char const *cp
, *emsg
;
1187 struct n_dig_msg_ctx
*dmcp
;
1188 struct n_cmd_arg
*cap
;
1189 struct n_cmd_arg_ctx
*cacp
;
1192 n_pstate_err_no
= n_ERR_NONE
;
1194 cap
= cacp
->cac_arg
;
1196 if(is_asccaseprefix(cp
= cap
->ca_arg
.ca_str
.s
, "create")){
1197 if(cacp
->cac_no
< 2 || cacp
->cac_no
> 3)
1201 /* Request to use STDOUT? */
1202 if(cacp
->cac_no
== 3){
1203 cp
= cap
->ca_next
->ca_arg
.ca_str
.s
;
1204 if(*cp
!= '-' || cp
[1] != '\0'){
1205 emsg
= N_("`digmsg': create: invalid I/O channel: %s\n");
1210 /* First of all, our context object */
1211 switch(a_dmsg_find(cp
= cap
->ca_arg
.ca_str
.s
, &dmcp
, TRU1
)){
1213 emsg
= N_("`digmsg': create: message number invalid: %s\n");
1216 emsg
= N_("`digmsg': create: message object already exists: %s\n");
1222 if(dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
)
1223 dmcp
->dmc_flags
= n_DIG_MSG_COMPOSE
| n_DIG_MSG_COMPOSE_DIGGED
;
1227 if((fp
= setinput(&mb
, dmcp
->dmc_mp
, NEED_HEADER
)) == NULL
){
1228 /* XXX Should have paniced before.. */
1230 emsg
= N_("`digmsg': create: mailbox I/O error for message: %s\n");
1234 n_memory_pool_push(dmcp
->dmc_mempool
, TRU1
);
1235 /* XXX n_header_extract error!! */
1236 n_header_extract((n_HEADER_EXTRACT_FULL
|
1237 n_HEADER_EXTRACT_PREFILL_RECEIVERS
|
1238 n_HEADER_EXTRACT_IGNORE_FROM_
), fp
, dmcp
->dmc_hp
, NULL
);
1239 n_memory_pool_pop(dmcp
->dmc_mempool
, FAL0
);
1242 if(cacp
->cac_no
== 3)
1243 dmcp
->dmc_fp
= n_stdout
;
1244 /* For compose mode simply use OF_REGISTER, the number of dangling
1245 * deleted files with open descriptors until next close_all_files()
1246 * should be very small; if this paradigm is changed
1247 * n_DIG_MSG_COMPOSE_GUT() needs to be adjusted */
1248 else if((dmcp
->dmc_fp
= Ftmp(NULL
, "digmsg", (OF_RDWR
| OF_UNLINK
|
1249 (dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
? OF_REGISTER
: 0)))
1251 dmcp
->dmc_flags
|= n_DIG_MSG_HAVE_FP
|
1252 (dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
? 0 : n_DIG_MSG_FCLOSE
);
1254 n_err(_("`digmsg': create: cannot create temporary file: %s\n"),
1255 n_err_to_doc(n_pstate_err_no
= n_err_no
));
1260 if(!(dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
)){
1261 dmcp
->dmc_last
= NULL
;
1262 if((dmcp
->dmc_next
= mb
.mb_digmsg
) != NULL
)
1263 dmcp
->dmc_next
->dmc_last
= dmcp
;
1264 mb
.mb_digmsg
= dmcp
;
1266 }else if(is_asccaseprefix(cp
, "remove")){
1267 if(cacp
->cac_no
!= 2)
1271 switch(a_dmsg_find(cp
= cap
->ca_arg
.ca_str
.s
, &dmcp
, FAL0
)){
1273 emsg
= N_("`digmsg': remove: message number invalid: %s\n");
1276 if(!(dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
) ||
1277 (dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE_DIGGED
))
1281 emsg
= N_("`digmsg': remove: no such message object: %s\n");
1285 if(!(dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
)){
1286 if(dmcp
->dmc_last
!= NULL
)
1287 dmcp
->dmc_last
->dmc_next
= dmcp
->dmc_next
;
1289 assert(dmcp
== mb
.mb_digmsg
);
1290 mb
.mb_digmsg
= dmcp
->dmc_next
;
1292 if(dmcp
->dmc_next
!= NULL
)
1293 dmcp
->dmc_next
->dmc_last
= dmcp
->dmc_last
;
1296 if(dmcp
->dmc_flags
& n_DIG_MSG_FCLOSE
)
1297 fclose(dmcp
->dmc_fp
);
1299 if(dmcp
->dmc_flags
& n_DIG_MSG_OWN_MEMPOOL
){
1300 n_memory_pool_push(dmcp
->dmc_mempool
, FAL0
);
1301 n_memory_pool_pop(dmcp
->dmc_mempool
, TRU1
);
1304 if(dmcp
->dmc_flags
& n_DIG_MSG_COMPOSE
)
1305 dmcp
->dmc_flags
= n_DIG_MSG_COMPOSE
;
1309 switch(a_dmsg_find(cp
, &dmcp
, FAL0
)){
1311 emsg
= N_("`digmsg': message number invalid: %s\n");
1314 emsg
= N_("`digmsg': no such message object: %s\n");
1321 if(dmcp
->dmc_flags
& n_DIG_MSG_HAVE_FP
){
1322 rewind(dmcp
->dmc_fp
);
1323 ftruncate(fileno(dmcp
->dmc_fp
), 0);
1326 n_memory_pool_push(dmcp
->dmc_mempool
, FAL0
);
1328 struct str cmds_b
, *cmdsp
;
1331 if(cap
== NULL
){ /* XXX cmd_arg_parse is stupid */
1333 cmdsp
->s
= n_UNCONST(cp
);
1336 cmdsp
= &cap
->ca_arg
.ca_str
;
1337 if((cap
= cap
->ca_next
) != NULL
)
1338 cp
= cap
->ca_arg
.ca_str
.s
;
1340 if(!a_dmsg_cmd(dmcp
->dmc_fp
, dmcp
, cmdsp
->s
, cmdsp
->l
, cp
))
1343 n_memory_pool_pop(dmcp
->dmc_mempool
, FAL0
);
1345 if(dmcp
->dmc_flags
& n_DIG_MSG_HAVE_FP
){
1346 rewind(dmcp
->dmc_fp
);
1347 n_digmsg_read_overlay
= dmcp
;
1353 return (vp
== NULL
);
1356 n_err(_("Synopsis: digmsg: <command> <-|msgno> [<:argument:>]\n"));
1359 n_err(V_(emsg
), n_shexp_quote_cp(cp
, FAL0
));
1361 n_pstate_err_no
= n_ERR_INVAL
;