chiark / gitweb /
'collection' no longer requires an encoding to be specified (or a module).
[disorder] / lib / mime.c
index a97c4d162ab0e6808fb127203b6987c4d6e27029..422e1f5a419c90efb303b4476e10f68c40547eb4 100644 (file)
@@ -107,7 +107,9 @@ static int iscrlf(const char *ptr) {
 }
 
 /** @brief Skip whitespace
+ * @param s Pointer into string
  * @param rfc822_comments If true, skip RFC822 nested comments
+ * @return Pointer into string after whitespace
  */
 static const char *skipwhite(const char *s, int rfc822_comments) {
   int c, depth;
@@ -207,7 +209,7 @@ static const char *parsetoken(const char *s, char **valuep,
  * @param s Start of field
  * @param typep Where to store type
  * @param parameternamep Where to store parameter name
- * @param parameternvaluep Wher to store parameter value
+ * @param parametervaluep Wher to store parameter value
  * @return 0 on success, non-0 on error
  *
  * See <a href="http://tools.ietf.org/html/rfc2045#section-5">RFC 2045 s5</a>.
@@ -360,9 +362,9 @@ int mime_multipart(const char *s,
 
 /** @brief Parse an RFC2388-style content-disposition field
  * @param s Start of field
- * @param typep Where to store type
+ * @param dispositionp Where to store disposition
  * @param parameternamep Where to store parameter name
- * @param parameternvaluep Wher to store parameter value
+ * @param parametervaluep Wher to store parameter value
  * @return 0 on success, non-0 on error
  *
  * See <a href="http://tools.ietf.org/html/rfc2388#section-3">RFC 2388 s3</a>
@@ -455,6 +457,8 @@ char *mime_qp(const char *s) {
  * @param s Header field value
  * @param cd Where to store result
  * @return 0 on success, non-0 on error
+ *
+ * See <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>.
  */
 int parse_cookie(const char *s,
                 struct cookiedata *cd) {
@@ -527,6 +531,132 @@ const struct cookie *find_cookie(const struct cookiedata *cd,
   return 0;
 }
 
+/** @brief RFC822 quoting
+ * @param s String to quote
+ * @param force If non-0, always quote
+ * @return Possibly quoted string
+ */
+char *quote822(const char *s, int force) {
+  const char *t;
+  struct dynstr d[1];
+  int c;
+
+  if(!force) {
+    /* See if we need to quote */
+    for(t = s; (c = (unsigned char)*t); ++t) {
+      if(tspecial(c) || http_separator(c) || whitespace(c))
+       break;
+    }
+    if(*t)
+      force = 1;
+  }
+
+  if(!force)
+    return xstrdup(s);
+
+  dynstr_init(d);
+  dynstr_append(d, '"');
+  for(t = s; (c = (unsigned char)*t); ++t) {
+    if(c == '"' || c == '\\')
+      dynstr_append(d, '\\');
+    dynstr_append(d, c);
+  }
+  dynstr_append(d, '"');
+  dynstr_terminate(d);
+  return d->vec;
+}
+
+/** @brief Return true if @p ptr points at trailing space */
+static int is_trailing_space(const char *ptr) {
+  if(*ptr == ' ' || *ptr == '\t') {
+    while(*ptr == ' ' || *ptr == '\t')
+      ++ptr;
+    return *ptr == '\n' || *ptr == 0;
+  } else
+    return 0;
+}
+
+/** @brief Encoding text as quoted-printable
+ * @param text String to encode
+ * @return Encoded string
+ *
+ * See <a href="http://tools.ietf.org/html/rfc2045#section-6.7">RFC2045
+ * s6.7</a>.
+ */
+char *mime_to_qp(const char *text) {
+  struct dynstr d[1];
+  int linelength = 0;                  /* length of current line */
+  char buffer[10];
+
+  dynstr_init(d);
+  /* The rules are:
+   * 1. Anything except newline can be replaced with =%02X
+   * 2. Newline, 33-60 and 62-126 stand for themselves (i.e. not '=')
+   * 3. Non-trailing space/tab stand for themselves.
+   * 4. Output lines are limited to 76 chars, with =<newline> being used
+   *    as a soft line break
+   * 5. Newlines aren't counted towards the 76 char limit.
+   */
+  while(*text) {
+    const int c = (unsigned char)*text;
+    if(c == '\n') {
+      /* Newline stands as itself */
+      dynstr_append(d, '\n');
+      linelength = 0;
+    } else if((c >= 33 && c <= 126 && c != '=')
+             || ((c == ' ' || c == '\t')
+                 && !is_trailing_space(text))) {
+      /* Things that can stand for themselves  */
+      dynstr_append(d, c);
+      ++linelength;
+    } else {
+      /* Anything else that needs encoding */
+      snprintf(buffer, sizeof buffer, "=%02X", c);
+      dynstr_append_string(d, buffer);
+      linelength += 3;
+    }
+    ++text;
+    if(linelength > 73 && *text && *text != '\n') {
+      /* Next character might overflow 76 character limit if encoded, so we
+       * insert a soft break */
+      dynstr_append_string(d, "=\n");
+      linelength = 0;
+    }
+  }
+  /* Ensure there is a final newline */
+  if(linelength)
+    dynstr_append(d, '\n');
+  /* That's all */
+  dynstr_terminate(d);
+  return d->vec;
+}
+
+/** @brief Encode text
+ * @param text Underlying UTF-8 text
+ * @param charsetp Where to store charset string
+ * @param encodingp Where to store encoding string
+ * @return Encoded text (might be @ref text)
+ */
+const char *mime_encode_text(const char *text,
+                            const char **charsetp,
+                            const char **encodingp) {
+  const char *ptr;
+
+  /* See if there are in fact any non-ASCII characters */
+  for(ptr = text; *ptr; ++ptr)
+    if((unsigned char)*ptr >= 128)
+      break;
+  if(!*ptr) {
+    /* Plain old ASCII, no encoding required */
+    *charsetp = "us-ascii";
+    *encodingp = "7bit";
+    return text;
+  }
+  *charsetp = "utf-8";
+  *encodingp = "quoted-printable";
+  return mime_to_qp(text);
+}
+
 /*
 Local Variables:
 c-basic-offset:2