upgraded HTTP protocol to 1.1
[syren.git] / src / sylib / syren_http.c
blob13393beda1daacc5027a7605cf52aa31450946ca
1 /*
2 Syren -- a lightweight downloader for Linux/BSD/MacOSX
3 inspired by Axel Copyright 2001-2002 Wilmer van der Gaast
4 version 0.0.6 (atomic alien)
5 coded by Ketmar // Vampire Avalon
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License with
18 the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL;
19 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 Suite 330, Boston, MA 02111-1307 USA
23 Syren http utilities
25 #ifndef _SYREN_HTTP_C
26 #define _SYREN_HTTP_C
28 #include "syren_http.h"
31 /* return ptr to next line */
32 static char *SyHTTPFixLine (char *curstr) {
33 char *nl = curstr, *res;
35 while (*nl && *nl != '\n') nl++;
36 res = nl+(*nl?1:0);
37 *nl = '\0';
38 if (nl > curstr && *(nl-1) == '\r') *(nl-1) = '\0';
40 return res;
44 TSyResult SyHTTPReadHeaders (TSyHdrs *hdrs, TSySocket *fd, const TSyPrintStr *pfn) {
45 char *hdrbuf, *curstr, *nextstr;
46 TSyResult res;
48 SyMessage(pfn, SY_MSG_NOTICE, "reading reply headers");
49 if (!hdrs) return SY_ERROR;
50 SyHdrClear(hdrs, SY_HDR_REPLY);
51 if (!fd || fd->fd < 0) return SY_ERROR;
52 hdrbuf = SyTCPReceiveHdrs(fd, 65536);
53 if (!hdrbuf) {
54 SyMessage(pfn, SY_MSG_ERROR, "error receiving reply headers");
55 return SY_ERROR;
57 curstr = hdrbuf;
58 while (*curstr) {
59 nextstr = SyHTTPFixLine(curstr);
60 if (!nextstr[0]) break;
61 SyMessage(pfn, SY_MSG_NOTICE, " %s", curstr);
62 res = SyHdrAddLine(hdrs, curstr);
63 if (res != SY_OK) {
64 free(hdrbuf);
65 SyMessage(pfn, SY_MSG_ERROR, "can't parse reply header string");
66 return SY_ERROR;
68 curstr = nextstr;
70 free(hdrbuf);
71 if (hdrs->code < 0) {
72 SyMessage(pfn, SY_MSG_ERROR, "empty reply headers");
73 return SY_ERROR;
75 SyMessage(pfn, SY_MSG_NOTICE, " reply headers received; code: %i", hdrs->code);
76 return SY_OK;
80 /* -1: none or error */
81 int64_t SyHTTPGetSize (const TSyHdrs *hdrs) {
82 int64_t res;
83 char *s, *t;
84 char *buf = SyHdrGetFieldValue(hdrs, "content-length");
85 res = SyStr2Long(buf); if (buf) free(buf);
86 if (res >= 0) return res;
87 /*Content-Disposition: attachment; filename="textpattern-4.0.6.tar.gz"; size = "304354"*/
88 buf = SyHdrGetFieldValue(hdrs, "Content-Disposition");
89 if (buf) {
90 s = buf;
91 while (*s) {
92 t = strcasestr(s, "size");
93 if (!t) break;
94 if (t != buf && isalnum(*(t-1))) { s = t+1; continue; }
95 t += 4; if (*t && isalnum(*t)) { s = t; continue; }
96 while (*t && *t <= ' ') t++;
97 if (*t != '=') { s = t; continue; }
98 t++; while (*t && *t <= ' ') t++;
99 if (!(*t)) break;
100 if (*t == '"') { t++; s = t; while (*s && *s != '"') s++; }
101 else { s = t; while (*s && (*s != ' ' && *s != ';')) s++; }
102 *s = '\0';
103 res = SyStr2Long(t);
104 break;
106 free(buf);
107 return res;
109 //Content-Range: bytes 2759580-21263647/21263648
110 buf = SyHdrGetFieldValue(hdrs, "Content-Range");
111 if (!buf) return -1;
112 t = strchr(buf, '/');
113 if (!strcasestr(buf, "bytes") || !t || !t[1]) { free(buf); return -1; }
114 for (s = ++t; *s; s++) if (!isdigit(*s)) { free(buf); return -1; }
115 res = SyStr2Long(t);
116 free(buf);
117 return res;
121 static TSyResult SyHTTPAddAuth (TSyHdrs *hdrs, const char *astr, const TSyURL *url) {
122 TSyResult res;
123 const char *user, *pass;
124 char *tmp, *s;
126 user = url->user; pass = url->pass;
127 if ((user && *user) || (pass && *pass)) {
128 tmp = SyBuildAuthStr(user, pass); if (!tmp) return SY_ERROR;
129 s = SySPrintf(astr, tmp);
130 free(tmp); if (!s) return SY_ERROR;
131 res = SyHdrAddLine(hdrs, s); free(s);
132 if (res != SY_OK) return SY_ERROR;
135 return SY_OK;
139 TSyResult SyHTTPBuildQuery (TSyHdrs *hdrs, const char *method, const TSyURL *url, const TSyURL *proxy) {
140 char port[32], *tmp, *s;
141 const char *mt;
142 TSyResult res;
143 if (!hdrs) return SY_ERROR;
145 SyHdrClear(hdrs, SY_HDR_REQUEST);
146 if (!url) return SY_ERROR;
147 /* query string */
148 if (url->port != 80) sprintf(port, ":%i", url->port); else *port = '\0';
149 mt = (method && *method)?method:"GET";
150 if (proxy) {
151 /*if (*url->user || *url->pass)
152 s = SySPrintf("%s %s://%s:%s@%s%s", mt, url->protostr, url->user, url->pass, url->host, port);
153 else*/
154 s = SySPrintf("%s %s://%s%s", mt, url->protostr, url->host, port);
155 } else s = SySPrintf("%s ", mt);
156 if (!s) return SY_ERROR;
157 tmp = SySPrintf("%s%s%s%s%s HTTP/1.1", s, url->dir, url->file, url->query, url->anchor);
158 free(s); if (!tmp) return SY_ERROR;
159 res = SyHdrAddLine(hdrs, tmp); free(tmp);
160 if (res != SY_OK) return SY_ERROR;
161 /* auth */
162 if (proxy) {
163 if (SyHTTPAddAuth(hdrs, "Proxy-Authorization: Basic %s", proxy) != SY_OK) return SY_ERROR;
165 if (SyHTTPAddAuth(hdrs, "Authorization: Basic %s", url) != SY_OK) return SY_ERROR;
166 /* host */
167 if ((url->proto == SY_PROTO_HTTPS && url->port != 443) ||
168 (url->proto != SY_PROTO_HTTPS && url->port != 80))
169 s = SySPrintf("Host: %s:%i", url->host, url->port); /* don't add 443 for HTTPS too */
170 else
171 s = SySPrintf("Host: %s", url->host);
172 if (!s) return SY_ERROR;
173 res = SyHdrAddLine(hdrs, s); free(s);
174 #ifdef SY_DNT
175 SyHdrAddLine(hdrs, "X-Tracking-Choice: do-not-track");
176 SyHdrAddLine(hdrs, "DNT: 1");
177 SyHdrAddLine(hdrs, "X-Do-Not-Track: 1");
178 #endif
179 return res;
183 TSyResult SyHTTPAddRange (TSyHdrs *hdrs, int from, int to) {
184 TSyResult res;
185 char *s;
186 if (!hdrs) return SY_ERROR;
187 if (from <= 0) {
188 if (to < 0) return SY_OK;
189 from = 0;
191 if (to >= 0 && to < from) return SY_ERROR;
192 if (to >= 0) {
193 if (from > 0) s = SySPrintf("Range: bytes=%i-%i", from, to);
194 else s = SySPrintf("Range: bytes=-%i", to);
195 } else {
196 if (from > 0) s = SySPrintf("Range: bytes=%i-", from); else return SY_OK;
198 res = SyHdrAddLine(hdrs, s); free(s);
199 return res;
203 /* FIXME: hide password! */
204 TSyResult SyHTTPSendQuery (TSySocket *fd, const TSyHdrs *hdrs, const TSyPrintStr *pfn) {
205 TSyKVListItem *item, *ci;
206 TSyKVList *cc;
208 if (!hdrs || !hdrs->fields || hdrs->type == SY_HDR_INVALID) return SY_ERROR;
209 if (!hdrs->firstLine) { SyMessage(pfn, SY_MSG_ERROR, "incomplete header"); return SY_ERROR; }
210 if (SyTCPSendLine(pfn, 1, fd, "%s", hdrs->firstLine) != SY_OK) return SY_ERROR;
211 /* auth */
212 item = hdrs->fields->first;
213 while (item) {
214 cc = item->udata;
215 if (cc && cc->count) {
216 ci = cc->first;
217 while (ci) {
218 if (SyTCPSendLine(pfn, 1, fd, "%s: %s", item->key, ci->value) != SY_OK) return SY_ERROR;
219 ci = ci->next;
221 } else {
222 if (SyTCPSendLine(pfn, 1, fd, "%s: %s", item->key, item->value) != SY_OK) return SY_ERROR;
224 item = item->next;
226 return SyTCPSendLine(pfn, 0, fd, "");
230 #endif