Added support for http.cookieFile

Added support for non-windows system. Borrowed and modified code from
libcurl cookie library to parse cookie

Change-Id: I598553aa65641925c2de9e157bfdf1120a16078e
diff --git a/src/transports/cookie.c b/src/transports/cookie.c
new file mode 100644
index 0000000..5e2f8d0
--- /dev/null
+++ b/src/transports/cookie.c
@@ -0,0 +1,757 @@
+/***************************************************************************
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef GIT_WINHTTP
+
+#include "cookie.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#define ISBLANK(x)  (int)((((unsigned char)x) == ' ') || \
+			  (((unsigned char)x) == '\t'))
+/*
+ * get_line() makes sure to only return complete whole lines that fit in 'len'
+ * bytes and end with a newline.
+ */
+static char *get_line(char *buf, int len, FILE *input)
+{
+	bool partial = false;
+	while(1) {
+		char *b = fgets(buf, len, input);
+		if(b) {
+			size_t rlen = strlen(b);
+			if(rlen && (b[rlen-1] == '\n')) {
+				if(partial) {
+					partial = false;
+					continue;
+				}
+				return b;
+			}
+			/* read a partial, discard the next piece that ends with newline */
+			partial = true;
+		}
+		else
+			break;
+	}
+	return NULL;
+}
+
+static void freecookie(struct Cookie *co)
+{
+	free(co->expirestr);
+	free(co->domain);
+	free(co->path);
+	free(co->spath);
+	free(co->name);
+	free(co->value);
+	free(co->maxage);
+	free(co->version);
+	free(co);
+}
+
+/*
+ * cookie path sanitize
+ */
+static char *sanitize_cookie_path(const char *cookie_path)
+{
+	size_t len;
+	char *new_path = strdup(cookie_path);
+	if(!new_path)
+		return NULL;
+
+	/* some stupid site sends path attribute with '"'. */
+	len = strlen(new_path);
+	if(new_path[0] == '\"') {
+		memmove((void *)new_path, (const void *)(new_path + 1), len);
+		len--;
+	}
+	if(len && (new_path[len - 1] == '\"')) {
+		new_path[len - 1] = 0x0;
+		len--;
+	}
+
+	/* RFC6265 5.2.4 The Path Attribute */
+	if(new_path[0] != '/') {
+		/* Let cookie-path be the default-path. */
+		free(new_path);
+		new_path = strdup("/");
+		return new_path;
+	}
+
+	/* convert /hoge/ to /hoge */
+	if(len && new_path[len - 1] == '/') {
+		new_path[len - 1] = 0x0;
+	}
+
+	return new_path;
+}
+
+/*
+ * remove_expired() removes expired cookies.
+ */
+static void remove_expired(struct CookieInfo *cookies)
+{
+	struct Cookie *co, *nx, *pv;
+	git_off_t now = (git_off_t)time(NULL);
+
+	co = cookies->cookies;
+	pv = NULL;
+	while(co) {
+		nx = co->next;
+		if(co->expires && co->expires < now) {
+			if(co == cookies->cookies) {
+				cookies->cookies = co->next;
+			}
+			else {
+				pv->next = co->next;
+			}
+			cookies->numcookies--;
+			freecookie(co);
+		}
+		else {
+			pv = co;
+		}
+		co = nx;
+	}
+}
+
+/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
+   its behavior is altered by the current locale. */
+char raw_toupper(char in)
+{
+	switch(in) {
+		case 'a':
+			return 'A';
+		case 'b':
+			return 'B';
+		case 'c':
+			return 'C';
+		case 'd':
+			return 'D';
+		case 'e':
+			return 'E';
+		case 'f':
+			return 'F';
+		case 'g':
+			return 'G';
+		case 'h':
+			return 'H';
+		case 'i':
+			return 'I';
+		case 'j':
+			return 'J';
+		case 'k':
+			return 'K';
+		case 'l':
+			return 'L';
+		case 'm':
+			return 'M';
+		case 'n':
+			return 'N';
+		case 'o':
+			return 'O';
+		case 'p':
+			return 'P';
+		case 'q':
+			return 'Q';
+		case 'r':
+			return 'R';
+		case 's':
+			return 'S';
+		case 't':
+			return 'T';
+		case 'u':
+			return 'U';
+		case 'v':
+			return 'V';
+		case 'w':
+			return 'W';
+		case 'x':
+			return 'X';
+		case 'y':
+			return 'Y';
+		case 'z':
+			return 'Z';
+	}
+	return in;
+}
+
+int strcasecompare(const char *first, const char *second)
+{
+	while(*first && *second) {
+		if(raw_toupper(*first) != raw_toupper(*second))
+			/* get out of the loop as soon as they don't
+			 * match */
+			break;
+		first++;
+		second++;
+	}
+	/* we do the comparison here (possibly again), just to make sure
+	 * that if the
+	 *      loop above is skipped because one of the strings reached
+	 *      zero, we must not
+	 *           return this as a successful match */
+	return (raw_toupper(*first) == raw_toupper(*second));
+}
+
+/*
+ * Return true if the given string is an IP(v4|v6) address.
+ */
+static bool isip(const char *domain)
+{
+	struct in_addr addr;
+#ifdef ENABLE_IPV6
+	struct in6_addr addr6;
+#endif
+
+	if(inet_pton(AF_INET, domain, &addr)
+#ifdef ENABLE_IPV6
+		 || inet_pton(AF_INET6, domain, &addr6)
+#endif
+		) {
+		/* domain name given as IP address */
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * matching cookie path and url path
+ * RFC6265 5.1.4 Paths and Path-Match
+ */
+static bool pathmatch(const char *cookie_path, const char *request_uri)
+{
+	size_t cookie_path_len;
+	size_t uri_path_len;
+	char *uri_path = NULL;
+	char *pos;
+	bool ret = false;
+
+	/* cookie_path must not have last '/' separator. ex: /sample */
+	cookie_path_len = strlen(cookie_path);
+	if(1 == cookie_path_len) {
+		/* cookie_path must be '/' */
+		return true;
+	}
+
+	uri_path = strdup(request_uri);
+	if(!uri_path)
+		return false;
+	pos = strchr(uri_path, '?');
+	if(pos)
+		*pos = 0x0;
+
+	/* #-fragments are already cut off! */
+	if(0 == strlen(uri_path) || uri_path[0] != '/') {
+		free(uri_path);
+		uri_path = strdup("/");
+		if(!uri_path)
+			return false;
+	}
+
+	/* here, RFC6265 5.1.4 says
+	   4. Output the characters of the uri-path from the first character up
+	      to, but not including, the right-most %x2F ("/").
+	   but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
+	   without redirect.
+	   Ignore this algorithm because /hoge is uri path for this case
+	   (uri path is not /).
+	 */
+
+	uri_path_len = strlen(uri_path);
+
+	if(uri_path_len < cookie_path_len) {
+		ret = false;
+		goto pathmatched;
+	}
+
+	/* not using checkprefix() because matching should be case-sensitive */
+	if(strncmp(cookie_path, uri_path, cookie_path_len)) {
+		ret = false;
+		goto pathmatched;
+	}
+
+	/* The cookie-path and the uri-path are identical. */
+	if(cookie_path_len == uri_path_len) {
+		ret = true;
+		goto pathmatched;
+	}
+
+	/* here, cookie_path_len < url_path_len */
+	if(uri_path[cookie_path_len] == '/') {
+		ret = true;
+		goto pathmatched;
+	}
+
+	ret = false;
+
+pathmatched:
+	free(uri_path);
+	return ret;
+}
+
+/* sort this so that the longest path gets before the shorter path */
+static int cookie_sort(const void *p1, const void *p2)
+{
+	struct Cookie *c1 = *(struct Cookie **)p1;
+	struct Cookie *c2 = *(struct Cookie **)p2;
+	size_t l1, l2;
+
+	/* 1 - compare cookie path lengths */
+	l1 = c1->path ? strlen(c1->path) : 0;
+	l2 = c2->path ? strlen(c2->path) : 0;
+
+	if(l1 != l2)
+		return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
+
+	/* 2 - compare cookie domain lengths */
+	l1 = c1->domain ? strlen(c1->domain) : 0;
+	l2 = c2->domain ? strlen(c2->domain) : 0;
+
+	if(l1 != l2)
+		return (l2 > l1) ? 1 : -1 ;  /* avoid size_t <=> int conversions */
+
+	/* 3 - compare cookie names */
+	if(c1->name && c2->name)
+		return strcmp(c1->name, c2->name);
+
+	/* sorry, can't be more deterministic */
+	return 0;
+}
+
+#define CLONE(field)                                   \
+	do {                                           \
+		if(src->field) {                       \
+			d->field = strdup(src->field); \
+			if(!d->field)                  \
+				goto fail;             \
+		}                                      \
+	} while(0)
+static struct Cookie *dup_cookie(struct Cookie *src)
+{
+	struct Cookie *d = calloc(sizeof(struct Cookie), 1);
+	if(d) {
+		CLONE(expirestr);
+		CLONE(domain);
+		CLONE(path);
+		CLONE(spath);
+		CLONE(name);
+		CLONE(value);
+		CLONE(maxage);
+		CLONE(version);
+		d->expires = src->expires;
+		d->tailmatch = src->tailmatch;
+		d->secure = src->secure;
+		d->httponly = src->httponly;
+	}
+	return d;
+
+	fail:
+	freecookie(d);
+	return NULL;
+}
+
+static bool tailmatch(const char *cooke_domain, const char *hostname)
+{
+	size_t cookie_domain_len = strlen(cooke_domain);
+	size_t hostname_len = strlen(hostname);
+
+	if(hostname_len < cookie_domain_len)
+		return false;
+
+	if(!strcasecompare(cooke_domain, hostname+hostname_len-cookie_domain_len))
+		return false;
+
+	/* A lead char of cookie_domain is not '.'.
+	   RFC6265 4.1.2.3. The Domain Attribute says:
+	   For example, if the value of the Domain attribute is
+	   "example.com", the user agent will include the cookie in the Cookie
+	   header when making HTTP requests to example.com, www.example.com, and
+	   www.corp.example.com.
+	 */
+	if(hostname_len == cookie_domain_len)
+		return true;
+	if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
+		return true;
+	return false;
+}
+
+struct Cookie *cookie_getlist(struct CookieInfo *c,
+			      const char *host, const char *path,
+			      bool secure)
+{
+	struct Cookie *newco;
+	struct Cookie *co;
+	time_t now = time(NULL);
+	struct Cookie *mainco=NULL;
+	size_t matches = 0;
+	bool is_ip;
+
+	if(!c || !c->cookies)
+		return NULL; /* no cookie struct or no cookies in the struct */
+
+	/* at first, remove expired cookies */
+	remove_expired(c);
+
+	/* check if host is an IP(v4|v6) address */
+	is_ip = isip(host);
+
+	co = c->cookies;
+
+	while(co) {
+		/* only process this cookie if it is not expired or had no expire
+		   date AND that if the cookie requires we're secure we must only
+		   continue if we are! */
+		if((!co->expires || (co->expires > now)) &&
+			 (co->secure?secure:true)) {
+
+			/* now check if the domain is correct */
+			if(!co->domain ||
+				 (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
+				 ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
+				/* the right part of the host matches the domain stuff in the
+				   cookie data */
+
+				/* now check the left part of the path with the cookies path
+				   requirement */
+				if(!co->spath || pathmatch(co->spath, path) ) {
+
+					/* and now, we know this is a match and we should create an
+					   entry for the return-linked-list */
+
+					newco = dup_cookie(co);
+					if(newco) {
+						/* then modify our next */
+						newco->next = mainco;
+
+						/* point the main to us */
+						mainco = newco;
+
+						matches++;
+					}
+					else {
+						fail:
+						/* failure, clear up the allocated chain and return NULL */
+						cookie_freelist(mainco);
+						return NULL;
+					}
+				}
+			}
+		}
+		co = co->next;
+	}
+
+	if(matches) {
+		/* Now we need to make sure that if there is a name appearing more than
+		   once, the longest specified path version comes first. To make this
+		   the swiftest way, we just sort them all based on path length. */
+		struct Cookie **array;
+		size_t i;
+
+		/* alloc an array and store all cookie pointers */
+		array = malloc(sizeof(struct Cookie *) * matches);
+		if(!array)
+			goto fail;
+
+		co = mainco;
+
+		for(i=0; co; co = co->next)
+			array[i++] = co;
+
+		/* now sort the cookie pointers in path length order */
+		qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
+
+		/* remake the linked list order according to the new order */
+
+		mainco = array[0]; /* start here */
+		for(i=0; i<matches-1; i++)
+			array[i]->next = array[i+1];
+		array[matches-1]->next = NULL; /* terminate the list */
+
+		free(array); /* remove the temporary data again */
+	}
+
+	return mainco; /* return the new list */
+}
+
+struct CookieInfo *cookie_loadfile(const char *cookie_file)
+{
+	struct CookieInfo *c;
+	FILE *fp = NULL;
+	char *line = NULL;
+
+	c = calloc(1, sizeof(struct CookieInfo));
+	if(!c)
+		return NULL; /* failed to get memory */
+
+	fp = fopen(cookie_file, "r");
+
+	if(!fp) {
+		goto fail;
+	}
+
+	if(fp) {
+		char *lineptr;
+
+		line = malloc(MAX_COOKIE_LINE);
+		if(!line)
+			goto fail;
+		while(get_line(line, MAX_COOKIE_LINE, fp)) {
+			lineptr=line;
+			while(*lineptr && ISBLANK(*lineptr))
+				lineptr++;
+
+			cookie_add(c, lineptr);
+		}
+		free(line); /* free the line buffer */
+
+		fclose(fp);
+	}
+	return c;
+
+fail:
+	cookie_cleanup(c);
+	if(line)
+		free(line);
+	if(fp)
+		fclose(fp);
+	return NULL; /* out of memory */
+}
+
+void cookie_cleanup(struct CookieInfo *c)
+{
+	if(c) {
+		cookie_freelist(c->cookies);
+		free(c); /* free the base struct as well */
+	}
+}
+
+void cookie_freelist(struct Cookie *co)
+{
+	struct Cookie *next;
+	while(co) {
+		next = co->next;
+		freecookie(co);
+		co = next;
+	}
+}
+
+struct Cookie *cookie_add(struct CookieInfo *c, char *lineptr)
+{
+	struct Cookie *clist;
+	struct Cookie *co;
+	struct Cookie *lastc = NULL;
+	bool replace_old = false;
+	bool badcookie = false; /* cookies are good by default. mmmmm yummy */
+	char *ptr;
+	char *firstptr;
+	char *tok_buf = NULL;
+	int fields;
+
+	/* First, alloc and init a new struct for it */
+	co = calloc(1, sizeof(struct Cookie));
+	if (!co) return NULL; /* bail out if we're this low on memory */
+
+	/* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
+	   marked with httpOnly after the domain name are not accessible
+	   from javascripts, but since curl does not operate at javascript
+	   level, we include them anyway. In Firefox's cookie files, these
+	   lines are preceded with #HttpOnly_ and then everything is
+	   as usual, so we skip 10 characters of the line..
+	   */
+	if (strncmp(lineptr, "#HttpOnly_", 10) == 0) {
+		lineptr += 10;
+		co->httponly = true;
+	}
+
+	if (lineptr[0] == '#') {
+		/* don't even try the comments */
+		free(co);
+		return NULL;
+	}
+	/* strip off the possible end-of-line characters */
+	ptr = strchr(lineptr, '\r');
+	if (ptr) *ptr = 0; /* clear it */
+	ptr = strchr(lineptr, '\n');
+	if (ptr) *ptr = 0; /* clear it */
+
+	firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
+
+	/* Now loop through the fields and init the struct we already have
+	   allocated */
+	for (ptr = firstptr, fields = 0; ptr && !badcookie;
+			ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
+		switch (fields) {
+			case 0:
+				if (ptr[0] == '.') /* skip preceding dots */
+					ptr++;
+				co->domain = strdup(ptr);
+				if (!co->domain) badcookie = true;
+				break;
+			case 1:
+				/* This field got its explanation on the 23rd of May 2001 by
+				   Andrés García:
+
+                                   flag: A true/false value indicating if all machines within a given
+                                   domain can access the variable. This value is set automatically by
+                                   the browser, depending on the value you set for the domain.
+
+                                   As far as I can see, it is set to true when the cookie says
+                                  .domain.com and to false when the domain is complete www.domain.com
+                                */
+				co->tailmatch = strcasecompare(ptr, "true") ? true : false;
+				break;
+			case 2:
+				/* It turns out, that sometimes the file format allows the path
+				   field to remain not filled in, we try to detect this and work
+				   around it! Andrés García made us aware of this... */
+				if (strcmp("true", ptr) && strcmp("false", ptr)) {
+					/* only if the path doesn't look like a boolean option! */
+					co->path = strdup(ptr);
+					if (!co->path)
+						badcookie = true;
+					else {
+						co->spath = sanitize_cookie_path(co->path);
+						if (!co->spath) {
+							badcookie = true; /* out of memory bad */
+						}
+					}
+					break;
+				}
+				/* this doesn't look like a path, make one up! */
+				co->path = strdup("/");
+				if (!co->path) badcookie = true;
+				co->spath = strdup("/");
+				if (!co->spath) badcookie = true;
+				fields++; /* add a field and fall down to secure */
+				/* FALLTHROUGH */
+			case 3:
+				co->secure = strcasecompare(ptr, "true") ? true : false;
+				break;
+			case 4:
+				co->expires = strtol(ptr, NULL, 10);
+				break;
+			case 5:
+				co->name = strdup(ptr);
+				if (!co->name) badcookie = true;
+				break;
+			case 6:
+				co->value = strdup(ptr);
+				if (!co->value) badcookie = true;
+				break;
+		}
+	}
+	if (6 == fields) {
+		/* we got a cookie with blank contents, fix it */
+		co->value = strdup("");
+		if (!co->value)
+			badcookie = true;
+		else
+			fields++;
+	}
+
+	if (!badcookie && (7 != fields))
+		/* we did not find the sufficient number of fields */
+		badcookie = true;
+
+	if (badcookie) {
+		freecookie(co);
+		return NULL;
+	}
+
+	/* now, we have parsed the incoming line, we must now check if this
+	   superceeds an already existing cookie, which it may if the previous have
+	   the same domain and path as this */
+
+	/* at first, remove expired cookies */
+	remove_expired(c);
+
+	clist = c->cookies;
+	replace_old = false;
+	while (clist) {
+		if (strcasecompare(clist->name, co->name)) {
+			/* the names are identical */
+
+			if (clist->domain && co->domain) {
+				if (strcasecompare(clist->domain, co->domain) &&
+					(clist->tailmatch == co->tailmatch))
+					/* The domains are identical */
+					replace_old = true;
+			} else if (!clist->domain && !co->domain)
+				replace_old = true;
+
+			if (replace_old) {
+				/* the domains were identical */
+
+				if (clist->spath && co->spath) {
+					if (strcasecompare(clist->spath, co->spath)) {
+						replace_old = true;
+					} else
+						replace_old = false;
+				} else if (!clist->spath && !co->spath)
+					replace_old = true;
+				else
+					replace_old = false;
+			}
+
+			if (replace_old) {
+				co->next = clist->next; /* get the next-pointer first */
+
+				/* then free all the old pointers */
+				free(clist->name);
+				free(clist->value);
+				free(clist->domain);
+				free(clist->path);
+				free(clist->spath);
+				free(clist->expirestr);
+				free(clist->version);
+				free(clist->maxage);
+
+				*clist = *co; /* then store all the new data */
+
+				free(co);   /* free the newly alloced memory */
+				co = clist; /* point to the previous struct instead */
+
+				/* We have replaced a cookie, now skip the rest of the list but
+				   make sure the 'lastc' pointer is properly set */
+				do {
+					lastc = clist;
+					clist = clist->next;
+				} while (clist);
+				break;
+			}
+		}
+		lastc = clist;
+		clist = clist->next;
+	}
+
+	if (!replace_old) {
+		/* then make the last item point on this new one */
+		if (lastc)
+			lastc->next = co;
+		else
+			c->cookies = co;
+		c->numcookies++; /* one more cookie in the jar */
+	}
+
+	return co;
+}
+
+#endif /* !GIT_WINHTTP */
diff --git a/src/transports/cookie.h b/src/transports/cookie.h
new file mode 100644
index 0000000..bacba31
--- /dev/null
+++ b/src/transports/cookie.h
@@ -0,0 +1,75 @@
+#ifndef HEADER_CURL_COOKIE_H
+#define HEADER_CURL_COOKIE_H
+/***************************************************************************
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include <stdbool.h>
+#include <git2/types.h>
+
+struct Cookie {
+	struct Cookie *next; /* next in the chain */
+	char *name;        /* <this> = value */
+	char *value;       /* name = <this> */
+	char *path;         /* path = <this> which is in Set-Cookie: */
+	char *spath;        /* sanitized cookie path */
+	char *domain;      /* domain = <this> */
+	git_off_t expires;  /* expires = <this> */
+	char *expirestr;   /* the plain text version */
+	bool tailmatch;    /* weather we do tail-matchning of the domain name */
+
+	/* RFC 2109 keywords. Version=1 means 2109-compliant cookie sending */
+	char *version;     /* Version = <value> */
+	char *maxage;      /* Max-Age = <value> */
+
+	bool secure;       /* whether the 'secure' keyword was used */
+	bool httponly;     /* true if the httponly directive is present */
+};
+
+struct CookieInfo {
+	/* linked list of cookies we know of */
+	struct Cookie *cookies;
+
+	long numcookies; /* number of cookies in the "jar" */
+};
+
+/* This is the maximum line length we accept for a cookie line. RFC 2109
+   section 6.3 says:
+
+   "at least 4096 bytes per cookie (as measured by the size of the characters
+   that comprise the cookie non-terminal in the syntax description of the
+   Set-Cookie header)"
+
+*/
+#define MAX_COOKIE_LINE 5000
+#define MAX_COOKIE_LINE_TXT "4999"
+
+/* This is the maximum length of a cookie name we deal with: */
+#define MAX_NAME 1024
+#define MAX_NAME_TXT "1023"
+
+/*
+ * Add a cookie to the internal list of cookies. The domain and path arguments
+ * are only used if the header boolean is TRUE.
+ */
+
+struct Cookie *cookie_add(struct CookieInfo *, char *lineptr);
+struct Cookie *cookie_getlist(struct CookieInfo *, const char *,
+                                   const char *, bool);
+void cookie_freelist(struct Cookie *cookies);
+void cookie_cleanup(struct CookieInfo *);
+struct CookieInfo *cookie_loadfile(const char *cookie_file);
+
+#endif /* HEADER_CURL_COOKIE_H */
diff --git a/src/transports/http.c b/src/transports/http.c
index cb4a6d0..9d00e91 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -18,6 +18,7 @@
 #include "tls_stream.h"
 #include "socket_stream.h"
 #include "curl_stream.h"
+#include "cookie.h"
 
 git_http_auth_scheme auth_schemes[] = {
 	{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
@@ -205,7 +206,10 @@
 	http_subtransport *t = OWNING_SUBTRANSPORT(s);
 	const char *path = t->connection_data.path ? t->connection_data.path : "/";
 	size_t i;
-
+	int error;
+	const char *cookie_file = NULL;
+	int cookie_count = 0;
+	git_config *config;
 	git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
 
 	git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent());
@@ -222,6 +226,39 @@
 	} else
 		git_buf_puts(buf, "Accept: */*\r\n");
 
+	if ((error = git_repository_config_snapshot(&config, t->owner->owner->repo)) < 0)
+		return error;
+	if ((error = git_config_get_string(&cookie_file, config, "http.cookieFile")) < 0) {
+		if (error == GIT_ENOTFOUND) {
+			giterr_clear();
+			error = 0;
+		} else {
+			git_config_free(config);
+			return error;
+		}
+	}
+	git_config_free(config);
+	if (cookie_file) {
+		struct CookieInfo *c = cookie_loadfile(cookie_file);
+		if (c){
+			struct Cookie *co = cookie_getlist(c, t->connection_data.host, t->connection_data.path, t->connection_data.use_ssl);
+			while (co) {
+				if(co->value) {
+					if (0 == cookie_count) {
+						git_buf_printf(buf, "Cookie: ");
+					}
+					git_buf_printf(buf, "%s%s=%s", cookie_count ? "; " : "", co->name, co->value);
+					cookie_count++;
+				}
+				co = co->next;
+			}
+			cookie_cleanup(c);
+		}
+	}
+	if (cookie_count){
+		git_buf_printf(buf, "\r\n");
+	}
+
 	for (i = 0; i < t->owner->custom_headers.count; i++) {
 		if (t->owner->custom_headers.strings[i])
 			git_buf_printf(buf, "%s\r\n", t->owner->custom_headers.strings[i]);
diff --git a/tests/online/fetch.c b/tests/online/fetch.c
index 827cb23..b2fa89f 100644
--- a/tests/online/fetch.c
+++ b/tests/online/fetch.c
@@ -84,11 +84,11 @@
 	cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
 	cl_git_pass(git_remote_download(remote, NULL, NULL));
     	git_remote_disconnect(remote);
-    	
+
 	git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL);
 	cl_git_pass(git_remote_download(remote, NULL, NULL));
 	git_remote_disconnect(remote);
-	
+
 	git_remote_free(remote);
 }
 
@@ -207,3 +207,36 @@
 
 	git_remote_free(remote);
 }
