1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include <glib.h>
21 #include <ctype.h>
22 
23 #include "utils.h"
24 
25 #define MAX_LINELEN	76
26 
27 #define IS_LBREAK(p) \
28 	(*(p) == '\0' || *(p) == '\n' || (*(p) == '\r' && *((p) + 1) == '\n'))
29 
30 #define SOFT_LBREAK_IF_REQUIRED(n)					\
31 	if (len + (n) > MAX_LINELEN ||					\
32 	    (len + (n) == MAX_LINELEN && (!IS_LBREAK(inp + 1)))) {	\
33 		*outp++ = '=';						\
34 		*outp++ = '\n';						\
35 		len = 0;						\
36 	}
37 
qp_encode_line(gchar * out,const guchar * in)38 void qp_encode_line(gchar *out, const guchar *in)
39 {
40 	const guchar *inp = in;
41 	gchar *outp = out;
42 	guchar ch;
43 	gint len = 0;
44 
45 	while (*inp != '\0') {
46 		ch = *inp;
47 
48 		if (IS_LBREAK(inp)) {
49 			*outp++ = '\n';
50 			len = 0;
51 			if (*inp == '\r')
52 				inp++;
53 			inp++;
54 		} else if (ch == '\t' || ch == ' ') {
55 			if (IS_LBREAK(inp + 1)) {
56 				SOFT_LBREAK_IF_REQUIRED(3);
57 				*outp++ = '=';
58 				get_hex_str(outp, ch);
59 				outp += 2;
60 				len += 3;
61 				inp++;
62 			} else {
63 				SOFT_LBREAK_IF_REQUIRED(1);
64 				*outp++ = *inp++;
65 				len++;
66 			}
67 		} else if ((ch >= 33 && ch <= 60) || (ch >= 62 && ch <= 126)) {
68 			SOFT_LBREAK_IF_REQUIRED(1);
69 			*outp++ = *inp++;
70 			len++;
71 		} else {
72 			SOFT_LBREAK_IF_REQUIRED(3);
73 			*outp++ = '=';
74 			get_hex_str(outp, ch);
75 			outp += 2;
76 			len += 3;
77 			inp++;
78 		}
79 	}
80 
81 	if (len > 0)
82 		*outp++ = '\n';
83 
84 	*outp = '\0';
85 }
86 
qp_decode_line(gchar * str)87 gint qp_decode_line(gchar *str)
88 {
89 	gchar *inp = str, *outp = str;
90 
91 	while (*inp != '\0') {
92 		if (*inp == '=') {
93 			if (inp[1] && inp[2] &&
94 			    get_hex_value((guchar *)outp, inp[1], inp[2])
95 			    == TRUE) {
96 				inp += 3;
97 			} else if (inp[1] == '\0' || g_ascii_isspace(inp[1])) {
98 				/* soft line break */
99 				break;
100 			} else {
101 				/* broken QP string */
102 				*outp = *inp++;
103 			}
104 		} else {
105 			*outp = *inp++;
106 		}
107 		outp++;
108 	}
109 
110 	*outp = '\0';
111 
112 	return outp - str;
113 }
114 
qp_decode_const(gchar * out,gint avail,const gchar * str)115 gint qp_decode_const(gchar *out, gint avail, const gchar *str)
116 {
117 	const gchar *inp = str;
118 	gchar *outp = out;
119 
120 	while (*inp != '\0' && avail > 0) {
121 		if (*inp == '=') {
122 			if (inp[1] && inp[2] &&
123 			    get_hex_value((guchar *)outp, inp[1], inp[2])
124 			    == TRUE) {
125 				inp += 3;
126 			} else if (inp[1] == '\0' || g_ascii_isspace(inp[1])) {
127 				/* soft line break */
128 				break;
129 			} else {
130 				/* broken QP string */
131 				*outp = *inp++;
132 			}
133 		} else {
134 			*outp = *inp++;
135 		}
136 		outp++;
137 		avail--;
138 	}
139 
140 	*outp = '\0';
141 
142 	return outp - out;
143 }
144 
qp_decode_q_encoding(guchar * out,const gchar * in,gint inlen)145 gint qp_decode_q_encoding(guchar *out, const gchar *in, gint inlen)
146 {
147 	const gchar *inp = in;
148 	guchar *outp = out;
149 
150 	if (inlen < 0)
151 		inlen = G_MAXINT;
152 
153 	while (inp - in < inlen && *inp != '\0') {
154 		if (*inp == '=' && inp + 3 - in <= inlen) {
155 			if (get_hex_value(outp, inp[1], inp[2]) == TRUE) {
156 				inp += 3;
157 			} else {
158 				*outp = *inp++;
159 			}
160 		} else if (*inp == '_') {
161 			*outp = ' ';
162 			inp++;
163 		} else {
164 			*outp = *inp++;
165 		}
166 		outp++;
167 	}
168 
169 	*outp = '\0';
170 
171 	return outp - out;
172 }
173 
qp_get_q_encoding_len(const guchar * str)174 gint qp_get_q_encoding_len(const guchar *str)
175 {
176 	const guchar *inp = str;
177 	gint len = 0;
178 
179 	while (*inp != '\0') {
180 		if (*inp == 0x20)
181 			len++;
182 		else if (*inp == '=' || *inp == '?' || *inp == '_' ||
183 			 *inp < 32 || *inp > 127 || g_ascii_isspace(*inp))
184 			len += 3;
185 		else
186 			len++;
187 
188 		inp++;
189 	}
190 
191 	return len;
192 }
193 
qp_q_encode(gchar * out,const guchar * in)194 void qp_q_encode(gchar *out, const guchar *in)
195 {
196 	const guchar *inp = in;
197 	gchar *outp = out;
198 
199 	while (*inp != '\0') {
200 		if (*inp == 0x20)
201 			*outp++ = '_';
202 		else if (*inp == '=' || *inp == '?' || *inp == '_' ||
203 			 *inp < 32 || *inp > 127 || g_ascii_isspace(*inp)) {
204 			*outp++ = '=';
205 			get_hex_str(outp, *inp);
206 			outp += 2;
207 		} else
208 			*outp++ = *inp;
209 
210 		inp++;
211 	}
212 
213 	*outp = '\0';
214 }
215