X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/93a18dd7a6b92922361383a4e6152380da5f3583..ec173fd2c:/uip/mhical.c?ds=sidebyside diff --git a/uip/mhical.c b/uip/mhical.c index 3249d085..078ad37e 100644 --- a/uip/mhical.c +++ b/uip/mhical.c @@ -6,11 +6,22 @@ */ #include "h/mh.h" +#include "sbr/fmt_new.h" +#include "sbr/getarguments.h" +#include "sbr/concat.h" +#include "sbr/smatch.h" +#include "sbr/ambigsw.h" +#include "sbr/path.h" +#include "sbr/print_version.h" +#include "sbr/print_help.h" +#include "sbr/error.h" #include "h/icalendar.h" +#include "sbr/datetime.h" #include "sbr/icalparse.h" -#include +#include "h/fmt_scan.h" #include "h/addrsbr.h" #include "h/mts.h" +#include "h/done.h" #include "h/utils.h" #include @@ -23,7 +34,7 @@ typedef enum act { ACT_CANCEL } act; -static void convert_to_reply (contentline *, act); +static int convert_to_reply (contentline *, act, char *); static void convert_to_cancellation (contentline *); static void convert_common (contentline *, act); static void dump_unfolded (FILE *, contentline *); @@ -40,6 +51,7 @@ static char *fold (char *, int); X("format string", 5, FMTSW) \ X("infile", 0, INFILESW) \ X("outfile", 0, OUTFILESW) \ + X("attendee", 0, ATTENDEESW) \ X("contenttype", 0, CONTENTTYPESW) \ X("nocontenttype", 0, NCONTENTTYPESW) \ X("unfold", 0, UNFOLDSW) \ @@ -59,7 +71,8 @@ vevent vevents = { NULL, NULL, NULL}; int parser_status = 0; int -main (int argc, char *argv[]) { +main (int argc, char *argv[]) +{ /* RFC 5322 § 3.3 date-time format, including the optional day-of-week and not including the optional seconds. The zone is required by the RFC but not always output by this @@ -69,14 +82,16 @@ main (int argc, char *argv[]) { act action = ACT_NONE; char *infile = NULL, *outfile = NULL; FILE *inputfile = NULL, *outputfile = NULL; - int contenttype = 0, unfold = 0; + char *attendee = NULL; + bool contenttype = false; + bool unfold = false; vevent *v, *nextvevent; char *form = "mhical.24hour", *format = NULL; char **argp, **arguments, *cp; icaldebug = 0; /* Global provided by bison (with name-prefix "ical"). */ - if (nmh_init(argv[0], 2)) { return 1; } + if (nmh_init(argv[0], true, false)) { return 1; } arguments = getarguments (invo_name, argc, argv, 1); argp = arguments; @@ -91,7 +106,7 @@ main (int argc, char *argv[]) { ambigsw (cp, switches); done (1); case UNKWNSW: - adios (NULL, "-%s unknown", cp); + die("-%s unknown", cp); case HELPSW: { char buf[128]; @@ -108,7 +123,7 @@ main (int argc, char *argv[]) { case REPLYSW: if (! (cp = *argp++) || (*cp == '-' && cp[1])) - adios (NULL, "missing argument to %s", argp[-2]); + die("missing argument to %s", argp[-2]); if (! strcasecmp (cp, "accept")) { action = ACT_ACCEPT; } else if (! strcasecmp (cp, "decline")) { @@ -118,7 +133,7 @@ main (int argc, char *argv[]) { } else if (! strcasecmp (cp, "delegate")) { action = ACT_DELEGATE; } else { - adios (NULL, "Unknown action: %s", cp); + die("Unknown action: %s", cp); } continue; @@ -128,35 +143,41 @@ main (int argc, char *argv[]) { case FORMSW: if (! (form = *argp++) || *form == '-') - adios (NULL, "missing argument to %s", argp[-2]); + die("missing argument to %s", argp[-2]); format = NULL; continue; case FMTSW: if (! (format = *argp++) || *format == '-') - adios (NULL, "missing argument to %s", argp[-2]); + die("missing argument to %s", argp[-2]); form = NULL; continue; case INFILESW: if (! (cp = *argp++) || (*cp == '-' && cp[1])) - adios (NULL, "missing argument to %s", argp[-2]); + die("missing argument to %s", argp[-2]); infile = *cp == '-' ? mh_xstrdup(cp) : path (cp, TFILE); continue; case OUTFILESW: if (! (cp = *argp++) || (*cp == '-' && cp[1])) - adios (NULL, "missing argument to %s", argp[-2]); + die("missing argument to %s", argp[-2]); outfile = *cp == '-' ? mh_xstrdup(cp) : path (cp, TFILE); continue; + case ATTENDEESW: + if (! (cp = *argp++) || (*cp == '-' && cp[1])) + die("missing argument to %s", argp[-2]); + attendee = cp; + continue; + case CONTENTTYPESW: - contenttype = 1; + contenttype = true; continue; case NCONTENTTYPESW: - contenttype = 0; + contenttype = false; continue; case UNFOLDSW: - unfold = 1; + unfold = true; continue; } } @@ -212,9 +233,12 @@ main (int argc, char *argv[]) { if (action == ACT_CANCEL) { convert_to_cancellation (v->contentlines); } else { - convert_to_reply (v->contentlines, action); + parser_status += + convert_to_reply (v->contentlines, action, attendee); + } + if (parser_status == 0) { + output (outputfile, v->contentlines, contenttype); } - output (outputfile, v->contentlines, contenttype); } free_contentlines (v->contentlines); @@ -237,13 +261,15 @@ main (int argc, char *argv[]) { free (outfile); } - return parser_status; + return parser_status > 0 ? 1 : 0; } /* * - Change METHOD from REQUEST to REPLY. * - Change PRODID. - * - Remove all ATTENDEE lines for other users (based on ismymbox ()). + * - Remove all ATTENDEE lines for other users (based on ismymbox()). + * If more than one address matches ismymbox(), the attendee argument, + * from the -attendee switch, must be used to select one. * - For the user's ATTENDEE line: * - Remove ROLE and RSVP parameters. * - Change PARTSTAT value to indicate reply action, e.g., ACCEPTED, @@ -251,13 +277,13 @@ main (int argc, char *argv[]) { * - Insert action at beginning of SUMMARY value. * - Remove all X- lines. * - Update DTSTAMP with current timestamp. - * - Remove all DESCRIPTION lines. * - Excise VALARM sections. */ -static void -convert_to_reply (contentline *clines, act action) { +static int +convert_to_reply (contentline *clines, act action, char *attendee) +{ char *partstat = NULL; - int found_my_attendee_line = 0; + unsigned int found_my_attendee_line = 0; contentline *node; convert_common (clines, action); @@ -300,8 +326,9 @@ convert_to_reply (contentline *clines, act action) { /* Need to flush getname after use. */ while (getname ("")) { continue; } - if (ismymbox (mn)) { - found_my_attendee_line = 1; + if (ismymbox (mn) && + (attendee == NULL || ! strcasecmp(mn->m_text, attendee))) { + ++found_my_attendee_line; for (p = node->params; p && p->param_name; p = p->next) { value_list *v; @@ -323,7 +350,7 @@ convert_to_reply (contentline *clines, act action) { } } - if (! found_my_attendee_line) { + if (found_my_attendee_line == 0) { /* Generate and attach an ATTENDEE line for me. */ contentline *node; @@ -338,16 +365,13 @@ convert_to_reply (contentline *clines, act action) { add_param_value (new_node, mh_xstrdup (getfullname ())); new_node->value = concat ("MAILTO:", getlocalmbox (), NULL); } + } else if (found_my_attendee_line > 1) { + inform("Multiple attendees match your address, " + "re-run with -attendee switch"); + return 1; } - /* Call find_contentline () with node as argument to find multiple - matching contentlines. */ - for (node = clines; - (node = find_contentline (node, "DESCRIPTION", 0)); - node = node->next) { - /* ACCEPT, at least, replies don't seem to have DESCRIPTIONS. */ - remove_contentline (node); - } + return 0; } /* @@ -361,7 +385,8 @@ convert_to_reply (contentline *clines, act action) { * - Excise VALARM sections. */ static void -convert_to_cancellation (contentline *clines) { +convert_to_cancellation (contentline *clines) +{ contentline *node; convert_common (clines, ACT_CANCEL); @@ -383,9 +408,10 @@ convert_to_cancellation (contentline *clines) { } static void -convert_common (contentline *clines, act action) { +convert_common (contentline *clines, act action) +{ contentline *node; - int in_valarm; + bool in_valarm; if ((node = find_contentline (clines, "METHOD", 0))) { free (node->value); @@ -394,7 +420,7 @@ convert_common (contentline *clines, act action) { if ((node = find_contentline (clines, "PRODID", 0))) { free (node->value); - node->value = mh_xstrdup ("nmh mhical v0.1"); + node->value = mh_xstrdup ("nmh mhical v0.5"); } if ((node = find_contentline (clines, "VERSION", 0))) { @@ -405,8 +431,8 @@ convert_common (contentline *clines, act action) { if (strcmp (node->value, "2.0")) { inform("supports the Version 2.0 specified by RFC 5545 " - "but iCalendar object has Version %s, continuing...", - node->value); + "but iCalendar object has Version %s, continuing...", + node->value); node->value = mh_xstrdup ("2.0"); } } @@ -425,7 +451,7 @@ convert_common (contentline *clines, act action) { insert = "Tentative: "; break; case ACT_DELEGATE: - adios (NULL, "Delegate replies are not supported"); + die("Delegate replies are not supported"); break; case ACT_CANCEL: insert = "Cancelled:"; @@ -444,7 +470,7 @@ convert_common (contentline *clines, act action) { node->value = tmp; } else { /* Should never get here. */ - adios (NULL, "Unknown action: %d", action); + die("Unknown action: %d", action); } } @@ -469,7 +495,7 @@ convert_common (contentline *clines, act action) { } /* Excise X- lines and VALARM section(s). */ - in_valarm = 0; + in_valarm = false; for (node = clines; node; node = node->next) { /* node->name will be NULL if the line was deleted. */ if (! node->name) { continue; } @@ -477,13 +503,13 @@ convert_common (contentline *clines, act action) { if (in_valarm) { if (! strcasecmp ("END", node->name) && ! strcasecmp ("VALARM", node->value)) { - in_valarm = 0; + in_valarm = false; } remove_contentline (node); } else { if (! strcasecmp ("BEGIN", node->name) && ! strcasecmp ("VALARM", node->value)) { - in_valarm = 1; + in_valarm = true; remove_contentline (node); } else if (! strncasecmp ("X-", node->name, 2)) { remove_contentline (node); @@ -494,7 +520,8 @@ convert_common (contentline *clines, act action) { /* Echo the input, but with unfolded lines. */ static void -dump_unfolded (FILE *file, contentline *clines) { +dump_unfolded (FILE *file, contentline *clines) +{ contentline *node; for (node = clines; node; node = node->next) { @@ -503,7 +530,8 @@ dump_unfolded (FILE *file, contentline *clines) { } static void -output (FILE *file, contentline *clines, int contenttype) { +output (FILE *file, contentline *clines, int contenttype) +{ contentline *node; if (contenttype) { @@ -535,11 +563,10 @@ output (FILE *file, contentline *clines, int contenttype) { line = fold (add (node->value, line), clines->cr_before_lf == CR_BEFORE_LF); - if (clines->cr_before_lf == LF_ONLY) { - fprintf (file, "%s\n", line); - } else { - fprintf (file, "%s\r\n", line); - } + fputs(line, file); + if (clines->cr_before_lf != LF_ONLY) + putc('\r', file); + putc('\n', file); free (line); } } @@ -557,10 +584,11 @@ output (FILE *file, contentline *clines, int contenttype) { * - attendees (limited to number specified in initialization) */ static void -display (FILE *file, contentline *clines, char *nfs) { +display (FILE *file, contentline *clines, char *nfs) +{ tzdesc_t timezones = load_timezones (clines); - int in_vtimezone; - int in_valarm; + bool in_vtimezone; + bool in_valarm; contentline *node; struct format *fmt; int dat[5] = { 0, 0, 0, INT_MAX, 0 }; @@ -595,7 +623,7 @@ display (FILE *file, contentline *clines, char *nfs) { } /* Only display DESCRIPTION lines that are outside VALARM section(s). */ - in_valarm = 0; + in_valarm = false; if ((c = fmt_findcomp ("description"))) { for (node = clines; node; node = node->next) { /* node->name will be NULL if the line was deleted. */ @@ -606,12 +634,12 @@ display (FILE *file, contentline *clines, char *nfs) { } else if (in_valarm) { if (! strcasecmp ("END", node->name) && ! strcasecmp ("VALARM", node->value)) { - in_valarm = 0; + in_valarm = false; } } else { if (! strcasecmp ("BEGIN", node->name) && ! strcasecmp ("VALARM", node->value)) { - in_valarm = 1; + in_valarm = true; } } } @@ -626,7 +654,7 @@ display (FILE *file, contentline *clines, char *nfs) { if ((c = fmt_findcomp ("dtstart"))) { /* Find DTSTART outsize of a VTIMEZONE section. */ - in_vtimezone = 0; + in_vtimezone = false; for (node = clines; node; node = node->next) { /* node->name will be NULL if the line was deleted. */ if (! node->name) { continue; } @@ -634,12 +662,12 @@ display (FILE *file, contentline *clines, char *nfs) { if (in_vtimezone) { if (! strcasecmp ("END", node->name) && ! strcasecmp ("VTIMEZONE", node->value)) { - in_vtimezone = 0; + in_vtimezone = false; } } else { if (! strcasecmp ("BEGIN", node->name) && ! strcasecmp ("VTIMEZONE", node->value)) { - in_vtimezone = 1; + in_vtimezone = true; } else if (! strcasecmp ("DTSTART", node->name)) { /* Got it: DTSTART outside of a VTIMEZONE section. */ char *datetime = format_datetime (timezones, node); @@ -726,7 +754,8 @@ display (FILE *file, contentline *clines, char *nfs) { } static const char * -identity (const contentline *node) { +identity (const contentline *node) +{ /* According to RFC 5545 § 3.3.3, an email address in the value must be a mailto URI. */ if (! strncasecmp (node->value, "mailto:", 7)) { @@ -756,7 +785,8 @@ identity (const contentline *node) { } static char * -format_params (char *line, param_list *p) { +format_params (char *line, param_list *p) +{ for ( ; p && p->param_name; p = p->next) { value_list *v; size_t num_values = 0; @@ -789,7 +819,8 @@ format_params (char *line, param_list *p) { } static char * -fold (char *line, int uses_cr) { +fold (char *line, int uses_cr) +{ size_t remaining = strlen (line); size_t current_line_len = 0; charstring_t folded_line = charstring_create (2 * remaining);