+
+void test_online_fetch__fake_httpcookiefile(void)
+{
+	git_remote *remote;
+	git_config *cfg;
+
+	cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git"));
+	cl_git_pass(git_repository_config(&cfg, _repo));
+	cl_git_pass(git_config_set_string(cfg, "http.cookieFile", "fake.cookie"));
+	git_config_free(cfg);
+	cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+
+	git_remote_free(remote);
+}
+
+void test_online_fetch__with_httpcookiefile(void)
+{
+	git_remote *remote;
+	git_config *cfg;
+	struct stat st;
+	const char *cookie = cl_fixture("cookie");
+
+	// make sure file exists
+	cl_git_pass(p_stat(cookie, &st));
+	cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git"));
+	cl_git_pass(git_repository_config(&cfg, _repo));
+
+	cl_git_pass(git_config_set_string(cfg, "http.cookieFile", cookie));
+	git_config_free(cfg);
+	cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+
+	git_remote_free(remote);
+}
diff --git a/tests/resources/cookie b/tests/resources/cookie
new file mode 100644
index 0000000..3594477
--- /dev/null
+++ b/tests/resources/cookie
@@ -0,0 +1 @@
+github.com FALSE / TRUE  2147483647  o git-user.github.com=1/QWmb-q5VvGj3v2CRqwhQucRQqUrdkfZPblgQ2WDxhSY