]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Ensure that mail headers are 7-bit clean, even when UTF-8 characters are used
authorTilghman Lesher <tilghman@meg.abyt.es>
Tue, 14 Oct 2008 17:41:08 +0000 (17:41 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Tue, 14 Oct 2008 17:41:08 +0000 (17:41 +0000)
in headers like 'Subject' and 'To'.
Closes AST-107.

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@148916 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_voicemail.c

index f6401b2b5f205465b1ff71d8c37b61d3d2bbf51d..f104d6ee8c57d9e3712a392685e29b720452b4fa 100644 (file)
@@ -2979,6 +2979,70 @@ static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
        return tm;
 }
 
+/*!\brief Check if the string would need encoding within the MIME standard, to
+ * avoid confusing certain mail software that expects messages to be 7-bit
+ * clean.
+ */
+static int check_mime(const char *str)
+{
+       for (; *str; str++) {
+               if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*!\brief Encode a string according to the MIME rules for encoding strings
+ * that are not 7-bit clean or contain control characters.
+ *
+ * Additionally, if the encoded string would exceed the MIME limit of 76
+ * characters per line, then the encoding will be broken up into multiple
+ * sections, separated by a space character, in order to facilitate
+ * breaking up the associated header across multiple lines.
+ *
+ * \param start A string to be encoded
+ * \param end An expandable buffer for holding the result
+ * \param preamble The length of the first line already used for this string,
+ * to ensure that each line maintains a maximum length of 76 chars.
+ * \param postamble the length of any additional characters appended to the
+ * line, used to ensure proper field wrapping.
+ * \retval The encoded string.
+ */
+static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
+{
+       char tmp[80];
+       int first_section = 1;
+       size_t endlen = 0, tmplen = 0;
+       *end = '\0';
+
+       tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
+       for (; *start; start++) {
+               int need_encoding = 0;
+               if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
+                       need_encoding = 1;
+               }
+               if ((first_section && need_encoding && preamble + tmplen > 70) ||
+                       (first_section && !need_encoding && preamble + tmplen > 72) ||
+                       (!first_section && need_encoding && tmplen > 70) ||
+                       (!first_section && !need_encoding && tmplen > 72)) {
+                       /* Start new line */
+                       endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
+                       tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
+                       first_section = 0;
+               }
+               if (need_encoding && *start == ' ') {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
+               } else if (need_encoding) {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
+               } else {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
+               }
+       }
+       snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
+       return end;
+}
+
 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
 {
        char date[256];
@@ -2990,14 +3054,28 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
        char tmpcmd[256];
        char enc_cidnum[256] = "", enc_cidname[256] = "";
        struct tm tm;
-       char *passdata2;
-       size_t len_passdata;
+       char *passdata = NULL, *passdata2;
+       size_t len_passdata, len_passdata2, tmplen;
 #ifdef IMAP_STORAGE
 #define ENDL "\r\n"
 #else
 #define ENDL "\n"
 #endif
 
+       /* One alloca for multiple fields */
+       len_passdata2 = strlen(vmu->fullname);
+       if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
+               len_passdata2 = tmplen;
+       }
+       if ((tmplen = strlen(emailtitle)) > len_passdata2) {
+               len_passdata2 = tmplen;
+       }
+       if ((tmplen = strlen(fromstring)) > len_passdata2) {
+               len_passdata2 = tmplen;
+       }
+       len_passdata2 = len_passdata2 * 3 + 200;
+       passdata2 = alloca(len_passdata2);
+
        if (cidnum) {
                strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
        }
@@ -3020,37 +3098,71 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
        if (*fromstring) {
                struct ast_channel *ast;
                if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
-                       char *passdata;
-                       int vmlen = strlen(fromstring)*3 + 200;
-                       if ((passdata = alloca(vmlen))) {
-                               memset(passdata, 0, vmlen);
-                               prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category);
-                               pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
-                               len_passdata = strlen(passdata) * 2 + 3;
-                               passdata2 = alloca(len_passdata);
-                               fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
-                       } else
-                               ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+                       char *ptr;
+                       memset(passdata2, 0, len_passdata2);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category);
+                       pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
+                       len_passdata = strlen(passdata2) * 3 + 300;
+                       passdata = alloca(len_passdata);
+                       if (check_mime(passdata2)) {
+                               int first_line = 1;
+                               encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
+                               while ((ptr = strchr(passdata, ' '))) {
+                                       *ptr = '\0';
+                                       fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
+                                       first_line = 0;
+                                       passdata = ptr + 1;
+                               }
+                               fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
+                       } else {
+                               fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata2), who);
+                       }
                        ast_channel_free(ast);
                } else
                        ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
        } else
                fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
-       len_passdata = strlen(vmu->fullname) * 2 + 3;
-       passdata2 = alloca(len_passdata);
-       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
+
+       if (check_mime(vmu->fullname)) {
+               int first_line = 1;
+               char *ptr;
+               encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
+               while ((ptr = strchr(passdata2, ' '))) {
+                       *ptr = '\0';
+                       fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
+                       first_line = 0;
+                       passdata2 = ptr + 1;
+               }
+               fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
+       } else {
+               fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
+       }
        if (emailsubject) {
                struct ast_channel *ast;
                if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
-                       char *passdata;
-                       int vmlen = strlen(emailsubject)*3 + 200;
-                       if ((passdata = alloca(vmlen))) {
-                               memset(passdata, 0, vmlen);
-                               prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
-                               pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
-                               fprintf(p, "Subject: %s" ENDL, passdata);
+                       int vmlen = strlen(emailsubject) * 3 + 200;
+                       /* Only allocate more space if the previous was not large enough */
+                       if (vmlen > len_passdata) {
+                               passdata = alloca(vmlen);
+                               len_passdata = vmlen;
+                       }
+
+                       memset(passdata, 0, len_passdata);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, len_passdata, category);
+                       pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
+                       if (check_mime(passdata)) {
+                               int first_line = 1;
+                               char *ptr;
+                               encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
+                               while ((ptr = strchr(passdata2, ' '))) {
+                                       *ptr = '\0';
+                                       fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
+                                       first_line = 0;
+                                       passdata2 = ptr + 1;
+                               }
+                               fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
                        } else {
-                               ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+                               fprintf(p, "Subject: %s" ENDL, passdata);
                        }
                        ast_channel_free(ast);
                } else {