* Remove unnecesary escape character in question to user when creating
[alpine.git] / pith / mailview.c
blobad3efd6d442053c31801238853832b803516a7fa
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2017 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 mailview.c
22 Implements message data gathering and formatting
24 ====*/
27 #include "headers.h"
28 #include "../pith/mailview.h"
29 #include "../pith/conf.h"
30 #include "../pith/msgno.h"
31 #include "../pith/editorial.h"
32 #include "../pith/mimedesc.h"
33 #include "../pith/margin.h"
34 #include "../pith/color.h"
35 #include "../pith/strlst.h"
36 #include "../pith/charset.h"
37 #include "../pith/status.h"
38 #include "../pith/maillist.h"
39 #include "../pith/mailcmd.h"
40 #include "../pith/mailindx.h"
41 #include "../pith/imap.h"
42 #include "../pith/detach.h"
43 #include "../pith/text.h"
44 #include "../pith/url.h"
45 #include "../pith/rfc2231.h"
46 #include "../pith/list.h"
47 #include "../pith/stream.h"
48 #include "../pith/send.h"
49 #include "../pith/filter.h"
50 #include "../pith/string.h"
51 #include "../pith/ablookup.h"
52 #include "../pith/escapes.h"
53 #include "../pith/keyword.h"
54 #include "../pith/smime.h"
57 #define FBUF_LEN (50)
59 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
63 * This is a list of header fields that are represented canonically
64 * by the c-client's ENVELOPE structure. The list is used by the
65 * two functions below to decide if a given field is included in this
66 * set.
68 static struct envelope_s {
69 char *name;
70 long val;
71 } envelope_hdrs[] = {
72 {"from", FE_FROM},
73 {"sender", FE_SENDER},
74 {"date", FE_DATE},
75 {"to", FE_TO},
76 {"cc", FE_CC},
77 {"bcc", FE_BCC},
78 {"newsgroups", FE_NEWSGROUPS},
79 {"subject", FE_SUBJECT},
80 {"message-id", FE_MESSAGEID},
81 {"reply-to", FE_REPLYTO},
82 {"followup-to", FE_FOLLOWUPTO},
83 {"in-reply-to", FE_INREPLYTO},
84 /* {"return-path", FE_RETURNPATH}, not usually filled in */
85 {"references", FE_REFERENCES},
86 {NULL, 0}
91 * Hook for optional display of rfc2369 content
93 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
99 * Internal prototypes
101 int format_blip_seen(long);
102 int is_an_env_hdr(char *);
103 int is_an_addr_hdr(char *);
104 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
105 fmt_env_t, gf_io_t, char *, char *, int);
106 int delineate_this_header(char *, char *, char **, char **);
107 char *url_embed(int);
108 int color_headers(long, char *, LT_INS_S **, void *);
109 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
110 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
111 int url_bogus_imap(char **, char *, char *);
112 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
113 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
114 gf_io_t, long, char *, int);
115 int any_hdr_color(char *);
116 void format_addr_string(MAILSTREAM *, long, char *, char *,
117 ADDRESS *, int, char *, gf_io_t);
118 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
119 void format_newsgroup_string(char *, char *, int, gf_io_t);
120 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
121 int format_env_puts(char *, gf_io_t);
122 int find_field(char **, char *, size_t);
123 int embed_color(COLOR_PAIR *, gf_io_t);
124 COLOR_PAIR *get_cur_embedded_color(void);
125 void clear_cur_embedded_color(void);
130 /*----------------------------------------------------------------------
131 Format a message message for viewing
133 Args: msgno -- The number of the message to view
134 env -- pointer to the message's envelope
135 body -- pointer to the message's body
136 handlesp -- address of pointer to the message's handles
137 flgs -- possible flags listed in pith/mailview.h with
138 prefix FM_
139 pc -- write to this function
141 Result: Returns true if no problems encountered, else false.
143 First the envelope is formatted; next a list of all attachments is
144 formatted if there is more than one. Then all the body parts are
145 formatted, fetching them as needed. This includes headers of included
146 message. Richtext is also formatted. An entry is made in the text for
147 parts that are not displayed or can't be displayed.
149 ----*/
151 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
152 HANDLE_S **handlesp, int flgs, gf_io_t pc)
154 char *decode_err = NULL;
155 HEADER_S h;
156 int width;
158 clear_cur_embedded_color();
160 if(!(flgs & FM_DISPLAY))
161 flgs |= FM_NOINDENT;
163 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
165 /*---- format and copy envelope ----*/
166 if(!(flgs & FM_NOEDITORIAL)){
167 if(ps_global->full_header == 1)
168 /* TRANSLATORS: User is viewing a message and all the quoted text is
169 being shown. */
170 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
171 else if(ps_global->full_header == 2)
172 q_status_message(SM_INFO, 0, 3,
173 /* TRANSLATORS: User is viewing a message and all of
174 the header text is being shown. */
175 _("Full header mode ON. All header text being included"));
178 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
179 switch(format_header(ps_global->mail_stream, msgno, NULL,
180 env, &h, NULL, handlesp, flgs, NULL, pc)){
182 case -1 : /* write error */
183 goto write_error;
185 case 1 : /* fetch error */
186 if(!(gf_puts("[ Error fetching header ]", pc)
187 && !gf_puts(NEWLINE, pc)))
188 goto write_error;
190 break;
193 if(!(body == NULL
194 || (ps_global->full_header == 2
195 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))))
196 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
198 /* write delimiter and body */
199 if(gf_puts(NEWLINE, pc)
200 && (decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
201 return(1);
204 write_error:
206 if(!(flgs & FM_DISPLAY))
207 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
208 decode_err ? decode_err : error_description(errno));
210 return(0);
214 char *
215 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
217 int filt_only_c0 = 0, wrapflags, error_found = 0;
218 int is_in_sig = OUT_SIG_BLOCK;
219 char *charset, *decode_err = NULL, *tmp1, *description;
220 ATTACH_S *a;
221 URL_HILITE_S uh;
222 gf_io_t gc;
224 if(body == NULL
225 || (ps_global->full_header == 2
226 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
228 /*--- Server is not an IMAP2bis, It can't parse MIME
229 so we just show the text here. Hopefully the
230 message isn't a MIME message
231 ---*/
232 void *text2;
234 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
235 msgno, NULL, NULL, NIL)) != NULL){
237 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
238 return("Write Error");
240 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
241 gf_filter_init();
244 * We need to translate the message
245 * into UTF-8, but that's trouble in the full header case
246 * because we don't know what to translate from. We'll just
247 * take a guess by looking for the first text part and
248 * using its charset.
250 if(body && body->type == TYPETEXT)
251 charset = parameter_val(body->parameter, "charset");
252 else if(body && body->type == TYPEMULTIPART && body->nested.part
253 && body->nested.part->body.type == TYPETEXT)
254 charset = parameter_val(body->nested.part->body.parameter, "charset");
255 else
256 charset = ps_global->display_charmap;
258 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
259 /* transliterate message text to UTF-8 */
260 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
263 /* link in filters, similar to what is done in decode_text() */
264 if(!ps_global->pass_ctrl_chars){
265 gf_link_filter(gf_escape_filter, NULL);
266 filt_only_c0 = 1;
267 gf_link_filter(gf_control_filter,
268 gf_control_filter_opt(&filt_only_c0));
271 gf_link_filter(gf_tag_filter, NULL);
273 if((F_ON(F_VIEW_SEL_URL, ps_global)
274 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
275 || F_ON(F_SCAN_ADDR, ps_global))
276 && handlesp){
277 gf_link_filter(gf_line_test,
278 gf_line_test_opt(url_hilite,
279 gf_url_hilite_opt(&uh,handlesp,0)));
282 if((flgs & FM_DISPLAY)
283 && !(flgs & FM_NOCOLOR)
284 && pico_usingcolor()
285 && ps_global->VAR_SIGNATURE_FORE_COLOR
286 && ps_global->VAR_SIGNATURE_BACK_COLOR){
287 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
290 if((flgs & FM_DISPLAY)
291 && !(flgs & FM_NOCOLOR)
292 && pico_usingcolor()
293 && ps_global->VAR_QUOTE1_FORE_COLOR
294 && ps_global->VAR_QUOTE1_BACK_COLOR){
295 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
298 if(!(flgs & FM_NOWRAP)){
299 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
300 if(flgs & FM_DISPLAY
301 && !(flgs & FM_NOCOLOR)
302 && pico_usingcolor())
303 wrapflags |= GFW_USECOLOR;
304 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
305 (flgs & FM_NOINDENT)
306 ? NULL : format_view_margin(),
308 wrapflags));
311 gf_link_filter(gf_nvtnl_local, NULL);
312 if((decode_err = gf_pipe(gc, pc)) != NULL){
313 /* TRANSLATORS: There was an error putting together a message for
314 viewing. The arg is the description of the error. */
315 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
316 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
317 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
318 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
319 && gf_puts(NEWLINE, pc))
320 decode_err = NULL;
321 else
322 return(decode_err);
326 if(!text2){
327 if(!gf_puts(NEWLINE, pc)
328 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
329 || !gf_puts(NEWLINE, pc)
330 || !gf_puts(NEWLINE, pc))
331 return("Write Error");
334 else{
335 int show_parts = 0;
337 /*======== Now loop through formatting all the parts =======*/
338 for(a = ps_global->atmts; a->description != NULL; a++) {
340 if(a->body->type == TYPEMULTIPART){
341 #ifdef SMIME
342 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
343 if(a->description){
344 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
345 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
346 return("Write Error");
349 #endif /* SMIME */
350 continue;
353 if(!a->shown) {
354 if(a->suppress_editorial)
355 continue;
357 if(!(flgs & FM_NOEDITORIAL)
358 && (!gf_puts(NEWLINE, pc)
359 || (decode_err = part_desc(a->number, a->body,
360 (flgs & FM_DISPLAY)
361 ? (a->can_display != MCD_NONE)
362 ? 1 : 2
363 : 3, width, flgs, pc))))
364 return("Write Error");
366 continue;
369 switch(a->body->type){
371 case TYPETEXT:
373 * If a message is multipart *and* the first part of it
374 * is text *and that text is empty, there is a good chance that
375 * there was actually something there that c-client was
376 * unable to parse. Here we report the empty message body
377 * and insert the raw RFC822.TEXT (if full-headers are
378 * on).
380 if(body->type == TYPEMULTIPART
381 && a == ps_global->atmts
382 && a->body->size.bytes == 0
383 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
384 char *err = NULL;
386 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
387 "Empty or malformed message%s.",
388 ps_global->full_header == 2
389 ? ". Displaying raw text"
390 : ". Use \"H\" to see raw text");
391 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
393 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
394 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
395 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
396 return("Write Error");
398 if(ps_global->full_header == 2
399 && (err = detach_raw(ps_global->mail_stream, msgno,
400 a->number, pc, flgs))){
401 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
402 "%s%s [ Formatting error: %s ]%s%s",
403 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
404 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
405 if(!gf_puts(tmp_20k_buf, pc))
406 return("Write Error");
409 break;
413 * Don't write our delimiter if this text part is
414 * the first part of a message/rfc822 segment...
416 if(show_parts && a != ps_global->atmts
417 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
418 #ifdef SMIME
419 || (a[-1].body->type == TYPEMULTIPART
420 && a[-1].body->subtype
421 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
422 && &a[-1] != ps_global->atmts
423 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
424 #endif /* SMIME */
426 && !(flgs & FM_NOEDITORIAL)){
427 tmp1 = a->body->description ? a->body->description
428 : "Attached Text";
429 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
430 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
432 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
433 description);
434 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
435 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
436 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
437 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
438 return("Write Error");
441 error_found += decode_text(a, msgno, pc, handlesp,
442 (flgs & FM_DISPLAY) ? InLine : QStatus,
443 flgs);
444 break;
446 case TYPEMESSAGE:
447 tmp1 = a->body->description ? a->body->description
448 : (strucmp(a->body->subtype, "delivery-status") == 0)
449 ? "Delivery Status"
450 : "Included Message";
451 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
452 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
454 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
455 description);
456 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
458 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
459 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
460 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
461 return("Write Error");
463 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
464 /* imapenvonly, we may not have all the headers we need */
465 if(a->body->nested.msg->env->imapenvonly)
466 mail_fetch_header(ps_global->mail_stream, msgno,
467 a->number, NULL, NULL, FT_PEEK);
468 switch(format_header(ps_global->mail_stream, msgno, a->number,
469 a->body->nested.msg->env, hp,
470 NULL, handlesp, flgs, NULL, pc)){
471 case -1 : /* write error */
472 return("Write Error");
474 case 1 : /* fetch error */
475 if(!(gf_puts("[ Error fetching header ]", pc)
476 && !gf_puts(NEWLINE, pc)))
477 return("Write Error");
479 break;
482 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
483 int *margin, avail, m1, m2;
485 avail = width;
486 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
488 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
489 avail -= m1;
491 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
492 avail -= m2;
494 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
495 || !gf_puts(NEWLINE, pc)
496 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
497 return("Write Error");
499 else
500 error_found += decode_text(a, msgno, pc, handlesp,
501 (flgs&FM_DISPLAY) ? InLine : QStatus,
502 flgs);
504 if(!gf_puts(NEWLINE, pc))
505 return("Write Error");
507 break;
509 default:
510 if((decode_err = part_desc(a->number, a->body,
511 (flgs & FM_DISPLAY) ? 1 : 3,
512 width, flgs, pc)) != NULL)
513 return("Write Error");
516 show_parts++;
519 if(!(!error_found
520 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
521 && format_blip_seen(msgno)))
522 return("Cannot format body.");
525 return(NULL);
530 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
532 ATTACH_S *a;
534 if(flgs & FM_NEW_MESS) {
535 zero_atmts(ps_global->atmts);
536 describe_mime(body, "", 1, 1, 0, flgs);
539 /*----- First do the list of parts/attachments if needed ----*/
540 if((flgs & FM_DISPLAY)
541 && (ps_global->atmts[1].description
542 || (ps_global->atmts[0].body
543 && ps_global->atmts[0].body->type != TYPETEXT))){
544 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
545 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
546 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
547 int sizewid, descwid, dashwid, partwid, padwid;
548 COLOR_PAIR *hdrcolor = NULL;
550 if((flgs & FM_DISPLAY)
551 && !(flgs & FM_NOCOLOR)
552 && pico_usingcolor()
553 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
554 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
555 && ps_global->VAR_NORM_FORE_COLOR
556 && ps_global->VAR_NORM_BACK_COLOR
557 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
558 ps_global->VAR_NORM_FORE_COLOR)
559 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
560 ps_global->VAR_NORM_BACK_COLOR))){
562 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
563 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
564 if(!pico_is_good_colorpair(hdrcolor))
565 free_color_pair(&hdrcolor);
569 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
572 * Attachment list header
575 avail = width;
577 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
578 avail -= m1;
580 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
581 avail -= m2;
583 hwid = MAX(avail, 0);
585 i = utf8_width(_("Parts/Attachments:"));
586 partwid = MIN(i, hwid);
587 padwid = hdrcolor ? (hwid-partwid) : 0;
589 if(m1 > 0){
590 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
591 if(!gf_puts(tmp, pc))
592 return(0);
595 utf8_snprintf(tmp, sizeof(tmp),
596 "%-*.*w%*.*s",
597 /* TRANSLATORS: A label */
598 partwid, partwid, _("Parts/Attachments:"),
599 padwid, padwid, "");
601 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
602 return(0);
605 /*----- Figure max display widths -----*/
606 for(a = ps_global->atmts; a->description != NULL; a++){
607 if((n = utf8_width(a->number)) > maxnumwid)
608 maxnumwid = n;
610 if((n = utf8_width(a->size)) > maxsizewid)
611 maxsizewid = n;
615 * ----- adjust max lengths for nice display -----
617 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
621 avail = width - m1 - m2;
623 s1 = MAX(MIN(1, avail), 0);
624 avail -= s1;
626 dwid = MAX(MIN(1, avail), 0);
627 avail -= dwid;
629 s2 = MAX(MIN(1, avail), 0);
630 avail -= s2;
632 maxnumwid = MIN(maxnumwid, width/3);
633 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
634 avail -= maxnumwid;
636 s3 = MAX(MIN(1, avail), 0);
637 avail -= s3;
639 shownwid = MAX(MIN(5, avail), 0);
640 avail -= shownwid;
642 s4 = MAX(MIN(3, avail), 0);
643 avail -= s4;
645 sizewid = MAX(MIN(maxsizewid, avail), 0);
646 avail -= sizewid;
648 s5 = MAX(MIN(2, avail), 0);
649 avail -= s5;
651 descwid = MAX(0, avail);
653 /*----- Format the list of attachments -----*/
654 for(a = ps_global->atmts; a->description != NULL; a++){
655 COLOR_PAIR *lastc = NULL;
656 char numbuf[50];
657 int thisdescwid, padwid;
659 #ifdef SMIME
660 if(a->body->type == TYPEMULTIPART
661 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
662 continue;
663 #endif /* SMIME */
665 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
666 thisdescwid = MIN(i, descwid);
667 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
669 if(m1 > 0){
670 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
671 if(!gf_puts(tmp, pc))
672 return(0);
675 utf8_snprintf(tmp, sizeof(tmp),
676 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
677 s1, s1, "",
678 dwid, dwid,
679 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
680 s2, s2, "",
681 maxnumwid, maxnumwid,
682 a->number
683 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
684 : "",
685 s3, s3, "",
686 shownwid, shownwid,
687 a->shown ? "Shown" :
688 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
689 ? "OK " : "",
690 s4, s4, "",
691 sizewid, sizewid,
692 a->size ? a->size : "",
693 s5, s5, "",
694 thisdescwid, thisdescwid,
695 (descwid > 2 && a->description) ? a->description : "");
697 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
698 return(0);
700 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
701 char buf[16], color[64];
702 int l;
703 HANDLE_S *h;
705 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
706 if(!(*pc)(' '))
707 return(0);
709 h = new_handle(handlesp);
710 h->type = Attach;
711 h->h.attach = a;
713 snprintf(buf, sizeof(buf), "%d", h->key);
714 buf[sizeof(buf)-1] = '\0';
716 if(!(flgs & FM_NOCOLOR)
717 && handle_start_color(color, sizeof(color), &l, 1)){
718 lastc = get_cur_embedded_color();
719 if(!gf_nputs(color, (long) l, pc))
720 return(0);
722 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
723 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
724 return(0);
726 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
727 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
728 return(0);
730 else
731 tmpp = tmp;
733 if(!format_env_puts(tmpp, pc))
734 return(0);
736 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
737 if(lastc){
738 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
739 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
740 return(0);
743 if(!embed_color(lastc, pc))
744 return(0);
746 free_color_pair(&lastc);
748 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
749 return(0);
751 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
752 return(0);
755 if(padwid > 0){
756 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
757 if(!gf_puts(tmp, pc))
758 return(0);
761 if(!gf_puts(NEWLINE, pc))
762 return(0);
766 * Dashed line after list
769 if(hdrcolor){
770 avail = width - m1 - m2;
771 hwid = MAX(avail, 0);
773 dashwid = MAX(MIN(40, hwid-2), 0);
774 padwid = hwid - dashwid;
775 if(m1 > 0){
776 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
777 if(!gf_puts(tmp, pc))
778 return(0);
781 snprintf(tmp, sizeof(tmp),
782 "%s%*.*s",
783 repeat_char(dashwid, '-'),
784 padwid, padwid, "");
786 else{
787 avail = width - m1 -2;
789 dashwid = MAX(MIN(40, avail), 0);
790 avail -= dashwid;
792 snprintf(tmp, sizeof(tmp),
793 "%*.*s%s",
794 m1, m1, "",
795 repeat_char(dashwid, '-'));
798 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
799 return(0);
801 if(hdrcolor)
802 free_color_pair(&hdrcolor);
805 return(1);
811 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
812 * of body part fetches as we're formatting) for the
813 * given message isn't set (likely because there
814 * weren't any parts suitable for display), then make
815 * sure to set it here.
818 format_blip_seen(long int msgno)
820 MESSAGECACHE *mc;
822 if(msgno > 0L && ps_global->mail_stream
823 && msgno <= ps_global->mail_stream->nmsgs
824 && (mc = mail_elt(ps_global->mail_stream, msgno))
825 && !mc->seen
826 && !ps_global->mail_stream->rdonly)
827 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
829 return(1);
834 * is_an_env_hdr - is this name a header in the envelope structure?
836 * name - the header name to check
839 is_an_env_hdr(char *name)
841 register int i;
843 for(i = 0; envelope_hdrs[i].name; i++)
844 if(!strucmp(name, envelope_hdrs[i].name))
845 return(1);
847 return(0);
854 * is_an_addr_hdr - is this an address header?
856 * name - the header name to check
859 is_an_addr_hdr(char *fieldname)
861 char fbuf[FBUF_LEN+1];
862 char *colon, *fname;
863 static char *addr_headers[] = {
864 "from",
865 "reply-to",
866 "to",
867 "cc",
868 "bcc",
869 "return-path",
870 "sender",
871 "x-sender",
872 "x-x-sender",
873 "resent-from",
874 "resent-to",
875 "resent-cc",
876 NULL
879 /* so it is pointing to NULL */
880 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
882 if((colon = strindex(fieldname, ':')) != NULL){
883 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
884 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
885 fname = fbuf;
887 else
888 fname = fieldname;
890 if(fname && *fname){
891 for(p = addr_headers; *p; p++)
892 if(!strucmp(fname, *p))
893 break;
896 return((*p) ? 1 : 0);
901 * Format a single field from the envelope
903 void
904 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
905 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
907 register int i;
909 if(!fmt_env)
910 fmt_env = format_envelope;
912 for(i = 0; envelope_hdrs[i].name; i++)
913 if(!strucmp(field, envelope_hdrs[i].name)){
914 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
915 return;
921 * Look through header string beginning with "begin", for the next
922 * occurrence of header "field". Set "start" to that. Set "end" to point one
923 * position past all of the continuation lines that go with "field".
924 * That is, if "end" is converted to a null
925 * character then the string "start" will be the next occurence of header
926 * "field" including all of its continuation lines. Assume we
927 * have CRLF's as end of lines.
929 * If "field" is NULL, then we just leave "start" pointing to "begin" and
930 * make "end" the end of that header.
932 * Returns 1 if found, 0 if not.
935 delineate_this_header(char *field, char *begin, char **start, char **end)
937 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
938 char *p;
939 char *begin_srch;
941 if(field == NULL){
942 if(!begin || !*begin || isspace((unsigned char)*begin))
943 return 0;
944 else
945 *start = begin;
947 else{
948 strncpy(tmpfield, field, sizeof(tmpfield)-2);
949 tmpfield[sizeof(tmpfield)-2] = '\0';
950 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
951 tmpfield[sizeof(tmpfield)-1] = '\0';
954 * We require that start is at the beginning of a line, so
955 * either it equals begin (which we assume is the beginning of a
956 * line) or it is preceded by a CRLF.
958 begin_srch = begin;
959 *start = srchstr(begin_srch, tmpfield);
960 while(*start && *start != begin
961 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
962 begin_srch = *start + 1;
963 *start = srchstr(begin_srch, tmpfield);
966 if(!*start)
967 return 0;
970 for(p = *start; *p; p++){
971 if(ISRFCEOL(p)
972 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
974 * The final 015 in the test above is to test for the end
975 * of the headers.
977 *end = p+2;
978 break;
982 if(!*p)
983 *end = p;
985 return 1;
991 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
993 *len = 0;
995 if(pico_usingcolor()){
996 char *fg = NULL, *bg = NULL, *s;
997 char *basefg = NULL, *basebg = NULL;
999 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1000 : ps_global->VAR_NORM_FORE_COLOR;
1001 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1002 : ps_global->VAR_NORM_BACK_COLOR;
1004 if(ps_global->VAR_SLCTBL_FORE_COLOR
1005 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1006 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1008 if(ps_global->VAR_SLCTBL_BACK_COLOR
1009 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1010 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1012 if(bg || fg){
1013 COLOR_PAIR *tmp;
1016 * The blacks are just known good colors for
1017 * testing whether the other color is good.
1019 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1020 bg ? bg : colorx(COL_BLACK))) != NULL){
1021 if(pico_is_good_colorpair(tmp))
1022 for(s = color_embed(fg, bg);
1023 (*len) < buflen && (colorstring[*len] = *s);
1024 s++, (*len)++)
1027 free_color_pair(&tmp);
1031 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1032 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1033 *len += 2;
1037 colorstring[buflen-1] = '\0';
1039 return(*len != 0);
1044 handle_end_color(char *colorstring, size_t buflen, int *len)
1046 *len = 0;
1047 if(pico_usingcolor()){
1048 char *fg = NULL, *bg = NULL, *s;
1051 * We need to change the fg and bg colors back even if they
1052 * are the same as the Normal Colors so that color_a_quote
1053 * will have a chance to pick up those colors as the ones to
1054 * switch to. We don't do this before the handle above so that
1055 * the quote color will flow into the selectable item when
1056 * the selectable item color is partly the same as the
1057 * normal color. That is, suppose the normal color was black on
1058 * cyan and the selectable color was blue on cyan, only a fg color
1059 * change. We preserve the only-a-fg-color-change in a quote by
1060 * letting the quote background color flow into the selectable text.
1062 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1063 fg = ps_global->VAR_NORM_FORE_COLOR;
1065 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1066 bg = ps_global->VAR_NORM_BACK_COLOR;
1068 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1069 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1070 *len = 2;
1073 if(fg || bg)
1074 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1078 colorstring[buflen-1] = '\0';
1080 return(*len != 0);
1084 char *
1085 url_embed(int embed)
1087 static char buf[3] = {TAG_EMBED};
1088 buf[1] = embed;
1089 buf[2] = '\0';
1090 return(buf);
1095 * Paint the signature.
1098 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1100 struct variable *vars = ps_global->vars;
1101 int *in_sig_block;
1102 COLOR_PAIR *col = NULL;
1104 if(is_in_sig == NULL)
1105 return 0;
1107 in_sig_block = (int *) is_in_sig;
1109 if(!strcmp(line, SIGDASHES))
1110 *in_sig_block = START_SIG_BLOCK;
1111 else if(*line == '\0')
1113 * Suggested by Eduardo: allow for a blank line right after
1114 * the sigdashes.
1116 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1117 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1118 else
1119 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1120 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1122 if(*in_sig_block != OUT_SIG_BLOCK
1123 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1124 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1125 VAR_SIGNATURE_BACK_COLOR))){
1126 if(!pico_is_good_colorpair(col))
1127 free_color_pair(&col);
1130 if(col){
1131 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1133 ins = gf_line_test_new_ins(ins, line,
1134 color_embed(col->fg, col->bg),
1135 (2 * RGBLEN) + 4);
1137 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1138 fg[sizeof(fg)-1] = '\0';
1139 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1140 bg[sizeof(bg)-1] = '\0';
1143 * Loop watching colors, and override with
1144 * signature color whenever the normal foreground and background
1145 * colors are in force.
1148 for(p = line; *p; )
1149 if(*p++ == TAG_EMBED){
1151 switch(*p++){
1152 case TAG_HANDLE :
1153 p += *p + 1; /* skip handle key */
1154 break;
1156 case TAG_FGCOLOR :
1157 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1158 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1159 p += RGBLEN; /* advance past color value */
1161 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1162 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1163 ins = gf_line_test_new_ins(ins, p,
1164 color_embed(col->fg,NULL),
1165 RGBLEN + 2);
1166 break;
1168 case TAG_BGCOLOR :
1169 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1170 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1171 p += RGBLEN; /* advance past color value */
1173 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1174 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1175 ins = gf_line_test_new_ins(ins, p,
1176 color_embed(NULL,col->bg),
1177 RGBLEN + 2);
1179 break;
1181 default :
1182 break;
1186 ins = gf_line_test_new_ins(ins, line + strlen(line),
1187 color_embed(VAR_NORM_FORE_COLOR,
1188 VAR_NORM_BACK_COLOR),
1189 (2 * RGBLEN) + 4);
1190 free_color_pair(&col);
1193 return 0;
1198 * Line filter to add color to displayed headers.
1201 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1203 static char field[FBUF_LEN + 1];
1204 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1205 char *p, *q, *value, *beg;
1206 COLOR_PAIR *color;
1207 int in_quote = 0, in_comment = 0, did_color = 0;
1208 struct variable *vars = ps_global->vars;
1210 field[FBUF_LEN] = '\0';
1212 if(isspace((unsigned char)*line)) /* continuation line */
1213 value = line;
1214 else{
1215 if(!(value = strindex(line, ':')))
1216 return(0);
1218 memset(field, 0, sizeof(field));
1219 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1222 for(value++; isspace((unsigned char)*value); value++)
1225 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1226 fg[sizeof(fg)-1] = '\0';
1227 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1228 bg[sizeof(bg)-1] = '\0';
1231 * Split into two cases depending on whether this is a header which
1232 * contains addresses or not. We may color addresses separately.
1234 if(is_an_addr_hdr(field)){
1237 * If none of the patterns are for this header, don't bother parsing
1238 * and checking each address.
1240 if(!any_hdr_color(field))
1241 return(0);
1244 * First check for patternless patterns which color whole line.
1246 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1247 if(pico_is_good_colorpair(color)){
1248 ins = gf_line_test_new_ins(ins, value,
1249 color_embed(color->fg, color->bg),
1250 (2 * RGBLEN) + 4);
1251 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1252 fg[sizeof(fg)-1] = '\0';
1253 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1254 bg[sizeof(bg)-1] = '\0';
1255 did_color++;
1257 else
1258 free_color_pair(&color);
1262 * Then go through checking address by address.
1263 * Keep track of quotes and watch for color changes, and override
1264 * with most recent header color whenever the normal foreground
1265 * and background colors are in force.
1267 beg = p = value;
1268 while(*p){
1269 switch(*p){
1270 case '\\':
1271 /* skip next character */
1272 if(*(p+1) && (in_comment || in_quote))
1273 p += 2;
1274 else
1275 p++;
1277 break;
1279 case '"':
1280 if(!in_comment)
1281 in_quote = 1 - in_quote;
1283 p++;
1284 break;
1286 case '(':
1287 in_comment++;
1288 p++;
1289 break;
1291 case ')':
1292 if(in_comment > 0)
1293 in_comment--;
1295 p++;
1296 break;
1298 case ',':
1299 if(!(in_quote || in_comment)){
1300 /* we reached the end of this address */
1301 *p = '\0';
1302 if(color)
1303 free_color_pair(&color);
1305 if((color = hdr_color(field, beg,
1306 ps_global->hdr_colors)) != NULL){
1307 if(pico_is_good_colorpair(color)){
1308 did_color++;
1309 ins = gf_line_test_new_ins(ins, beg,
1310 color_embed(color->fg,
1311 color->bg),
1312 (2 * RGBLEN) + 4);
1313 *p = ',';
1314 for(q = p; q > beg &&
1315 isspace((unsigned char)*(q-1)); q--)
1318 ins = gf_line_test_new_ins(ins, q,
1319 color_embed(fg, bg),
1320 (2 * RGBLEN) + 4);
1322 else
1323 free_color_pair(&color);
1325 else
1326 *p = ',';
1328 for(p++; isspace((unsigned char)*p); p++)
1331 beg = p;
1333 else
1334 p++;
1336 break;
1338 case TAG_EMBED:
1339 switch(*(++p)){
1340 case TAG_HANDLE:
1341 p++;
1342 p += *p + 1; /* skip handle key */
1343 break;
1345 case TAG_FGCOLOR:
1346 p++;
1347 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1348 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1349 p += RGBLEN; /* advance past color value */
1351 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1352 ins = gf_line_test_new_ins(ins, p,
1353 color_embed(color->fg,NULL),
1354 RGBLEN + 2);
1355 break;
1357 case TAG_BGCOLOR:
1358 p++;
1359 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1360 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1361 p += RGBLEN; /* advance past color value */
1363 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1364 ins = gf_line_test_new_ins(ins, p,
1365 color_embed(NULL,color->bg),
1366 RGBLEN + 2);
1368 break;
1370 default:
1371 break;
1374 break;
1376 default:
1377 p++;
1378 break;
1382 for(q = beg; *q && isspace((unsigned char)*q); q++)
1385 if(*q && !(in_quote || in_comment)){
1386 /* we reached the end of this address */
1387 if(color)
1388 free_color_pair(&color);
1390 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1391 if(pico_is_good_colorpair(color)){
1392 did_color++;
1393 ins = gf_line_test_new_ins(ins, beg,
1394 color_embed(color->fg,
1395 color->bg),
1396 (2 * RGBLEN) + 4);
1397 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1400 ins = gf_line_test_new_ins(ins, q,
1401 color_embed(fg, bg),
1402 (2 * RGBLEN) + 4);
1404 else
1405 free_color_pair(&color);
1409 if(color)
1410 free_color_pair(&color);
1412 if(did_color)
1413 ins = gf_line_test_new_ins(ins, line + strlen(line),
1414 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1415 VAR_HEADER_GENERAL_BACK_COLOR),
1416 (2 * RGBLEN) + 4);
1418 else{
1420 color = hdr_color(field, value, ps_global->hdr_colors);
1422 if(color){
1423 if(pico_is_good_colorpair(color)){
1424 ins = gf_line_test_new_ins(ins, value,
1425 color_embed(color->fg, color->bg),
1426 (2 * RGBLEN) + 4);
1429 * Loop watching colors, and override with header
1430 * color whenever the normal foreground and background
1431 * colors are in force.
1433 p = value;
1434 while(*p)
1435 if(*p++ == TAG_EMBED){
1437 switch(*p++){
1438 case TAG_HANDLE:
1439 p += *p + 1; /* skip handle key */
1440 break;
1442 case TAG_FGCOLOR:
1443 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1444 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1445 p += RGBLEN; /* advance past color value */
1447 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1448 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1449 ins = gf_line_test_new_ins(ins, p,
1450 color_embed(color->fg,NULL),
1451 RGBLEN + 2);
1452 break;
1454 case TAG_BGCOLOR:
1455 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1456 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1457 p += RGBLEN; /* advance past color value */
1459 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1460 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1461 ins = gf_line_test_new_ins(ins, p,
1462 color_embed(NULL,color->bg),
1463 RGBLEN + 2);
1465 break;
1467 default:
1468 break;
1472 ins = gf_line_test_new_ins(ins, line + strlen(line),
1473 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1474 VAR_HEADER_GENERAL_BACK_COLOR),
1475 (2 * RGBLEN) + 4);
1478 free_color_pair(&color);
1482 return(0);
1487 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1489 register char *lp, *up = NULL, *urlp = NULL,
1490 *weburlp = NULL, *mailurlp = NULL;
1491 int n, n1, n2, n3, l;
1492 char buf[256], color[256];
1493 HANDLE_S *h;
1494 URL_HILITE_S *uh;
1496 for(lp = line; ; lp = up + n){
1497 /* scan for all of them so we can choose the first */
1498 if(F_ON(F_VIEW_SEL_URL,ps_global))
1499 urlp = rfc1738_scan(lp, &n1);
1500 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1501 weburlp = web_host_scan(lp, &n2);
1502 if(F_ON(F_SCAN_ADDR,ps_global))
1503 mailurlp = mail_addr_scan(lp, &n3);
1505 if(urlp || weburlp || mailurlp){
1506 up = urlp ? urlp :
1507 weburlp ? weburlp : mailurlp;
1508 if(up == urlp && weburlp && weburlp < up)
1509 up = weburlp;
1510 if(mailurlp && mailurlp < up)
1511 up = mailurlp;
1513 if(up == urlp){
1514 n = n1;
1515 weburlp = mailurlp = NULL;
1517 else if(up == weburlp){
1518 n = n2;
1519 mailurlp = NULL;
1521 else{
1522 n = n3;
1523 weburlp = NULL;
1526 else
1527 break;
1529 uh = (URL_HILITE_S *) local;
1531 h = new_handle(uh->handlesp);
1532 h->type = URL;
1533 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1534 snprintf(h->h.url.path, n+10, "%s%.*s",
1535 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1536 h->h.url.path[n+10-1] = '\0';
1538 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1539 ins = gf_line_test_new_ins(ins, up, color, l);
1540 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1541 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1543 buf[0] = TAG_EMBED;
1544 buf[1] = TAG_HANDLE;
1545 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1546 buf[sizeof(buf)-1] = '\0';
1547 buf[2] = strlen(&buf[3]);
1548 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1550 /* in case it was the current selection */
1551 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1553 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1554 ins = gf_line_test_new_ins(ins, up + n, color, l);
1555 else
1556 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1558 urlp = weburlp = mailurlp = NULL;
1561 return(0);
1566 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1568 static int check_for_urls = 0;
1569 register char *lp;
1571 if(isspace((unsigned char)*line)) /* continuation, check or not
1572 depending on last line */
1573 lp = line;
1574 else{
1575 check_for_urls = 0;
1576 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1577 FieldType ft;
1579 *lp = '\0';
1581 if(((ft = pine_header_standard(line)) == FreeText
1582 || ft == Subject
1583 || ft == TypeUnknown)
1584 && strucmp(line, "message-id")
1585 && strucmp(line, "newsgroups")
1586 && strucmp(line, "references")
1587 && strucmp(line, "in-reply-to")
1588 && strucmp(line, "received")
1589 && strucmp(line, "date")){
1590 check_for_urls = 1;
1593 *lp = ':';
1597 if(check_for_urls)
1598 (void) url_hilite(linenum, lp + 1, ins, local);
1600 return(0);
1605 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1607 char *p;
1608 int wid = 0;
1609 int total_wid;
1610 struct variable *vars = ps_global->vars;
1612 if(!line[0])
1613 return 0;
1615 total_wid = *((int *) local);
1617 /* calculate width of line */
1618 p = line;
1619 while(*p){
1621 switch(*p){
1622 case TAG_EMBED:
1623 p++;
1624 switch(*p){
1625 case TAG_HANDLE:
1626 p++;
1627 p += *p + 1; /* skip handle key */
1628 break;
1630 case TAG_FGCOLOR :
1631 case TAG_BGCOLOR :
1632 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
1633 break;
1635 case TAG_INVON:
1636 case TAG_INVOFF:
1637 case TAG_BOLDON:
1638 case TAG_BOLDOFF:
1639 case TAG_ULINEON:
1640 case TAG_ULINEOFF:
1641 p++;
1642 break;
1644 default: /* literal embed char */
1645 break;
1648 break;
1650 case TAB:
1651 p++;
1652 while(((++wid) & 0x07) != 0) /* add tab's spaces */
1655 break;
1657 default:
1658 wid += width_at_this_position((unsigned char *) p, strlen(p));
1659 p++;
1660 break;
1664 if(total_wid > wid){
1665 ins = gf_line_test_new_ins(ins, line + strlen(line),
1666 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1667 VAR_HEADER_GENERAL_BACK_COLOR),
1668 (2 * RGBLEN) + 4);
1669 ins = gf_line_test_new_ins(ins, line+strlen(line),
1670 repeat_char(total_wid-wid, ' '), total_wid-wid);
1671 ins = gf_line_test_new_ins(ins, line + strlen(line),
1672 color_embed(VAR_NORM_FORE_COLOR,
1673 VAR_NORM_BACK_COLOR),
1674 (2 * RGBLEN) + 4);
1677 return(0);
1682 #define UES_LEN 12
1683 #define UES_MAX 32
1685 url_external_specific_handler(char *url, int len)
1687 static char list[UES_LEN * UES_MAX];
1689 if(url){
1690 char *p;
1691 int i;
1693 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
1694 if(strlen(p) == len && !struncmp(p, url, len))
1695 return(1);
1697 else{ /* initialize! */
1698 char **l, *test, *cmd, *p, *p2;
1699 int i = 0, n;
1701 memset(list, 0, sizeof(list));
1702 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
1703 get_pair(*l, &test, &cmd, 1, 1);
1705 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
1706 *p2 = '\0';
1708 for(p += 8; *p && i < UES_MAX; p += n)
1709 if((p2 = strchr(p, ',')) != NULL){
1710 if((n = p2 - p) < UES_LEN){
1711 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
1712 i++;
1714 else
1715 dprint((1,
1716 "* * * HANLDER TOO LONG: %.*s\n", n,
1717 p ? p : "?"));
1719 n++;
1721 else{
1722 if(strlen(p) <= UES_LEN){
1723 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
1724 i++;
1727 break;
1731 if(test)
1732 fs_give((void **) &test);
1734 if(cmd)
1735 fs_give((void **) &cmd);
1739 return(0);
1744 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
1745 imapuid_t *uid, char **search, int silent)
1747 char *url, *scheme, *p, *cmd, *server = NULL,
1748 *user = NULL, *auth = NULL, *mailbox = NULL,
1749 *section = NULL;
1750 size_t l;
1751 int rv = URL_IMAP_ERROR;
1754 * Since we're planting nulls, operate on a temporary copy...
1756 scheme = silent ? NULL : "IMAP";
1757 url = cpystr(true_url + 7);
1759 /* Try to pick apart the "iserver" portion */
1760 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
1761 *cmd++ = '\0';
1763 else{
1764 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
1765 url ? url : "?"));
1766 cmd = &url[strlen(url)-1]; /* assume only iserver */
1769 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
1770 *p++ = '\0';
1771 server = rfc1738_str(p);
1773 /* only ";auth=*" supported (and also ";auth=anonymous") */
1774 if((p = srchstr(url, ";auth=")) != NULL){
1775 *p = '\0';
1776 auth = rfc1738_str(p + 6);
1779 if(*url)
1780 user = rfc1738_str(url);
1782 else
1783 server = rfc1738_str(url);
1785 if(!*server)
1786 return(url_bogus_imap(&url, scheme, "No server specified"));
1789 * "iserver" in hand, pick apart the "icommand"...
1791 p = NULL;
1792 if(!*cmd || (p = srchstr(cmd, ";type="))){
1793 char *criteria;
1796 * No "icommand" (all top-level folders) or "imailboxlist"...
1798 if(p){
1799 *p = '\0'; /* tie off criteria */
1800 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
1801 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
1802 rv |= URL_IMAP_IMBXLSTLSUB;
1803 else if(strucmp(p, "list"))
1804 return(url_bogus_imap(&url, scheme,
1805 "Invalid list type specified"));
1807 else{
1808 rv |= URL_IMAP_ISERVERONLY;
1809 criteria = "";
1812 /* build folder list from specified server/criteria/list-method */
1813 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
1814 *folder = (char *) fs_get((l+1) * sizeof(char));
1815 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
1816 user ? "user=\"" : "Anonymous",
1817 user ? user : "",
1818 user ? "\"" : "",
1819 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
1820 (*folder)[l] = '\0';
1821 rv |= URL_IMAP_IMAILBOXLIST;
1823 else{
1824 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
1825 *p = '\0'; /* tie off mailbox [uidvalidity] */
1826 if((section = srchstr(p += 6, "/;section=")) != NULL){
1827 *section = '\0'; /* tie off UID */
1828 section = rfc1738_str(section + 10);
1829 /* BUG: verify valid section spec ala rfc 2060 */
1830 dprint((2,
1831 "-- URL IMAP FOLDER: section not used: %s\n",
1832 section ? section : "?"));
1835 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
1836 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
1838 /* optional "uidvalidity"? */
1839 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
1840 *p = '\0';
1841 p += 13;
1842 if(!(*uid_val = rfc1738_num(&p)) || *p)
1843 return(url_bogus_imap(&url, scheme,
1844 "Invalid UIDVALIDITY"));
1847 mailbox = rfc1738_str(cmd);
1848 rv = URL_IMAP_IMESSAGEPART;
1850 else{ /* "imessagelist" */
1851 /* optional "uidvalidity"? */
1852 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
1853 *p = '\0';
1854 p += 13;
1855 if(!(*uid_val = rfc1738_num(&p)) || *p)
1856 return(url_bogus_imap(&url, scheme,
1857 "Invalid UIDVALIDITY"));
1860 /* optional "enc_search"? */
1861 if((p = strchr(cmd, '?')) != NULL){
1862 *p = '\0';
1863 if(search)
1864 *search = cpystr(rfc1738_str(p + 1));
1865 /* BUG: verify valid search spec ala rfc 2060 */
1868 mailbox = rfc1738_str(cmd);
1869 rv = URL_IMAP_IMESSAGELIST;
1872 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
1873 q_status_message(SM_ORDER, 3, 3,
1874 "Unsupported authentication method. Using standard login.");
1877 * At this point our structure should contain the
1878 * digested url. Now put it together for c-client...
1880 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
1881 + (user ? (strlen(user)+2) : 9);
1882 *folder = (char *) fs_get((l+1) * sizeof(char));
1883 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
1884 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
1885 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
1886 user ? user : "",
1887 user ? "\"" : "",
1888 mailbox);
1889 (*folder)[l] = '\0';
1892 fs_give((void **) &url);
1893 return(rv);
1898 url_bogus_imap(char **freeme, char *url, char *problem)
1900 fs_give((void **) freeme);
1901 (void) url_bogus(url, problem);
1902 return(URL_IMAP_ERROR);
1907 * url_bogus - report url syntax errors and such
1910 url_bogus(char *url, char *reason)
1912 dprint((2, "-- bogus url \"%s\": %s\n",
1913 url ? url : "<NULL URL>", reason ? reason : "?"));
1914 if(url)
1915 q_status_message3(SM_ORDER|SM_DING, 2, 3,
1916 "Malformed \"%.*s\" URL: %.200s",
1917 (void *) (strchr(url, ':') - url), url, reason);
1919 return(0);
1924 /*----------------------------------------------------------------------
1925 Format header text suitable for display
1927 Args: stream -- mail stream for various header text fetches
1928 msgno -- sequence number in stream of message we're interested in
1929 section -- which section of message
1930 env -- pointer to msg's envelope
1931 hdrs -- struct containing what's to get formatted
1932 prefix -- prefix to append to each output line
1933 handlesp -- address of pointer to the message's handles
1934 flags -- FM_ flags, see pith/mailview.h
1935 final_pc -- function to write header text with
1937 Result: 0 if all's well, -1 if write error, 1 if fetch error
1939 NOTE: Blank-line delimiter is NOT written here. Newlines are written
1940 in the local convention.
1942 ----*/
1943 #define FHT_OK 0
1944 #define FHT_WRTERR -1
1945 #define FHT_FTCHERR 1
1947 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1948 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
1949 fmt_env_t fmt_env, gf_io_t final_pc)
1951 int rv = FHT_OK;
1952 int nfields, i;
1953 char *h = NULL, **fields = NULL, **v, *q, *start,
1954 *finish, *current;
1955 STORE_S *tmp_store;
1956 URL_HILITE_S uh;
1957 gf_io_t tmp_pc, tmp_gc;
1958 struct variable *vars = ps_global->vars;
1960 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
1961 gf_set_so_writec(&tmp_pc, tmp_store);
1962 else
1963 return(FHT_WRTERR);
1965 if(!fmt_env)
1966 fmt_env = format_envelope;
1968 if(ps_global->full_header == 2){
1969 rv = format_raw_header(stream, msgno, section, tmp_pc);
1971 else{
1973 * First, calculate how big a fields array we need.
1976 /* Custom header viewing list specified */
1977 if(hdrs->type == HD_LIST){
1978 /* view all these headers */
1979 if(!hdrs->except){
1980 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
1981 if(!is_an_env_hdr(q))
1982 nfields++;
1984 /* view all except these headers */
1985 else{
1986 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
1987 nfields++;
1989 if(nfields > 1)
1990 nfields--; /* subtract one for ALL_EXCEPT field */
1993 else
1994 nfields = 6; /* default view */
1996 /* allocate pointer space */
1997 if(nfields){
1998 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
1999 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2002 if(hdrs->type == HD_LIST){
2003 /* view all these headers */
2004 if(!hdrs->except){
2005 /* put the non-envelope headers in fields */
2006 if(nfields)
2007 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2008 if(!is_an_env_hdr(q))
2009 fields[i++] = q;
2011 /* view all except these headers */
2012 else{
2013 /* put the list of headers not to view in fields */
2014 if(nfields)
2015 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2016 if(strucmp(ALL_EXCEPT, q))
2017 fields[i++] = q;
2020 v = hdrs->h.l;
2022 else{
2023 if(nfields){
2024 fields[i = 0] = "Resent-Date";
2025 fields[++i] = "Resent-From";
2026 fields[++i] = "Resent-To";
2027 fields[++i] = "Resent-cc";
2028 fields[++i] = "Resent-Subject";
2031 v = fields;
2034 /* custom view with exception list */
2035 if(hdrs->type == HD_LIST && hdrs->except){
2037 * Go through each header in h and print it.
2038 * First we check to see if it is an envelope header so we
2039 * can print our envelope version of it instead of the raw version.
2042 /* fetch all the other headers */
2043 if(nfields)
2044 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2046 for(current = h;
2047 h && delineate_this_header(NULL, current, &start, &finish);
2048 current = finish){
2049 char tmp[MAILTMPLEN+1];
2050 char *colon_loc;
2052 colon_loc = strindex(start, ':');
2053 if(colon_loc && colon_loc < finish){
2054 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2055 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2057 else
2058 colon_loc = NULL;
2060 if(colon_loc && is_an_env_hdr(tmp)){
2061 char *dummystart, *dummyfinish;
2064 * Pretty format for env hdrs.
2065 * If the same header appears more than once, only
2066 * print the last to avoid duplicates.
2067 * They should have been combined in the env when parsed.
2069 if(!delineate_this_header(tmp, current+1, &dummystart,
2070 &dummyfinish))
2071 format_env_hdr(stream, msgno, section, env,
2072 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2074 else{
2075 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2076 hdrs->charset, flags)) != 0)
2077 goto write_error;
2078 else
2079 start = finish;
2083 /* custom view or default */
2084 else{
2085 /* fetch the non-envelope headers */
2086 if(nfields)
2087 h = pine_fetchheader_lines(stream, msgno, section, fields);
2089 /* default envelope for default view */
2090 if(hdrs->type == HD_BFIELD)
2091 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2093 /* go through each header in list, v initialized above */
2094 for(; (q = *v) != NULL; v++){
2095 if(is_an_env_hdr(q)){
2096 /* pretty format for env hdrs */
2097 format_env_hdr(stream, msgno, section, env,
2098 fmt_env, tmp_pc, q, hdrs->charset, flags);
2100 else{
2102 * Go through h finding all occurences of this header
2103 * and all continuation lines, and output.
2105 for(current = h;
2106 h && delineate_this_header(q,current,&start,&finish);
2107 current = finish){
2108 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2109 hdrs->charset, flags)) != 0)
2110 goto write_error;
2111 else
2112 start = finish;
2120 write_error:
2122 gf_clear_so_writec(tmp_store);
2124 if(!rv){ /* valid data? Do wrapping and filtering... */
2125 int column;
2126 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2127 STORE_S *df_store = NULL;
2129 so_seek(tmp_store, 0L, 0);
2131 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2134 * Test for and act on any display filter
2135 * This barely makes sense. The display filter is going
2136 * to be getting UTF-8'ized headers here. In pre-alpine
2137 * pine the display filter was being fed already decoded
2138 * headers in whatever character set they were in.
2139 * The good news is that that didn't make much
2140 * sense either, so this shouldn't break anything.
2141 * It seems unlikely that anybody is doing anything useful
2142 * with the header part of display filters.
2144 if(ps_global->tools.display_filter
2145 && ps_global->tools.display_filter_trigger
2146 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2147 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2149 gf_set_so_writec(&tmp_pc, df_store);
2150 gf_set_so_readc(&tmp_gc, df_store);
2151 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2152 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2153 _("Formatting error: %s"), errstr);
2154 rv = FHT_WRTERR;
2156 else
2157 so_seek(df_store, 0L, 0);
2159 gf_clear_so_writec(df_store);
2161 else{
2162 q_status_message(SM_ORDER | SM_DING, 3, 3,
2163 "No space for filtered text.");
2164 rv = FHT_WRTERR;
2167 else{
2168 so_seek(tmp_store, 0L, 0);
2169 gf_set_so_readc(&tmp_gc, tmp_store);
2172 if(!rv){
2173 int *margin, wrapflags = GFW_ONCOMMA;
2175 gf_filter_init();
2176 gf_link_filter(gf_local_nvtnl, NULL);
2178 if((F_ON(F_VIEW_SEL_URL, ps_global)
2179 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2180 || F_ON(F_SCAN_ADDR, ps_global))
2181 && handlesp){
2182 gf_link_filter(gf_line_test,
2183 gf_line_test_opt(url_hilite_hdr,
2184 gf_url_hilite_opt(&uh,handlesp,1)));
2185 wrapflags |= GFW_HANDLES;
2188 if((flags & FM_DISPLAY)
2189 && !(flags & FM_NOCOLOR)
2190 && pico_usingcolor()
2191 && ((VAR_NORM_FORE_COLOR
2192 && VAR_HEADER_GENERAL_FORE_COLOR
2193 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2195 (VAR_NORM_BACK_COLOR
2196 && VAR_HEADER_GENERAL_BACK_COLOR
2197 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2198 wrapflags |= GFW_HDRCOLOR;
2200 if((flags & FM_DISPLAY)
2201 && !(flags & FM_NOCOLOR)
2202 && pico_usingcolor()
2203 && ps_global->hdr_colors){
2204 gf_link_filter(gf_line_test,
2205 gf_line_test_opt(color_headers, NULL));
2206 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2209 if(prefix && *prefix)
2210 column = MAX(column-strlen(prefix), 50);
2212 margin = format_view_margin();
2214 if(!(flags & FM_NOWRAP))
2215 gf_link_filter(gf_wrap,
2216 gf_wrap_filter_opt(column, column,
2217 (flags & FM_NOINDENT) ? NULL : margin,
2218 4, wrapflags));
2220 if(prefix && *prefix)
2221 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2223 if((flags & FM_DISPLAY)
2224 && !(flags & FM_NOCOLOR)
2225 && pico_usingcolor()
2226 && ((VAR_NORM_FORE_COLOR
2227 && VAR_HEADER_GENERAL_FORE_COLOR
2228 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2230 (VAR_NORM_BACK_COLOR
2231 && VAR_HEADER_GENERAL_BACK_COLOR
2232 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2233 int right_margin;
2234 int total_wid;
2236 right_margin = margin ? margin[1] : 0;
2237 total_wid = column - right_margin;
2239 gf_link_filter(gf_line_test,
2240 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2241 wrapflags |= GFW_HANDLES;
2244 gf_link_filter(gf_nvtnl_local, NULL);
2246 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2247 rv = FHT_WRTERR;
2248 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2249 "Can't build header : %.200s", errstr);
2253 if(df_store){
2254 gf_clear_so_readc(df_store);
2255 so_give(&df_store);
2257 else
2258 gf_clear_so_readc(tmp_store);
2261 so_give(&tmp_store);
2263 if(h)
2264 fs_give((void **)&h);
2266 if(fields)
2267 fs_give((void **)&fields);
2269 return(rv);
2273 /*----------------------------------------------------------------------
2274 Format RAW header text for display
2276 Args: stream -- mail stream for various header text fetches
2277 rawno -- sequence number in stream of message we're interested in
2278 section -- which section of message
2279 pc -- function to write header text with
2281 Result: 0 if all's well, -1 if write error, 1 if fetch error
2283 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2284 in the local convention.
2286 ----*/
2288 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2290 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2291 unsigned char c;
2293 if(h){
2294 while(*h){
2295 if(ISRFCEOL(h)){
2296 h += 2;
2297 if(!gf_puts(NEWLINE, pc))
2298 return(FHT_WRTERR);
2300 if(ISRFCEOL(h)) /* all done! */
2301 return(FHT_OK);
2303 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2304 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2305 c = (unsigned char) *h++;
2306 if(!((*pc)(c >= 0x80 ? '~' : '^')
2307 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2308 return(FHT_WRTERR);
2310 else if(!(*pc)(*h++))
2311 return(FHT_WRTERR);
2314 else
2315 return(FHT_FTCHERR);
2317 return(FHT_OK);
2322 /*----------------------------------------------------------------------
2323 Format c-client envelope data suitable for display
2325 Args: s -- mail stream for various header text fetches
2326 n -- raw sequence number in stream of message we're interested in
2327 sect -- which section of message
2328 e -- pointer to msg's envelope
2329 pc -- function to write header text with
2330 which -- which header lines to write
2331 oacs --
2332 flags -- FM_ flags, see pith/mailview.h
2334 Result: 0 if all's well, -1 if write error, 1 if fetch error
2336 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2337 in the local convention.
2339 ----*/
2340 void
2341 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2342 long int which, char *oacs, int flags)
2344 char *q, *p2, buftmp[MAILTMPLEN];
2346 if(!e)
2347 return;
2349 if((which & FE_DATE) && e->date) {
2350 q = "Date: ";
2351 snprintf(buftmp, sizeof(buftmp), "%s",
2352 F_ON(F_DATES_TO_LOCAL,ps_global)
2353 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2354 buftmp[sizeof(buftmp)-1] = '\0';
2355 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2356 SIZEOF_20KBUF, buftmp);
2357 gf_puts(q, pc);
2358 format_env_puts(p2, pc);
2359 gf_puts(NEWLINE, pc);
2362 if((which & FE_FROM) && e->from)
2363 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2365 if((which & FE_REPLYTO) && e->reply_to
2366 && (!e->from || !address_is_same(e->reply_to, e->from)))
2367 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2369 if((which & FE_TO) && e->to)
2370 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2372 if((which & FE_CC) && e->cc)
2373 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2375 if((which & FE_BCC) && e->bcc)
2376 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2378 if((which & FE_RETURNPATH) && e->return_path)
2379 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2380 flags, oacs, pc);
2382 if((which & FE_NEWSGROUPS) && e->newsgroups){
2383 int bogus = NIL;
2384 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2385 if (!e->ngpathexists && e->message_id &&
2386 strncmp (e->message_id,"<alpine.",8) &&
2387 strncmp (e->message_id,"<Pine.",6) &&
2388 strncmp (e->message_id,"<MS-C.",6) &&
2389 strncmp (e->message_id,"<MailManager.",13) &&
2390 strncmp (e->message_id,"<EasyMail.",11) &&
2391 strncmp (e->message_id,"<ML-",4)) bogus = T;
2393 if(bogus)
2394 q_status_message(SM_ORDER, 0, 3,
2395 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2398 if((which & FE_FOLLOWUPTO) && e->followup_to)
2399 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2401 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2402 char *freeme = NULL;
2404 q = "Subject: ";
2405 gf_puts(q, pc);
2407 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2409 if(flags & FM_DISPLAY
2410 && (ps_global->display_keywords_in_subject
2411 || ps_global->display_keywordinits_in_subject)){
2413 /* don't bother if no keywords are defined */
2414 if(some_user_flags_defined(s))
2415 p2 = freeme = prepend_keyword_subject(s, n, p2,
2416 ps_global->display_keywords_in_subject ? KW : KWInit,
2417 NULL, ps_global->VAR_KW_BRACES);
2420 format_env_puts(p2, pc);
2422 if(freeme)
2423 fs_give((void **) &freeme);
2425 gf_puts(NEWLINE, pc);
2428 if((which & FE_SENDER) && e->sender
2429 && (!e->from || !address_is_same(e->sender, e->from)))
2430 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2432 if((which & FE_MESSAGEID) && e->message_id){
2433 q = "Message-ID: ";
2434 gf_puts(q, pc);
2435 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2436 format_env_puts(p2, pc);
2437 gf_puts(NEWLINE, pc);
2440 if((which & FE_INREPLYTO) && e->in_reply_to){
2441 q = "In-Reply-To: ";
2442 gf_puts(q, pc);
2443 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
2444 format_env_puts(p2, pc);
2445 gf_puts(NEWLINE, pc);
2448 if((which & FE_REFERENCES) && e->references) {
2449 q = "References: ";
2450 gf_puts(q, pc);
2451 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2452 format_env_puts(p2, pc);
2453 gf_puts(NEWLINE, pc);
2461 * The argument fieldname is something like "Subject:..." or "Subject".
2462 * Look through the specs in speccolor for a match of the fieldname,
2463 * and return the color that goes with any match, or NULL.
2464 * Caller should free the color.
2466 COLOR_PAIR *
2467 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2469 SPEC_COLOR_S *hc = NULL;
2470 COLOR_PAIR *color_pair = NULL;
2471 char *colon, *fname;
2472 char fbuf[FBUF_LEN+1];
2473 int gotit;
2474 PATTERN_S *pat;
2476 colon = strindex(fieldname, ':');
2477 if(colon){
2478 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2479 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2480 fname = fbuf;
2482 else
2483 fname = fieldname;
2485 if(fname && *fname)
2486 for(hc = speccolor; hc; hc = hc->next)
2487 if(hc->spec && !strucmp(fname, hc->spec)){
2488 if(!hc->val)
2489 break;
2491 gotit = 0;
2492 for(pat = hc->val; !gotit && pat; pat = pat->next)
2493 if(srchstr(value, pat->substring))
2494 gotit++;
2496 if(gotit)
2497 break;
2500 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2501 color_pair = new_color_pair(hc->fg, hc->bg);
2503 return(color_pair);
2508 * The argument fieldname is something like "Subject:..." or "Subject".
2509 * Look through the specs in hdr_colors for a match of the fieldname,
2510 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2513 any_hdr_color(char *fieldname)
2515 SPEC_COLOR_S *hc = NULL;
2516 char *colon, *fname;
2517 char fbuf[FBUF_LEN+1];
2519 colon = strindex(fieldname, ':');
2520 if(colon){
2521 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2522 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2523 fname = fbuf;
2525 else
2526 fname = fieldname;
2528 if(fname && *fname)
2529 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2530 if(hc->spec && !strucmp(fname, hc->spec))
2531 break;
2533 return(hc ? 1 : 0);
2537 /*----------------------------------------------------------------------
2538 Format an address field, wrapping lines nicely at commas
2540 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2541 addr -- ADDRESS structure to format
2543 Result: A formatted, malloced string is returned.
2544 ----------------------------------------------------------------------*/
2545 void
2546 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2547 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2549 char *ptmp, *mtmp;
2550 int trailing = 0, group = 0;
2551 ADDRESS *atmp;
2553 if(!addr)
2554 return;
2557 * quickly run down address list to make sure none are patently bogus.
2558 * If so, just blat raw field out.
2560 for(atmp = addr; stream && atmp; atmp = atmp->next)
2561 if(atmp->host && atmp->host[0] == '.'){
2562 char *field, *fields[2];
2564 fields[1] = NULL;
2565 fields[0] = cpystr(field_name);
2566 if((ptmp = strchr(fields[0], ':')) != NULL)
2567 *ptmp = '\0';
2569 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2570 char *h, *t;
2572 for(t = h = field; *h ; t++)
2573 if(*t == '\015' && *(t+1) == '\012'){
2574 *t = '\0'; /* tie off line */
2575 format_env_puts(h, pc);
2576 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2577 gf_puts(NEWLINE, pc); /* more to write */
2578 else
2579 break;
2581 else if(!*t){ /* shouldn't happen much */
2582 if(h != t)
2583 format_env_puts(h, pc);
2585 break;
2588 fs_give((void **)&field);
2591 fs_give((void **)&fields[0]);
2592 gf_puts(NEWLINE, pc);
2593 dprint((2, "Error in \"%s\" field address\n",
2594 field_name ? field_name : "?"));
2595 return;
2598 gf_puts(field_name, pc);
2600 while(addr){
2601 atmp = addr->next; /* remember what's next */
2602 addr->next = NULL;
2603 if(!addr->host && addr->mailbox){
2604 mtmp = addr->mailbox;
2605 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2606 (unsigned char *)tmp_20k_buf,
2607 SIZEOF_20KBUF, addr->mailbox));
2610 ptmp = addr->personal; /* RFC 1522 personal name? */
2611 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2612 tmp_20k_buf[10000-1] = '\0';
2614 if(!trailing) /* 1st pass, just address */
2615 trailing++;
2616 else{ /* else comma, unless */
2617 if(!((group == 1 && addr->host) /* 1st addr in group, */
2618 || (!addr->host && !addr->mailbox))){ /* or end of group */
2619 gf_puts(",", pc);
2620 #if 0
2621 gf_puts(NEWLINE, pc); /* ONE address/line please */
2622 gf_puts(" ", pc);
2623 #endif
2626 gf_puts(" ", pc);
2629 pine_rfc822_write_address_noquote(addr, pc, &group);
2631 addr->personal = ptmp; /* restore old personal ptr */
2632 if(!addr->host && addr->mailbox){
2633 fs_give((void **)&addr->mailbox);
2634 addr->mailbox = mtmp;
2637 addr->next = atmp;
2638 addr = atmp;
2641 gf_puts(NEWLINE, pc);
2647 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
2648 /* RFC822 continuation, must start with CRLF */
2649 #define RFC822CONT "\015\012 "
2651 /* Write RFC822 address with some quoting turned off.
2652 * Accepts:
2653 * address to interpret
2655 * (This is a copy of c-client's rfc822_write_address except
2656 * we don't quote double quote and dot in personal names. It writes
2657 * to a gf_io_t instead of to a buffer so that we don't have to worry
2658 * about fixed sized buffer overflowing. It's also special cased to deal
2659 * with only a single address.)
2661 * The idea is that there are some places where we'd just like to display
2662 * the personal name as is before applying confusing quoting. However,
2663 * we do want to be careful not to break things that should be quoted so
2664 * we'll only use this where we are sure. Quoting may look ugly but it
2665 * doesn't usually break anything.
2667 void
2668 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
2670 extern const char *rspecials;
2672 if (adr->host) { /* ordinary address? */
2673 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
2674 else { /* no, must use phrase <route-addr> form */
2675 if (adr->personal)
2676 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
2678 gf_puts(" <", pc); /* write address delimiter */
2679 pine_rfc822_address(adr, pc);
2680 gf_puts (">", pc); /* closing delimiter */
2683 if(*group)
2684 (*group)++;
2686 else if (adr->mailbox) { /* start of group? */
2687 /* yes, write group name */
2688 pine_rfc822_cat (adr->mailbox, rspecials, pc);
2690 gf_puts (": ", pc); /* write group identifier */
2691 *group = 1; /* in a group */
2693 else if (*group) { /* must be end of group (but be paranoid) */
2694 gf_puts (";", pc);
2695 *group = 0; /* no longer in that group */
2700 /* Write RFC822 route-address to string
2701 * Accepts:
2702 * address to interpret
2705 void
2706 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
2708 extern char *wspecials;
2710 if (adr && adr->host) { /* no-op if no address */
2711 if (adr->adl) { /* have an A-D-L? */
2712 gf_puts (adr->adl, pc);
2713 gf_puts (":", pc);
2715 /* write mailbox name */
2716 pine_rfc822_cat (adr->mailbox, wspecials, pc);
2717 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
2718 gf_puts ("@", pc); /* host delimiter */
2719 gf_puts (adr->host, pc); /* write host name */
2725 /* Concatenate RFC822 string
2726 * Accepts:
2727 * pointer to string to concatenate
2728 * list of special characters
2731 void
2732 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
2734 char *s;
2736 if (strpbrk (src,specials)) { /* any specials present? */
2737 gf_puts ("\"", pc); /* opening quote */
2738 /* truly bizarre characters in there? */
2739 while ((s = strpbrk (src,"\\\"")) != NULL) {
2740 char save[2];
2742 /* turn it into a null-terminated piece */
2743 save[0] = *s;
2744 save[1] = '\0';
2745 *s = '\0';
2746 gf_puts (src, pc); /* yes, output leader */
2747 *s = save[0];
2748 gf_puts ("\\", pc); /* quoting */
2749 gf_puts (save, pc); /* output the bizarre character */
2750 src = ++s; /* continue after the bizarre character */
2752 if (*src) gf_puts (src, pc);/* output non-bizarre string */
2753 gf_puts ("\"", pc); /* closing quote */
2755 else gf_puts (src, pc); /* otherwise it's the easy case */
2759 /*----------------------------------------------------------------------
2760 Format an address field, wrapping lines nicely at commas
2762 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
2763 newsgrps -- ADDRESS structure to format
2765 Result: A formatted, malloced string is returned.
2767 The resuling lines formatted are 80 columns wide.
2768 ----------------------------------------------------------------------*/
2769 void
2770 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
2772 char buf[MAILTMPLEN];
2773 int trailing = 0, llen, alen;
2774 char *next_ng;
2776 if(!newsgrps || !*newsgrps)
2777 return;
2779 gf_puts(field_name, pc);
2781 llen = strlen(field_name);
2782 while(*newsgrps){
2783 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
2784 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
2785 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
2786 newsgrps = next_ng;
2787 if(*newsgrps)
2788 newsgrps++;
2789 alen = strlen(buf);
2790 if(!trailing){ /* first time thru, just address */
2791 llen += alen;
2792 trailing++;
2794 else{ /* else preceding comma */
2795 gf_puts(",", pc);
2796 llen++;
2798 if(alen + llen + 1 > 76){
2799 gf_puts(NEWLINE, pc);
2800 gf_puts(" ", pc);
2801 llen = alen + 5;
2803 else{
2804 gf_puts(" ", pc);
2805 llen += alen + 1;
2809 if(alen && llen > 76){ /* handle long addresses */
2810 register char *q, *p = &buf[alen-1];
2812 while(p > buf){
2813 if(isspace((unsigned char)*p)
2814 && (llen - (alen - (int)(p - buf))) < 76){
2815 for(q = buf; q < p; q++)
2816 (*pc)(*q); /* write character */
2818 gf_puts(NEWLINE, pc);
2819 gf_puts(" ", pc);
2820 gf_puts(p, pc);
2821 break;
2823 else
2824 p--;
2827 if(p == buf) /* no reasonable break point */
2828 gf_puts(buf, pc);
2830 else
2831 gf_puts(buf, pc);
2834 gf_puts(NEWLINE, pc);
2839 /*----------------------------------------------------------------------
2840 Format a text field that's part of some raw (non-envelope) message header
2842 Args: start --
2843 finish --
2844 pc --
2846 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
2848 ----------------------------------------------------------------------*/
2850 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
2852 register char *current;
2853 unsigned char *p, *tmp = NULL, c;
2854 size_t n, len;
2855 char ch;
2856 int rv = FHT_OK;
2858 ch = *finish;
2859 *finish = '\0';
2861 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
2862 len = n+1;
2863 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
2865 else{
2866 len = SIZEOF_20KBUF;
2867 p = (unsigned char *) tmp_20k_buf;
2870 if(islower((unsigned char)(*start)))
2871 *start = toupper((unsigned char)(*start));
2873 current = (char *) rfc1522_decode_to_utf8(p, len, start);
2875 /* output from start to finish */
2876 while(*current && rv == FHT_OK)
2877 if(ISRFCEOL(current)){
2878 if(!gf_puts(NEWLINE, pc))
2879 rv = FHT_WRTERR;
2881 current += 2;
2883 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
2884 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
2885 c = (unsigned char) *current++;
2886 if(!((*pc)(c >= 0x80 ? '~' : '^')
2887 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2888 rv = FHT_WRTERR;
2890 else if(!(*pc)(*current++))
2891 rv = FHT_WRTERR;
2893 if(tmp)
2894 fs_give((void **) &tmp);
2896 *finish = ch;
2898 return(rv);
2904 /*----------------------------------------------------------------------
2905 Format a text field that's part of some raw (non-envelope) message header
2907 Args: s --
2908 pc --
2910 Result: Output
2912 ----------------------------------------------------------------------*/
2914 format_env_puts(char *s, gf_io_t pc)
2916 if(ps_global->pass_ctrl_chars)
2917 return(gf_puts(s, pc));
2919 for(; *s; s++)
2920 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
2921 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
2922 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
2923 return(0);
2925 else if(!(*pc)(*s))
2926 return(0);
2928 return(1);
2932 char *
2933 display_parameters(PARAMETER *params)
2935 int n, longest = 0;
2936 char *d, *printme;
2937 PARAMETER *p;
2938 PARMLIST_S *parmlist;
2940 for(p = params; p; p = p->next) /* ok if we include *'s */
2941 if(p->attribute && (n = strlen(p->attribute)) > longest)
2942 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
2944 d = tmp_20k_buf;
2945 tmp_20k_buf[0] = '\0';
2946 if((parmlist = rfc2231_newparmlist(params)) != NULL){
2947 n = 0; /* n overloaded */
2948 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
2949 if(n++){
2950 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
2951 tmp_20k_buf[10000-1] = '\0';
2952 d += strlen(d);
2955 if(parmlist->value){
2956 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
2957 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
2958 sqzspaces(printme);
2960 else
2961 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
2963 else
2964 printme = "";
2966 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
2967 parmlist->attrib ? parmlist->attrib : "", printme);
2969 tmp_20k_buf[10000-1] = '\0';
2970 d += strlen(d);
2973 rfc2231_free_parmlist(&parmlist);
2976 return(tmp_20k_buf);
2980 /*----------------------------------------------------------------------
2981 Fetch the requested header fields from the msgno specified
2983 Args: stream -- mail stream of open folder
2984 msgno -- number of message to get header lines from
2985 fields -- array of pointers to desired fields
2987 Returns: allocated string containing matched header lines,
2988 NULL on error.
2989 ----*/
2990 char *
2991 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
2993 STRINGLIST *sl;
2994 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
2995 char **pflds = NULL, **pp = NULL, **qq;
2998 * If the user misconfigures it is possible to have one of the fields
2999 * set to the empty string instead of a header name. We want to catch
3000 * that here instead of asking the server the nonsensical question.
3002 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3003 if(!**pp)
3004 break;
3006 if(pp && *pp){ /* found an empty header field, fix it */
3007 pflds = copy_list_array(fields);
3008 for(pp = pflds; pp && *pp; pp++){
3009 if(!**pp){ /* scoot rest of the lines up */
3010 free_this = *pp;
3011 for(qq = pp; *qq; qq++)
3012 *qq = *(qq+1);
3014 if(free_this)
3015 fs_give((void **) &free_this);
3019 /* no headers to look for, return NULL */
3020 if(pflds && !*pflds && !(flags & FT_NOT)){
3021 free_list_array(&pflds);
3022 return(NULL);
3025 else
3026 pflds = fields;
3028 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3029 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3030 if (sl)
3031 free_strlst(&sl);
3033 if(!h){
3034 if(pflds && pflds != fields)
3035 free_list_array(&pflds);
3037 return(NULL);
3040 while(find_field(&h, tmp, sizeof(tmp))){
3041 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3044 /* interesting field? */
3045 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3047 * Hold off allocating space for matching fields until
3048 * we at least find one to copy...
3050 if(!match)
3051 match = m = fs_get(strlen(h) + strlen(p) + 1);
3053 while(*p) /* copy field name */
3054 *m++ = *p++;
3056 while(*h && (*m++ = *h++)) /* header includes colon */
3057 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3058 break;
3060 *m = '\0'; /* tie off match string */
3062 else{ /* no match, pass this field */
3063 while(*h && !(*h++ == '\n'
3064 && (*h == '\r' || !isspace((unsigned char)*h))))
3069 if(pflds && pflds != fields)
3070 free_list_array(&pflds);
3072 return(match ? match : cpystr(""));
3077 find_field(char **h, char *tmp, size_t ntmp)
3079 char *otmp = tmp;
3081 if(!h || !*h || !**h || isspace((unsigned char)**h))
3082 return(0);
3084 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3085 *tmp++ = *(*h)++;
3087 *tmp = '\0';
3088 return(1);
3092 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3096 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3098 if(cp && cp->fg){
3099 if(_last_embedded_fg_color)
3100 fs_give((void **)&_last_embedded_fg_color);
3102 _last_embedded_fg_color = cpystr(cp->fg);
3104 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3105 gf_puts(color_to_asciirgb(cp->fg), pc)))
3106 return 0;
3109 if(cp && cp->bg){
3110 if(_last_embedded_bg_color)
3111 fs_give((void **)&_last_embedded_bg_color);
3113 _last_embedded_bg_color = cpystr(cp->bg);
3115 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3116 gf_puts(color_to_asciirgb(cp->bg), pc)))
3117 return 0;
3120 return 1;
3124 COLOR_PAIR *
3125 get_cur_embedded_color(void)
3127 COLOR_PAIR *ret;
3129 if(_last_embedded_fg_color && _last_embedded_bg_color)
3130 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3131 else
3132 ret = pico_get_cur_color();
3134 return(ret);
3138 void
3139 clear_cur_embedded_color(void)
3141 if(_last_embedded_fg_color)
3142 fs_give((void **)&_last_embedded_fg_color);
3144 if(_last_embedded_bg_color)
3145 fs_give((void **)&_last_embedded_bg_color);
3150 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3152 *len = 0;
3154 if(pico_usingcolor()){
3155 char *fg = NULL, *bg = NULL, *s;
3157 if(ps_global->VAR_SLCTBL_FORE_COLOR
3158 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3159 ps_global->VAR_NORM_FORE_COLOR))
3160 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3162 if(ps_global->VAR_SLCTBL_BACK_COLOR
3163 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3164 ps_global->VAR_NORM_BACK_COLOR))
3165 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3167 if(bg || fg){
3168 COLOR_PAIR *tmp;
3171 * The blacks are just known good colors for
3172 * testing whether the other color is good.
3174 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3175 bg ? bg : colorx(COL_BLACK))) != NULL){
3176 if(pico_is_good_colorpair(tmp))
3177 for(s = color_embed(fg, bg);
3178 (*len) < buflen && (colorstring[*len] = *s);
3179 s++, (*len)++)
3182 free_color_pair(&tmp);
3186 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3187 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3188 *len += 2;
3192 colorstring[buflen-1] = '\0';
3194 return(*len != 0);
3199 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3201 *len = 0;
3202 if(pico_usingcolor()){
3203 char *fg = NULL, *bg = NULL, *s;
3204 char *basefg = NULL, *basebg = NULL;
3206 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3207 : ps_global->VAR_NORM_FORE_COLOR;
3208 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3209 : ps_global->VAR_NORM_BACK_COLOR;
3212 * We need to change the fg and bg colors back even if they
3213 * are the same as the Normal Colors so that color_a_quote
3214 * will have a chance to pick up those colors as the ones to
3215 * switch to. We don't do this before the handle above so that
3216 * the quote color will flow into the selectable item when
3217 * the selectable item color is partly the same as the
3218 * normal color. That is, suppose the normal color was black on
3219 * cyan and the selectable color was blue on cyan, only a fg color
3220 * change. We preserve the only-a-fg-color-change in a quote by
3221 * letting the quote background color flow into the selectable text.
3223 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3224 fg = basefg;
3226 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3227 bg = basebg;
3229 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3230 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3231 *len = 2;
3234 if(fg || bg)
3235 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3239 colorstring[buflen-1] = '\0';
3241 return(*len != 0);
3246 * Helper routine that is of limited use.
3247 * We need to tally up the screen width of
3248 * a UTF-8 string as we go through the string.
3249 * We just want the width of the character starting
3250 * at str (and no longer than remaining_octets).
3251 * If we're plopped into the middle of a UTF-8
3252 * character we just want to return width zero.
3255 width_at_this_position(unsigned char *str, unsigned long n)
3257 unsigned char *inputp = str;
3258 unsigned long remaining_octets = n;
3259 UCS ucs;
3260 int width = 0;
3262 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3263 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3264 width = wcellwidth(ucs);
3265 /* Writechar will print a '?' */
3266 if(width < 0)
3267 width = 1;
3270 return(width);