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 "winhttp_private.h" 20 21 static domain_t *add_domain( session_t *session, WCHAR *name ) 22 { 23 domain_t *domain; 24 25 if (!(domain = heap_alloc_zero( sizeof(domain_t) ))) return NULL; 26 27 list_init( &domain->entry ); 28 list_init( &domain->cookies ); 29 30 domain->name = strdupW( name ); 31 list_add_tail( &session->cookie_cache, &domain->entry ); 32 33 TRACE("%s\n", debugstr_w(domain->name)); 34 return domain; 35 } 36 37 static cookie_t *find_cookie( domain_t *domain, const WCHAR *path, const WCHAR *name ) 38 { 39 struct list *item; 40 cookie_t *cookie; 41 42 LIST_FOR_EACH( item, &domain->cookies ) 43 { 44 cookie = LIST_ENTRY( item, cookie_t, entry ); 45 if (!strcmpW( cookie->path, path ) && !strcmpW( cookie->name, name )) 46 { 47 TRACE("found %s=%s\n", debugstr_w(cookie->name), debugstr_w(cookie->value)); 48 return cookie; 49 } 50 } 51 return NULL; 52 } 53 54 static BOOL domain_match( const WCHAR *name, domain_t *domain, BOOL partial ) 55 { 56 TRACE("comparing %s with %s\n", debugstr_w(name), debugstr_w(domain->name)); 57 58 if (partial && !strstrW( name, domain->name )) return FALSE; 59 else if (!partial && strcmpW( name, domain->name )) return FALSE; 60 return TRUE; 61 } 62 63 static void free_cookie( cookie_t *cookie ) 64 { 65 heap_free( cookie->name ); 66 heap_free( cookie->value ); 67 heap_free( cookie->path ); 68 heap_free( cookie ); 69 } 70 71 static void delete_cookie( cookie_t *cookie ) 72 { 73 list_remove( &cookie->entry ); 74 free_cookie( cookie ); 75 } 76 77 void delete_domain( domain_t *domain ) 78 { 79 cookie_t *cookie; 80 struct list *item, *next; 81 82 LIST_FOR_EACH_SAFE( item, next, &domain->cookies ) 83 { 84 cookie = LIST_ENTRY( item, cookie_t, entry ); 85 delete_cookie( cookie ); 86 } 87 88 list_remove( &domain->entry ); 89 heap_free( domain->name ); 90 heap_free( domain ); 91 } 92 93 static BOOL add_cookie( session_t *session, cookie_t *cookie, WCHAR *domain_name, WCHAR *path ) 94 { 95 domain_t *domain = NULL; 96 cookie_t *old_cookie; 97 struct list *item; 98 99 LIST_FOR_EACH( item, &session->cookie_cache ) 100 { 101 domain = LIST_ENTRY( item, domain_t, entry ); 102 if (domain_match( domain_name, domain, FALSE )) break; 103 domain = NULL; 104 } 105 if (!domain) 106 { 107 if (!(domain = add_domain( session, domain_name ))) return FALSE; 108 } 109 else if ((old_cookie = find_cookie( domain, path, cookie->name ))) delete_cookie( old_cookie ); 110 111 cookie->path = strdupW( path ); 112 list_add_head( &domain->cookies, &cookie->entry ); 113 114 TRACE("domain %s path %s <- %s=%s\n", debugstr_w(domain_name), debugstr_w(cookie->path), 115 debugstr_w(cookie->name), debugstr_w(cookie->value)); 116 return TRUE; 117 } 118 119 static cookie_t *parse_cookie( const WCHAR *string ) 120 { 121 cookie_t *cookie; 122 const WCHAR *p; 123 int len; 124 125 if (!(p = strchrW( string, '=' ))) p = string + strlenW( string ); 126 len = p - string; 127 while (len && string[len - 1] == ' ') len--; 128 if (!len) return NULL; 129 130 if (!(cookie = heap_alloc_zero( sizeof(cookie_t) ))) return NULL; 131 list_init( &cookie->entry ); 132 133 if (!(cookie->name = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 134 { 135 heap_free( cookie ); 136 return NULL; 137 } 138 memcpy( cookie->name, string, len * sizeof(WCHAR) ); 139 cookie->name[len] = 0; 140 141 if (*p++ == '=') 142 { 143 while (*p == ' ') p++; 144 len = strlenW( p ); 145 while (len && p[len - 1] == ' ') len--; 146 147 if (!(cookie->value = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 148 { 149 free_cookie( cookie ); 150 return NULL; 151 } 152 memcpy( cookie->value, p, len * sizeof(WCHAR) ); 153 cookie->value[len] = 0; 154 } 155 return cookie; 156 } 157 158 struct attr 159 { 160 WCHAR *name; 161 WCHAR *value; 162 }; 163 164 static void free_attr( struct attr *attr ) 165 { 166 if (!attr) return; 167 heap_free( attr->name ); 168 heap_free( attr->value ); 169 heap_free( attr ); 170 } 171 172 static struct attr *parse_attr( const WCHAR *str, int *used ) 173 { 174 const WCHAR *p = str, *q; 175 struct attr *attr; 176 int len; 177 178 while (*p == ' ') p++; 179 q = p; 180 while (*q && *q != ' ' && *q != '=' && *q != ';') q++; 181 len = q - p; 182 if (!len) return NULL; 183 184 if (!(attr = heap_alloc( sizeof(struct attr) ))) return NULL; 185 if (!(attr->name = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 186 { 187 heap_free( attr ); 188 return NULL; 189 } 190 memcpy( attr->name, p, len * sizeof(WCHAR) ); 191 attr->name[len] = 0; 192 attr->value = NULL; 193 194 p = q; 195 while (*p == ' ') p++; 196 if (*p++ == '=') 197 { 198 while (*p == ' ') p++; 199 q = p; 200 while (*q && *q != ';') q++; 201 len = q - p; 202 while (len && p[len - 1] == ' ') len--; 203 204 if (!(attr->value = heap_alloc( (len + 1) * sizeof(WCHAR) ))) 205 { 206 free_attr( attr ); 207 return NULL; 208 } 209 memcpy( attr->value, p, len * sizeof(WCHAR) ); 210 attr->value[len] = 0; 211 } 212 213 while (*q == ' ') q++; 214 if (*q == ';') q++; 215 *used = q - str; 216 217 return attr; 218 } 219 220 BOOL set_cookies( request_t *request, const WCHAR *cookies ) 221 { 222 static const WCHAR pathW[] = {'p','a','t','h',0}; 223 static const WCHAR domainW[] = {'d','o','m','a','i','n',0}; 224 BOOL ret = FALSE; 225 WCHAR *buffer, *p; 226 WCHAR *cookie_domain = NULL, *cookie_path = NULL; 227 struct attr *attr, *domain = NULL, *path = NULL; 228 session_t *session = request->connect->session; 229 cookie_t *cookie; 230 int len, used; 231 232 len = strlenW( cookies ); 233 if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; 234 strcpyW( buffer, cookies ); 235 236 p = buffer; 237 while (*p && *p != ';') p++; 238 if (*p == ';') *p++ = 0; 239 if (!(cookie = parse_cookie( buffer ))) 240 { 241 heap_free( buffer ); 242 return FALSE; 243 } 244 len = strlenW( p ); 245 while (len && (attr = parse_attr( p, &used ))) 246 { 247 if (!strcmpiW( attr->name, domainW )) 248 { 249 domain = attr; 250 cookie_domain = attr->value; 251 } 252 else if (!strcmpiW( attr->name, pathW )) 253 { 254 path = attr; 255 cookie_path = attr->value; 256 } 257 else 258 { 259 FIXME( "unhandled attribute %s\n", debugstr_w(attr->name) ); 260 free_attr( attr ); 261 } 262 len -= used; 263 p += used; 264 } 265 if (!cookie_domain && !(cookie_domain = strdupW( request->connect->servername ))) goto end; 266 if (!cookie_path && !(cookie_path = strdupW( request->path ))) goto end; 267 268 if ((p = strrchrW( cookie_path, '/' )) && p != cookie_path) *p = 0; 269 ret = add_cookie( session, cookie, cookie_domain, cookie_path ); 270 271 end: 272 if (!ret) free_cookie( cookie ); 273 if (domain) free_attr( domain ); 274 else heap_free( cookie_domain ); 275 if (path) free_attr( path ); 276 else heap_free( cookie_path ); 277 heap_free( buffer ); 278 return ret; 279 } 280 281 BOOL add_cookie_headers( request_t *request ) 282 { 283 struct list *domain_cursor; 284 session_t *session = request->connect->session; 285 286 LIST_FOR_EACH( domain_cursor, &session->cookie_cache ) 287 { 288 domain_t *domain = LIST_ENTRY( domain_cursor, domain_t, entry ); 289 if (domain_match( request->connect->servername, domain, TRUE )) 290 { 291 struct list *cookie_cursor; 292 TRACE("found domain %s\n", debugstr_w(domain->name)); 293 294 LIST_FOR_EACH( cookie_cursor, &domain->cookies ) 295 { 296 cookie_t *cookie = LIST_ENTRY( cookie_cursor, cookie_t, entry ); 297 298 TRACE("comparing path %s with %s\n", debugstr_w(request->path), debugstr_w(cookie->path)); 299 300 if (strstrW( request->path, cookie->path ) == request->path) 301 { 302 const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '}; 303 int len, len_cookie = sizeof(cookieW) / sizeof(cookieW[0]), len_name = strlenW( cookie->name ); 304 WCHAR *header; 305 306 len = len_cookie + len_name; 307 if (cookie->value) len += strlenW( cookie->value ) + 1; 308 if (!(header = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; 309 310 memcpy( header, cookieW, len_cookie * sizeof(WCHAR) ); 311 strcpyW( header + len_cookie, cookie->name ); 312 if (cookie->value) 313 { 314 header[len_cookie + len_name] = '='; 315 strcpyW( header + len_cookie + len_name + 1, cookie->value ); 316 } 317 318 TRACE("%s\n", debugstr_w(header)); 319 add_request_headers( request, header, len, 320 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON ); 321 heap_free( header ); 322 } 323 } 324 } 325 } 326 return TRUE; 327 } 328