Merge branch 'ical'
[alpine.git] / pith / mailview.c
blobdc9b29b67fb2b926126a28404bce62b5ee4767cf
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/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
60 #define FBUF_LEN (50)
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
69 * set.
71 static struct envelope_s {
72 char *name;
73 long val;
74 } envelope_hdrs[] = {
75 {"from", FE_FROM},
76 {"sender", FE_SENDER},
77 {"date", FE_DATE},
78 {"to", FE_TO},
79 {"cc", FE_CC},
80 {"bcc", FE_BCC},
81 {"newsgroups", FE_NEWSGROUPS},
82 {"subject", FE_SUBJECT},
83 {"message-id", FE_MESSAGEID},
84 {"reply-to", FE_REPLYTO},
85 {"followup-to", FE_FOLLOWUPTO},
86 {"in-reply-to", FE_INREPLYTO},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES},
89 {NULL, 0}
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
108 fmt_env_t, gf_io_t, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S **, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
116 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
117 gf_io_t, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM *, long, char *, char *,
120 ADDRESS *, int, char *, gf_io_t);
121 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t);
123 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
124 int format_env_puts(char *, gf_io_t);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR *, gf_io_t);
127 COLOR_PAIR *get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
141 prefix FM_
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
152 ----*/
154 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
155 HANDLE_S **handlesp, int flgs, gf_io_t pc)
157 char *decode_err = NULL;
158 HEADER_S h;
159 int width;
161 clear_cur_embedded_color();
163 if(!(flgs & FM_DISPLAY))
164 flgs |= FM_NOINDENT;
166 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs & FM_NOEDITORIAL)){
170 if(ps_global->full_header == 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
172 being shown. */
173 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
174 else if(ps_global->full_header == 2)
175 q_status_message(SM_INFO, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
182 switch(format_header(ps_global->mail_stream, msgno, NULL,
183 env, &h, NULL, handlesp, flgs, NULL, pc)){
185 case -1 : /* write error */
186 goto write_error;
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc)
190 && !gf_puts(NEWLINE, pc)))
191 goto write_error;
193 break;
196 if(!(body == NULL
197 || (ps_global->full_header == 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
199 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
200 format_calendar(msgno, body, handlesp, flgs, width, pc);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE, pc)
205 && (decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
206 return(1);
209 write_error:
211 if(!(flgs & FM_DISPLAY))
212 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
213 decode_err ? decode_err : error_description(errno));
215 return(0);
218 void
219 format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
221 int avail, m1, m2, hwid, i, partwid, padwid;
222 int s1, s2, dwid;
223 int *margin;
224 char padding[1024];
225 VEVENT_SUMMARY_S *vesy; /* vevent summary */
227 vesy = ical_vevent_summary(vcal);
229 if(vesy == NULL) return;
231 if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
232 cflags |= ~FC_FULL;
234 avail = width;
235 margin = (cflags & FC_FULL) ? NULL
236 : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
238 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
239 avail -= m1;
241 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
242 avail -= m2;
244 hwid = MAX(avail, 0);
245 padwid = 0;
247 if(cflags & FC_SUMMARY){
248 i = utf8_width(_("Calendar Entry:"));
249 partwid = MIN(i, hwid);
250 if(m1 > 0){
251 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
252 if(!gf_puts(tmp_20k_buf, pc))
253 return;
256 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
257 partwid, partwid, _("Calendar Entry:"),
258 padwid, padwid, "");
260 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
261 return;
263 else
264 partwid = 0;
266 if(m1 > 0){
267 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
268 if(!gf_puts(tmp_20k_buf, pc))
269 return;
272 avail = width - m1 - m2;
274 s1 = MAX(MIN(1, avail), 0);
275 avail -= s1;
277 dwid = MAX(MIN(1, avail), 0);
278 avail -= dwid;
280 s2 = MAX(MIN(1, avail), 0);
281 avail -= s2;
283 if(cflags & FC_SUMMARY)
284 utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
285 s1, s1, "", dwid, dwid, "", s2, s2, "");
286 else
287 padding[0] = '\0';
289 if(vesy->cancel){
290 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
291 padding, _("This event was cancelled!"));
292 if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
293 gf_puts(tmp_20k_buf, pc);
294 gf_puts(NEWLINE, pc);
295 (*pc)(TAG_EMBED);
296 (*pc)(TAG_BOLDOFF);
300 if(vesy->organizer){
301 if(vesy->sender != NULL){
302 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
303 padding, _("Sent-by: "), vesy->sender);
304 gf_puts(tmp_20k_buf, pc);
305 gf_puts(NEWLINE, pc);
308 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
309 padding, _("Organizer: "), vesy->organizer);
310 gf_puts(tmp_20k_buf, pc);
311 gf_puts(NEWLINE, pc);
312 } /* end of if(organizer) */
314 if(vesy->location){
315 ical_remove_escapes(&vesy->location);
316 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
317 padding, _("Location: "), vesy->location);
318 gf_puts(tmp_20k_buf, pc);
319 gf_puts(NEWLINE, pc);
320 } /* end of if location */
322 if(vesy->evstart){
323 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
324 padding, _("Start Date: "), vesy->evstart);
325 gf_puts(tmp_20k_buf, pc);
326 gf_puts(NEWLINE, pc);
327 } /* end of if dtstart */
329 if(vesy->duration){
330 int i;
332 for(i = 0; vesy->duration[i] != NULL; i++){
333 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
334 padding, _("Duration: "), vesy->duration[i]);
335 gf_puts(tmp_20k_buf, pc);
336 gf_puts(NEWLINE, pc);
338 } /* end of DURATION */
339 else if(vesy->evend){
340 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
341 padding, _("End Date: "), vesy->evend);
342 gf_puts(tmp_20k_buf, pc);
343 gf_puts(NEWLINE, pc);
344 } else { /* end of if dtend */
345 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
346 padding, _("No duration nor end time found for this event"));
347 gf_puts(tmp_20k_buf, pc);
348 gf_puts(NEWLINE, pc);
349 } /* end of else for if (duration) and if (dtend) */
351 if(vesy->attendee){
352 #define MAX_DISPLAYED 3
353 int i;
355 for(i = 0; vesy->attendee[i] != NULL; i++){
356 if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED)
357 break;
359 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
360 padding,
361 _("Attendee: "), vesy->attendee[i]);
362 gf_puts(tmp_20k_buf, pc);
363 gf_puts(NEWLINE, pc);
365 if(cflags & FC_SUMMARY){
366 COLOR_PAIR *lastc = NULL;
367 char numbuf[50];
368 int thisdescwid;
369 COLOR_PAIR *hdrcolor = NULL;
371 if((flgs & FM_DISPLAY)
372 && !(flgs & FM_NOCOLOR)
373 && pico_usingcolor()
374 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
375 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
376 && ps_global->VAR_NORM_FORE_COLOR
377 && ps_global->VAR_NORM_BACK_COLOR
378 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
379 ps_global->VAR_NORM_FORE_COLOR)
380 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
381 ps_global->VAR_NORM_BACK_COLOR))){
383 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
384 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
385 if(!pico_is_good_colorpair(hdrcolor))
386 free_color_pair(&hdrcolor);
390 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
391 return;
393 gf_puts(padding, pc);
394 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
396 if(handlesp){
397 char buf[16], color[64];
398 int l;
399 HANDLE_S *h;
401 h = new_handle(handlesp);
402 h->type = Attach;
403 h->h.attach = a;
405 snprintf(buf, sizeof(buf), "%d", h->key);
406 buf[sizeof(buf)-1] = '\0';
408 if(!(flgs & FM_NOCOLOR)
409 && handle_start_color(color, sizeof(color), &l, 1)){
410 lastc = get_cur_embedded_color();
411 if(!gf_nputs(color, (long) l, pc))
412 return;
414 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
415 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
416 return;
418 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
419 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
420 return;
421 } else
422 tmp_20k_buf[0] = '\0';
424 if(!format_env_puts(tmp_20k_buf, pc))
425 return;
427 if(handlesp){
428 if(lastc){
429 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
430 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
431 return;
434 if(!embed_color(lastc, pc))
435 return;
437 free_color_pair(&lastc);
439 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
440 return;
442 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
443 return;
446 if(padwid > 0){
447 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
448 if(!gf_puts(tmp_20k_buf, pc))
449 return;
452 if(!gf_puts(NEWLINE, pc))
453 return;
455 } /* end of ATTENDEES */
457 free_vevent_summary(&vesy);
459 avail = width - m1 -2;
461 dwid = MAX(MIN(40, avail), 0);
462 avail -= dwid;
464 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
465 repeat_char(dwid, '-'));
466 gf_puts(tmp_20k_buf, pc);
470 format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
472 char *rawtext, *caltext;
473 unsigned long callen;
474 VCALENDAR_S *vcal = NULL;
475 ATTACH_S *a;
476 BODY *b;
478 if(flgs & FM_NEW_MESS) {
479 zero_atmts(ps_global->atmts);
480 describe_mime(body, "", 1, 1, 0, flgs);
483 for(a = ps_global->atmts; a->description != NULL; a++){
484 if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
485 b = mail_body (ps_global->mail_stream, msgno, a->number);
486 if(b == NULL){
487 gf_puts(_("Error fetching calendar body part"), pc);
488 gf_puts(NEWLINE, pc);
489 continue;
491 if(b->sparep == NULL){
492 rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
493 if(rawtext == NULL || *rawtext == '\0'){
494 gf_puts(_("Error fetching calendar text"), pc);
495 gf_puts(NEWLINE, pc);
496 continue;
498 rawtext[callen] = '\0'; /* chop off cookie */
499 switch(b->encoding){
500 case ENCBASE64:
501 caltext = rfc822_base64(rawtext, strlen(rawtext), &callen);
502 if(caltext == NULL){
503 gf_puts(_("Error in calendar base64 encoding"), pc);
504 gf_puts(NEWLINE, pc);
505 continue;
507 break;
509 case ENCQUOTEDPRINTABLE:
510 caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
511 if(caltext == NULL){
512 gf_puts(_("Error in calendar quoted printable encoding"), pc);
513 gf_puts(NEWLINE, pc);
514 continue;
516 break;
518 default: caltext = cpystr(rawtext);
520 if(caltext != NULL){
521 vcal = ical_parse_text(caltext);
522 if(vcal != NULL) vcal->encoding = b->encoding;
523 b->sparep = create_body_sparep(iCalType, (void *) vcal);
524 fs_give((void **) &caltext);
527 else if(get_body_sparep_type(b->sparep) == iCalType)
528 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
529 if(vcal != NULL && vcal->comp != NULL){
530 if(vcal->comp[VEvent] != NULL){
531 format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
532 } /* else another type of entry in the calendar */
534 gf_puts(NEWLINE, pc);
537 return 0;
541 char *
542 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
544 int filt_only_c0 = 0, wrapflags, error_found = 0;
545 int is_in_sig = OUT_SIG_BLOCK;
546 char *charset, *decode_err = NULL, *tmp1, *description;
547 ATTACH_S *a;
548 URL_HILITE_S uh;
549 gf_io_t gc;
551 if(body == NULL
552 || (ps_global->full_header == 2
553 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
555 /*--- Server is not an IMAP2bis, It can't parse MIME
556 so we just show the text here. Hopefully the
557 message isn't a MIME message
558 ---*/
559 void *text2;
561 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
562 msgno, NULL, NULL, NIL)) != NULL){
564 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
565 return("Write Error");
567 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
568 gf_filter_init();
571 * We need to translate the message
572 * into UTF-8, but that's trouble in the full header case
573 * because we don't know what to translate from. We'll just
574 * take a guess by looking for the first text part and
575 * using its charset.
577 if(body && body->type == TYPETEXT)
578 charset = parameter_val(body->parameter, "charset");
579 else if(body && body->type == TYPEMULTIPART && body->nested.part
580 && body->nested.part->body.type == TYPETEXT)
581 charset = parameter_val(body->nested.part->body.parameter, "charset");
582 else
583 charset = ps_global->display_charmap;
585 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
586 /* transliterate message text to UTF-8 */
587 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
590 /* link in filters, similar to what is done in decode_text() */
591 if(!ps_global->pass_ctrl_chars){
592 gf_link_filter(gf_escape_filter, NULL);
593 filt_only_c0 = 1;
594 gf_link_filter(gf_control_filter,
595 gf_control_filter_opt(&filt_only_c0));
598 gf_link_filter(gf_tag_filter, NULL);
600 if((F_ON(F_VIEW_SEL_URL, ps_global)
601 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
602 || F_ON(F_SCAN_ADDR, ps_global))
603 && handlesp){
604 gf_link_filter(gf_line_test,
605 gf_line_test_opt(url_hilite,
606 gf_url_hilite_opt(&uh,handlesp,0)));
609 if((flgs & FM_DISPLAY)
610 && !(flgs & FM_NOCOLOR)
611 && pico_usingcolor()
612 && ps_global->VAR_SIGNATURE_FORE_COLOR
613 && ps_global->VAR_SIGNATURE_BACK_COLOR){
614 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
617 if((flgs & FM_DISPLAY)
618 && !(flgs & FM_NOCOLOR)
619 && pico_usingcolor()
620 && ps_global->VAR_QUOTE1_FORE_COLOR
621 && ps_global->VAR_QUOTE1_BACK_COLOR){
622 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
625 if(!(flgs & FM_NOWRAP)){
626 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
627 if(flgs & FM_DISPLAY
628 && !(flgs & FM_NOCOLOR)
629 && pico_usingcolor())
630 wrapflags |= GFW_USECOLOR;
631 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
632 (flgs & FM_NOINDENT)
633 ? NULL : format_view_margin(),
635 wrapflags));
638 gf_link_filter(gf_nvtnl_local, NULL);
639 if((decode_err = gf_pipe(gc, pc)) != NULL){
640 /* TRANSLATORS: There was an error putting together a message for
641 viewing. The arg is the description of the error. */
642 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
643 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
644 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
645 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
646 && gf_puts(NEWLINE, pc))
647 decode_err = NULL;
648 else
649 return(decode_err);
653 if(!text2){
654 if(!gf_puts(NEWLINE, pc)
655 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
656 || !gf_puts(NEWLINE, pc)
657 || !gf_puts(NEWLINE, pc))
658 return("Write Error");
661 else{
662 int show_parts = 0;
664 /*======== Now loop through formatting all the parts =======*/
665 for(a = ps_global->atmts; a->description != NULL; a++) {
666 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
667 continue;
669 if(a->body->type == TYPEMULTIPART){
670 #ifdef SMIME
671 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
672 if(a->description){
673 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
674 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
675 return("Write Error");
678 #endif /* SMIME */
679 continue;
682 if(!a->shown) {
683 if(a->suppress_editorial)
684 continue;
686 if(!(flgs & FM_NOEDITORIAL)
687 && (!gf_puts(NEWLINE, pc)
688 || (decode_err = part_desc(a->number, a->body,
689 (flgs & FM_DISPLAY)
690 ? (a->can_display != MCD_NONE)
691 ? 1 : 2
692 : 3, width, flgs, pc))))
693 return("Write Error");
695 continue;
698 switch(a->body->type){
700 case TYPETEXT:
702 * If a message is multipart *and* the first part of it
703 * is text *and that text is empty, there is a good chance that
704 * there was actually something there that c-client was
705 * unable to parse. Here we report the empty message body
706 * and insert the raw RFC822.TEXT (if full-headers are
707 * on).
709 if(body->type == TYPEMULTIPART
710 && a == ps_global->atmts
711 && a->body->size.bytes == 0
712 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
713 char *err = NULL;
715 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
716 "Empty or malformed message%s.",
717 ps_global->full_header == 2
718 ? ". Displaying raw text"
719 : ". Use \"H\" to see raw text");
720 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
722 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
723 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
724 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
725 return("Write Error");
727 if(ps_global->full_header == 2
728 && (err = detach_raw(ps_global->mail_stream, msgno,
729 a->number, pc, flgs))){
730 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
731 "%s%s [ Formatting error: %s ]%s%s",
732 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
733 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
734 if(!gf_puts(tmp_20k_buf, pc))
735 return("Write Error");
738 break;
742 * Don't write our delimiter if this text part is
743 * the first part of a message/rfc822 segment...
745 if(show_parts && a != ps_global->atmts
746 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
747 #ifdef SMIME
748 || (a[-1].body->type == TYPEMULTIPART
749 && a[-1].body->subtype
750 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
751 && &a[-1] != ps_global->atmts
752 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
753 #endif /* SMIME */
755 && !(flgs & FM_NOEDITORIAL)){
756 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
757 tmp1 = "Calendar entry";
758 else
759 tmp1 = a->body->description ? a->body->description
760 : "Attached Text";
761 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
762 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
764 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
765 description);
766 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
767 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
768 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
769 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
770 return("Write Error");
772 /* skip calendar */
773 if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
774 error_found += decode_text(a, msgno, pc, handlesp,
775 (flgs & FM_DISPLAY) ? InLine : QStatus,
776 flgs);
777 break;
779 case TYPEMESSAGE:
780 tmp1 = a->body->description ? a->body->description
781 : (strucmp(a->body->subtype, "delivery-status") == 0)
782 ? "Delivery Status"
783 : "Included Message";
784 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
785 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
787 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
788 description);
789 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
791 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
792 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
793 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
794 return("Write Error");
796 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
797 /* imapenvonly, we may not have all the headers we need */
798 if(a->body->nested.msg->env->imapenvonly)
799 mail_fetch_header(ps_global->mail_stream, msgno,
800 a->number, NULL, NULL, FT_PEEK);
801 switch(format_header(ps_global->mail_stream, msgno, a->number,
802 a->body->nested.msg->env, hp,
803 NULL, handlesp, flgs, NULL, pc)){
804 case -1 : /* write error */
805 return("Write Error");
807 case 1 : /* fetch error */
808 if(!(gf_puts("[ Error fetching header ]", pc)
809 && !gf_puts(NEWLINE, pc)))
810 return("Write Error");
812 break;
815 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
816 int *margin, avail, m1, m2;
818 avail = width;
819 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
821 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
822 avail -= m1;
824 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
825 avail -= m2;
827 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
828 || !gf_puts(NEWLINE, pc)
829 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
830 return("Write Error");
832 else
833 error_found += decode_text(a, msgno, pc, handlesp,
834 (flgs&FM_DISPLAY) ? InLine : QStatus,
835 flgs);
837 if(!gf_puts(NEWLINE, pc))
838 return("Write Error");
840 break;
842 default:
843 if((decode_err = part_desc(a->number, a->body,
844 (flgs & FM_DISPLAY) ? 1 : 3,
845 width, flgs, pc)) != NULL)
846 return("Write Error");
849 show_parts++;
852 if(!(!error_found
853 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
854 && format_blip_seen(msgno)))
855 return("Cannot format body.");
858 return(NULL);
863 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
865 ATTACH_S *a;
867 if(flgs & FM_NEW_MESS) {
868 zero_atmts(ps_global->atmts);
869 describe_mime(body, "", 1, 1, 0, flgs);
872 /*----- First do the list of parts/attachments if needed ----*/
873 if((flgs & FM_DISPLAY)
874 && (ps_global->atmts[1].description
875 || (ps_global->atmts[0].body
876 && ps_global->atmts[0].body->type != TYPETEXT))){
877 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
878 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
879 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
880 int sizewid, descwid, dashwid, partwid, padwid;
881 COLOR_PAIR *hdrcolor = NULL;
883 if((flgs & FM_DISPLAY)
884 && !(flgs & FM_NOCOLOR)
885 && pico_usingcolor()
886 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
887 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
888 && ps_global->VAR_NORM_FORE_COLOR
889 && ps_global->VAR_NORM_BACK_COLOR
890 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
891 ps_global->VAR_NORM_FORE_COLOR)
892 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
893 ps_global->VAR_NORM_BACK_COLOR))){
895 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
896 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
897 if(!pico_is_good_colorpair(hdrcolor))
898 free_color_pair(&hdrcolor);
902 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
905 * Attachment list header
908 avail = width;
910 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
911 avail -= m1;
913 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
914 avail -= m2;
916 hwid = MAX(avail, 0);
918 i = utf8_width(_("Parts/Attachments:"));
919 partwid = MIN(i, hwid);
920 padwid = hdrcolor ? (hwid-partwid) : 0;
922 if(m1 > 0){
923 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
924 if(!gf_puts(tmp, pc))
925 return(0);
928 utf8_snprintf(tmp, sizeof(tmp),
929 "%-*.*w%*.*s",
930 /* TRANSLATORS: A label */
931 partwid, partwid, _("Parts/Attachments:"),
932 padwid, padwid, "");
934 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
935 return(0);
938 /*----- Figure max display widths -----*/
939 for(a = ps_global->atmts; a->description != NULL; a++){
940 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
941 continue;
943 if((n = utf8_width(a->number)) > maxnumwid)
944 maxnumwid = n;
946 if((n = utf8_width(a->size)) > maxsizewid)
947 maxsizewid = n;
951 * ----- adjust max lengths for nice display -----
953 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
957 avail = width - m1 - m2;
959 s1 = MAX(MIN(1, avail), 0);
960 avail -= s1;
962 dwid = MAX(MIN(1, avail), 0);
963 avail -= dwid;
965 s2 = MAX(MIN(1, avail), 0);
966 avail -= s2;
968 maxnumwid = MIN(maxnumwid, width/3);
969 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
970 avail -= maxnumwid;
972 s3 = MAX(MIN(1, avail), 0);
973 avail -= s3;
975 shownwid = MAX(MIN(5, avail), 0);
976 avail -= shownwid;
978 s4 = MAX(MIN(3, avail), 0);
979 avail -= s4;
981 sizewid = MAX(MIN(maxsizewid, avail), 0);
982 avail -= sizewid;
984 s5 = MAX(MIN(2, avail), 0);
985 avail -= s5;
987 descwid = MAX(0, avail);
989 /*----- Format the list of attachments -----*/
990 for(a = ps_global->atmts; a->description != NULL; a++){
991 COLOR_PAIR *lastc = NULL;
992 char numbuf[50];
993 int thisdescwid, padwid;
995 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
996 continue;
997 #ifdef SMIME
998 if(a->body->type == TYPEMULTIPART
999 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
1000 continue;
1001 #endif /* SMIME */
1003 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
1004 thisdescwid = MIN(i, descwid);
1005 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
1007 if(m1 > 0){
1008 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1009 if(!gf_puts(tmp, pc))
1010 return(0);
1013 utf8_snprintf(tmp, sizeof(tmp),
1014 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1015 s1, s1, "",
1016 dwid, dwid,
1017 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
1018 s2, s2, "",
1019 maxnumwid, maxnumwid,
1020 a->number
1021 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
1022 : "",
1023 s3, s3, "",
1024 shownwid, shownwid,
1025 a->shown ? "Shown" :
1026 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
1027 ? "OK " : "",
1028 s4, s4, "",
1029 sizewid, sizewid,
1030 a->size ? a->size : "",
1031 s5, s5, "",
1032 thisdescwid, thisdescwid,
1033 (descwid > 2 && a->description) ? a->description : "");
1035 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
1036 return(0);
1038 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1039 char buf[16], color[64];
1040 int l;
1041 HANDLE_S *h;
1043 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
1044 if(!(*pc)(' '))
1045 return(0);
1047 h = new_handle(handlesp);
1048 h->type = Attach;
1049 h->h.attach = a;
1051 snprintf(buf, sizeof(buf), "%d", h->key);
1052 buf[sizeof(buf)-1] = '\0';
1054 if(!(flgs & FM_NOCOLOR)
1055 && handle_start_color(color, sizeof(color), &l, 1)){
1056 lastc = get_cur_embedded_color();
1057 if(!gf_nputs(color, (long) l, pc))
1058 return(0);
1060 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
1061 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
1062 return(0);
1064 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1065 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
1066 return(0);
1068 else
1069 tmpp = tmp;
1071 if(!format_env_puts(tmpp, pc))
1072 return(0);
1074 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1075 if(lastc){
1076 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1077 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1078 return(0);
1081 if(!embed_color(lastc, pc))
1082 return(0);
1084 free_color_pair(&lastc);
1086 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1087 return(0);
1089 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
1090 return(0);
1093 if(padwid > 0){
1094 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
1095 if(!gf_puts(tmp, pc))
1096 return(0);
1099 if(!gf_puts(NEWLINE, pc))
1100 return(0);
1104 * Dashed line after list
1107 if(hdrcolor){
1108 avail = width - m1 - m2;
1109 hwid = MAX(avail, 0);
1111 dashwid = MAX(MIN(40, hwid-2), 0);
1112 padwid = hwid - dashwid;
1113 if(m1 > 0){
1114 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1115 if(!gf_puts(tmp, pc))
1116 return(0);
1119 snprintf(tmp, sizeof(tmp),
1120 "%s%*.*s",
1121 repeat_char(dashwid, '-'),
1122 padwid, padwid, "");
1124 else{
1125 avail = width - m1 -2;
1127 dashwid = MAX(MIN(40, avail), 0);
1128 avail -= dashwid;
1130 snprintf(tmp, sizeof(tmp),
1131 "%*.*s%s",
1132 m1, m1, "",
1133 repeat_char(dashwid, '-'));
1136 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
1137 return(0);
1139 if(hdrcolor)
1140 free_color_pair(&hdrcolor);
1143 return(1);
1149 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1150 * of body part fetches as we're formatting) for the
1151 * given message isn't set (likely because there
1152 * weren't any parts suitable for display), then make
1153 * sure to set it here.
1156 format_blip_seen(long int msgno)
1158 MESSAGECACHE *mc;
1160 if(msgno > 0L && ps_global->mail_stream
1161 && msgno <= ps_global->mail_stream->nmsgs
1162 && (mc = mail_elt(ps_global->mail_stream, msgno))
1163 && !mc->seen
1164 && !ps_global->mail_stream->rdonly)
1165 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
1167 return(1);
1172 * is_an_env_hdr - is this name a header in the envelope structure?
1174 * name - the header name to check
1177 is_an_env_hdr(char *name)
1179 register int i;
1181 for(i = 0; envelope_hdrs[i].name; i++)
1182 if(!strucmp(name, envelope_hdrs[i].name))
1183 return(1);
1185 return(0);
1192 * is_an_addr_hdr - is this an address header?
1194 * name - the header name to check
1197 is_an_addr_hdr(char *fieldname)
1199 char fbuf[FBUF_LEN+1];
1200 char *colon, *fname;
1201 static char *addr_headers[] = {
1202 "from",
1203 "reply-to",
1204 "to",
1205 "cc",
1206 "bcc",
1207 "return-path",
1208 "sender",
1209 "x-sender",
1210 "x-x-sender",
1211 "resent-from",
1212 "resent-to",
1213 "resent-cc",
1214 NULL
1217 /* so it is pointing to NULL */
1218 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
1220 if((colon = strindex(fieldname, ':')) != NULL){
1221 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
1222 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
1223 fname = fbuf;
1225 else
1226 fname = fieldname;
1228 if(fname && *fname){
1229 for(p = addr_headers; *p; p++)
1230 if(!strucmp(fname, *p))
1231 break;
1234 return((*p) ? 1 : 0);
1239 * Format a single field from the envelope
1241 void
1242 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1243 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
1245 register int i;
1247 if(!fmt_env)
1248 fmt_env = format_envelope;
1250 for(i = 0; envelope_hdrs[i].name; i++)
1251 if(!strucmp(field, envelope_hdrs[i].name)){
1252 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
1253 return;
1259 * Look through header string beginning with "begin", for the next
1260 * occurrence of header "field". Set "start" to that. Set "end" to point one
1261 * position past all of the continuation lines that go with "field".
1262 * That is, if "end" is converted to a null
1263 * character then the string "start" will be the next occurence of header
1264 * "field" including all of its continuation lines. Assume we
1265 * have CRLF's as end of lines.
1267 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1268 * make "end" the end of that header.
1270 * Returns 1 if found, 0 if not.
1273 delineate_this_header(char *field, char *begin, char **start, char **end)
1275 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
1276 char *p;
1277 char *begin_srch;
1279 if(field == NULL){
1280 if(!begin || !*begin || isspace((unsigned char)*begin))
1281 return 0;
1282 else
1283 *start = begin;
1285 else{
1286 strncpy(tmpfield, field, sizeof(tmpfield)-2);
1287 tmpfield[sizeof(tmpfield)-2] = '\0';
1288 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
1289 tmpfield[sizeof(tmpfield)-1] = '\0';
1292 * We require that start is at the beginning of a line, so
1293 * either it equals begin (which we assume is the beginning of a
1294 * line) or it is preceded by a CRLF.
1296 begin_srch = begin;
1297 *start = srchstr(begin_srch, tmpfield);
1298 while(*start && *start != begin
1299 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
1300 begin_srch = *start + 1;
1301 *start = srchstr(begin_srch, tmpfield);
1304 if(!*start)
1305 return 0;
1308 for(p = *start; *p; p++){
1309 if(ISRFCEOL(p)
1310 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
1312 * The final 015 in the test above is to test for the end
1313 * of the headers.
1315 *end = p+2;
1316 break;
1320 if(!*p)
1321 *end = p;
1323 return 1;
1329 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
1331 *len = 0;
1333 if(pico_usingcolor()){
1334 char *fg = NULL, *bg = NULL, *s;
1335 char *basefg = NULL, *basebg = NULL;
1337 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1338 : ps_global->VAR_NORM_FORE_COLOR;
1339 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1340 : ps_global->VAR_NORM_BACK_COLOR;
1342 if(ps_global->VAR_SLCTBL_FORE_COLOR
1343 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1344 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1346 if(ps_global->VAR_SLCTBL_BACK_COLOR
1347 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1348 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1350 if(bg || fg){
1351 COLOR_PAIR *tmp;
1354 * The blacks are just known good colors for
1355 * testing whether the other color is good.
1357 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1358 bg ? bg : colorx(COL_BLACK))) != NULL){
1359 if(pico_is_good_colorpair(tmp))
1360 for(s = color_embed(fg, bg);
1361 (*len) < buflen && (colorstring[*len] = *s);
1362 s++, (*len)++)
1365 free_color_pair(&tmp);
1369 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1370 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1371 *len += 2;
1375 colorstring[buflen-1] = '\0';
1377 return(*len != 0);
1382 handle_end_color(char *colorstring, size_t buflen, int *len)
1384 *len = 0;
1385 if(pico_usingcolor()){
1386 char *fg = NULL, *bg = NULL, *s;
1389 * We need to change the fg and bg colors back even if they
1390 * are the same as the Normal Colors so that color_a_quote
1391 * will have a chance to pick up those colors as the ones to
1392 * switch to. We don't do this before the handle above so that
1393 * the quote color will flow into the selectable item when
1394 * the selectable item color is partly the same as the
1395 * normal color. That is, suppose the normal color was black on
1396 * cyan and the selectable color was blue on cyan, only a fg color
1397 * change. We preserve the only-a-fg-color-change in a quote by
1398 * letting the quote background color flow into the selectable text.
1400 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1401 fg = ps_global->VAR_NORM_FORE_COLOR;
1403 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1404 bg = ps_global->VAR_NORM_BACK_COLOR;
1406 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1407 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1408 *len = 2;
1411 if(fg || bg)
1412 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1416 colorstring[buflen-1] = '\0';
1418 return(*len != 0);
1422 char *
1423 url_embed(int embed)
1425 static char buf[3] = {TAG_EMBED};
1426 buf[1] = embed;
1427 buf[2] = '\0';
1428 return(buf);
1433 * Paint the signature.
1436 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1438 struct variable *vars = ps_global->vars;
1439 int *in_sig_block;
1440 COLOR_PAIR *col = NULL;
1442 if(is_in_sig == NULL)
1443 return 0;
1445 in_sig_block = (int *) is_in_sig;
1447 if(!strcmp(line, SIGDASHES))
1448 *in_sig_block = START_SIG_BLOCK;
1449 else if(*line == '\0')
1451 * Suggested by Eduardo: allow for a blank line right after
1452 * the sigdashes.
1454 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1455 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1456 else
1457 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1458 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1460 if(*in_sig_block != OUT_SIG_BLOCK
1461 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1462 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1463 VAR_SIGNATURE_BACK_COLOR))){
1464 if(!pico_is_good_colorpair(col))
1465 free_color_pair(&col);
1468 if(col){
1469 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1471 ins = gf_line_test_new_ins(ins, line,
1472 color_embed(col->fg, col->bg),
1473 (2 * RGBLEN) + 4);
1475 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1476 fg[sizeof(fg)-1] = '\0';
1477 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1478 bg[sizeof(bg)-1] = '\0';
1481 * Loop watching colors, and override with
1482 * signature color whenever the normal foreground and background
1483 * colors are in force.
1486 for(p = line; *p; )
1487 if(*p++ == TAG_EMBED){
1489 switch(*p++){
1490 case TAG_HANDLE :
1491 p += *p + 1; /* skip handle key */
1492 break;
1494 case TAG_FGCOLOR :
1495 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1496 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1497 p += RGBLEN; /* advance past color value */
1499 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1500 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1501 ins = gf_line_test_new_ins(ins, p,
1502 color_embed(col->fg,NULL),
1503 RGBLEN + 2);
1504 break;
1506 case TAG_BGCOLOR :
1507 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1508 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1509 p += RGBLEN; /* advance past color value */
1511 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1512 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1513 ins = gf_line_test_new_ins(ins, p,
1514 color_embed(NULL,col->bg),
1515 RGBLEN + 2);
1517 break;
1519 default :
1520 break;
1524 ins = gf_line_test_new_ins(ins, line + strlen(line),
1525 color_embed(VAR_NORM_FORE_COLOR,
1526 VAR_NORM_BACK_COLOR),
1527 (2 * RGBLEN) + 4);
1528 free_color_pair(&col);
1531 return 0;
1536 * Line filter to add color to displayed headers.
1539 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1541 static char field[FBUF_LEN + 1];
1542 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1543 char *p, *q, *value, *beg;
1544 COLOR_PAIR *color;
1545 int in_quote = 0, in_comment = 0, did_color = 0;
1546 struct variable *vars = ps_global->vars;
1548 field[FBUF_LEN] = '\0';
1550 if(isspace((unsigned char)*line)) /* continuation line */
1551 value = line;
1552 else{
1553 if(!(value = strindex(line, ':')))
1554 return(0);
1556 memset(field, 0, sizeof(field));
1557 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1560 for(value++; isspace((unsigned char)*value); value++)
1563 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1564 fg[sizeof(fg)-1] = '\0';
1565 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1566 bg[sizeof(bg)-1] = '\0';
1569 * Split into two cases depending on whether this is a header which
1570 * contains addresses or not. We may color addresses separately.
1572 if(is_an_addr_hdr(field)){
1575 * If none of the patterns are for this header, don't bother parsing
1576 * and checking each address.
1578 if(!any_hdr_color(field))
1579 return(0);
1582 * First check for patternless patterns which color whole line.
1584 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1585 if(pico_is_good_colorpair(color)){
1586 ins = gf_line_test_new_ins(ins, value,
1587 color_embed(color->fg, color->bg),
1588 (2 * RGBLEN) + 4);
1589 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1590 fg[sizeof(fg)-1] = '\0';
1591 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1592 bg[sizeof(bg)-1] = '\0';
1593 did_color++;
1595 else
1596 free_color_pair(&color);
1600 * Then go through checking address by address.
1601 * Keep track of quotes and watch for color changes, and override
1602 * with most recent header color whenever the normal foreground
1603 * and background colors are in force.
1605 beg = p = value;
1606 while(*p){
1607 switch(*p){
1608 case '\\':
1609 /* skip next character */
1610 if(*(p+1) && (in_comment || in_quote))
1611 p += 2;
1612 else
1613 p++;
1615 break;
1617 case '"':
1618 if(!in_comment)
1619 in_quote = 1 - in_quote;
1621 p++;
1622 break;
1624 case '(':
1625 in_comment++;
1626 p++;
1627 break;
1629 case ')':
1630 if(in_comment > 0)
1631 in_comment--;
1633 p++;
1634 break;
1636 case ',':
1637 if(!(in_quote || in_comment)){
1638 /* we reached the end of this address */
1639 *p = '\0';
1640 if(color)
1641 free_color_pair(&color);
1643 if((color = hdr_color(field, beg,
1644 ps_global->hdr_colors)) != NULL){
1645 if(pico_is_good_colorpair(color)){
1646 did_color++;
1647 ins = gf_line_test_new_ins(ins, beg,
1648 color_embed(color->fg,
1649 color->bg),
1650 (2 * RGBLEN) + 4);
1651 *p = ',';
1652 for(q = p; q > beg &&
1653 isspace((unsigned char)*(q-1)); q--)
1656 ins = gf_line_test_new_ins(ins, q,
1657 color_embed(fg, bg),
1658 (2 * RGBLEN) + 4);
1660 else
1661 free_color_pair(&color);
1663 else
1664 *p = ',';
1666 for(p++; isspace((unsigned char)*p); p++)
1669 beg = p;
1671 else
1672 p++;
1674 break;
1676 case TAG_EMBED:
1677 switch(*(++p)){
1678 case TAG_HANDLE:
1679 p++;
1680 p += *p + 1; /* skip handle key */
1681 break;
1683 case TAG_FGCOLOR:
1684 p++;
1685 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1686 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1687 p += RGBLEN; /* advance past color value */
1689 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1690 ins = gf_line_test_new_ins(ins, p,
1691 color_embed(color->fg,NULL),
1692 RGBLEN + 2);
1693 break;
1695 case TAG_BGCOLOR:
1696 p++;
1697 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1698 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1699 p += RGBLEN; /* advance past color value */
1701 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1702 ins = gf_line_test_new_ins(ins, p,
1703 color_embed(NULL,color->bg),
1704 RGBLEN + 2);
1706 break;
1708 default:
1709 break;
1712 break;
1714 default:
1715 p++;
1716 break;
1720 for(q = beg; *q && isspace((unsigned char)*q); q++)
1723 if(*q && !(in_quote || in_comment)){
1724 /* we reached the end of this address */
1725 if(color)
1726 free_color_pair(&color);
1728 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1729 if(pico_is_good_colorpair(color)){
1730 did_color++;
1731 ins = gf_line_test_new_ins(ins, beg,
1732 color_embed(color->fg,
1733 color->bg),
1734 (2 * RGBLEN) + 4);
1735 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1738 ins = gf_line_test_new_ins(ins, q,
1739 color_embed(fg, bg),
1740 (2 * RGBLEN) + 4);
1742 else
1743 free_color_pair(&color);
1747 if(color)
1748 free_color_pair(&color);
1750 if(did_color)
1751 ins = gf_line_test_new_ins(ins, line + strlen(line),
1752 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1753 VAR_HEADER_GENERAL_BACK_COLOR),
1754 (2 * RGBLEN) + 4);
1756 else{
1758 color = hdr_color(field, value, ps_global->hdr_colors);
1760 if(color){
1761 if(pico_is_good_colorpair(color)){
1762 ins = gf_line_test_new_ins(ins, value,
1763 color_embed(color->fg, color->bg),
1764 (2 * RGBLEN) + 4);
1767 * Loop watching colors, and override with header
1768 * color whenever the normal foreground and background
1769 * colors are in force.
1771 p = value;
1772 while(*p)
1773 if(*p++ == TAG_EMBED){
1775 switch(*p++){
1776 case TAG_HANDLE:
1777 p += *p + 1; /* skip handle key */
1778 break;
1780 case TAG_FGCOLOR:
1781 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1782 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1783 p += RGBLEN; /* advance past color value */
1785 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1786 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1787 ins = gf_line_test_new_ins(ins, p,
1788 color_embed(color->fg,NULL),
1789 RGBLEN + 2);
1790 break;
1792 case TAG_BGCOLOR:
1793 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1794 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1795 p += RGBLEN; /* advance past color value */
1797 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1798 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1799 ins = gf_line_test_new_ins(ins, p,
1800 color_embed(NULL,color->bg),
1801 RGBLEN + 2);
1803 break;
1805 default:
1806 break;
1810 ins = gf_line_test_new_ins(ins, line + strlen(line),
1811 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1812 VAR_HEADER_GENERAL_BACK_COLOR),
1813 (2 * RGBLEN) + 4);
1816 free_color_pair(&color);
1820 return(0);
1825 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1827 register char *lp, *up = NULL, *urlp = NULL,
1828 *weburlp = NULL, *mailurlp = NULL;
1829 int n, n1, n2, n3, l;
1830 char buf[256], color[256];
1831 HANDLE_S *h;
1832 URL_HILITE_S *uh;
1834 for(lp = line; ; lp = up + n){
1835 /* scan for all of them so we can choose the first */
1836 if(F_ON(F_VIEW_SEL_URL,ps_global))
1837 urlp = rfc1738_scan(lp, &n1);
1838 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1839 weburlp = web_host_scan(lp, &n2);
1840 if(F_ON(F_SCAN_ADDR,ps_global))
1841 mailurlp = mail_addr_scan(lp, &n3);
1843 if(urlp || weburlp || mailurlp){
1844 up = urlp ? urlp :
1845 weburlp ? weburlp : mailurlp;
1846 if(up == urlp && weburlp && weburlp < up)
1847 up = weburlp;
1848 if(mailurlp && mailurlp < up)
1849 up = mailurlp;
1851 if(up == urlp){
1852 n = n1;
1853 weburlp = mailurlp = NULL;
1855 else if(up == weburlp){
1856 n = n2;
1857 mailurlp = NULL;
1859 else{
1860 n = n3;
1861 weburlp = NULL;
1864 else
1865 break;
1867 uh = (URL_HILITE_S *) local;
1869 h = new_handle(uh->handlesp);
1870 h->type = URL;
1871 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1872 snprintf(h->h.url.path, n+10, "%s%.*s",
1873 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1874 h->h.url.path[n+10-1] = '\0';
1876 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1877 ins = gf_line_test_new_ins(ins, up, color, l);
1878 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1879 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1881 buf[0] = TAG_EMBED;
1882 buf[1] = TAG_HANDLE;
1883 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1884 buf[sizeof(buf)-1] = '\0';
1885 buf[2] = strlen(&buf[3]);
1886 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1888 /* in case it was the current selection */
1889 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1891 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1892 ins = gf_line_test_new_ins(ins, up + n, color, l);
1893 else
1894 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1896 urlp = weburlp = mailurlp = NULL;
1899 return(0);
1904 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1906 static int check_for_urls = 0;
1907 register char *lp;
1909 if(isspace((unsigned char)*line)) /* continuation, check or not
1910 depending on last line */
1911 lp = line;
1912 else{
1913 check_for_urls = 0;
1914 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1915 FieldType ft;
1917 *lp = '\0';
1919 if(((ft = pine_header_standard(line)) == FreeText
1920 || ft == Subject
1921 || ft == TypeUnknown)
1922 && strucmp(line, "message-id")
1923 && strucmp(line, "newsgroups")
1924 && strucmp(line, "references")
1925 && strucmp(line, "in-reply-to")
1926 && strucmp(line, "received")
1927 && strucmp(line, "date")){
1928 check_for_urls = 1;
1931 *lp = ':';
1935 if(check_for_urls)
1936 (void) url_hilite(linenum, lp + 1, ins, local);
1938 return(0);
1943 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1945 char *p;
1946 int wid = 0;
1947 int total_wid;
1948 struct variable *vars = ps_global->vars;
1950 if(!line[0])
1951 return 0;
1953 total_wid = *((int *) local);
1955 /* calculate width of line */
1956 p = line;
1957 while(*p){
1959 switch(*p){
1960 case TAG_EMBED:
1961 p++;
1962 switch(*p){
1963 case TAG_HANDLE:
1964 p++;
1965 p += *p + 1; /* skip handle key */
1966 break;
1968 case TAG_FGCOLOR :
1969 case TAG_BGCOLOR :
1970 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
1971 break;
1973 case TAG_INVON:
1974 case TAG_INVOFF:
1975 case TAG_BOLDON:
1976 case TAG_BOLDOFF:
1977 case TAG_ULINEON:
1978 case TAG_ULINEOFF:
1979 p++;
1980 break;
1982 default: /* literal embed char */
1983 break;
1986 break;
1988 case TAB:
1989 p++;
1990 while(((++wid) & 0x07) != 0) /* add tab's spaces */
1993 break;
1995 default:
1996 wid += width_at_this_position((unsigned char *) p, strlen(p));
1997 p++;
1998 break;
2002 if(total_wid > wid){
2003 ins = gf_line_test_new_ins(ins, line + strlen(line),
2004 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
2005 VAR_HEADER_GENERAL_BACK_COLOR),
2006 (2 * RGBLEN) + 4);
2007 ins = gf_line_test_new_ins(ins, line+strlen(line),
2008 repeat_char(total_wid-wid, ' '), total_wid-wid);
2009 ins = gf_line_test_new_ins(ins, line + strlen(line),
2010 color_embed(VAR_NORM_FORE_COLOR,
2011 VAR_NORM_BACK_COLOR),
2012 (2 * RGBLEN) + 4);
2015 return(0);
2020 #define UES_LEN 12
2021 #define UES_MAX 32
2023 url_external_specific_handler(char *url, int len)
2025 static char list[UES_LEN * UES_MAX];
2027 if(url){
2028 char *p;
2029 int i;
2031 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
2032 if(strlen(p) == len && !struncmp(p, url, len))
2033 return(1);
2035 else{ /* initialize! */
2036 char **l, *test, *cmd, *p, *p2;
2037 int i = 0, n;
2039 memset(list, 0, sizeof(list));
2040 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
2041 get_pair(*l, &test, &cmd, 1, 1);
2043 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
2044 *p2 = '\0';
2046 for(p += 8; *p && i < UES_MAX; p += n)
2047 if((p2 = strchr(p, ',')) != NULL){
2048 if((n = p2 - p) < UES_LEN){
2049 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
2050 i++;
2052 else
2053 dprint((1,
2054 "* * * HANLDER TOO LONG: %.*s\n", n,
2055 p ? p : "?"));
2057 n++;
2059 else{
2060 if(strlen(p) <= UES_LEN){
2061 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
2062 i++;
2065 break;
2069 if(test)
2070 fs_give((void **) &test);
2072 if(cmd)
2073 fs_give((void **) &cmd);
2077 return(0);
2082 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
2083 imapuid_t *uid, char **search, int silent)
2085 char *url, *scheme, *p, *cmd, *server = NULL,
2086 *user = NULL, *auth = NULL, *mailbox = NULL,
2087 *section = NULL;
2088 size_t l;
2089 int rv = URL_IMAP_ERROR;
2092 * Since we're planting nulls, operate on a temporary copy...
2094 scheme = silent ? NULL : "IMAP";
2095 url = cpystr(true_url + 7);
2097 /* Try to pick apart the "iserver" portion */
2098 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
2099 *cmd++ = '\0';
2101 else{
2102 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2103 url ? url : "?"));
2104 cmd = &url[strlen(url)-1]; /* assume only iserver */
2107 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
2108 *p++ = '\0';
2109 server = rfc1738_str(p);
2111 /* only ";auth=*" supported (and also ";auth=anonymous") */
2112 if((p = srchstr(url, ";auth=")) != NULL){
2113 *p = '\0';
2114 auth = rfc1738_str(p + 6);
2117 if(*url)
2118 user = rfc1738_str(url);
2120 else
2121 server = rfc1738_str(url);
2123 if(!*server)
2124 return(url_bogus_imap(&url, scheme, "No server specified"));
2127 * "iserver" in hand, pick apart the "icommand"...
2129 p = NULL;
2130 if(!*cmd || (p = srchstr(cmd, ";type="))){
2131 char *criteria;
2134 * No "icommand" (all top-level folders) or "imailboxlist"...
2136 if(p){
2137 *p = '\0'; /* tie off criteria */
2138 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
2139 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
2140 rv |= URL_IMAP_IMBXLSTLSUB;
2141 else if(strucmp(p, "list"))
2142 return(url_bogus_imap(&url, scheme,
2143 "Invalid list type specified"));
2145 else{
2146 rv |= URL_IMAP_ISERVERONLY;
2147 criteria = "";
2150 /* build folder list from specified server/criteria/list-method */
2151 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
2152 *folder = (char *) fs_get((l+1) * sizeof(char));
2153 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
2154 user ? "user=\"" : "Anonymous",
2155 user ? user : "",
2156 user ? "\"" : "",
2157 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
2158 (*folder)[l] = '\0';
2159 rv |= URL_IMAP_IMAILBOXLIST;
2161 else{
2162 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
2163 *p = '\0'; /* tie off mailbox [uidvalidity] */
2164 if((section = srchstr(p += 6, "/;section=")) != NULL){
2165 *section = '\0'; /* tie off UID */
2166 section = rfc1738_str(section + 10);
2167 /* BUG: verify valid section spec ala rfc 2060 */
2168 dprint((2,
2169 "-- URL IMAP FOLDER: section not used: %s\n",
2170 section ? section : "?"));
2173 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
2174 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
2176 /* optional "uidvalidity"? */
2177 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2178 *p = '\0';
2179 p += 13;
2180 if(!(*uid_val = rfc1738_num(&p)) || *p)
2181 return(url_bogus_imap(&url, scheme,
2182 "Invalid UIDVALIDITY"));
2185 mailbox = rfc1738_str(cmd);
2186 rv = URL_IMAP_IMESSAGEPART;
2188 else{ /* "imessagelist" */
2189 /* optional "uidvalidity"? */
2190 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2191 *p = '\0';
2192 p += 13;
2193 if(!(*uid_val = rfc1738_num(&p)) || *p)
2194 return(url_bogus_imap(&url, scheme,
2195 "Invalid UIDVALIDITY"));
2198 /* optional "enc_search"? */
2199 if((p = strchr(cmd, '?')) != NULL){
2200 *p = '\0';
2201 if(search)
2202 *search = cpystr(rfc1738_str(p + 1));
2203 /* BUG: verify valid search spec ala rfc 2060 */
2206 mailbox = rfc1738_str(cmd);
2207 rv = URL_IMAP_IMESSAGELIST;
2210 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
2211 q_status_message(SM_ORDER, 3, 3,
2212 "Unsupported authentication method. Using standard login.");
2215 * At this point our structure should contain the
2216 * digested url. Now put it together for c-client...
2218 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
2219 + (user ? (strlen(user)+2) : 9);
2220 *folder = (char *) fs_get((l+1) * sizeof(char));
2221 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
2222 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
2223 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
2224 user ? user : "",
2225 user ? "\"" : "",
2226 mailbox);
2227 (*folder)[l] = '\0';
2230 fs_give((void **) &url);
2231 return(rv);
2236 url_bogus_imap(char **freeme, char *url, char *problem)
2238 fs_give((void **) freeme);
2239 (void) url_bogus(url, problem);
2240 return(URL_IMAP_ERROR);
2245 * url_bogus - report url syntax errors and such
2248 url_bogus(char *url, char *reason)
2250 dprint((2, "-- bogus url \"%s\": %s\n",
2251 url ? url : "<NULL URL>", reason ? reason : "?"));
2252 if(url)
2253 q_status_message3(SM_ORDER|SM_DING, 2, 3,
2254 "Malformed \"%.*s\" URL: %.200s",
2255 (void *) (strchr(url, ':') - url), url, reason);
2257 return(0);
2262 /*----------------------------------------------------------------------
2263 Format header text suitable for display
2265 Args: stream -- mail stream for various header text fetches
2266 msgno -- sequence number in stream of message we're interested in
2267 section -- which section of message
2268 env -- pointer to msg's envelope
2269 hdrs -- struct containing what's to get formatted
2270 prefix -- prefix to append to each output line
2271 handlesp -- address of pointer to the message's handles
2272 flags -- FM_ flags, see pith/mailview.h
2273 final_pc -- function to write header text with
2275 Result: 0 if all's well, -1 if write error, 1 if fetch error
2277 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2278 in the local convention.
2280 ----*/
2281 #define FHT_OK 0
2282 #define FHT_WRTERR -1
2283 #define FHT_FTCHERR 1
2285 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
2286 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
2287 fmt_env_t fmt_env, gf_io_t final_pc)
2289 int rv = FHT_OK;
2290 int nfields, i;
2291 char *h = NULL, **fields = NULL, **v, *q, *start,
2292 *finish, *current;
2293 STORE_S *tmp_store;
2294 URL_HILITE_S uh;
2295 gf_io_t tmp_pc, tmp_gc;
2296 struct variable *vars = ps_global->vars;
2298 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2299 gf_set_so_writec(&tmp_pc, tmp_store);
2300 else
2301 return(FHT_WRTERR);
2303 if(!fmt_env)
2304 fmt_env = format_envelope;
2306 if(ps_global->full_header == 2){
2307 rv = format_raw_header(stream, msgno, section, tmp_pc);
2309 else{
2311 * First, calculate how big a fields array we need.
2314 /* Custom header viewing list specified */
2315 if(hdrs->type == HD_LIST){
2316 /* view all these headers */
2317 if(!hdrs->except){
2318 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2319 if(!is_an_env_hdr(q))
2320 nfields++;
2322 /* view all except these headers */
2323 else{
2324 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2325 nfields++;
2327 if(nfields > 1)
2328 nfields--; /* subtract one for ALL_EXCEPT field */
2331 else
2332 nfields = 6; /* default view */
2334 /* allocate pointer space */
2335 if(nfields){
2336 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2337 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2340 if(hdrs->type == HD_LIST){
2341 /* view all these headers */
2342 if(!hdrs->except){
2343 /* put the non-envelope headers in fields */
2344 if(nfields)
2345 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2346 if(!is_an_env_hdr(q))
2347 fields[i++] = q;
2349 /* view all except these headers */
2350 else{
2351 /* put the list of headers not to view in fields */
2352 if(nfields)
2353 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2354 if(strucmp(ALL_EXCEPT, q))
2355 fields[i++] = q;
2358 v = hdrs->h.l;
2360 else{
2361 if(nfields){
2362 fields[i = 0] = "Resent-Date";
2363 fields[++i] = "Resent-From";
2364 fields[++i] = "Resent-To";
2365 fields[++i] = "Resent-cc";
2366 fields[++i] = "Resent-Subject";
2369 v = fields;
2372 /* custom view with exception list */
2373 if(hdrs->type == HD_LIST && hdrs->except){
2375 * Go through each header in h and print it.
2376 * First we check to see if it is an envelope header so we
2377 * can print our envelope version of it instead of the raw version.
2380 /* fetch all the other headers */
2381 if(nfields)
2382 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2384 for(current = h;
2385 h && delineate_this_header(NULL, current, &start, &finish);
2386 current = finish){
2387 char tmp[MAILTMPLEN+1];
2388 char *colon_loc;
2390 colon_loc = strindex(start, ':');
2391 if(colon_loc && colon_loc < finish){
2392 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2393 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2395 else
2396 colon_loc = NULL;
2398 if(colon_loc && is_an_env_hdr(tmp)){
2399 char *dummystart, *dummyfinish;
2402 * Pretty format for env hdrs.
2403 * If the same header appears more than once, only
2404 * print the last to avoid duplicates.
2405 * They should have been combined in the env when parsed.
2407 if(!delineate_this_header(tmp, current+1, &dummystart,
2408 &dummyfinish))
2409 format_env_hdr(stream, msgno, section, env,
2410 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2412 else{
2413 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2414 hdrs->charset, flags)) != 0)
2415 goto write_error;
2416 else
2417 start = finish;
2421 /* custom view or default */
2422 else{
2423 /* fetch the non-envelope headers */
2424 if(nfields)
2425 h = pine_fetchheader_lines(stream, msgno, section, fields);
2427 /* default envelope for default view */
2428 if(hdrs->type == HD_BFIELD)
2429 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2431 /* go through each header in list, v initialized above */
2432 for(; (q = *v) != NULL; v++){
2433 if(is_an_env_hdr(q)){
2434 /* pretty format for env hdrs */
2435 format_env_hdr(stream, msgno, section, env,
2436 fmt_env, tmp_pc, q, hdrs->charset, flags);
2438 else{
2440 * Go through h finding all occurences of this header
2441 * and all continuation lines, and output.
2443 for(current = h;
2444 h && delineate_this_header(q,current,&start,&finish);
2445 current = finish){
2446 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2447 hdrs->charset, flags)) != 0)
2448 goto write_error;
2449 else
2450 start = finish;
2458 write_error:
2460 gf_clear_so_writec(tmp_store);
2462 if(!rv){ /* valid data? Do wrapping and filtering... */
2463 int column;
2464 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2465 STORE_S *df_store = NULL;
2467 so_seek(tmp_store, 0L, 0);
2469 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2472 * Test for and act on any display filter
2473 * This barely makes sense. The display filter is going
2474 * to be getting UTF-8'ized headers here. In pre-alpine
2475 * pine the display filter was being fed already decoded
2476 * headers in whatever character set they were in.
2477 * The good news is that that didn't make much
2478 * sense either, so this shouldn't break anything.
2479 * It seems unlikely that anybody is doing anything useful
2480 * with the header part of display filters.
2482 if(ps_global->tools.display_filter
2483 && ps_global->tools.display_filter_trigger
2484 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2485 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2487 gf_set_so_writec(&tmp_pc, df_store);
2488 gf_set_so_readc(&tmp_gc, df_store);
2489 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2490 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2491 _("Formatting error: %s"), errstr);
2492 rv = FHT_WRTERR;
2494 else
2495 so_seek(df_store, 0L, 0);
2497 gf_clear_so_writec(df_store);
2499 else{
2500 q_status_message(SM_ORDER | SM_DING, 3, 3,
2501 "No space for filtered text.");
2502 rv = FHT_WRTERR;
2505 else{
2506 so_seek(tmp_store, 0L, 0);
2507 gf_set_so_readc(&tmp_gc, tmp_store);
2510 if(!rv){
2511 int *margin, wrapflags = GFW_ONCOMMA;
2513 gf_filter_init();
2514 gf_link_filter(gf_local_nvtnl, NULL);
2516 if((F_ON(F_VIEW_SEL_URL, ps_global)
2517 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2518 || F_ON(F_SCAN_ADDR, ps_global))
2519 && handlesp){
2520 gf_link_filter(gf_line_test,
2521 gf_line_test_opt(url_hilite_hdr,
2522 gf_url_hilite_opt(&uh,handlesp,1)));
2523 wrapflags |= GFW_HANDLES;
2526 if((flags & FM_DISPLAY)
2527 && !(flags & FM_NOCOLOR)
2528 && pico_usingcolor()
2529 && ((VAR_NORM_FORE_COLOR
2530 && VAR_HEADER_GENERAL_FORE_COLOR
2531 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2533 (VAR_NORM_BACK_COLOR
2534 && VAR_HEADER_GENERAL_BACK_COLOR
2535 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2536 wrapflags |= GFW_HDRCOLOR;
2538 if((flags & FM_DISPLAY)
2539 && !(flags & FM_NOCOLOR)
2540 && pico_usingcolor()
2541 && ps_global->hdr_colors){
2542 gf_link_filter(gf_line_test,
2543 gf_line_test_opt(color_headers, NULL));
2544 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2547 if(prefix && *prefix)
2548 column = MAX(column-strlen(prefix), 50);
2550 margin = format_view_margin();
2552 if(!(flags & FM_NOWRAP))
2553 gf_link_filter(gf_wrap,
2554 gf_wrap_filter_opt(column, column,
2555 (flags & FM_NOINDENT) ? NULL : margin,
2556 4, wrapflags));
2558 if(prefix && *prefix)
2559 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2561 if((flags & FM_DISPLAY)
2562 && !(flags & FM_NOCOLOR)
2563 && pico_usingcolor()
2564 && ((VAR_NORM_FORE_COLOR
2565 && VAR_HEADER_GENERAL_FORE_COLOR
2566 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2568 (VAR_NORM_BACK_COLOR
2569 && VAR_HEADER_GENERAL_BACK_COLOR
2570 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2571 int right_margin;
2572 int total_wid;
2574 right_margin = margin ? margin[1] : 0;
2575 total_wid = column - right_margin;
2577 gf_link_filter(gf_line_test,
2578 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2579 wrapflags |= GFW_HANDLES;
2582 gf_link_filter(gf_nvtnl_local, NULL);
2584 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2585 rv = FHT_WRTERR;
2586 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2587 "Can't build header : %.200s", errstr);
2591 if(df_store){
2592 gf_clear_so_readc(df_store);
2593 so_give(&df_store);
2595 else
2596 gf_clear_so_readc(tmp_store);
2599 so_give(&tmp_store);
2601 if(h)
2602 fs_give((void **)&h);
2604 if(fields)
2605 fs_give((void **)&fields);
2607 return(rv);
2611 /*----------------------------------------------------------------------
2612 Format RAW header text for display
2614 Args: stream -- mail stream for various header text fetches
2615 rawno -- sequence number in stream of message we're interested in
2616 section -- which section of message
2617 pc -- function to write header text with
2619 Result: 0 if all's well, -1 if write error, 1 if fetch error
2621 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2622 in the local convention.
2624 ----*/
2626 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2628 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2629 unsigned char c;
2631 if(h){
2632 while(*h){
2633 if(ISRFCEOL(h)){
2634 h += 2;
2635 if(!gf_puts(NEWLINE, pc))
2636 return(FHT_WRTERR);
2638 if(ISRFCEOL(h)) /* all done! */
2639 return(FHT_OK);
2641 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2642 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2643 c = (unsigned char) *h++;
2644 if(!((*pc)(c >= 0x80 ? '~' : '^')
2645 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2646 return(FHT_WRTERR);
2648 else if(!(*pc)(*h++))
2649 return(FHT_WRTERR);
2652 else
2653 return(FHT_FTCHERR);
2655 return(FHT_OK);
2660 /*----------------------------------------------------------------------
2661 Format c-client envelope data suitable for display
2663 Args: s -- mail stream for various header text fetches
2664 n -- raw sequence number in stream of message we're interested in
2665 sect -- which section of message
2666 e -- pointer to msg's envelope
2667 pc -- function to write header text with
2668 which -- which header lines to write
2669 oacs --
2670 flags -- FM_ flags, see pith/mailview.h
2672 Result: 0 if all's well, -1 if write error, 1 if fetch error
2674 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2675 in the local convention.
2677 ----*/
2678 void
2679 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2680 long int which, char *oacs, int flags)
2682 char *q, *p2, buftmp[MAILTMPLEN];
2684 if(!e)
2685 return;
2687 if((which & FE_DATE) && e->date) {
2688 q = "Date: ";
2689 snprintf(buftmp, sizeof(buftmp), "%s",
2690 F_ON(F_DATES_TO_LOCAL,ps_global)
2691 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2692 buftmp[sizeof(buftmp)-1] = '\0';
2693 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2694 SIZEOF_20KBUF, buftmp);
2695 gf_puts(q, pc);
2696 format_env_puts(p2, pc);
2697 gf_puts(NEWLINE, pc);
2700 if((which & FE_FROM) && e->from)
2701 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2703 if((which & FE_REPLYTO) && e->reply_to
2704 && (!e->from || !address_is_same(e->reply_to, e->from)))
2705 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2707 if((which & FE_TO) && e->to)
2708 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2710 if((which & FE_CC) && e->cc)
2711 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2713 if((which & FE_BCC) && e->bcc)
2714 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2716 if((which & FE_RETURNPATH) && e->return_path)
2717 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2718 flags, oacs, pc);
2720 if((which & FE_NEWSGROUPS) && e->newsgroups){
2721 int bogus = NIL;
2722 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2723 if (!e->ngpathexists && e->message_id &&
2724 strncmp (e->message_id,"<alpine.",8) &&
2725 strncmp (e->message_id,"<Pine.",6) &&
2726 strncmp (e->message_id,"<MS-C.",6) &&
2727 strncmp (e->message_id,"<MailManager.",13) &&
2728 strncmp (e->message_id,"<EasyMail.",11) &&
2729 strncmp (e->message_id,"<ML-",4)) bogus = T;
2731 if(bogus)
2732 q_status_message(SM_ORDER, 0, 3,
2733 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2736 if((which & FE_FOLLOWUPTO) && e->followup_to)
2737 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2739 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2740 char *freeme = NULL;
2742 q = "Subject: ";
2743 gf_puts(q, pc);
2745 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2747 if(flags & FM_DISPLAY
2748 && (ps_global->display_keywords_in_subject
2749 || ps_global->display_keywordinits_in_subject)){
2751 /* don't bother if no keywords are defined */
2752 if(some_user_flags_defined(s))
2753 p2 = freeme = prepend_keyword_subject(s, n, p2,
2754 ps_global->display_keywords_in_subject ? KW : KWInit,
2755 NULL, ps_global->VAR_KW_BRACES);
2758 format_env_puts(p2, pc);
2760 if(freeme)
2761 fs_give((void **) &freeme);
2763 gf_puts(NEWLINE, pc);
2766 if((which & FE_SENDER) && e->sender
2767 && (!e->from || !address_is_same(e->sender, e->from)))
2768 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2770 if((which & FE_MESSAGEID) && e->message_id){
2771 q = "Message-ID: ";
2772 gf_puts(q, pc);
2773 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2774 format_env_puts(p2, pc);
2775 gf_puts(NEWLINE, pc);
2778 if((which & FE_INREPLYTO) && e->in_reply_to){
2779 q = "In-Reply-To: ";
2780 gf_puts(q, pc);
2781 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);
2782 format_env_puts(p2, pc);
2783 gf_puts(NEWLINE, pc);
2786 if((which & FE_REFERENCES) && e->references) {
2787 q = "References: ";
2788 gf_puts(q, pc);
2789 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2790 format_env_puts(p2, pc);
2791 gf_puts(NEWLINE, pc);
2799 * The argument fieldname is something like "Subject:..." or "Subject".
2800 * Look through the specs in speccolor for a match of the fieldname,
2801 * and return the color that goes with any match, or NULL.
2802 * Caller should free the color.
2804 COLOR_PAIR *
2805 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2807 SPEC_COLOR_S *hc = NULL;
2808 COLOR_PAIR *color_pair = NULL;
2809 char *colon, *fname;
2810 char fbuf[FBUF_LEN+1];
2811 int gotit;
2812 PATTERN_S *pat;
2814 colon = strindex(fieldname, ':');
2815 if(colon){
2816 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2817 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2818 fname = fbuf;
2820 else
2821 fname = fieldname;
2823 if(fname && *fname)
2824 for(hc = speccolor; hc; hc = hc->next)
2825 if(hc->spec && !strucmp(fname, hc->spec)){
2826 if(!hc->val)
2827 break;
2829 gotit = 0;
2830 for(pat = hc->val; !gotit && pat; pat = pat->next)
2831 if(srchstr(value, pat->substring))
2832 gotit++;
2834 if(gotit)
2835 break;
2838 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2839 color_pair = new_color_pair(hc->fg, hc->bg);
2841 return(color_pair);
2846 * The argument fieldname is something like "Subject:..." or "Subject".
2847 * Look through the specs in hdr_colors for a match of the fieldname,
2848 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2851 any_hdr_color(char *fieldname)
2853 SPEC_COLOR_S *hc = NULL;
2854 char *colon, *fname;
2855 char fbuf[FBUF_LEN+1];
2857 colon = strindex(fieldname, ':');
2858 if(colon){
2859 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2860 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2861 fname = fbuf;
2863 else
2864 fname = fieldname;
2866 if(fname && *fname)
2867 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2868 if(hc->spec && !strucmp(fname, hc->spec))
2869 break;
2871 return(hc ? 1 : 0);
2875 /*----------------------------------------------------------------------
2876 Format an address field, wrapping lines nicely at commas
2878 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2879 addr -- ADDRESS structure to format
2881 Result: A formatted, malloced string is returned.
2882 ----------------------------------------------------------------------*/
2883 void
2884 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2885 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2887 char *ptmp, *mtmp;
2888 int trailing = 0, group = 0;
2889 ADDRESS *atmp;
2891 if(!addr)
2892 return;
2895 * quickly run down address list to make sure none are patently bogus.
2896 * If so, just blat raw field out.
2898 for(atmp = addr; stream && atmp; atmp = atmp->next)
2899 if(atmp->host && atmp->host[0] == '.'){
2900 char *field, *fields[2];
2902 fields[1] = NULL;
2903 fields[0] = cpystr(field_name);
2904 if((ptmp = strchr(fields[0], ':')) != NULL)
2905 *ptmp = '\0';
2907 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2908 char *h, *t;
2910 for(t = h = field; *h ; t++)
2911 if(*t == '\015' && *(t+1) == '\012'){
2912 *t = '\0'; /* tie off line */
2913 format_env_puts(h, pc);
2914 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2915 gf_puts(NEWLINE, pc); /* more to write */
2916 else
2917 break;
2919 else if(!*t){ /* shouldn't happen much */
2920 if(h != t)
2921 format_env_puts(h, pc);
2923 break;
2926 fs_give((void **)&field);
2929 fs_give((void **)&fields[0]);
2930 gf_puts(NEWLINE, pc);
2931 dprint((2, "Error in \"%s\" field address\n",
2932 field_name ? field_name : "?"));
2933 return;
2936 gf_puts(field_name, pc);
2938 while(addr){
2939 atmp = addr->next; /* remember what's next */
2940 addr->next = NULL;
2941 if(!addr->host && addr->mailbox){
2942 mtmp = addr->mailbox;
2943 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2944 (unsigned char *)tmp_20k_buf,
2945 SIZEOF_20KBUF, addr->mailbox));
2948 ptmp = addr->personal; /* RFC 1522 personal name? */
2949 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2950 tmp_20k_buf[10000-1] = '\0';
2952 if(!trailing) /* 1st pass, just address */
2953 trailing++;
2954 else{ /* else comma, unless */
2955 if(!((group == 1 && addr->host) /* 1st addr in group, */
2956 || (!addr->host && !addr->mailbox))){ /* or end of group */
2957 gf_puts(",", pc);
2958 #if 0
2959 gf_puts(NEWLINE, pc); /* ONE address/line please */
2960 gf_puts(" ", pc);
2961 #endif
2964 gf_puts(" ", pc);
2967 pine_rfc822_write_address_noquote(addr, pc, &group);
2969 addr->personal = ptmp; /* restore old personal ptr */
2970 if(!addr->host && addr->mailbox){
2971 fs_give((void **)&addr->mailbox);
2972 addr->mailbox = mtmp;
2975 addr->next = atmp;
2976 addr = atmp;
2979 gf_puts(NEWLINE, pc);
2985 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
2986 /* RFC822 continuation, must start with CRLF */
2987 #define RFC822CONT "\015\012 "
2989 /* Write RFC822 address with some quoting turned off.
2990 * Accepts:
2991 * address to interpret
2993 * (This is a copy of c-client's rfc822_write_address except
2994 * we don't quote double quote and dot in personal names. It writes
2995 * to a gf_io_t instead of to a buffer so that we don't have to worry
2996 * about fixed sized buffer overflowing. It's also special cased to deal
2997 * with only a single address.)
2999 * The idea is that there are some places where we'd just like to display
3000 * the personal name as is before applying confusing quoting. However,
3001 * we do want to be careful not to break things that should be quoted so
3002 * we'll only use this where we are sure. Quoting may look ugly but it
3003 * doesn't usually break anything.
3005 void
3006 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
3008 extern const char *rspecials;
3010 if (adr->host) { /* ordinary address? */
3011 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3012 else { /* no, must use phrase <route-addr> form */
3013 if (adr->personal)
3014 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3016 gf_puts(" <", pc); /* write address delimiter */
3017 pine_rfc822_address(adr, pc);
3018 gf_puts (">", pc); /* closing delimiter */
3021 if(*group)
3022 (*group)++;
3024 else if (adr->mailbox) { /* start of group? */
3025 /* yes, write group name */
3026 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3028 gf_puts (": ", pc); /* write group identifier */
3029 *group = 1; /* in a group */
3031 else if (*group) { /* must be end of group (but be paranoid) */
3032 gf_puts (";", pc);
3033 *group = 0; /* no longer in that group */
3038 /* Write RFC822 route-address to string
3039 * Accepts:
3040 * address to interpret
3043 void
3044 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
3046 extern char *wspecials;
3048 if (adr && adr->host) { /* no-op if no address */
3049 if (adr->adl) { /* have an A-D-L? */
3050 gf_puts (adr->adl, pc);
3051 gf_puts (":", pc);
3053 /* write mailbox name */
3054 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3055 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3056 gf_puts ("@", pc); /* host delimiter */
3057 gf_puts (adr->host, pc); /* write host name */
3063 /* Concatenate RFC822 string
3064 * Accepts:
3065 * pointer to string to concatenate
3066 * list of special characters
3069 void
3070 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
3072 char *s;
3074 if (strpbrk (src,specials)) { /* any specials present? */
3075 gf_puts ("\"", pc); /* opening quote */
3076 /* truly bizarre characters in there? */
3077 while ((s = strpbrk (src,"\\\"")) != NULL) {
3078 char save[2];
3080 /* turn it into a null-terminated piece */
3081 save[0] = *s;
3082 save[1] = '\0';
3083 *s = '\0';
3084 gf_puts (src, pc); /* yes, output leader */
3085 *s = save[0];
3086 gf_puts ("\\", pc); /* quoting */
3087 gf_puts (save, pc); /* output the bizarre character */
3088 src = ++s; /* continue after the bizarre character */
3090 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3091 gf_puts ("\"", pc); /* closing quote */
3093 else gf_puts (src, pc); /* otherwise it's the easy case */
3097 /*----------------------------------------------------------------------
3098 Format an address field, wrapping lines nicely at commas
3100 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3101 newsgrps -- ADDRESS structure to format
3103 Result: A formatted, malloced string is returned.
3105 The resuling lines formatted are 80 columns wide.
3106 ----------------------------------------------------------------------*/
3107 void
3108 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
3110 char buf[MAILTMPLEN];
3111 int trailing = 0, llen, alen;
3112 char *next_ng;
3114 if(!newsgrps || !*newsgrps)
3115 return;
3117 gf_puts(field_name, pc);
3119 llen = strlen(field_name);
3120 while(*newsgrps){
3121 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3122 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3123 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3124 newsgrps = next_ng;
3125 if(*newsgrps)
3126 newsgrps++;
3127 alen = strlen(buf);
3128 if(!trailing){ /* first time thru, just address */
3129 llen += alen;
3130 trailing++;
3132 else{ /* else preceding comma */
3133 gf_puts(",", pc);
3134 llen++;
3136 if(alen + llen + 1 > 76){
3137 gf_puts(NEWLINE, pc);
3138 gf_puts(" ", pc);
3139 llen = alen + 5;
3141 else{
3142 gf_puts(" ", pc);
3143 llen += alen + 1;
3147 if(alen && llen > 76){ /* handle long addresses */
3148 register char *q, *p = &buf[alen-1];
3150 while(p > buf){
3151 if(isspace((unsigned char)*p)
3152 && (llen - (alen - (int)(p - buf))) < 76){
3153 for(q = buf; q < p; q++)
3154 (*pc)(*q); /* write character */
3156 gf_puts(NEWLINE, pc);
3157 gf_puts(" ", pc);
3158 gf_puts(p, pc);
3159 break;
3161 else
3162 p--;
3165 if(p == buf) /* no reasonable break point */
3166 gf_puts(buf, pc);
3168 else
3169 gf_puts(buf, pc);
3172 gf_puts(NEWLINE, pc);
3177 /*----------------------------------------------------------------------
3178 Format a text field that's part of some raw (non-envelope) message header
3180 Args: start --
3181 finish --
3182 pc --
3184 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3186 ----------------------------------------------------------------------*/
3188 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
3190 register char *current;
3191 unsigned char *p, *tmp = NULL, c;
3192 size_t n, len;
3193 char ch;
3194 int rv = FHT_OK;
3196 ch = *finish;
3197 *finish = '\0';
3199 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3200 len = n+1;
3201 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3203 else{
3204 len = SIZEOF_20KBUF;
3205 p = (unsigned char *) tmp_20k_buf;
3208 if(islower((unsigned char)(*start)))
3209 *start = toupper((unsigned char)(*start));
3211 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3213 /* output from start to finish */
3214 while(*current && rv == FHT_OK)
3215 if(ISRFCEOL(current)){
3216 if(!gf_puts(NEWLINE, pc))
3217 rv = FHT_WRTERR;
3219 current += 2;
3221 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3222 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3223 c = (unsigned char) *current++;
3224 if(!((*pc)(c >= 0x80 ? '~' : '^')
3225 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3226 rv = FHT_WRTERR;
3228 else if(!(*pc)(*current++))
3229 rv = FHT_WRTERR;
3231 if(tmp)
3232 fs_give((void **) &tmp);
3234 *finish = ch;
3236 return(rv);
3242 /*----------------------------------------------------------------------
3243 Format a text field that's part of some raw (non-envelope) message header
3245 Args: s --
3246 pc --
3248 Result: Output
3250 ----------------------------------------------------------------------*/
3252 format_env_puts(char *s, gf_io_t pc)
3254 if(ps_global->pass_ctrl_chars)
3255 return(gf_puts(s, pc));
3257 for(; *s; s++)
3258 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3259 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3260 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3261 return(0);
3263 else if(!(*pc)(*s))
3264 return(0);
3266 return(1);
3270 char *
3271 display_parameters(PARAMETER *params)
3273 int n, longest = 0;
3274 char *d, *printme;
3275 PARAMETER *p;
3276 PARMLIST_S *parmlist;
3278 for(p = params; p; p = p->next) /* ok if we include *'s */
3279 if(p->attribute && (n = strlen(p->attribute)) > longest)
3280 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3282 d = tmp_20k_buf;
3283 tmp_20k_buf[0] = '\0';
3284 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3285 n = 0; /* n overloaded */
3286 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3287 if(n++){
3288 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3289 tmp_20k_buf[10000-1] = '\0';
3290 d += strlen(d);
3293 if(parmlist->value){
3294 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3295 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3296 sqzspaces(printme);
3298 else
3299 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3301 else
3302 printme = "";
3304 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3305 parmlist->attrib ? parmlist->attrib : "", printme);
3307 tmp_20k_buf[10000-1] = '\0';
3308 d += strlen(d);
3311 rfc2231_free_parmlist(&parmlist);
3314 return(tmp_20k_buf);
3318 /*----------------------------------------------------------------------
3319 Fetch the requested header fields from the msgno specified
3321 Args: stream -- mail stream of open folder
3322 msgno -- number of message to get header lines from
3323 fields -- array of pointers to desired fields
3325 Returns: allocated string containing matched header lines,
3326 NULL on error.
3327 ----*/
3328 char *
3329 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3331 STRINGLIST *sl;
3332 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3333 char **pflds = NULL, **pp = NULL, **qq;
3336 * If the user misconfigures it is possible to have one of the fields
3337 * set to the empty string instead of a header name. We want to catch
3338 * that here instead of asking the server the nonsensical question.
3340 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3341 if(!**pp)
3342 break;
3344 if(pp && *pp){ /* found an empty header field, fix it */
3345 pflds = copy_list_array(fields);
3346 for(pp = pflds; pp && *pp; pp++){
3347 if(!**pp){ /* scoot rest of the lines up */
3348 free_this = *pp;
3349 for(qq = pp; *qq; qq++)
3350 *qq = *(qq+1);
3352 if(free_this)
3353 fs_give((void **) &free_this);
3357 /* no headers to look for, return NULL */
3358 if(pflds && !*pflds && !(flags & FT_NOT)){
3359 free_list_array(&pflds);
3360 return(NULL);
3363 else
3364 pflds = fields;
3366 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3367 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3368 if (sl)
3369 free_strlst(&sl);
3371 if(!h){
3372 if(pflds && pflds != fields)
3373 free_list_array(&pflds);
3375 return(NULL);
3378 while(find_field(&h, tmp, sizeof(tmp))){
3379 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3382 /* interesting field? */
3383 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3385 * Hold off allocating space for matching fields until
3386 * we at least find one to copy...
3388 if(!match)
3389 match = m = fs_get(strlen(h) + strlen(p) + 1);
3391 while(*p) /* copy field name */
3392 *m++ = *p++;
3394 while(*h && (*m++ = *h++)) /* header includes colon */
3395 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3396 break;
3398 *m = '\0'; /* tie off match string */
3400 else{ /* no match, pass this field */
3401 while(*h && !(*h++ == '\n'
3402 && (*h == '\r' || !isspace((unsigned char)*h))))
3407 if(pflds && pflds != fields)
3408 free_list_array(&pflds);
3410 return(match ? match : cpystr(""));
3415 find_field(char **h, char *tmp, size_t ntmp)
3417 char *otmp = tmp;
3419 if(!h || !*h || !**h || isspace((unsigned char)**h))
3420 return(0);
3422 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3423 *tmp++ = *(*h)++;
3425 *tmp = '\0';
3426 return(1);
3430 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3434 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3436 if(cp && cp->fg){
3437 if(_last_embedded_fg_color)
3438 fs_give((void **)&_last_embedded_fg_color);
3440 _last_embedded_fg_color = cpystr(cp->fg);
3442 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3443 gf_puts(color_to_asciirgb(cp->fg), pc)))
3444 return 0;
3447 if(cp && cp->bg){
3448 if(_last_embedded_bg_color)
3449 fs_give((void **)&_last_embedded_bg_color);
3451 _last_embedded_bg_color = cpystr(cp->bg);
3453 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3454 gf_puts(color_to_asciirgb(cp->bg), pc)))
3455 return 0;
3458 return 1;
3462 COLOR_PAIR *
3463 get_cur_embedded_color(void)
3465 COLOR_PAIR *ret;
3467 if(_last_embedded_fg_color && _last_embedded_bg_color)
3468 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3469 else
3470 ret = pico_get_cur_color();
3472 return(ret);
3476 void
3477 clear_cur_embedded_color(void)
3479 if(_last_embedded_fg_color)
3480 fs_give((void **)&_last_embedded_fg_color);
3482 if(_last_embedded_bg_color)
3483 fs_give((void **)&_last_embedded_bg_color);
3488 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3490 *len = 0;
3492 if(pico_usingcolor()){
3493 char *fg = NULL, *bg = NULL, *s;
3495 if(ps_global->VAR_SLCTBL_FORE_COLOR
3496 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3497 ps_global->VAR_NORM_FORE_COLOR))
3498 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3500 if(ps_global->VAR_SLCTBL_BACK_COLOR
3501 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3502 ps_global->VAR_NORM_BACK_COLOR))
3503 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3505 if(bg || fg){
3506 COLOR_PAIR *tmp;
3509 * The blacks are just known good colors for
3510 * testing whether the other color is good.
3512 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3513 bg ? bg : colorx(COL_BLACK))) != NULL){
3514 if(pico_is_good_colorpair(tmp))
3515 for(s = color_embed(fg, bg);
3516 (*len) < buflen && (colorstring[*len] = *s);
3517 s++, (*len)++)
3520 free_color_pair(&tmp);
3524 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3525 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3526 *len += 2;
3530 colorstring[buflen-1] = '\0';
3532 return(*len != 0);
3537 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3539 *len = 0;
3540 if(pico_usingcolor()){
3541 char *fg = NULL, *bg = NULL, *s;
3542 char *basefg = NULL, *basebg = NULL;
3544 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3545 : ps_global->VAR_NORM_FORE_COLOR;
3546 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3547 : ps_global->VAR_NORM_BACK_COLOR;
3550 * We need to change the fg and bg colors back even if they
3551 * are the same as the Normal Colors so that color_a_quote
3552 * will have a chance to pick up those colors as the ones to
3553 * switch to. We don't do this before the handle above so that
3554 * the quote color will flow into the selectable item when
3555 * the selectable item color is partly the same as the
3556 * normal color. That is, suppose the normal color was black on
3557 * cyan and the selectable color was blue on cyan, only a fg color
3558 * change. We preserve the only-a-fg-color-change in a quote by
3559 * letting the quote background color flow into the selectable text.
3561 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3562 fg = basefg;
3564 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3565 bg = basebg;
3567 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3568 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3569 *len = 2;
3572 if(fg || bg)
3573 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3577 colorstring[buflen-1] = '\0';
3579 return(*len != 0);
3584 * Helper routine that is of limited use.
3585 * We need to tally up the screen width of
3586 * a UTF-8 string as we go through the string.
3587 * We just want the width of the character starting
3588 * at str (and no longer than remaining_octets).
3589 * If we're plopped into the middle of a UTF-8
3590 * character we just want to return width zero.
3593 width_at_this_position(unsigned char *str, unsigned long n)
3595 unsigned char *inputp = str;
3596 unsigned long remaining_octets = n;
3597 UCS ucs;
3598 int width = 0;
3600 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3601 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3602 width = wcellwidth(ucs);
3603 /* Writechar will print a '?' */
3604 if(width < 0)
3605 width = 1;
3608 return(width);