]> diplodocus.org Git - nmh/blobdiff - uip/mhical.c
Add basic support for the STLS command in POP
[nmh] / uip / mhical.c
index 3249d0855c25dd4a17d1645aa9bbe39d37f7ebe3..078ad37ec66d11c8bad3d27a6807b689ab6584cd 100644 (file)
@@ -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 <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>
 
@@ -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);