Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-mime-filter-canon.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2002 Ximian, Inc.
4  *
5  * Authors: Jeffrey Stedfast <fejj@ximian.com>
6  *          Michael Zucchi <notzed@ximian.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 /* canonicalisation filter, used for secure mime incoming and outgoing */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <ctype.h>
30 #include <string.h>
31
32 #include "camel-mime-filter-canon.h"
33
34 static void filter (CamelMimeFilter *f, char *in, size_t len, size_t prespace,
35                     char **out, size_t *outlen, size_t *outprespace);
36 static void complete (CamelMimeFilter *f, char *in, size_t len,
37                       size_t prespace, char **out, size_t *outlen,
38                       size_t *outprespace);
39 static void reset (CamelMimeFilter *f);
40
41
42 static void
43 camel_mime_filter_canon_class_init (CamelMimeFilterCanonClass *klass)
44 {
45         CamelMimeFilterClass *mime_filter_class = (CamelMimeFilterClass *) klass;
46         
47         mime_filter_class->filter = filter;
48         mime_filter_class->complete = complete;
49         mime_filter_class->reset = reset;
50 }
51
52 CamelType
53 camel_mime_filter_canon_get_type (void)
54 {
55         static CamelType type = CAMEL_INVALID_TYPE;
56         
57         if (type == CAMEL_INVALID_TYPE) {
58                 type = camel_type_register (camel_mime_filter_get_type(), "CamelMimeFilterCanon",
59                                             sizeof (CamelMimeFilterCanon),
60                                             sizeof (CamelMimeFilterCanonClass),
61                                             (CamelObjectClassInitFunc) camel_mime_filter_canon_class_init,
62                                             NULL,
63                                             NULL,
64                                             NULL);
65         }
66         
67         return type;
68 }
69
70 static void
71 filter_run(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace, int last)
72 {
73         register unsigned char *inptr, c;
74         const unsigned char *inend, *start;
75         char *starto;
76         register char *o;
77         int lf = 0;
78         guint32 flags;
79
80         flags = ((CamelMimeFilterCanon *)f)->flags;
81
82         /* first, work out how much space we need */
83         inptr = in;
84         inend = in+len;
85         while (inptr < inend)
86                 if (*inptr++ == '\n')
87                         lf++;
88
89         /* worst case, extra 3 chars per line
90            "From \n" -> "=46rom \r\n"
91            We add 1 extra incase we're called from complete, when we didn't end in \n */
92
93         camel_mime_filter_set_size(f, len+lf*3+4, FALSE);
94
95         o = f->outbuf;
96         inptr = in;
97         start = inptr;
98         starto = o;
99         while (inptr < inend) {
100                 /* first, check start of line, we always start at the start of the line */
101                 c = *inptr;
102                 if (flags & CAMEL_MIME_FILTER_CANON_FROM && c == 'F') {
103                         inptr++;
104                         if (inptr < inend-4) {
105                                 if (strncmp(inptr, "rom ", 4) == 0) {
106                                         strcpy(o, "=46rom ");
107                                         inptr+=4;
108                                         o+= 7;
109                                 } else
110                                         *o++ = 'F';
111                         } else if (last)
112                                 *o++ = 'F';
113                         else
114                                 break;
115                 }
116
117                 /* now scan for end of line */
118                 while (inptr < inend) {
119                         c = *inptr++;
120                         if (c == '\n') {
121                                 /* check to strip trailing space */
122                                 if (flags & CAMEL_MIME_FILTER_CANON_STRIP) {
123                                         while (o>starto && (o[-1] == ' ' || o[-1] == '\t' || o[-1]=='\r'))
124                                                 o--;
125                                 }
126                                 /* check end of line canonicalisation */
127                                 if (o>starto) {
128                                         if (flags & CAMEL_MIME_FILTER_CANON_CRLF) {
129                                                 if (o[-1] != '\r')
130                                                         *o++ = '\r';
131                                         } else {
132                                                 if (o[-1] == '\r')
133                                                         o--;
134                                         }
135                                 } else if (flags & CAMEL_MIME_FILTER_CANON_CRLF) {
136                                         /* empty line */
137                                         *o++ = '\r';
138                                 }
139                                 
140                                 *o++ = c;
141                                 start = inptr;
142                                 starto = o;
143                                 break;
144                         } else
145                                 *o++ = c;
146                 }
147         }
148
149         /* TODO: We should probably track if we end somewhere in the middle of a line,
150            otherwise we potentially backup a full line, which could be large */
151
152         /* we got to the end of the data without finding anything, backup to start and re-process next time around */
153         if (last) {
154                 *outlen = o - f->outbuf;
155         } else {
156                 camel_mime_filter_backup(f, start, inend - start);
157                 *outlen = starto - f->outbuf;
158         }
159
160         *out = f->outbuf;
161         *outprespace = f->outpre;
162 }
163
164 static void
165 filter(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
166 {
167         filter_run(f, in, len, prespace, out, outlen, outprespace, FALSE);
168 }
169
170 static void 
171 complete(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
172 {
173         filter_run(f, in, len, prespace, out, outlen, outprespace, TRUE);
174 }
175
176 static void
177 reset (CamelMimeFilter *f)
178 {
179         /* no-op */
180 }
181
182
183 /**
184  * camel_mime_filter_canon_new:
185  * @flags: bitwise flags defining the behaviour of the filter
186  *
187  * Create a new filter to canonicalise an input stream.
188  *
189  * Returns a new #CamelMimeFilterCanon
190  **/
191 CamelMimeFilter *
192 camel_mime_filter_canon_new(guint32 flags)
193 {
194         CamelMimeFilterCanon *chomp = CAMEL_MIME_FILTER_CANON (camel_object_new (CAMEL_MIME_FILTER_CANON_TYPE));
195
196         chomp->flags = flags;
197
198         return (CamelMimeFilter *) chomp;
199 }