📄 rfc822.c
字号:
/* ======================================================================== * Copyright 1988-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * * ======================================================================== *//* * Program: RFC 2822 and MIME routines * * Author: Mark Crispin * UW Technology * University of Washington * Seattle, WA 98195 * Internet: MRC@Washington.EDU * * Date: 27 July 1988 * Last Edited: 14 May 2008 * * This original version of this file is * Copyright 1988 Stanford University * and was developed in the Symbolic Systems Resources Group of the Knowledge * Systems Laboratory at Stanford University in 1987-88, and was funded by the * Biomedical Research Technology Program of the NationalInstitutes of Health * under grant number RR-00785. */#include <ctype.h>#include <stdio.h>#include <time.h>#include "c-client.h"/* Support for deprecated features in earlier specifications. Note that this * module follows RFC 2822, and all use of "rfc822" in function names is * for compatibility. Only the code identified by the conditionals below * follows the earlier documents. */#define RFC733 1 /* parse "at" */#define RFC822 0 /* generate A-D-L (MUST be 0 for 2822) *//* RFC-822 static data */#define RFC822CONT " " /* RFC 2822 continuation */ /* should have been "Remailed-" */#define RESENTPREFIX "ReSent-"static char *resentprefix = RESENTPREFIX; /* syntax error host string */static const char *errhst = ERRHOST;/* Body formats constant strings, must match definitions in mail.h */char *body_types[TYPEMAX+1] = { "TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "MODEL", "X-UNKNOWN"};char *body_encodings[ENCMAX+1] = { "7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE", "X-UNKNOWN"};/* Token delimiting special characters */ /* RFC 2822 specials */const char *specials = " ()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177"; /* RFC 2822 phrase specials (no space) */const char *rspecials = "()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177"; /* RFC 2822 dot-atom specials (no dot) */const char *wspecials = " ()<>@,;:\\\"[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177"; /* RFC 2045 MIME body token specials */const char *tspecials = " ()<>@,;:\\\"[]/?=\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";/* Subtype defaulting (a no-no, but regretably necessary...) * Accepts: type code * Returns: default subtype name */char *rfc822_default_subtype (unsigned short type){ switch (type) { case TYPETEXT: /* default is TEXT/PLAIN */ return "PLAIN"; case TYPEMULTIPART: /* default is MULTIPART/MIXED */ return "MIXED"; case TYPEMESSAGE: /* default is MESSAGE/RFC822 */ return "RFC822"; case TYPEAPPLICATION: /* default is APPLICATION/OCTET-STREAM */ return "OCTET-STREAM"; case TYPEAUDIO: /* default is AUDIO/BASIC */ return "BASIC"; default: /* others have no default subtype */ return "UNKNOWN"; }}/* RFC 2822 parsing routines *//* Parse an RFC 2822 message * Accepts: pointer to return envelope * pointer to return body * pointer to header * header byte count * pointer to body stringstruct * pointer to local host name * recursion depth * source driver flags */void rfc822_parse_msg_full (ENVELOPE **en,BODY **bdy,char *s,unsigned long i, STRING *bs,char *host,unsigned long depth, unsigned long flags){ char c,*t,*d; char *tmp = (char *) fs_get ((size_t) i + 100); ENVELOPE *env = (*en = mail_newenvelope ()); BODY *body = bdy ? (*bdy = mail_newbody ()) : NIL; long MIMEp = -1; /* flag that MIME semantics are in effect */ parseline_t pl = (parseline_t) mail_parameters (NIL,GET_PARSELINE,NIL); if (!host) host = BADHOST; /* make sure that host is non-null */ while (i && *s != '\n') { /* until end of header */ t = tmp; /* initialize buffer pointer */ c = ' '; /* and previous character */ while (i && c) { /* collect text until logical end of line */ switch (c = *s++) { /* slurp a character */ case '\015': /* return, possible end of logical line */ if (*s == '\n') break; /* ignore if LF follows */ case '\012': /* LF, possible end of logical line */ /* tie off unless next line starts with WS */ if (*s != ' ' && *s != '\t') *t++ = c = '\0'; break; case '\t': /* tab */ *t++ = ' '; /* coerce to space */ break; default: /* all other characters */ *t++ = c; /* insert the character into the line */ break; } if (!--i) *t++ = '\0'; /* see if end of header */ } /* find header item type */ if (t = d = strchr (tmp,':')) { *d++ = '\0'; /* tie off header item, point at its data */ while (*d == ' ') d++; /* flush whitespace */ while ((tmp < t--) && (*t == ' ')) *t = '\0'; ucase (tmp); /* coerce to uppercase */ /* external callback */ if (pl) (*pl) (env,tmp,d,host); switch (*tmp) { /* dispatch based on first character */ case '>': /* possible >From: */ if (!strcmp (tmp+1,"FROM")) rfc822_parse_adrlist (&env->from,d,host); break; case 'B': /* possible bcc: */ if (!strcmp (tmp+1,"CC")) rfc822_parse_adrlist (&env->bcc,d,host); break; case 'C': /* possible cc: or Content-<mumble>*/ if (!strcmp (tmp+1,"C")) rfc822_parse_adrlist (&env->cc,d,host); else if ((tmp[1] == 'O') && (tmp[2] == 'N') && (tmp[3] == 'T') && (tmp[4] == 'E') && (tmp[5] == 'N') && (tmp[6] == 'T') && (tmp[7] == '-') && body) switch (MIMEp) { case -1: /* unknown if MIME or not */ if (!(MIMEp = /* see if MIME-Version header exists */ search ((unsigned char *) s-1,i, (unsigned char *)"\012MIME-Version",(long) 13))) {#if 1 /* This is a disgusting kludge, and most of the messages which * benefit from it are spam. */ if (!strcmp (tmp+8,"TRANSFER-ENCODING") || (!strcmp (tmp+8,"TYPE") && strchr (d,'/'))) { MM_LOG ("Warning: MIME header encountered in non-MIME message", PARSE); MIMEp = 1; /* declare MIME now */ } else#endif break; /* non-MIME message */ } case T: /* definitely MIME */ rfc822_parse_content_header (body,tmp+8,d); } break; case 'D': /* possible Date: */ if (!env->date && !strcmp (tmp+1,"ATE")) env->date = cpystr (d); break; case 'F': /* possible From: */ if (!strcmp (tmp+1,"ROM")) rfc822_parse_adrlist (&env->from,d,host); else if (!strcmp (tmp+1,"OLLOWUP-TO")) { t = env->followup_to = (char *) fs_get (1 + strlen (d)); while (c = *d++) if (c != ' ') *t++ = c; *t++ = '\0'; } break; case 'I': /* possible In-Reply-To: */ if (!env->in_reply_to && !strcmp (tmp+1,"N-REPLY-TO")) env->in_reply_to = cpystr (d); break; case 'M': /* possible Message-ID: or MIME-Version: */ if (!env->message_id && !strcmp (tmp+1,"ESSAGE-ID")) env->message_id = cpystr (d); else if (!strcmp (tmp+1,"IME-VERSION")) { /* tie off at end of phrase */ if (t = rfc822_parse_phrase (d)) *t = '\0'; rfc822_skipws (&d); /* skip whitespace */ /* known version? */ if (strcmp (d,"1.0") && strcmp (d,"RFC-XXXX")) MM_LOG ("Warning: message has unknown MIME version",PARSE); MIMEp = T; /* note that we are MIME */ } break; case 'N': /* possible Newsgroups: */ if (!env->newsgroups && !strcmp (tmp+1,"EWSGROUPS")) { t = env->newsgroups = (char *) fs_get (1 + strlen (d)); while (c = *d++) if (c != ' ') *t++ = c; *t++ = '\0'; } break; case 'R': /* possible Reply-To: */ if (!strcmp (tmp+1,"EPLY-TO")) rfc822_parse_adrlist (&env->reply_to,d,host); else if (!env->references && !strcmp (tmp+1,"EFERENCES")) env->references = cpystr (d); break; case 'S': /* possible Subject: or Sender: */ if (!env->subject && !strcmp (tmp+1,"UBJECT")) env->subject = cpystr (d); else if (!strcmp (tmp+1,"ENDER")) rfc822_parse_adrlist (&env->sender,d,host); break; case 'T': /* possible To: */ if (!strcmp (tmp+1,"O")) rfc822_parse_adrlist (&env->to,d,host); break; default: break; } } } fs_give ((void **) &tmp); /* done with scratch buffer */ /* default Sender: and Reply-To: to From: */ if (!env->sender) env->sender = rfc822_cpy_adr (env->from); if (!env->reply_to) env->reply_to = rfc822_cpy_adr (env->from); /* now parse the body */ if (body) rfc822_parse_content (body,bs,host,depth,flags);}/* Parse a message body content * Accepts: pointer to body structure * body string * pointer to local host name * recursion depth * source driver flags */void rfc822_parse_content (BODY *body,STRING *bs,char *h,unsigned long depth, unsigned long flags){ char c,c1,*s,*s1; int f; unsigned long i,j,k,m; PARAMETER *param; PART *part = NIL; if (depth > MAXMIMEDEPTH) { /* excessively deep recursion? */ body->type = TYPETEXT; /* yes, probably a malicious MIMEgram */ MM_LOG ("Ignoring excessively deep MIME recursion",PARSE); } if (!body->subtype) /* default subtype if still unknown */ body->subtype = cpystr (rfc822_default_subtype (body->type)); /* note offset and sizes */ body->contents.offset = GETPOS (bs); /* note internal body size in all cases */ body->size.bytes = body->contents.text.size = i = SIZE (bs); if (!(flags & DR_CRLF)) body->size.bytes = strcrlflen (bs); switch (body->type) { /* see if anything else special to do */ case TYPETEXT: /* text content */ if (!body->parameter) { /* no parameters set */ body->parameter = mail_newbody_parameter (); body->parameter->attribute = cpystr ("CHARSET"); while (i--) { /* count lines and guess charset */ c = SNX (bs); /* get current character */ /* charset still unknown? */ if (!body->parameter->value) { if ((c == I2C_ESC) && (i && i--) && ((c = SNX (bs)) == I2C_MULTI) && (i && i--) && (((c = SNX (bs)) == I2CS_94x94_JIS_NEW) || (c == I2CS_94x94_JIS_OLD))) body->parameter->value = cpystr ("ISO-2022-JP"); else if (c & 0x80) body->parameter->value = cpystr ("X-UNKNOWN"); } if (c == '\n') body->size.lines++; } /* 7-bit content */ if (!body->parameter->value) switch (body->encoding) { case ENC7BIT: /* unadorned 7-bit */ case ENC8BIT: /* unadorned 8-bit (but 7-bit content) */ case ENCBINARY: /* binary (but 7-bit content( */ body->parameter->value = cpystr ("US-ASCII"); break; default: /* QUOTED-PRINTABLE, BASE64, etc. */ body->parameter->value = cpystr ("X-UNKNOWN"); break; } } /* just count lines */ else while (i--) if ((SNX (bs)) == '\n') body->size.lines++; break; case TYPEMESSAGE: /* encapsulated message */ /* encapsulated RFC-822 message? */ if (!strcmp (body->subtype,"RFC822")) { body->nested.msg = mail_newmsg (); switch (body->encoding) { /* make sure valid encoding */ case ENC7BIT: /* these are valid nested encodings */ case ENC8BIT: case ENCBINARY: break; default: MM_LOG ("Ignoring nested encoding of message contents",PARSE); } /* hunt for blank line */ for (c = '\012',j = 0; (i > j) && ((c != '\012') || (CHR(bs) != '\012')); j++) if ((c1 = SNX (bs)) != '\015') c = c1; if (i > j) { /* unless no more text */ c1 = SNX (bs); /* body starts here */ j++; /* advance count */ } /* note body text offset and header size */ body->nested.msg->header.text.size = j; body->nested.msg->text.text.size = body->contents.text.size - j; body->nested.msg->text.offset = GETPOS (bs); body->nested.msg->full.offset = body->nested.msg->header.offset = body->contents.offset; body->nested.msg->full.text.size = body->contents.text.size; /* copy header string */ SETPOS (bs,body->contents.offset); s = (char *) fs_get ((size_t) j + 1); for (s1 = s,k = j; k--; *s1++ = SNX (bs)); s[j] = '\0'; /* tie off string (not really necessary) */ /* now parse the body */ rfc822_parse_msg_full (&body->nested.msg->env,&body->nested.msg->body,s, j,bs,h,depth+1,flags); fs_give ((void **) &s); /* free header string */ /* restore position */ SETPOS (bs,body->contents.offset); } /* count number of lines */ while (i--) if (SNX (bs) == '\n') body->size.lines++; break; case TYPEMULTIPART: /* multiple parts */ switch (body->encoding) { /* make sure valid encoding */ case ENC7BIT: /* these are valid nested encodings */ case ENC8BIT: case ENCBINARY: break; default: MM_LOG ("Ignoring nested encoding of multipart contents",PARSE); } /* remember if digest */ f = !strcmp (body->subtype,"DIGEST"); /* find cookie */ for (s1 = NIL,param = body->parameter; param && !s1; param = param->next) if (!strcmp (param->attribute,"BOUNDARY")) s1 = param->value; if (!s1) s1 = "-"; /* yucky default */ j = strlen (s1) + 2; /* length of cookie and header */ c = '\012'; /* initially at beginning of line */ while (i > j) { /* examine data */ if (m = GETPOS (bs)) m--; /* get position in front of character */ switch (c) { /* examine each line */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -