*/
#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 <h/fmt_scan.h>
+#include "h/fmt_scan.h"
#include "h/addrsbr.h"
#include "h/mts.h"
+#include "h/done.h"
#include "h/utils.h"
#include <time.h>
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 *);
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) \
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
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;
ambigsw (cp, switches);
done (1);
case UNKWNSW:
- adios (NULL, "-%s unknown", cp);
+ die("-%s unknown", cp);
case HELPSW: {
char buf[128];
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")) {
} else if (! strcasecmp (cp, "delegate")) {
action = ACT_DELEGATE;
} else {
- adios (NULL, "Unknown action: %s", cp);
+ die("Unknown action: %s", cp);
}
continue;
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;
}
}
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);
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,
* - 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);
/* 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;
}
}
- if (! found_my_attendee_line) {
+ if (found_my_attendee_line == 0) {
/* Generate and attach an ATTENDEE line for me. */
contentline *node;
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;
}
/*
* - Excise VALARM sections.
*/
static void
-convert_to_cancellation (contentline *clines) {
+convert_to_cancellation (contentline *clines)
+{
contentline *node;
convert_common (clines, ACT_CANCEL);
}
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);
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))) {
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");
}
}
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:";
node->value = tmp;
} else {
/* Should never get here. */
- adios (NULL, "Unknown action: %d", action);
+ die("Unknown action: %d", 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; }
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);
/* 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) {
}
static void
-output (FILE *file, contentline *clines, int contenttype) {
+output (FILE *file, contentline *clines, int contenttype)
+{
contentline *node;
if (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);
}
}
* - 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 };
}
/* 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. */
} 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;
}
}
}
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; }
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);
}
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)) {
}
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;
}
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);