1 /* 2 * Copyright 2008 Hans Leidekker for CodeWeavers 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include "config.h" 20 #include <stdarg.h> 21 22 #include "wine/debug.h" 23 #include "wine/list.h" 24 25 #include "windef.h" 26 #include "winbase.h" 27 #include "winhttp.h" 28 29 #include "winhttp_private.h" 30 31 WINE_DEFAULT_DEBUG_CHANNEL(winhttp); 32 33 static domain_t *add_domain( session_t *session, WCHAR *name ) 34 { 35 domain_t *domain; 36 37 if (!(domain = heap_alloc_zero( sizeof(domain_t) ))) return NULL; 38 39 list_init( &domain->entry ); 40 list_init( &domain->cookies ); 41 42 domain->name = strdupW( name ); 43 list_add_tail( &session->cookie_cache, &domain->entry ); 44 45 TRACE("%s\n", debugstr_w(domain->name)); 46 return domain; 47 } 48 49 static cookie_t *find_cookie( domain_t *domain, const WCHAR *path, const WCHAR *name ) 50 { 51 struct list *item; 52 cookie_t *cookie; 53 54 LIST_FOR_EACH( item, &domain->cookies ) 55 { 56 cookie = LIST_ENTRY( item, cookie_t, entry ); 57 if (!strcmpW( cookie->path, path ) && !strcmpW( cookie->name, name )) 58 { 59 TRACE("found %s=%s\n", debugstr_w(cookie->name), debugstr_w(cookie->value)); 60 return cookie; 61 } 62 } 63 return NULL; 64 } 65 66 static BOOL domain_match( const WCHAR *name, domain_t *domain, BOOL partial ) 67 { 68 TRACE("comparing %s with %s\n", debugstr_w(name), debugstr_w(domain->name)); 69 70 if (partial && !strstrW( name, domain->name )) return FALSE; 71 else if (!partial && strcmpW( name, domain->name )) return FALSE; 72 return TRUE; 73 } 74 75 static void free_cookie( cookie_t *cookie ) 76 { 77 heap_free( cookie->name ); 78 heap_free( cookie->value ); 79 heap_free( cookie->path ); 80 heap_free( cookie ); 81 } 82 83 static void delete_cookie( cookie_t *cookie ) 84 { 85 list_remove( &cookie->entry ); 86 free_cookie( cookie ); 87 } 88 89 void delete_domain( domain_t *domain ) 90 { 91 cookie_t *cookie; 92 struct list *item, *next; 93 94 LIST_FOR_EACH_SAFE( item, next, &domain->cookies ) 95 { 96 cookie = LIST_ENTRY( item, cookie_t, entry ); 97 delete_cookie( cookie ); 98 } 99 100 list_remove( &domain->entry ); 101 heap_free( domain->name ); 102 heap_free( domain ); 103 } 104 105 static BOOL add_cookie( session_t *session, cookie_t *cookie, WCHAR *domain_name, WCHAR *path ) 106 { 107 domain_t *domain = NULL; 108 cookie_t *old_cookie; 109 struct list *item; 110 111 LIST_FOR_EACH( item, &session->cookie_cache ) 112 { 113 domain = LIST_ENTRY( item, domain_t, entry ); 114 if (domain_match( domain_name, domain, FALSE )) break; 115 domain = NULL; 116 } 117 if (!domain) 118 { 119 if (!(domain = add_domain( session, domain_name ))) return FALSE; 120 } 121 else if ((old_cookie = find_cookie( domain, path, cookie->name ))) delete_cookie( old_cookie ); 122 123 cookie->path = strdupW( path ); 124 list_add_head( &domain->cookies, &cookie->entry ); 125 126 TRACE("domain %s path %s <- %s=%s\n", debugstr_w(domain_name), debugstr_w(cookie->path), 127 debugstr_w(cookie->name), debugstr_w(cookie->value)); 128 return TRUE; 129 } 130 131 static cookie_t *parse_cookie( const WCHAR *string ) 132 { 133 cookie_t *cookie; 134 const WCHAR *p; 135 int len; 136 137 if (!(p = strchrW( string, '=' ))) p = string + strlenW( string ); 138 len = p - string; 139 while (len && string[len - 1] == ' ') len--; 140 if (!len) return NULL; 141 142 if (!(cookie = heap_alloc_zero( sizeof(cookie_t) ))) return NULL; 143 list_init( &cookie->entry ); 144 145 if (!(cookie->name = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 146 { 147 heap_free( cookie ); 148 return NULL; 149 } 150 memcpy( cookie->name, string, len * sizeof(WCHAR) ); 151 cookie->name[len] = 0; 152 153 if (*p++ == '=') 154 { 155 while (*p == ' ') p++; 156 len = strlenW( p ); 157 while (len && p[len - 1] == ' ') len--; 158 159 if (!(cookie->value = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 160 { 161 free_cookie( cookie ); 162 return NULL; 163 } 164 memcpy( cookie->value, p, len * sizeof(WCHAR) ); 165 cookie->value[len] = 0; 166 } 167 return cookie; 168 } 169 170 struct attr 171 { 172 WCHAR *name; 173 WCHAR *value; 174 }; 175 176 static void free_attr( struct attr *attr ) 177 { 178 if (!attr) return; 179 heap_free( attr->name ); 180 heap_free( attr->value ); 181 heap_free( attr ); 182 } 183 184 static struct attr *parse_attr( const WCHAR *str, int *used ) 185 { 186 const WCHAR *p = str, *q; 187 struct attr *attr; 188 int len; 189 190 while (*p == ' ') p++; 191 q = p; 192 while (*q && *q != ' ' && *q != '=' && *q != ';') q++; 193 len = q - p; 194 if (!len) return NULL; 195 196 if (!(attr = heap_alloc( sizeof(struct attr) ))) return NULL; 197 if (!(attr->name = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 198 { 199 heap_free( attr ); 200 return NULL; 201 } 202 memcpy( attr->name, p, len * sizeof(WCHAR) ); 203 attr->name[len] = 0; 204 attr->value = NULL; 205 206 p = q; 207 while (*p == ' ') p++; 208 if (*p++ == '=') 209 { 210 while (*p == ' ') p++; 211 q = p; 212 while (*q && *q != ';') q++; 213 len = q - p; 214 while (len && p[len - 1] == ' ') len--; 215 216 if (!(attr->value = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 217 { 218 free_attr( attr ); 219 return NULL; 220 } 221 memcpy( attr->value, p, len * sizeof(WCHAR) ); 222 attr->value[len] = 0; 223 } 224 225 while (*q == ' ') q++; 226 if (*q == ';') q++; 227 *used = q - str; 228 229 return attr; 230 } 231 232 BOOL set_cookies( request_t *request, const WCHAR *cookies ) 233 { 234 static const WCHAR pathW[] = {'p','a','t','h',0}; 235 static const WCHAR domainW[] = {'d','o','m','a','i','n',0}; 236 BOOL ret = FALSE; 237 WCHAR *buffer, *p; 238 WCHAR *cookie_domain = NULL, *cookie_path = NULL; 239 struct attr *attr, *domain = NULL, *path = NULL; 240 session_t *session = request->connect->session; 241 cookie_t *cookie; 242 int len, used; 243 244 len = strlenW( cookies ); 245 if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; 246 strcpyW( buffer, cookies ); 247 248 p = buffer; 249 while (*p && *p != ';') p++; 250 if (*p == ';') *p++ = 0; 251 if (!(cookie = parse_cookie( buffer ))) 252 { 253 heap_free( buffer ); 254 return FALSE; 255 } 256 len = strlenW( p ); 257 while (len && (attr = parse_attr( p, &used ))) 258 { 259 if (!strcmpiW( attr->name, domainW )) 260 { 261 domain = attr; 262 cookie_domain = attr->value; 263 } 264 else if (!strcmpiW( attr->name, pathW )) 265 { 266 path = attr; 267 cookie_path = attr->value; 268 } 269 else 270 { 271 FIXME( "unhandled attribute %s\n", debugstr_w(attr->name) ); 272 free_attr( attr ); 273 } 274 len -= used; 275 p += used; 276 } 277 if (!cookie_domain && !(cookie_domain = strdupW( request->connect->servername ))) goto end; 278 if (!cookie_path && !(cookie_path = strdupW( request->path ))) goto end; 279 280 if ((p = strrchrW( cookie_path, '/' )) && p != cookie_path) *p = 0; 281 ret = add_cookie( session, cookie, cookie_domain, cookie_path ); 282 283 end: 284 if (!ret) free_cookie( cookie ); 285 if (domain) free_attr( domain ); 286 else heap_free( cookie_domain ); 287 if (path) free_attr( path ); 288 else heap_free( cookie_path ); 289 heap_free( buffer ); 290 return ret; 291 } 292 293 BOOL add_cookie_headers( request_t *request ) 294 { 295 struct list *domain_cursor; 296 session_t *session = request->connect->session; 297 298 LIST_FOR_EACH( domain_cursor, &session->cookie_cache ) 299 { 300 domain_t *domain = LIST_ENTRY( domain_cursor, domain_t, entry ); 301 if (domain_match( request->connect->servername, domain, TRUE )) 302 { 303 struct list *cookie_cursor; 304 TRACE("found domain %s\n", debugstr_w(domain->name)); 305 306 LIST_FOR_EACH( cookie_cursor, &domain->cookies ) 307 { 308 cookie_t *cookie = LIST_ENTRY( cookie_cursor, cookie_t, entry ); 309 310 TRACE("comparing path %s with %s\n", debugstr_w(request->path), debugstr_w(cookie->path)); 311 312 if (strstrW( request->path, cookie->path ) == request->path) 313 { 314 const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '}; 315 int len, len_cookie = sizeof(cookieW) / sizeof(cookieW[0]), len_name = strlenW( cookie->name ); 316 WCHAR *header; 317 318 len = len_cookie + len_name; 319 if (cookie->value) len += strlenW( cookie->value ) + 1; 320 if (!(header = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; 321 322 memcpy( header, cookieW, len_cookie * sizeof(WCHAR) ); 323 strcpyW( header + len_cookie, cookie->name ); 324 if (cookie->value) 325 { 326 header[len_cookie + len_name] = '='; 327 strcpyW( header + len_cookie + len_name + 1, cookie->value ); 328 } 329 330 TRACE("%s\n", debugstr_w(header)); 331 add_request_headers( request, header, len, 332 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON ); 333 heap_free( header ); 334 } 335 } 336 } 337 } 338 return TRUE; 339 } 340