Updated.
[wine] / dlls / shlwapi / url.c
1 /*
2  * Url functions
3  *
4  * Copyright 2000 Huw D M Davies for CodeWeavers.
5  */
6
7 #include <string.h>
8 #include "windef.h"
9 #include "winbase.h"
10 #include "winerror.h"
11 #include "shlwapi.h"
12 #include "debugtools.h"
13
14 DEFAULT_DEBUG_CHANNEL(shell);
15
16 static const unsigned char HashDataLookup[256] = {
17  0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
18  0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
19  0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
20  0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
21  0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
22  0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
23  0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
24  0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
25  0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
26  0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
27  0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
28  0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
29  0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
30  0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
31  0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
32  0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
33  0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
34  0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
35  0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
36  0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
37
38 static BOOL URL_NeedEscape(CHAR ch, DWORD dwFlags)
39 {
40
41     if (isalnum(ch))
42         return FALSE;
43
44     if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
45         if(ch == ' ')
46             return TRUE;
47         else
48             return FALSE;
49     }
50
51     if (ch <= 31 || ch >= 127)
52         return TRUE;
53
54     else {
55         switch (ch) {
56         case ' ':
57         case '<':
58         case '>':
59         case '\"':
60         case '{':
61         case '}':
62         case '|':
63         case '\\':
64         case '^':
65         case ']':
66         case '[':
67         case '`':
68         case '&':
69             return TRUE;
70
71         default:
72             return FALSE;
73         }
74     }
75 }
76
77 /*************************************************************************
78  *        UrlCanonicalizeA     [SHLWAPI]
79  */
80 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
81         LPDWORD pcchCanonicalized, DWORD dwFlags)
82 {
83     HRESULT hr = S_OK;
84
85     LPSTR lpszUrlCpy;
86     INT nLen;
87
88     TRACE("(%s %p %p 0x%08lx)\n", debugstr_a(pszUrl), pszCanonicalized,
89           pcchCanonicalized, dwFlags);
90
91     nLen = strlen(pszUrl);
92     lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nLen + 1);
93
94     if (dwFlags & URL_DONT_SIMPLIFY)
95         memcpy(lpszUrlCpy, pszUrl, nLen + 1);
96     else {
97         FIXME("Simplify path\n");
98         memcpy(lpszUrlCpy, pszUrl, nLen + 1);
99     }
100
101     if(dwFlags & URL_UNESCAPE)
102         UrlUnescapeA(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
103
104     if(dwFlags & (URL_ESCAPE_UNSAFE | URL_ESCAPE_SPACES_ONLY)) {
105         DWORD EscapeFlags = dwFlags & (URL_ESCAPE_SPACES_ONLY
106                                        /* | URL_ESCAPE_PERCENT */);
107         hr = UrlEscapeA(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
108                         EscapeFlags);
109     } else { /* No escapping needed, just copy the string */
110         nLen = strlen(lpszUrlCpy);
111         if(nLen < *pcchCanonicalized)
112             memcpy(pszCanonicalized, lpszUrlCpy, nLen + 1);
113         else {
114             hr = E_POINTER;
115             nLen++;
116         }
117         *pcchCanonicalized = nLen;
118     }
119
120     HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
121   
122     return hr;
123 }
124
125 /*************************************************************************
126  *        UrlCanonicalizeW     [SHLWAPI]
127  */
128 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized, 
129                                 LPDWORD pcchCanonicalized, DWORD dwFlags)
130 {
131     FIXME("(%s %p %p 0x%08lx): stub\n",debugstr_w(pszUrl),
132           pszCanonicalized, pcchCanonicalized, dwFlags);
133     return E_NOTIMPL;
134 }
135
136 /*************************************************************************
137  *      UrlEscapeA      [SHLWAPI]
138  *
139  * Converts unsafe characters into their escape sequences.
140  *
141  * The converted string is returned in pszEscaped if the buffer size
142  * (which should be supplied in pcchEscaped) is large enough, in this
143  * case the function returns S_OK and pcchEscaped contains the length
144  * of the escaped string.  If the buffer is not large enough the
145  * function returns E_POINTER and pcchEscaped contains the required
146  * buffer size (including room for the '\0').
147  *
148  * By default the function stops converting at the first '?' or
149  * '#'. [MSDN says differently].  If URL_ESCAPE_SPACE_ONLY flag is set
150  * then only spaces are converted, but the conversion continues past a
151  * '?' or '#'.
152  *
153  * BUGS:
154  *
155  * None of the URL_ define values are documented, so they were
156  * determined by trial and error.  MSDN mentions URL_ESCAPE_PERCENT
157  * but I can't find a value that does this under win2000.
158  * URL_DONT_ESCAPE_EXTRA_INFO appears to be the default which is what
159  * we assume here.  URL_ESCAPE_SEGMENT_ONLY is not implemented
160  * (value??).  A value of 0x2000 for dwFlags seems to escape
161  * '/'s too - this is neither documented on MSDN nor implemented here.
162  * For character values that are converted see URL_NeedEscape.
163  */
164 HRESULT WINAPI UrlEscapeA(
165         LPCSTR pszUrl,
166         LPSTR pszEscaped,
167         LPDWORD pcchEscaped,
168         DWORD dwFlags)
169 {
170     LPCSTR src;
171     DWORD needed = 0, ret;
172     BOOL stop_escapping = FALSE;
173     char next[3], *dst = pszEscaped;
174     char hex[] = "0123456789ABCDEF";
175     INT len;
176
177     TRACE("(%s %p %p 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
178           pcchEscaped, dwFlags);
179
180     if(dwFlags & ~URL_ESCAPE_SPACES_ONLY)
181         FIXME("Unimplemented flags: %08lx\n", dwFlags);
182
183     for(src = pszUrl; *src; src++) {
184         if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
185            (*src == '#' || *src == '?'))
186             stop_escapping = TRUE;
187
188         if(URL_NeedEscape(*src, dwFlags) && stop_escapping == FALSE) {
189             next[0] = '%';
190             next[1] = hex[(*src >> 4) & 0xf];
191             next[2] = hex[*src & 0xf];
192             len = 3;
193         } else {
194             next[0] = *src;
195             len = 1;
196         }
197
198         if(needed + len <= *pcchEscaped) {
199             memcpy(dst, next, len);
200             dst += len;
201         }
202         needed += len;
203     }
204
205     if(needed < *pcchEscaped) {
206         *dst = '\0';
207         ret = S_OK;
208     } else {
209         needed++; /* add one for the '\0' */
210         ret = E_POINTER;
211     }
212     *pcchEscaped = needed;
213     return ret;
214 }       
215
216 /*************************************************************************
217  *      UrlEscapeW      [SHLWAPI]
218  */
219 HRESULT WINAPI UrlEscapeW(
220         LPCWSTR pszUrl,
221         LPWSTR pszEscaped,
222         LPDWORD pcchEscaped,
223         DWORD dwFlags)
224 {
225     FIXME("(%s %p %p 0x%08lx): stub\n",debugstr_w(pszUrl),
226           pszEscaped, pcchEscaped, dwFlags);
227     return E_NOTIMPL;
228 }
229
230
231 /*************************************************************************
232  *      UrlUnescapeA    [SHLWAPI]
233  *
234  * Converts escape sequences back to ordinary characters.
235  * 
236  * If URL_ESCAPE_INPLACE is set in dwFlags then pszUnescaped and
237  * pcchUnescaped are ignored and the converted string is returned in
238  * pszUrl, otherwise the string is returned in pszUnescaped.
239  * pcchUnescaped should contain the size of pszUnescaped on calling
240  * and will contain the length the the returned string on return if
241  * the buffer is big enough else it will contain the buffer size
242  * required (including room for the '\0').  The function returns S_OK
243  * on success or E_POINTER if the buffer is not large enough.  If the
244  * URL_DONT_ESCAPE_EXTRA_INFO flag is set then the conversion stops at
245  * the first occurrence of either '?' or '#'.
246  *
247  */
248 HRESULT WINAPI UrlUnescapeA(
249         LPCSTR pszUrl,
250         LPSTR pszUnescaped,
251         LPDWORD pcchUnescaped,
252         DWORD dwFlags)
253 {
254     char *dst, next;
255     LPCSTR src;
256     HRESULT ret;
257     DWORD needed;
258     BOOL stop_unescapping = FALSE;
259
260     TRACE("(%s, %p, %p, %08lx): stub\n", debugstr_a(pszUrl), pszUnescaped,
261           pcchUnescaped, dwFlags);
262
263     if(dwFlags & URL_UNESCAPE_INPLACE)
264         dst = (char*)pszUrl;
265     else
266         dst = pszUnescaped;
267
268     for(src = pszUrl, needed = 0; *src; src++, needed++) {
269         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
270            (*src == '#' || *src == '?')) {
271             stop_unescapping = TRUE;
272             next = *src;
273         } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
274                   && stop_unescapping == FALSE) {
275             INT ih;
276             char buf[3];
277             memcpy(buf, src + 1, 2);
278             buf[2] = '\0';
279             ih = strtol(buf, NULL, 16);
280             next = (CHAR) ih;
281             src += 2; /* Advance to end of escape */
282         } else
283             next = *src;
284
285         if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
286             *dst++ = next;
287     }
288
289     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
290         *dst = '\0';
291         ret = S_OK;
292     } else {
293         needed++; /* add one for the '\0' */
294         ret = E_POINTER;
295     }
296     if(!(dwFlags & URL_UNESCAPE_INPLACE))
297         *pcchUnescaped = needed;
298
299     return ret;
300 }
301
302 /*************************************************************************
303  *      UrlUnescapeW    [SHLWAPI]
304  */
305 HRESULT WINAPI UrlUnescapeW(
306         LPCWSTR pszUrl,
307         LPWSTR pszUnescaped,
308         LPDWORD pcchUnescaped,
309         DWORD dwFlags)
310 {
311     FIXME("(%s, %p, %p, %08lx): stub\n", debugstr_w(pszUrl), pszUnescaped,
312           pcchUnescaped, dwFlags);
313     return E_NOTIMPL;
314 }
315
316 /*************************************************************************
317  *      HashData        [SHLWAPI]
318  *
319  * Hash an input block into a variable sized digest.
320  */
321 BOOL WINAPI HashData(const unsigned char *lpSrc, INT nSrcLen,
322                      unsigned char *lpDest, INT nDestLen)
323 {
324   INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
325
326   if (IsBadReadPtr(lpSrc, nSrcLen) ||
327       IsBadWritePtr(lpDest, nDestLen))
328     return FALSE;
329
330   while (destCount >= 0)
331   {
332     lpDest[destCount] = (destCount & 0xff);
333     destCount--;
334   }
335
336   while (srcCount >= 0)
337   {
338     destCount = nDestLen - 1;
339     while (destCount >= 0)
340     {
341       lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
342       destCount--;
343     }
344     srcCount--;
345   }
346   return TRUE;
347 }
348
349 /*************************************************************************
350  *      UrlHashA        [SHLWAPI]
351  *
352  * Hash an ASCII URL.
353  */
354 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, INT nDestLen)
355 {
356   if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
357     return E_INVALIDARG;
358
359   HashData(pszUrl, strlen(pszUrl), lpDest, nDestLen);
360   return NOERROR;
361 }
362