]> git.donarmstrong.com Git - roundcube.git/blob - program/lib/utf7.inc
Merge commit 'upstream/0.1_rc1_dfsg'
[roundcube.git] / program / lib / utf7.inc
1 <?php
2
3 /*
4  *  Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org>
5  * 
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  * 
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  Translated from C to PHP by Thomas Bruederli <roundcube@gmail.com>
17  */ 
18
19
20 /**
21  * Convert the data ($str) from RFC 2060's UTF-7 to UTF-8.
22  * If input data is invalid, return the original input string.
23  * RFC 2060 obviously intends the encoding to be unique (see
24  * point 5 in section 5.1.3), so we reject any non-canonical
25  * form, such as &ACY- (instead of &-) or &AMA-&AMA- (instead
26  * of &AMAAwA-).
27  */
28 function utf7_to_utf8($str)
29 {
30   $Index_64 = array(
31       -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
32       -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
33       -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, 63,-1,-1,-1,
34       52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
35       -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
36       15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
37       -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
38       41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
39   );
40
41   $u7len = strlen($str);
42   $p = $err = '';
43
44   for ($i=0; $u7len > 0; $i++, $u7len--)
45   {
46     $u7 = $str{$i};
47     if ($u7 == '&')
48     {
49       $i++;
50       $u7len--;
51       $u7 = $str{$i};
52       
53       if ($u7len && $u7 == '-')
54       {
55         $p .= '&';
56         continue;
57       }
58
59       $ch = 0;
60       $k = 10;
61       for (; $u7len > 0; $i++, $u7len--)
62       {
63         $u7 = $str{$i};
64
65         if ((ord($u7) & 0x80) || ($b = $Index_64[ord($u7)]) == -1)
66           break;
67
68         if ($k > 0)
69         {
70           $ch |= $b << $k;
71           $k -= 6;
72         }
73         else
74         {
75           $ch |= $b >> (-$k);
76           if ($ch < 0x80)
77           {
78             /* Printable US-ASCII */
79             if (0x20 <= $ch && $ch < 0x7f)
80               return $err;
81            $p .= chr($ch);
82           }
83           else if ($ch < 0x800)
84           {
85             $p .= chr(0xc0 | ($ch >> 6));
86             $p .= chr(0x80 | ($ch & 0x3f));
87           }
88           else
89           {
90             $p .= chr(0xe0 | ($ch >> 12));
91             $p .= chr(0x80 | (($ch >> 6) & 0x3f));
92             $p .= chr(0x80 | ($ch & 0x3f));
93           }
94
95           $ch = ($b << (16 + $k)) & 0xffff;
96           $k += 10;
97         }
98       }
99
100       /* Non-zero or too many extra bits */
101       if ($ch || $k < 6)
102         return $err;
103         
104       /* BASE64 not properly terminated */
105       if (!$u7len || $u7 != '-')
106         return $err;
107         
108       /* Adjacent BASE64 sections */
109       if ($u7len > 2 && $str{$i+1} == '&' && $str{$i+2} != '-')
110         return $err;
111     }
112     /* Not printable US-ASCII */
113     else if (ord($u7) < 0x20 || ord($u7) >= 0x7f)
114       return $err;
115     else
116       $p .= $u7;
117   }
118
119   return $p;
120 }
121
122
123 /**
124  * Convert the data ($str) from UTF-8 to RFC 2060's UTF-7.
125  * Unicode characters above U+FFFF are replaced by U+FFFE.
126  * If input data is invalid, return an empty string.
127  */
128 function utf8_to_utf7($str)
129 {
130   $B64Chars = array(
131     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
132     'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
133     'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
134     't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
135     '8', '9', '+', ','
136   );
137
138   $u8len = strlen($str);
139   $base64 = $i = 0;
140   $p = $err = '';
141
142   while ($u8len)
143   {
144     $u8 = $str{$i};
145     $c = ord($u8);
146     
147     if ($c < 0x80)
148     {
149       $ch = $c;
150       $n = 0;
151     }
152     else if ($c < 0xc2)
153       return $err;
154     else if ($c < 0xe0)
155     {
156       $ch = $c & 0x1f;
157       $n = 1;
158     }
159     else if ($c < 0xf0)
160     {
161       $ch = $c & 0x0f;
162       $n = 2;
163     }
164     else if ($c < 0xf8)
165     {
166       $ch = $c & 0x07;
167       $n = 3;
168     }
169     else if ($c < 0xfc)
170     {
171       $ch = $c & 0x03;
172       $n = 4;
173     }
174     else if ($c < 0xfe)
175     {
176       $ch = $c & 0x01;
177       $n = 5;
178     }
179     else
180       return $err;
181
182     $i++;
183     $u8len--;
184
185     if ($n > $u8len)
186       return $err;
187
188     for ($j=0; $j < $n; $j++)
189     {
190       $o = ord($str{$i+$j});
191       if (($o & 0xc0) != 0x80)
192         return $err;
193       $ch = ($ch << 6) | ($o & 0x3f);
194     }
195     
196     if ($n > 1 && !($ch >> ($n * 5 + 1)))
197       return $err;
198     
199     $i += $n;
200     $u8len -= $n;
201
202     if ($ch < 0x20 || $ch >= 0x7f)
203     {
204       if (!$base64)
205       {
206         $p .= '&';
207         $base64 = 1;
208         $b = 0;
209         $k = 10;
210       }
211       if ($ch & ~0xffff)
212         $ch = 0xfffe;
213       
214       $p .= $B64Chars[($b | $ch >> $k)];
215       $k -= 6;
216       for (; $k >= 0; $k -= 6)
217         $p .= $B64Chars[(($ch >> $k) & 0x3f)];
218
219       $b = ($ch << (-$k)) & 0x3f;
220       $k += 16;
221     }
222     else
223     {
224       if ($base64)
225       {
226         if ($k > 10)
227           $p .= $B64Chars[$b];
228         $p .= '-';
229         $base64 = 0;
230       }
231       
232       $p .= chr($ch);
233       if (chr($ch) == '&')
234         $p .= '-';
235     }
236   }
237
238   if ($base64)
239   {
240     if ($k > 10)
241       $p .= $B64Chars[$b];
242     $p .= '-';
243   }
244
245   return $p;
246 }
247
248 ?>