summaryrefslogtreecommitdiff
path: root/src/mime
diff options
context:
space:
mode:
Diffstat (limited to 'src/mime')
-rw-r--r--src/mime/base64.cc132
-rw-r--r--src/mime/def.cc149
-rw-r--r--src/mime/enc.h36
-rw-r--r--src/mime/mailbox.cc137
-rw-r--r--src/mime/mailbox.h60
-rw-r--r--src/mime/mime.cc188
-rw-r--r--src/mime/mime.h99
-rw-r--r--src/mime/pack.cc143
-rw-r--r--src/mime/pack.h48
-rw-r--r--src/mime/qp.cc150
-rw-r--r--src/mime/test.cc108
11 files changed, 1250 insertions, 0 deletions
diff --git a/src/mime/base64.cc b/src/mime/base64.cc
new file mode 100644
index 0000000..842bbd3
--- /dev/null
+++ b/src/mime/base64.cc
@@ -0,0 +1,132 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <mime/enc.h>
21
22static unsigned char char2six[256]={
23 65,65,65,65,65,65,65,65,
24 65,65,65,65,65,65,65,65,
25 65,65,65,65,65,65,65,65,
26 65,65,65,65,65,65,65,65,
27 65,65,65,65,65,65,65,65,
28 65,65,65,62,65,65,65,63,
29 52,53,54,55,56,57,58,59,
30 60,61,65,65,65,64,65,65,
31 65, 0, 1, 2, 3, 4, 5, 6,
32 7, 8, 9,10,11,12,13,14,
33 15,16,17,18,19,20,21,22,
34 23,24,25,65,65,65,65,65,
35 65,26,27,28,29,30,31,32,
36 33,34,35,36,37,38,39,40,
37 41,42,43,44,45,46,47,48,
38 49,50,51,65,65,65,65,65,
39 65,65,65,65,65,65,65,65,
40 65,65,65,65,65,65,65,65,
41 65,65,65,65,65,65,65,65,
42 65,65,65,65,65,65,65,65,
43 65,65,65,65,65,65,65,65,
44 65,65,65,65,65,65,65,65,
45 65,65,65,65,65,65,65,65,
46 65,65,65,65,65,65,65,65,
47 65,65,65,65,65,65,65,65,
48 65,65,65,65,65,65,65,65,
49 65,65,65,65,65,65,65,65,
50 65,65,65,65,65,65,65,65,
51 65,65,65,65,65,65,65,65,
52 65,65,65,65,65,65,65,65,
53 65,65,65,65,65,65,65,65,
54 65,65,65,65,65,65,65,65
55};
56static unsigned char six2char[64]={
57 'A','B','C','D','E','F','G','H',
58 'I','J','K','L','M','N','O','P',
59 'Q','R','S','T','U','V','W','X',
60 'Y','Z','a','b','c','d','e','f',
61 'g','h','i','j','k','l','m','n',
62 'o','p','q','r','s','t','u','v',
63 'w','x','y','z','0','1','2','3',
64 '4','5','6','7','8','9','+','/'
65};
66inline void tri2quad(mstream& out,unsigned code,int m=4)
67{
68 char buf[4];
69 buf[3]=six2char[code&63U]; code>>=6;
70 buf[2]=six2char[code&63U]; code>>=6;
71 buf[1]=six2char[code&63U]; code>>=6;
72 buf[0]=six2char[code&63U];
73 out.write(buf,m);
74}
75inline void quad2tri(mstream& out,unsigned code,int m=3)
76{
77 char buf[3];
78 buf[2]=code&255U; code>>=8;
79 buf[1]=code&255U; code>>=8;
80 buf[0]=code&255U;
81 out.write(buf,m);
82}
83
84void encode64(mstream& out,const sref& in)
85{
86 unsigned m=0,code=0,wrap=0;
87 for(const char* p=in.begin();p!=in.end();p++) {
88 code<<=8;
89 code|=(unsigned char)*p;
90 if(++m==3) {
91 tri2quad(out,code);
92 m=0;
93 if(++wrap==15) { out<<CRLF; wrap=0; }
94 }
95 }
96 if(m==1) {
97 code<<=16;
98 tri2quad(out,code,2);
99 out<<"==";
100 if(++wrap==15) { out<<CRLF; wrap=0; }
101 }
102 else if(m==2) {
103 code<<=8;
104 tri2quad(out,code,3);
105 out.put('=');
106 if(++wrap==15) { out<<CRLF; wrap=0; }
107 }
108 if(wrap>0) out<<CRLF;
109}
110
111void decode64(mstream& out,const sref& in)
112{
113 unsigned m=0,code=0,six=0;
114 for(const char* p=in.begin();p!=in.end();p++) {
115 six=char2six[(unsigned char)*p];
116 if(six>=64) continue;
117 code<<=6;
118 code|=six;
119 if(++m==4) {
120 quad2tri(out,code);
121 m=0;
122 }
123 }
124 if(m==2) {
125 code<<=12;
126 quad2tri(out,code,1);
127 }
128 else if(m==3) {
129 code<<=6;
130 quad2tri(out,code,2);
131 }
132}
diff --git a/src/mime/def.cc b/src/mime/def.cc
new file mode 100644
index 0000000..0a692cd
--- /dev/null
+++ b/src/mime/def.cc
@@ -0,0 +1,149 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <mime/mime.h>
21#include <mt/msmap.h>
22
23struct table_t { char ext[16],name[64]; };
24
25static table_t table[]={
26 {"ai","application/postscript"},
27 {"aif","audio/x-aiff"},
28 {"aifc","audio/x-aiff"},
29 {"aiff","audio/x-aiff"},
30 {"au","audio/basic"},
31 {"avi","video/x-msvideo"},
32 {"bcpio","application/x-bcpio"},
33 {"bin","application/octet-stream"},
34 {"cdf","application/x-netcdf"},
35 {"class","application/octet-stream"},
36 {"cpio","application/x-cpio"},
37 {"cpt","application/mac-compactpro"},
38 {"csh","application/x-csh"},
39 {"css","text/css"},
40 {"dcr","application/x-director"},
41 {"dir","application/x-director"},
42 {"dms","application/octet-stream"},
43 {"doc","application/msword"},
44 {"dvi","application/x-dvi"},
45 {"dxr","application/x-director"},
46 {"eps","application/postscript"},
47 {"etx","text/x-setext"},
48 {"exe","application/octet-stream"},
49 {"gif","image/gif"},
50 {"gtar","application/x-gtar"},
51 {"gz","application/x-gzip"},
52 {"hdf","application/x-hdf"},
53 {"hqx","application/mac-binhex40"},
54 {"htc","text/x-htc"},
55 {"hts","text/plain"},
56 {"htx","text/x-htx"},
57 {"htm","text/html"},
58 {"html","text/html"},
59 {"ice","x-conference/x-cooltalk"},
60 {"ief","image/ief"},
61 {"jpe","image/jpeg"},
62 {"jpeg","image/jpeg"},
63 {"jpg","image/jpeg"},
64 {"kar","audio/midi"},
65 {"latex","application/x-latex"},
66 {"lha","application/octet-stream"},
67 {"lzh","application/octet-stream"},
68 {"man","application/x-troff-man"},
69 {"me","application/x-troff-me"},
70 {"mid","audio/midi"},
71 {"midi","audio/midi"},
72 {"mif","application/x-mif"},
73 {"mov","video/quicktime"},
74 {"movie","video/x-sgi-movie"},
75 {"mp2","audio/mpeg"},
76 {"mp3","audio/mpeg"},
77 {"mpe","video/mpeg"},
78 {"mpeg","video/mpeg"},
79 {"mpg","video/mpeg"},
80 {"mpga","audio/mpeg"},
81 {"ms","application/x-troff-ms"},
82 {"nc","application/x-netcdf"},
83 {"oda","application/oda"},
84 {"pbm","image/x-portable-bitmap"},
85 {"pdb","chemical/x-pdb"},
86 {"pdf","application/pdf"},
87 {"pgm","image/x-portable-graymap"},
88 {"png","image/png"},
89 {"pnm","image/x-portable-anymap"},
90 {"ppm","image/x-portable-pixmap"},
91 {"ppt","application/powerpoint"},
92 {"ps","application/postscript"},
93 {"qt","video/quicktime"},
94 {"ra","audio/x-realaudio"},
95 {"ram","audio/x-pn-realaudio"},
96 {"ras","image/x-cmu-raster"},
97 {"rgb","image/x-rgb"},
98 {"roff","application/x-troff"},
99 {"rpm","audio/x-pn-realaudio-plugin"},
100 {"rtf","application/rtf"},
101 {"rtx","text/richtext"},
102 {"sgm","text/x-sgml"},
103 {"sgml","text/x-sgml"},
104 {"sh","application/x-sh"},
105 {"shar","application/x-shar"},
106 {"sit","application/x-stuffit"},
107 {"skd","application/x-koan"},
108 {"skm","application/x-koan"},
109 {"skp","application/x-koan"},
110 {"skt","application/x-koan"},
111 {"snd","audio/basic"},
112 {"src","application/x-wais-source"},
113 {"sv4cpio","application/x-sv4cpio"},
114 {"sv4crc","application/x-sv4crc"},
115 {"t","application/x-troff"},
116 {"tar","application/x-tar"},
117 {"tcl","application/x-tcl"},
118 {"tex","application/x-tex"},
119 {"texi","application/x-texinfo"},
120 {"texinfo","application/x-texinfo"},
121 {"tif","image/tiff"},
122 {"tiff","image/tiff"},
123 {"tr","application/x-troff"},
124 {"tsv","text/tab-separated-values"},
125 {"txt","text/plain"},
126 {"ustar","application/x-ustar"},
127 {"vcd","application/x-cdlink"},
128 {"vrml","x-world/x-vrml"},
129 {"wav","audio/x-wav"},
130 {"wrl","x-world/x-vrml"},
131 {"xbm","image/x-xbitmap"},
132 {"xpm","image/x-xpixmap"},
133 {"xwd","image/x-xwindowdump"},
134 {"xyz","chemical/x-pdb"},
135 {"zip","application/zip"},
136 {"",""}
137};
138
139const sref* file2mime(const sref& file)
140{
141 static rrmap mime;
142 if(mime.empty()) {
143 for(table_t* p=table;*p->ext;p++) mime.add(sref(p->ext),sref(p->name));
144 }
145 mstring path=file.past_last('.');
146 path.tolower();
147 if(path.nempty()) return mime.get(path);
148 return 0;
149}
diff --git a/src/mime/enc.h b/src/mime/enc.h
new file mode 100644
index 0000000..a6ec57a
--- /dev/null
+++ b/src/mime/enc.h
@@ -0,0 +1,36 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef ENCMIMEH
21#define ENCMIMEH
22
23#include <mime/pack.h>
24
25const mstring trans_base64="base64";
26const mstring trans_quote="quoted-printable";
27const mstring mime_default="iso-8859-1";
28
29void decode64(mstream& out,const sref& in);
30void encode64(mstream& out,const sref& in);
31void decodeQP(mstream& out,const sref& in);
32void encodeQP(mstream& out,const sref& in);
33void decodeHeader(mstream& out,mstring& mime,const sref& in);
34void encodeHeader(mstream& out,const sref& mime,const sref& in);
35
36#endif
diff --git a/src/mime/mailbox.cc b/src/mime/mailbox.cc
new file mode 100644
index 0000000..69f8140
--- /dev/null
+++ b/src/mime/mailbox.cc
@@ -0,0 +1,137 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <mime/mailbox.h>
21
22static int FromHead(const sref& body)
23{
24 sref tag,from,rest=body;
25 Split(tag,rest);
26 Split(from,rest);
27 return tag==from_tag&&htcTime(0,rest)>=0;
28}
29
30/////////////////////////////////////////////////////////////////////
31
32const mstring nextmsg_tag="\nFrom ";
33const mstring sep_tag="--";
34
35Message::Message()
36{
37}
38
39int Message::Special(sref line)
40{
41 line=Rest(line);
42 from=from0=First(line);
43 date=Rest(line);
44 idate=htcTime(0,date);
45 return idate>=0;
46}
47
48int Message::Handle(const sref& name,
49 const sref& rawval,
50 const sref& val,
51 const rrpile& opts)
52{
53 mstring mime;
54 if(Mime::Handle(name,rawval,val,opts)) return 1;
55 else if(eqn(name,from_tag)) {
56 swrite sw(from);
57 decodeHeader(sw,mime,rawval);
58 }
59 else if(eqn(name,subject_tag)) {
60 swrite sw(subject);
61 decodeHeader(sw,mime,rawval);
62 }
63 else if(eqn(name,to_tag)) {
64 swrite sw(to);
65 decodeHeader(sw,mime,rawval);
66 }
67 else if(eqn(name,msgid_tag)) msgid=rawval;
68 else if(eqn(name,received_tag)) received.push_back(rawval);
69 else return 0;
70 return 1;
71}
72
73void Message::putHead(mstream& out) const
74{
75 if(from0.nempty()) { out<<"From "<<from0<<" "<<date<<"\r\n"; }
76 if(putval(out,from_tag,from)) putend(out);
77 if(putval(out,subject_tag,subject)) putend(out);
78 if(putval(out,to_tag,to)) putend(out);
79 if(putval(out,msgid_tag,msgid)) putend(out);
80 for(spile::const_iterator p=received.begin();p!=received.end();p++)
81 if(putval(out,received_tag,*p)) putend(out);
82 Mime::putHead(out);
83}
84
85///////////////////////////////////////////////////////////////
86
87void AppendMessage(FILE* fp,const sref& msg)
88{
89 fwrite(msg.data(),1,msg.size(),fp);
90}
91
92void AppendMessages(FILE* fp,const Mailbox& box)
93{
94 for(int i=0;i<box.size();i++) AppendMessage(fp,box[i]);
95}
96
97int PutMessages(const Mailbox& box,const mstring& path)
98{
99 FILE* fp=fopen(path.c_str(),"w+");
100 if(!fp) return 0;
101 AppendMessages(fp,box);
102 return fclose(fp)==0;
103}
104
105int AppendMessages(const Mailbox& box,const mstring& path)
106{
107 FILE* fp=fopen(path.c_str(),"a+");
108 if(!fp) return 0;
109 AppendMessages(fp,box);
110 return fclose(fp)==0;
111}
112
113///////////////////////////////////////////////////////////////
114
115int GetMessages(Mailbox& box,const mstring& path)
116{
117 mstring buffer; if(buffer.load(path)<0) return -1;
118 sref next,rest=buffer;
119 int old=box.size();
120 while(rest.nempty()) {
121 Message msg(rest);
122 if(msg.from0.empty()) break; // should not happen, I guess
123
124 if(msg.cont_length.nempty())
125 next=msg.body.right(atoi(msg.cont_length));
126 else if(msg.cont_type_boundary.nempty())
127 next=msg.body.past_first(sep_tag+msg.cont_type_boundary+sep_tag).right_first(nextmsg_tag).popf();
128 else
129 next=rest.right_first(nextmsg_tag).popf();
130
131 box.push_back(rest.left(next-rest));
132
133 rest=next.right_first_not_of(cset_ws);
134 while(rest.nempty()&&!FromHead(rest)) rest=rest.right_first(nextmsg_tag).popf();
135 }
136 return box.size()-old;
137}
diff --git a/src/mime/mailbox.h b/src/mime/mailbox.h
new file mode 100644
index 0000000..3e5838c
--- /dev/null
+++ b/src/mime/mailbox.h
@@ -0,0 +1,60 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef MAILBOXH
21#define MAILBOXH
22
23#include <mime/mime.h>
24#include <mt/dates.h>
25
26const mstring from_tag="From";
27const mstring to_tag="To";
28const mstring subject_tag="Subject";
29const mstring msgid_tag="Message-ID";
30const mstring received_tag="Received";
31
32class Message : public Mime {
33public:
34 Message();
35 Message(const sref& msg) { Init(msg); }
36
37 mstring from0,from,date,subject,to,msgid;
38 spile received;
39 time_t idate;
40
41 void putHead(mstream& out) const;
42protected:
43 int Special(sref line);
44 int Handle(const sref& name,
45 const sref& rawval,
46 const sref& val,
47 const rrpile& opts);
48};
49
50typedef msvec<mstring> Mailbox;
51
52void AppendMessage(FILE* fp,const sref& msg);
53void AppendMessages(FILE* fp,const Mailbox& box);
54
55int PutMessages(const Mailbox& box,const mstring& path);
56int AppendMessages(const Mailbox& box,const mstring& path);
57
58int GetMessages(Mailbox& box,const mstring& path);
59
60#endif
diff --git a/src/mime/mime.cc b/src/mime/mime.cc
new file mode 100644
index 0000000..a7a1884
--- /dev/null
+++ b/src/mime/mime.cc
@@ -0,0 +1,188 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <mime/mime.h>
21
22const charset par_left("([{"),par_right("}])");
23
24inline int is_field(const sref& s) {
25 sref p=s.left_first(':');
26 while(cset_spaces.test(p.back())) p=p.popb();
27 return p.nempty()&&p.find_of(cset_spaces)<0;
28}
29
30inline int getquoted(const sref& s,char c) {
31 int i,quote=0,par=0;
32 for(i=0;i<s.size();i++) {
33 if(s[i]=='\"') quote=!quote;
34 else if(par_left.test(s[i])) par++;
35 else if(par_right.test(s[i])) par--;
36 else if(s[i]==c&&!quote&&!par) break;
37 }
38 return i;
39}
40
41void Mime::Init(const sref& message)
42{
43 mstring line;
44 rrpile pile;
45 sref first;
46 head=body=message;
47 while(Splitln(first,body)) {
48 if(first.empty()) break;
49 if(!is_field(first)) Special(first);
50 else {
51 line=first;
52 while(cset_spaces.test(*body)) {
53 Splitln(first,body);
54 line.append(1,' ');
55 line+=first.right_first_not_of(cset_spaces);
56 }
57 sref opt=line;
58 int col=opt.find(':');
59 sref name=Trim(opt.left(col),cset_quotes);
60 opt=opt.adv(col).popf();
61 sref rawval=Trim(opt);
62 int semi=getquoted(opt,';');
63 sref val=Trim(opt.left(semi),cset_quotes);
64 if(getquoted(val,'=')<val.size()) val.clear();
65 else opt=opt.adv(semi).popf();
66 pile.clear();
67 while(opt.nempty()) {
68 sref optname=opt.left(getquoted(opt,'='));
69 opt=opt.adv(optname).popf();
70 semi=getquoted(opt,';');
71 sref optval=opt.left(semi);
72 opt=opt.adv(semi).popf();
73 optname=Trim(optname,cset_quotes);
74 optval=Trim(optval,cset_quotes);
75 if(optname.nempty()) pile.push_back(rrstring(optname,optval));
76 }
77 Handle(name,rawval,val,pile);
78 }
79 }
80 head=head.left(body-head);
81}
82
83
84///////////////////////////////////////////////////////////////////////////
85
86Mime::Mime()
87{
88 mime_version="1.0";
89}
90
91int Mime::Special(sref line)
92{
93 return 0;
94}
95
96int Mime::Handle(const sref& name,
97 const sref& rawval,
98 const sref& val,
99 const rrpile& opts) {
100 if(eqn(name,mime_version_tag)) mime_version=val;
101 else if(eqn(name,cont_type_tag)) {
102 (cont_type=val).tolower();
103 for(rrpile::const_iterator p=opts.begin();p!=opts.end();p++) {
104 if(eqn(p->first,charset_tag)) cont_type_charset=p->second;
105 else if(eqn(p->first,name_tag)) cont_type_name=p->second;
106 else if(eqn(p->first,boundary_tag)) cont_type_boundary=p->second;
107 }
108 }
109 else if(eqn(name,cont_id_tag)) cont_id=val;
110 else if(eqn(name,cont_desc_tag)) cont_desc=val;
111 else if(eqn(name,cont_disp_tag)) {
112 cont_disp=val;
113 for(rrpile::const_iterator p=opts.begin();p!=opts.end();p++) {
114 if(eqn(p->first,name_tag)) cont_disp_name=p->second;
115 else if(eqn(p->first,file_tag)) cont_disp_file=p->second;
116 }
117 }
118 else if(eqn(name,cont_trans_enc_tag)) cont_trans_enc=val;
119 else if(eqn(name,cont_length_tag)) cont_length=val;
120 else return 0;
121 return 1;
122}
123
124///////////////////////////////////////////////////////////////////////////
125
126void Mime::putHead(mstream& out) const
127{
128 if(putval(out,mime_version_tag,mime_version)) putend(out);
129 if(putval(out,cont_type_tag,cont_type)) {
130 putopt(out,charset_tag,cont_type_charset);
131 putopt(out,name_tag,cont_type_name);
132 putopt(out,boundary_tag,cont_type_boundary);
133 putend(out);
134 }
135 if(putval(out,cont_id_tag,cont_id)) putend(out);
136 if(putval(out,cont_desc_tag,cont_desc)) putend(out);
137 if(putval(out,cont_disp_tag,cont_disp)) {
138 putopt(out,name_tag,cont_disp_name);
139 putopt(out,file_tag,cont_disp_file);
140 putend(out);
141 }
142 if(putval(out,cont_trans_enc_tag,cont_trans_enc)) putend(out);
143 if(putval(out,cont_length_tag,cont_length)) putend(out);
144}
145
146
147///////////////////////////////////////////////////////////////////////////
148
149int Mime::Multipart(rpile& list) const
150{
151 if(cont_type_boundary.empty()) return 0;
152 mstring bound=sref("--")+cont_type_boundary;
153 sref rest=body.past_first(bound).past_nl();
154 while(rest.nempty()) {
155 int pos=rest.find(bound);
156 if(pos>=0) {
157 sref first=rest.left(pos);
158 if(first.back()=='\n') {
159 first=first.popb();
160 if(first.back()=='\r') first=first.popb();
161 }
162 list.push_back(first);
163 rest=rest.right(pos).adv(bound.size());
164 if(*rest=='-') rest.clear(); else rest=rest.past_nl();
165 }
166 else {
167 list.push_back(rest);
168 rest.clear();
169 }
170 }
171 return !list.empty();
172}
173
174void Mime::Pick(const sref& mime)
175{
176 rpile list;
177 if(Multipart(list)) {
178 if(eqn(cont_type,mime_multialt)) {
179 int pos=0;
180 for(int i=0;i<list.size();i++) {
181 Mime msg(list[i]);
182 if(eqn(msg.cont_type,mime)) { pos=i; break; }
183 }
184 Init(list[pos]);
185 }
186 }
187}
188
diff --git a/src/mime/mime.h b/src/mime/mime.h
new file mode 100644
index 0000000..fb2ae0f
--- /dev/null
+++ b/src/mime/mime.h
@@ -0,0 +1,99 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef MIMEH
21#define MIMEH
22
23#include <mime/enc.h>
24
25// MIME Headers
26const mstring mime_version_tag="MIME-Version";
27const mstring cont_type_tag="Content-Type";
28const mstring cont_trans_enc_tag="Content-Transfer-Encoding";
29const mstring cont_id_tag="Content-ID";
30const mstring cont_desc_tag="Content-Description";
31const mstring cont_disp_tag="Content-Disposition";
32const mstring cont_length_tag="Content-Length";
33// Option headers
34const mstring name_tag="name";
35const mstring file_tag="filename";
36const mstring charset_tag="charset";
37const mstring boundary_tag="boundary";
38const mstring path_tag="path";
39// Some Content types
40const mstring mime_textplain="text/plain";
41const mstring mime_texthtml="text/html";
42const mstring mime_texthtc="text/x-htc";
43const mstring mime_multialt="multipart/alternative";
44
45/////////////////////////////////////////////////////////////////
46
47const sref* file2mime(const sref& file);
48
49/////////////////////////////////////////////////////////////////
50
51class Mime {
52public:
53 sref head,body;
54
55 mstring mime_version;
56 mstring cont_type,cont_type_charset,cont_type_name,cont_type_boundary;
57 mstring cont_id;
58 mstring cont_desc;
59 mstring cont_disp,cont_disp_name,cont_disp_file;
60 mstring cont_trans_enc;
61 mstring cont_length;
62
63 virtual void putHead(mstream& out) const;
64
65 void Init(const sref& in);
66
67 int Multipart(rpile& list) const;
68 void Pick(const sref& mime);
69
70 Mime();
71 Mime(const sref& msg) { Init(msg); }
72
73protected:
74 virtual int Special(sref line);
75 virtual int Handle(const sref& name,
76 const sref& rawval,
77 const sref& val,
78 const rrpile& opts);
79};
80
81//////////////////////////////////////////////////////////////////
82
83inline int putval(mstream& out,const sref& tag,const sref& val) {
84 if(val.nempty()) { out<<tag<<": "<<val; return 1; } else return 0;
85}
86inline void putopt(mstream& out,const sref& tag,const sref& val) {
87 if(val.nempty()) out<<"; "<<tag<<"=\""<<val<<"\"";
88}
89inline int putopt1(mstream& out,const sref& tag,const sref& val) {
90 if(val.nempty()) { out<<tag<<"=\""<<val<<"\""; return 1; } else return 0;
91}
92inline int putopt2(mstream& out,const sref& tag,const sref& val) {
93 if(val.nempty()) { out<<tag<<"="<<val; return 1; } else return 0;
94}
95inline void putend(mstream& out) { out<<CRLF; }
96
97//////////////////////////////////////////////////////////////////
98
99#endif
diff --git a/src/mime/pack.cc b/src/mime/pack.cc
new file mode 100644
index 0000000..78fde26
--- /dev/null
+++ b/src/mime/pack.cc
@@ -0,0 +1,143 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <mime/pack.h>
21
22const char hex2char[16]={
23 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
24};
25const unsigned char2hex[256]={
26 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
27 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
28 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
29 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
30 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
31 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
32 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
33 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
34};
35
36mstream& cashex(mstream& out,unsigned char c) {
37 return out.put(hex2char[c>>4]).put(hex2char[c&15]);
38}
39char hexasc(unsigned char c1,unsigned char c2) {
40 return ((char2hex[c1]<<4)|char2hex[c2]);
41}
42
43void Pack(mstream& out,const sref& s)
44{
45 static const charset escaped("&=%+\"'? ");
46 static const charset conv=~cset_printable|escaped;
47 sref q,p=s;
48 while(p.nempty()) {
49 p=p.adv(q=p.left_first_of(conv));
50 out<<q;
51 while(conv.test(p.front())) {
52 char c=*p; p=p.popf();
53 if(c==' ') out.put('+');
54 else {
55 out.put('%');
56 cashex(out,c);
57 }
58 }
59 }
60}
61
62void Pack(mstream& out,const rpile& list)
63{
64 for(rpile::const_iterator p=list.begin();p!=list.end();++p) {
65 Pack(out,*p);
66 out.put('&');
67 }
68}
69
70void Pack(mstream& out,const rrpile& list)
71{
72 for(rrpile::const_iterator p=list.begin();p!=list.end();++p) {
73 Pack(out,p->first);
74 out.put('=');
75 Pack(out,p->second);
76 out.put('&');
77 }
78}
79
80///////////////////////////////////////////////////////////////
81
82void Unpack(mstream& out,const sref& in)
83{
84 static const charset tags("%+");
85 sref e,p=in;
86 while(p.nempty()) {
87 p=p.adv(e=p.left_first_of(tags));
88 out<<e;
89 while(tags.test(p.front())) {
90 char c=*p; p=p.popf();
91 if(c=='+') out.put(' ');
92 else if(c=='%') {
93 unsigned char c1=p[0],c2=p[1]; p=p.adv(2);
94 out.put(hexasc(c1,c2));
95 }
96 else out.put(c);
97 }
98 }
99}
100
101void Unpack(spile& list,const sref& in)
102{
103 sref e,p=in;
104 while(p.nempty()) {
105 p=p.adv(e=p.left_first('&')).popf();
106 if(e.nempty()) {
107 list.push_back(mstring());
108 swrite sw(list.back());
109 Unpack(sw,e);
110 }
111 }
112}
113
114void Unpack(sspile& list,const sref& in)
115{
116 sref e1,e2,p=in;
117 while(p.nempty()) {
118 p=p.adv(e1=p.left_first('=')).popf();
119 if(e1.nempty()) {
120 p=p.adv(e2=p.left_first('&')).popf();
121 list.push_back(sstring());
122 swrite sw1(list.back().first);
123 Unpack(sw1,e1);
124 swrite sw2(list.back().second);
125 Unpack(sw2,e2);
126 }
127 }
128}
129
130///////////////////////////////////////////////////////////////
131
132void CRLF2LF(mstream& out,const sref& in)
133{
134 sref first,rest=in;
135 while(Splitln(first,rest)) out<<first<<"\n";
136}
137
138void LF2CRLF(mstream& out,const sref& in)
139{
140 sref first,rest=in;
141 while(Splitln(first,rest)) out<<first<<CRLF;
142}
143
diff --git a/src/mime/pack.h b/src/mime/pack.h
new file mode 100644
index 0000000..e750aac
--- /dev/null
+++ b/src/mime/pack.h
@@ -0,0 +1,48 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef PACKH
21#define PACKH
22
23#include <mt/mset.h>
24#include <mt/msmap.h>
25
26// Really mimefunctions, but needed here
27
28mstream& cashex(mstream& out,unsigned char c);
29char hexasc(unsigned char c1,unsigned char c2);
30
31// Packing means application/x-www-form-urlencoded
32
33void Unpack(mstream& out,const sref& in);
34void Unpack(spile& list,const sref& in);
35void Unpack(sspile& list,const sref& in);
36
37void Pack(mstream& out,const sref& in);
38void Pack(mstream& out,const rpile& list);
39void Pack(mstream& out,const rrpile& list);
40
41// CR LF and CRLF business
42
43const mstring CRLF="\r\n";
44
45void CRLF2LF(mstream& out,const sref& in);
46void LF2CRLF(mstream& out,const sref& in);
47
48#endif
diff --git a/src/mime/qp.cc b/src/mime/qp.cc
new file mode 100644
index 0000000..78dc36f
--- /dev/null
+++ b/src/mime/qp.cc
@@ -0,0 +1,150 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <mime/enc.h>
21#include <mime/pack.h>
22#include <algorithm>
23
24const charset special("=\"#$\\^`{|}~");
25//const charset special("=!\"#$@[\\]^`{|}~");
26const charset headspecial("_?");
27const charset printable=cset_printable&~special;
28const charset printhead=printable&~headspecial;
29const mstring beg_tag("=?"),end_tag("?=");
30const mstring enc_qp_tag("Q"),enc_64_tag("B");
31const int WRAPAT=76;
32const int HEADWRAP=WRAPAT-7;
33
34void decodeQP(mstream& out,const sref& in)
35{
36 const char* p=in.begin();
37 while(p<in.end()) {
38 unsigned char c=*p++;
39 if(c=='=') {
40 if(cset_ws.test(*p)) p=in(p).past_nl().data();
41 else {
42 unsigned char c1=*p++,c2=*p++;
43 out.put(hexasc(c1,c2));
44 }
45 }
46 else out.put(c);
47 }
48}
49
50void encodeQP(mstream& out,const sref& in)
51{
52 sref first,rest=in;
53 while(Splitln(first,rest)) {
54 int n=0;
55 const char* p=first.begin();
56 while(p<first.end()) {
57 unsigned char c=*p++;
58 if(printable.test(c)) {
59 if(first(p).empty()&&cset_spaces.test(c)) { cashex(out.put('='),c); n+=3; }
60 else { out.put(c); n++; }
61 }
62 else { cashex(out.put('='),c); n+=3; }
63 if(first(p).nempty()&&n>=WRAPAT) { out.put('=')<<CRLF; n=0; }
64 }
65 out<<CRLF;
66 }
67}
68
69static void decodeHQ(mstream& out,const sref& in)
70{
71 const char* p=in.begin();
72 while(p<in.end()) {
73 unsigned char c=*p++;
74 if(c=='=') {
75 unsigned char c1=*p++,c2=*p++;
76 out.put(hexasc(c1,c2));
77 }
78 else if(c=='_') out.put(' ');
79 else out.put(c);
80 }
81}
82
83static int encodeHQ(mstream& out,const sref& in)
84{
85 int enc=0;
86 const char* p=in.begin();
87 while(p<in.end()) {
88 unsigned char c=*p++;
89 if(!printable.test(c)) enc=1;
90 if(printhead.test(c)) { if(c==32) out.put('_'); else out.put(c); }
91 else cashex(out.put('='),c);
92 }
93 return enc;
94}
95
96void decodeHeader(mstream& out,mstring& mime,const sref& in)
97{
98 sref e,p=in;
99 while(p.nempty()) {
100 p=p.adv(e=p.left_first(beg_tag));
101 out<<e;
102 if(p.nempty()) {
103 p=p.adv(beg_tag.size());
104 mime=p.left_first('?');
105 p=p.adv(mime.size());
106 if(p.empty()) { out<<beg_tag<<mime; p.clear(); }
107 else {
108 p=p.popf();
109 sref enc=p.left_first('?');
110 p=p.adv(enc);
111 if(p.empty()) { out<<beg_tag<<mime<<"?"<<enc; p.clear(); }
112 else {
113 p=p.adv(e=p.popf().left_first(end_tag));
114 if(p.empty()) { out<<beg_tag<<mime<<"?"<<enc<<"?"<<e; p.clear(); }
115 else {
116 p=p.adv(end_tag.size());
117 if(eqn(enc,enc_qp_tag)) decodeHQ(out,e);
118 else if(eqn(enc,enc_64_tag)) decode64(out,e);
119 else out<<"BAD: ["<<mime<<"]["<<enc<<"]["<<e<<"]";
120 }
121 }
122 }
123 }
124 }
125}
126
127void encodeHeader(mstream& out,const sref& mime,const sref& in)
128{
129 mstring tmp;
130 swrite sw(tmp);
131 if(encodeHQ(sw,in)) {
132 int wrapat=HEADWRAP-mime.size();
133 sref e,p=tmp;
134 p=p.adv(e=p.left(std::min(wrapat,p.size())));
135 out<<beg_tag<<mime<<"?Q?"<<e<<end_tag;
136 while(p.nempty()) {
137 p=p.adv(e=p.left(std::min(wrapat,p.size())));
138 out<<"\r\n\t"<<beg_tag<<mime<<"?Q?"<<e<<end_tag;
139 }
140 }
141 else {
142 sref e,p=in;
143 p=p.adv(e=p.left(std::min(WRAPAT,p.size())));
144 out<<e;
145 while(p.nempty()) {
146 p=p.adv(e=p.left(std::min(WRAPAT,p.size())));
147 out<<"\r\n\t"<<e;
148 }
149 }
150}
diff --git a/src/mime/test.cc b/src/mime/test.cc
new file mode 100644
index 0000000..6392904
--- /dev/null
+++ b/src/mime/test.cc
@@ -0,0 +1,108 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <mime/mailbox.h>
21#include <mime/mime.h>
22#include <mime/enc.h>
23#include <sys/stat.h>
24
25static fp_stream mout(stdout),merr(stderr);
26
27static void Show(mstream& out,const Message& msg)
28{
29 if(msg.cont_type.empty()||
30 eqn(msg.cont_type,mime_textplain)||
31 eqn(msg.cont_type,mime_texthtml)) {
32 if(eqn(msg.cont_trans_enc,trans_quote)) {
33 //decodeQP(out,msg.body);
34 mswrite tmp;
35 decodeQP(tmp,msg.body);
36 encodeQP(out,tmp.str());
37 }
38 else {
39 //out<<msg.body<<"\n";
40 encodeQP(out,msg.body);
41 }
42 }
43 else out<<"(not shown)\n";
44}
45
46main(int argc,char* argv[])
47{
48 if(argc<2) {
49 merr<<"Usage: "<<argv[0]<<" <source> [index]\n";
50 return 1;
51 }
52 Mailbox box;
53 GetMessages(box,argv[1]);
54 merr<<"Found "<<box.size()<<" messages\n";
55 if(argc>2) {
56 Message msg(box[atoi(argv[2])]);
57 //msg.Pick(mime_textplain);
58
59 mswrite tmp;
60 encodeHeader(tmp,mime_default,msg.subject);
61 msg.subject=tmp.str();
62
63 msg.putHead(mout);
64 mout<<"-----------------------------------------\n";
65 rpile part;
66 if(msg.Multipart(part)) {
67 for(int i=0;i<part.size();i++) {
68 Message pm(part[i]);
69 pm.putHead(mout);
70 mout<<"-------------------------------\n";
71 Show(mout,pm);
72 mout<<"-------------------------------\n";
73 }
74 }
75 else {
76 Show(mout,msg);
77 }
78
79 mout<<"**********\n";
80 mout<<msg.head;
81 mout<<"**********\n";
82 mout<<msg.body;
83 mout<<"**********\n";
84
85 }
86
87 return 0;
88}
89
90/*
91main(int argc,char* argv[])
92{
93 if(argc<2) {
94 merr<<"Usage: "<<argv[0]<<" <source>\n";
95 return 1;
96 }
97 mstring buffer; buffer.load(argv[1]);
98 merr<<buffer.size()<<"\n";
99 mswrite enc;
100 encode64(enc,buffer);
101 merr<<enc.str().size()<<"\n";
102 mswrite nbuf;
103 decode64(nbuf,enc.str());
104 merr<<nbuf.str().size()<<"\n";
105 if(nbuf.str()==buffer) merr<<"Match.\n";
106 mout<<enc.str();
107}
108*/