Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/net/http.e | 9 | 11 | 81.82% | 187 | 219 | 85.39% | 32 |
Routine | Executed | Lines | Unexecuted | |
get_http() | 46 | 54 | 85.19% | 8 |
get_recvheader() | 8 | 13 | 61.54% | 5 |
get_sendheader() | 8 | 13 | 61.54% | 5 |
set_sendheader() | 7 | 12 | 58.33% | 5 |
set_sendheader_useragent_msie() | 0 | 3 | 0.00% | 3 |
eunet_format_sendheader() | 32 | 34 | 94.12% | 2 |
get_http_use_cookie() | 0 | 2 | 0.00% | 2 |
eunet_parse() | 22 | 23 | 95.65% | 1 |
get_url() | 12 | 13 | 92.31% | 1 |
parse_recvheader() | 13 | 13 | 100.00% | 0 |
set_sendheader_default() | 13 | 13 | 100.00% | 0 |
# | Executed | |
1 | --**** | |
2 | -- == HTTP | |
3 | -- | |
4 | -- < | |
5 | ||
6 | namespace http | |
7 | ||
8 | include std/socket.e as sock | |
9 | include std/net/url.e | |
10 | include std/net/dns.e | |
11 | include std/text.e | |
12 | include std/get.e as val | |
13 | include euphoria/info.e | |
14 | ||
15 | --**** | |
16 | -- === Constants | |
17 | ||
18 | public constant | |
19 | 1 | HTTP_HEADER_HTTPVERSION = 1, |
20 | 1 | HTTP_HEADER_GET = 2, |
21 | 1 | HTTP_HEADER_HOST = 3, |
22 | 1 | HTTP_HEADER_REFERER = 4, |
23 | 1 | HTTP_HEADER_USERAGENT = 5, |
24 | 1 | HTTP_HEADER_ACCEPT = 6, |
25 | 1 | HTTP_HEADER_ACCEPTCHARSET = 7, |
26 | 1 | HTTP_HEADER_ACCEPTENCODING = 8, |
27 | 1 | HTTP_HEADER_ACCEPTLANGUAGE = 9, |
28 | 1 | HTTP_HEADER_ACCEPTRANGES = 10, |
29 | 1 | HTTP_HEADER_AUTHORIZATION = 11, |
30 | 1 | HTTP_HEADER_DATE = 12, |
31 | 1 | HTTP_HEADER_IFMODIFIEDSINCE = 13, |
32 | 1 | HTTP_HEADER_POST = 14, |
33 | 1 | HTTP_HEADER_POSTDATA = 15, |
34 | 1 | HTTP_HEADER_CONTENTTYPE = 16, |
35 | 1 | HTTP_HEADER_CONTENTLENGTH = 17, |
36 | 1 | HTTP_HEADER_FROM = 18, |
37 | 1 | HTTP_HEADER_KEEPALIVE = 19, |
38 | 1 | HTTP_HEADER_CACHECONTROL = 20, |
39 | 1 | HTTP_HEADER_CONNECTION = 21 |
40 | ||
41 | sequence | |
42 | 1 | this_cookiejar = {}, |
43 | 1 | sendheader = {}, -- HTTP header sequence , sent to somewhere (usually the server) |
44 | 1 | recvheader = {}, -- HTTP header sequence , received from somewhere (usually the server) |
45 | 1 | defaultsendheader = {} -- a list of what may be sent in the sendheader, and minimum typical values |
46 | ||
47 | 3 | |
48 | integer slen, spt, flag | |
49 | sequence parsed, upperc, uppers | |
50 | ||
51 | 3 | upperc = "" |
52 | 3 | uppers = "" |
53 | ||
54 | 3 | if atom(c) then |
55 | 0 | c = {c} |
56 | end if | |
57 | ||
58 | 3 | parsed = {} |
59 | 3 | slen = length(s) |
60 | 3 | spt = 1 |
61 | 3 | flag = 0 |
62 | ||
63 | 3 | upperc = upper(c) |
64 | 3 | uppers = upper(s) |
65 | 3 | for i = 1 to slen do |
66 | 800 | if find(uppers[i],upperc) then |
67 | 52 | if flag = 1 then |
68 | 26 | parsed = append(parsed,s[spt..i-1]) |
69 | 26 | flag = 0 |
70 | 26 | spt = i+1 |
71 | else | |
72 | 26 | spt += 1 |
73 | end if | |
74 | else | |
75 | 748 | flag = 1 |
76 | end if | |
77 | 800 | end for |
78 | ||
79 | 3 | if flag = 1 then |
80 | 3 | parsed = append(parsed,s[spt..slen]) |
81 | end if | |
82 | ||
83 | 3 | return parsed |
84 | end function | |
85 | ||
86 | --**** | |
87 | -- === Header management | |
88 | -- | |
89 | ||
90 | --** | |
91 | -- Retrieve either the whole sendheader sequence, or just a single | |
92 | -- field. | |
93 | -- | |
94 | -- Parameters: | |
95 | -- # ##field## : an object indicating which part is being requested, see Comments section. | |
96 | -- | |
97 | -- Returns: | |
98 | -- An **object**, either: | |
99 | -- * -1 if the field cannot be found, | |
100 | -- * ##{{"label","delimiter","value"},...}## for the whole sendheader sequence | |
101 | -- * a three-element sequence in the form ##{"label","delimiter","value"}## when only a single field is selected. | |
102 | -- | |
103 | -- Comments: | |
104 | -- ##field## can be either an HTTP_HEADER_xxx access constant, | |
105 | -- the number 0 to retrieve the whole sendheader sequence, or | |
106 | -- a string matching one of the header field labels. The string is | |
107 | -- not case sensitive. | |
108 | -- | |
109 | ||
110 | 75 | |
111 | -- if field is 0, return the whole sequence. | |
112 | -- if field is 1..length(sendheader), return just that field | |
113 | -- if field is invalid, return -1. -- no, this is a problem , some code needs a sequence | |
114 | -- if field is a sequence, try to match it to sendheader[x][1]. | |
115 | ||
116 | -- Kat: should i return [1] & [2] as well? Mike: yes | |
117 | -- most server interfaces return the only value, saves parsing | |
118 | -- we'll return a {"Name","spacer","value"} format | |
119 | ||
120 | sequence upperfield | |
121 | ||
122 | 75 | if sequence(field) then |
123 | 75 | upperfield = upper(field) |
124 | 75 | for idx = 1 to length(sendheader) do |
125 | 549 | if equal(upperfield,upper(sendheader[idx][1])) then |
126 | 57 | return sendheader[idx] |
127 | end if | |
128 | 492 | end for |
129 | 18 | return {"","",""} |
130 | 0 | elsif field < 0 or field > length(sendheader) then |
131 | 0 | return {"","",""} |
132 | 0 | elsif field = 0 then |
133 | 0 | return sendheader |
134 | else | |
135 | 0 | return sendheader[field] |
136 | end if | |
137 | end function | |
138 | ||
139 | --** | |
140 | -- Sets header elements to default values. The default User Agent | |
141 | -- is Opera (currently the most standards compliant). Before setting | |
142 | -- any header option individually, programs must call this procedure. | |
143 | -- | |
144 | -- See Also: | |
145 | -- [[:get_sendheader]], [[:set_sendheader]], [[:set_sendheader_useragent_msie]] | |
146 | ||
147 | 4 | |
148 | 4 | sequence tempnewheader = {} |
149 | 4 | sequence temps = "" |
150 | 4 | sequence whoami = "OpenEuphoria " & version_string_long() |
151 | ||
152 | -- this sets some defaults, if not previously set to something by the user | |
153 | -- if a header line was previously set by the user, do not change it here | |
154 | -- httpversion MUST come before GET in this program: some servers default to 1.0, even if you say 1.1 | |
155 | -- NO spaces around [3] on httpversion | |
156 | -- POSTDATA MUST come before Content-Length in this program | |
157 | -- Referer is often used by sites to be sure your fetch was from one of their own pages | |
158 | -- headers with [3] = "" won't be sent | |
159 | -- you can add more [1], and modify [3], [2] is the ' ' or ": " (GET and POST have no ": ") | |
160 | ||
161 | 4 | defaultsendheader = { |
162 | {"httpversion","","HTTP/1.0"}, -- not a legal http headerline, but to append to GET or POST later on | |
163 | {"GET"," ",""}, -- [3] = the filename you want | |
164 | {"POST"," ",""}, -- [3] = the filename you want | |
165 | {"Host",": ",""}, -- the domain. You might think this was obvious, but for vhosting sites it's necessary. | |
166 | {"Referer",": ",""}, -- i know it's misspelled, but that's official! , the site that sent you to this one | |
167 | {"User-Agent",": ", whoami}, | |
168 | {"Accept-Charset",": ","utf-8,ISO-8859-1;q=0.7,*;q=0.5"}, | |
169 | {"Accept-Ranges",": ",""}, | |
170 | {"Authorization",": ",""}, | |
171 | {"Date",": ",""}, -- who cares if the server has my time? Except for cookie timeouts, that is. | |
172 | {"If-Modified-Since",": ",""}, -- for keeping a local cache sync'd | |
173 | {"POSTDATA","",""}, -- not a legal headerline, but has to go somewhere; put the POST data here, it will be appended to the bottom later | |
174 | {"Content-Type",": ",""}, -- if POST or PUT transaction | |
175 | {"Content-Length",": ",""}, -- if POST or PUT transaction | |
176 | {"From",": ",""}, -- possible in POST or PUT or Authorization | |
177 | {"Keep-Alive",": ",""}, -- set value depending on Connection | |
178 | {"Cache-Control",": ",""}, | |
179 | {"Connection",": ","close"} -- this is usually "close", sometimes "keep-alive" for http/1.1 and SSL, even if you set "close" the server may ignore you | |
180 | } | |
181 | ||
182 | ||
183 | -- the following not only puts the default header lines, | |
184 | -- it sorts the already-set lines to match the defaultsendheader order | |
185 | 4 | for defaultndx = 1 to length(defaultsendheader) do -- loop through defaultsendheader |
186 | 72 | temps = get_sendheader(defaultsendheader[defaultndx][1]) -- see if it was already set to something |
187 | 72 | if equal(temps[1],"") then |
188 | 18 | tempnewheader &= {defaultsendheader[defaultndx]} -- so set the default line |
189 | else | |
190 | 54 | tempnewheader &= {temps} -- use the pre-definition |
191 | end if | |
192 | 72 | end for |
193 | ||
194 | 4 | sendheader = tempnewheader |
195 | 4 | end procedure |
196 | ||
197 | --** | |
198 | -- Set an individual header field. | |
199 | -- | |
200 | -- Parameters: | |
201 | -- # ##whatheader## : an object, either an explicit name string or a HTTP_HEADER_xxx constant | |
202 | -- # ##whatdata## : a string, the associated data | |
203 | -- | |
204 | -- Comments: | |
205 | -- If the requested field is not one of the default header fields, | |
206 | -- the field MUST be set by string. This will increase the length | |
207 | -- of the header overall. | |
208 | -- | |
209 | -- Example 1: | |
210 | -- | |
211 | -- set_sendheader("Referer","search.yahoo.com") | |
212 | -- | |
213 | -- | |
214 | -- See Also: | |
215 | -- [[:get_sendheader]] | |
216 | ||
217 | 26 | |
218 | 26 | if atom(whatheader) then |
219 | 0 | if whatheader > 0 and whatheader <= length(sendheader) then -- how does this work? |
220 | 0 | sendheader[whatheader][3] = whatdata |
221 | end if | |
222 | 0 | return |
223 | end if | |
224 | ||
225 | 26 | for idx = 1 to length(sendheader) do |
226 | -- is this whatheader already in sendheader? | |
227 | 214 | if match(upper(whatheader),upper(sendheader[idx][1])) then |
228 | -- then simply set it to this value | |
229 | 26 | sendheader[idx][3] = whatdata |
230 | 26 | return |
231 | end if | |
232 | 188 | end for |
233 | ||
234 | -- ok, if we got here, then whatheader isn't in sendheader | |
235 | ||
236 | -- you better know what you are doing here! | |
237 | -- ": " is supplied as default, lets hope it's not an aberration like GET or POST | |
238 | -- this doesn't put it in any correct order | |
239 | 0 | sendheader = append(sendheader,{whatheader, ": ",whatdata}) |
240 | 0 | end procedure |
241 | ||
242 | --** | |
243 | -- Inform listener that user agent is Microsoft (R) Internet Explorer (TM). | |
244 | -- | |
245 | -- Comments: | |
246 | -- This is a convenience procedure to tell a website that a Microsoft | |
247 | -- Internet Explorer (TM) browser is requesting data. Because some | |
248 | -- websites format their response differently (or simply refuse data) | |
249 | -- depending on the browser, this procedure provides a quick means | |
250 | -- around that. | |
251 | -- For example, see: | |
252 | -- http://www.missporters.org/podium/nonsupport.aspx | |
253 | ||
254 | 0 | |
255 | 0 | set_sendheader("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)") |
256 | 0 | end procedure |
257 | ||
258 | -------------------------------------------------------------------------------- | |
259 | -- this can also be used to flatten the sendheader record, for printing, etc | |
260 | 3 | |
261 | sequence | |
262 | 3 | tempheader = "", |
263 | 3 | temppostdata = "", |
264 | 3 | httpversion = "" |
265 | ||
266 | 3 | for idx = 1 to length(sendheader) do |
267 | 54 | if not equal("httpversion",sendheader[idx][1]) and |
268 | not equal("POSTDATA",sendheader[idx][1]) | |
269 | then | |
270 | -- if the data field is not empty... | |
271 | 48 | if not equal("",sendheader[idx][3]) then |
272 | 18 | switch sendheader[idx][1] with fallthru do |
273 | case "GET" then | |
274 | -- remove any trailing nulls | |
275 | 2 | while find(0, sendheader[idx][3]) do |
276 | 3 | sendheader[idx][3][find(0, sendheader[idx][3])] = ' ' |
277 | 3 | end while |
278 | -- append the http version | |
279 | 2 | tempheader &= sendheader[idx][1] & sendheader[idx][2] & |
280 | sendheader[idx][3] & " " & httpversion & "\r\n" | |
281 | 2 | break |
282 | ||
283 | case "POST" then | |
284 | -- remove any trailing nulls | |
285 | 1 | while find(0, sendheader[idx][3]) do |
286 | 1 | sendheader[idx][3][find(0, sendheader[idx][3])] = ' ' |
287 | 1 | end while |
288 | -- append the http version | |
289 | 1 | tempheader &= sendheader[idx][1] & sendheader[idx][2] & |
290 | sendheader[idx][3] & " " & httpversion & "\r\n" | |
291 | 1 | break |
292 | ||
293 | case else | |
294 | -- remove any trailing nulls | |
295 | 15 | while find(0, sendheader[idx][3]) do |
296 | 0 | sendheader[idx][3][find(0, sendheader[idx][3])] = ' ' |
297 | 0 | end while |
298 | -- else just flatten the sequence | |
299 | 15 | tempheader &= sendheader[idx][1] & sendheader[idx][2] & |
300 | sendheader[idx][3] & "\r\n" | |
301 | end switch | |
302 | end if | |
303 | end if | |
304 | ||
305 | -- this is done here because | |
306 | -- this is where the POSTDATA is moved to the bottom of the header, | |
307 | -- the Content-length and Content-Type is filled in | |
308 | ||
309 | 54 | if equal("POSTDATA",sendheader[idx][1]) and not equal("",sendheader[idx][3]) then |
310 | -- POSTDATA was set to something | |
311 | 1 | set_sendheader("Content-Type","application/x-www-form-urlencoded") |
312 | 1 | temppostdata = sendheader[idx][3] |
313 | 1 | set_sendheader("Content-Length",sprintf("%d",length(temppostdata))) |
314 | 1 | sendheader[idx][3] = "" -- clear it, so it's not accidentally sent again |
315 | end if | |
316 | ||
317 | 54 | if equal("httpversion",sendheader[idx][1]) and not equal("",sendheader[idx][3]) then |
318 | 3 | httpversion = sendheader[idx][3] |
319 | end if | |
320 | 54 | end for |
321 | ||
322 | 3 | tempheader &= "\r\n" -- end of header |
323 | 3 | if not equal(temppostdata,"") then-- but if there's POST data, |
324 | 1 | tempheader = tempheader & temppostdata & "\r\n" -- tack that on too |
325 | end if | |
326 | ||
327 | 3 | return tempheader |
328 | end function | |
329 | ||
330 | --** | |
331 | -- Populates the internal sequence recvheader from the flat string header. | |
332 | -- | |
333 | -- Parameters: | |
334 | -- # ##header## : a string, the header data | |
335 | -- | |
336 | -- Comments: | |
337 | -- This must be called prior to calling [[:get_recvheader]](). | |
338 | ||
339 | 3 | |
340 | sequence junk | |
341 | atom place | |
342 | ||
343 | 3 | junk = {"",""} -- init it, it looks like this |
344 | 3 | recvheader = eunet_parse(header,{10,13}) -- could be \n or \r or both |
345 | 3 | for idx = 1 to length(recvheader) do |
346 | 29 | place = match(": ",recvheader[idx]) |
347 | 29 | if place then |
348 | 26 | junk[1] = recvheader[idx][1..place-1] |
349 | 26 | junk[2] = recvheader[idx][place+2 .. $] |
350 | 26 | recvheader[idx] = junk |
351 | else | |
352 | 3 | if match("HTTP/",upper(recvheader[idx])) then |
353 | 3 | recvheader[idx] = {"httpversion",recvheader[idx]} -- what else to call that line? |
354 | end if | |
355 | end if | |
356 | 29 | end for |
357 | 3 | end procedure |
358 | ||
359 | --** | |
360 | -- Return the value of a named field in the received http header as | |
361 | -- returned by the most recent call to [[:get_http]]. | |
362 | -- | |
363 | -- Parameters: | |
364 | -- # ##field## : an object, either a string holding a field name (case insensitive), | |
365 | -- 0 to return the whole header, or a numerical index. | |
366 | -- | |
367 | -- Returns: | |
368 | -- An **object**, | |
369 | -- * -1 on error | |
370 | -- * a sequence in the form, ##{field name, field value}## on success. | |
371 | ||
372 | 3 | |
373 | sequence upperfield | |
374 | -- recvheader was parsed out previously in parse_recvheader() | |
375 | -- if field is 0, return the whole sequence. | |
376 | -- if field is 1..length(recvheader), return just that field | |
377 | -- if field is invalid, return -1. | |
378 | -- if field is a sequence, try to match it to recvheader[x][1]. | |
379 | ||
380 | -- we'll NOT return a {"Name","value"} format | |
381 | -- because that leads to using a junk seq to get the [2] from | |
382 | -- --> And yet, that's exactly what we're doing. -- Mike. | |
383 | ||
384 | 3 | if sequence(field) and equal(field,"") then |
385 | 0 | return -1 |
386 | end if | |
387 | ||
388 | 3 | if atom(field) then |
389 | 0 | if ( field <= 0 ) or ( field > length(recvheader) ) then |
390 | 0 | return -1 |
391 | end if | |
392 | ||
393 | 0 | return recvheader[field] |
394 | end if | |
395 | ||
396 | 3 | upperfield = upper(field) |
397 | 3 | for idx = 1 to length(recvheader) do |
398 | 27 | if equal(upperfield,upper(recvheader[idx][1])) then |
399 | 3 | return recvheader[idx] -- {"header_name","value"} |
400 | end if | |
401 | 24 | end for |
402 | ||
403 | 0 | return -1 -- not found! |
404 | end function | |
405 | ||
406 | --**** | |
407 | -- === Web interface | |
408 | -- | |
409 | ||
410 | --** | |
411 | -- Returns data from an http internet site. | |
412 | -- | |
413 | -- Parameters: | |
414 | -- # ##inet_addr## : a sequence holding an address | |
415 | -- # ##hostname## : a string, the name for the host | |
416 | -- # ##file## : a file name to transmit | |
417 | -- | |
418 | -- Returns: | |
419 | -- A **sequence**, empty sequence on error, of length 2 on success, | |
420 | -- like ##{sequence header, sequence data}##. | |
421 | ||
422 | 3 | |
423 | object junk, junk2, header | |
424 | sock:socket sock | |
425 | atom success, last_data_len, gotheader, contentlen, last | |
426 | sequence data | |
427 | ||
428 | -- Notes for future additions: | |
429 | -- HUGE differences in HTTP/1.1 vs HTTP/1.0 | |
430 | -- GET /index.html HTTP/1.1 | |
431 | ||
432 | 3 | if length(inet_addr)=0 then |
433 | 0 | return {"",""} |
434 | end if | |
435 | ||
436 | 3 | if length(file)=0 or file[1]!='/' then |
437 | 1 | file = '/' & file |
438 | end if | |
439 | ||
440 | 3 | junk = get_sendheader("POSTDATA") |
441 | -- was the POSTDATA set? | |
442 | 3 | if equal(junk[3],"") then |
443 | -- if no, assume it's a GET | |
444 | 2 | set_sendheader("GET",file) |
445 | else | |
446 | -- if so, then it's definitely a POST (err, or PUT, but we don't do PUT) | |
447 | 1 | set_sendheader("POST",file) |
448 | end if | |
449 | ||
450 | -- This is required for virtual shared hosting. On dedicated boxes on fixed ip, | |
451 | -- you can http to the ip, and GET/POST is enough to deal with it. Setting it is | |
452 | -- safe, either way. | |
453 | 3 | set_sendheader("HOST",hostname) |
454 | ||
455 | 3 | last_data_len = 0 |
456 | 3 | sock = sock:create(AF_INET,SOCK_STREAM,0) |
457 | 3 | success = sock:connect(sock,inet_addr,port) |
458 | ||
459 | 3 | if success = sock:OK then |
460 | -- eunet_format_sendheader sets up the header to sent, | |
461 | -- putting the POST data at the end, | |
462 | -- filling in the CONTENT-LENGTH, | |
463 | -- and avoiding sending empty fields for any field | |
464 | 3 | success = sock:send(sock,eunet_format_sendheader(),0) |
465 | ||
466 | -- } end version 1.3.0 mod | |
467 | 3 | data = "" |
468 | 3 | header= {} |
469 | 3 | contentlen = 0 |
470 | 3 | gotheader = 0 |
471 | 3 | if success |
472 | then | |
473 | 3 | last = time() |
474 | 3 | while sequence(junk) with entry do |
475 | 3 | data = data & junk |
476 | 3 | if gotheader and equal(contentlen,length(data)) then |
477 | 0 | exit -- we got all the server said it had |
478 | end if | |
479 | 3 | if not gotheader and match({13,10,13,10},data) then -- we got the header in there |
480 | 3 | header = data[1..match({13,10,13,10},data)-1] -- split off the header |
481 | 3 | data = data[match({13,10,13,10},data)+4..$] -- and the data is what's left, we keep using data in the sock loop |
482 | 3 | parse_recvheader(header) -- sets up recvheader -- global var |
483 | 3 | junk = get_recvheader("Content-Length") |
484 | 3 | if not equal(junk,-1) then |
485 | 3 | junk = val:value(junk[2]) |
486 | 3 | contentlen = junk[2] |
487 | 3 | if equal(contentlen,0) then exit end if -- there's no more |
488 | 3 | if equal(contentlen,length(data)) then exit end if -- there's no more |
489 | end if | |
490 | 0 | gotheader = 1 -- we got what we came for here |
491 | end if | |
492 | entry | |
493 | ||
494 | 3 | junk2 = sock:select(sock, {}, {}, timeout) -- status check |
495 | -- Do we have readable data? | |
496 | 3 | if (length(junk2[1]) > 2) and equal(junk2[1][2],1) then |
497 | 3 | last = time() |
498 | 3 | junk = sock:receive(sock, 0) -- then recieve it |
499 | else | |
500 | -- assume server has hung, abort | |
501 | 0 | exit |
502 | end if | |
503 | 3 | end while |
504 | else | |
505 | 0 | header = -1 |
506 | 0 | data = "could not send or recieve using socket" |
507 | end if -- if success -- sock:send | |
508 | else | |
509 | 0 | header = -1 |
510 | 0 | data = "could not connect to socket" |
511 | end if -- if success = 1 then -- sock:connect | |
512 | 3 | if sock:close(sock) then end if |
513 | ||
514 | -- clear any POSTDATA | |
515 | 3 | set_sendheader("POSTDATA", "") |
516 | 3 | set_sendheader("POST", "") |
517 | 3 | set_sendheader("GET", "") |
518 | 3 | set_sendheader("Content-Type", "") |
519 | 3 | set_sendheader("Content-Length", "0") |
520 | 3 | set_sendheader_default() |
521 | ||
522 | 3 | return {header,data} |
523 | end function | |
524 | ||
525 | --** | |
526 | -- Works the same as [[:get_url]](), but maintains an internal | |
527 | -- state register based on cookies received. | |
528 | -- | |
529 | -- Warning: | |
530 | -- This function is not yet implemented. | |
531 | -- | |
532 | -- Parameters: | |
533 | -- # ##inet_addr## : a sequence holding an address | |
534 | -- # ##hostname## : a string, the name for the host | |
535 | -- # ##file## : a file name to transmit | |
536 | -- | |
537 | -- Returns: | |
538 | -- A **sequence**, {header, body} on success, or an empty sequence on error. | |
539 | -- | |
540 | -- Example 1: | |
541 | -- | |
542 | -- addrinfo = getaddrinfo("www.yahoo.com","http",0) | |
543 | -- if atom(addrinfo) or length(addrinfo) < 1 or | |
544 | -- length(addrinfo[1]) < 5 then | |
545 | -- puts(1,"Uh, oh") | |
546 | -- return {} | |
547 | -- else | |
548 | -- inet_addr = addrinfo[1][5] | |
549 | -- end if | |
550 | -- data = get_http_use_cookie(inet_addr,"www.yahoo.com","") | |
551 | -- | |
552 | -- | |
553 | -- See also: | |
554 | -- [[:get_url]] | |
555 | ||
556 | 0 | |
557 | /* | |
558 | atom socket, success, last_data_len, cpos, offset | |
559 | sequence header, header2, body, data, updata | |
560 | sequence cookielist, request, cookie | |
561 | object junk -- a general throwaway temp var | |
562 | ||
563 | cookielist = {} | |
564 | request = "" | |
565 | -- cookie = {name,domain,path,expires,encrypted,version} | |
566 | ||
567 | if length(inet_addr)=0 then | |
568 | return {"",""} | |
569 | end if | |
570 | ||
571 | if length(file)=0 or file[1]!='/' then file = '/'&file end if | |
572 | ||
573 | -- was the POSTDATA set? | |
574 | junk = get_sendheader("POSTDATA") | |
575 | if equal(junk[3],"") then | |
576 | -- if no, assume it's a GET | |
577 | set_sendheader("GET",file) | |
578 | else | |
579 | -- if so, then it's definitely a POST (err, or PUT, but we don't do PUT) | |
580 | set_sendheader("POST",file) | |
581 | end if | |
582 | ||
583 | -- This is required for virtual shared hosting. | |
584 | -- On dedicated boxes on fixed ip, | |
585 | -- you can http to the ip, and GET/POST is enough to deal with it. | |
586 | -- Setting it is safe, either way. | |
587 | set_sendheader("HOST",hostname) | |
588 | ||
589 | for ctr = 1 to length(this_cookiejar) do | |
590 | if sequence(this_cookiejar[ctr]) and length(this_cookiejar[ctr])>=2 and | |
591 | sequence(this_cookiejar[ctr][1]) and | |
592 | (match(hostname,this_cookiejar[ctr][2])>0 or match(this_cookiejar[ctr][2],hostname)>0) and | |
593 | (length(file)=0 or match(this_cookiejar[ctr][3],file)>0) | |
594 | then | |
595 | cookielist = append(cookielist,this_cookiejar[ctr]) | |
596 | end if | |
597 | end for | |
598 | ||
599 | -- TO DO: Sort cookielist by domain, path (longer path before shorter path) | |
600 | -- request = sprintf("GET /%s HTTP/1.0\nHost: %s\n",{file,hostname}) | |
601 | for idx = 1 to length(cookielist) do | |
602 | -- if idx = 1 then | |
603 | -- request = request & "Cookie: "&cookielist[idx][1] | |
604 | -- else | |
605 | -- request = request & " "&cookielist[idx][1] | |
606 | -- end if | |
607 | request = request & cookielist[idx][1] | |
608 | if length(cookielist[idx][3])>0 then | |
609 | request = request & "; $Path=" & cookielist[idx][3] | |
610 | end if | |
611 | if idx < length(cookielist) then | |
612 | --request = request & ";\n" | |
613 | request = request & ";" | |
614 | else | |
615 | --request = request & "\n" | |
616 | end if | |
617 | end for | |
618 | -- request = request & "\n" | |
619 | set_sendheader("Cookie",request) | |
620 | ||
621 | data = {} | |
622 | last_data_len = 0 | |
623 | socket = sock:create(AF_INET,SOCK_STREAM,0) | |
624 | success = sock:connect(AF_INET, socket,inet_addr) | |
625 | if success = 0 then | |
626 | -- success = sock:send(socket,request,0) | |
627 | success = sock:send(socket,eunet_format_sendheader(),0) | |
628 | -- } end version 1.3.0 modification | |
629 | if success > 0 then | |
630 | junk = sock:receive(sock, 0) | |
631 | while sequence(junk) do | |
632 | data = data & junk | |
633 | if gotheader and equal(contentlen,length(data)) then | |
634 | exit -- we got all the server said it had | |
635 | end if | |
636 | if not gotheader and match({13,10,13,10},data) then -- we got the header in there | |
637 | header = data[1..match({13,10,13,10},data)-1] -- split off the header | |
638 | data = data[match({13,10,13,10},data)+4..$] -- and the data is what's left, we keep using data in the sock loop | |
639 | parse_recvheader(header) -- sets up recvheader -- global var | |
640 | junk = get_recvheader("Content-Length") | |
641 | if not equal(junk,-1) then | |
642 | junk = val:value(junk[2]) | |
643 | contentlen = junk[2] | |
644 | if equal(contentlen,0) then exit end if -- there's no more | |
645 | if equal(contentlen,length(data)) then exit end if -- there's no more | |
646 | end if | |
647 | gotheader = 1 -- we got what we came for here | |
648 | end if | |
649 | junk = sock:receive(sock, 0) | |
650 | end while | |
651 | end if | |
652 | end if | |
653 | if close_socket(socket) then end if | |
654 | ||
655 | ||
656 | header2 = header | |
657 | cpos = match("SET-COOKIE",upper(header2)) -- this should be using get_recvheader() etc | |
658 | while cpos > 0 do | |
659 | header2 = header2[cpos+10 .. $] | |
660 | data = header2 | |
661 | cpos = find(':',data) | |
662 | if cpos > 0 then | |
663 | data = data[cpos+1..$] | |
664 | end if | |
665 | offset = 0 | |
666 | cpos = match(13&10,data) | |
667 | while cpos > 1 and data[offset+cpos-1]=';' do | |
668 | offset = offset + cpos + 2 | |
669 | cpos = match(13&10,data[offset..$]) | |
670 | end while | |
671 | offset = offset + cpos - 1 | |
672 | data = data[1..offset] | |
673 | updata = upper(data) | |
674 | cookie = {"","","","","N",""} | |
675 | offset = match("PATH=",updata) | |
676 | if offset > 0 then | |
677 | cpos = find(';',data[offset .. $]) | |
678 | if cpos = 0 then cpos = length(data)-offset+2 end if | |
679 | cookie[3] = data[offset+5..offset+cpos-2] | |
680 | end if | |
681 | cpos = find(';',data) | |
682 | if cpos = 0 then cpos = length(data)+1 end if | |
683 | cookie[1] = _socket_trim(data[1..cpos-1]) | |
684 | if cpos > length(data) then | |
685 | data = "" | |
686 | updata = "" | |
687 | else | |
688 | data = data[cpos+1 .. $] | |
689 | updata = updata[cpos+1..length(data)] | |
690 | end if | |
691 | offset = match("DOMAIN=",updata) | |
692 | if offset > 0 then | |
693 | cpos = find(';',data[offset .. $]) | |
694 | if cpos = 0 then cpos = length(data)-offset+2 end if | |
695 | cookie[2] = data[offset+7..offset+cpos-2] | |
696 | -- Offset is base 1. If the semicolon is in the first position, cpos | |
697 | -- is also 1. Since we don't want to include the semicolon, we need | |
698 | -- to subtract 1 for offset's base and 1 to go to the char before | |
699 | -- cpos, thus the subtracting of two. In the case of end of string | |
700 | -- (cpos = 0), we need to add those two back to compensate for the | |
701 | -- different scenario (+offset-offset = 0 and +2-2 = 0, therefore | |
702 | -- cpos = length(data), which is what we want). | |
703 | end if | |
704 | offset = match("EXPIRES=",updata) | |
705 | if offset > 0 then | |
706 | cpos = find(';',data[offset .. $]) | |
707 | if cpos = 0 then cpos = length(data)-offset+2 end if | |
708 | cookie[4] = data[offset+8..offset+cpos-2] | |
709 | end if | |
710 | offset = match("VERSION=",updata) | |
711 | if offset > 0 then | |
712 | cpos = find(';',data[offset..$]) | |
713 | if cpos = 0 then cpos = length(data)-offset+2 end if | |
714 | cookie[6] = data[offset+8..offset+cpos-2] | |
715 | end if | |
716 | offset = match("MAX-AGE=",updata) | |
717 | if offset > 0 then | |
718 | cpos = find(';',data[offset..$]) | |
719 | if cpos = 0 then cpos = length(data)-offset+2 end if | |
720 | cookie[4] = data[offset+8..offset+cpos-2] | |
721 | end if | |
722 | offset = match("SECURE",updata) | |
723 | if offset > 0 then | |
724 | cookie[5] = "Y" | |
725 | end if | |
726 | cpos = find('=',cookie[1]) | |
727 | if cpos > 0 then | |
728 | request = cookie[1][1..cpos] | |
729 | else | |
730 | request = "=" | |
731 | end if | |
732 | cpos = 0 | |
733 | for ctr = 1 to length(this_cookiejar) do | |
734 | if sequence(this_cookiejar[ctr]) and length(this_cookiejar[ctr])>=2 and | |
735 | match(cookie[1],this_cookiejar[ctr][1])>0 and | |
736 | eu:compare(cookie[2],this_cookiejar[ctr][2])=0 and | |
737 | eu:compare(this_cookiejar[ctr][3],cookie[3])=0 then | |
738 | this_cookiejar[ctr] = cookie | |
739 | cpos = ctr | |
740 | exit | |
741 | end if | |
742 | end for | |
743 | if cpos = 0 then | |
744 | this_cookiejar = append(this_cookiejar,cookie) | |
745 | end if | |
746 | cpos = match("SET-COOKIE",upper(header2)) | |
747 | end while | |
748 | ||
749 | -- clear any POSTDATA | |
750 | set_sendheader("POSTDATA", "") | |
751 | set_sendheader("POST", "") | |
752 | set_sendheader("GET", "") | |
753 | set_sendheader("Content-Type", "") | |
754 | set_sendheader("Content-Length", "0") | |
755 | set_sendheader_default() | |
756 | ||
757 | return {header,body} | |
758 | */ | |
759 | 0 | return -1 |
760 | end function | |
761 | ||
762 | --** | |
763 | -- Returns data from an http internet site. | |
764 | -- | |
765 | -- Parameters: | |
766 | -- # ##url##: URL to access | |
767 | -- # ##post_data##: Optional post data | |
768 | -- | |
769 | -- Returns: | |
770 | -- A **sequence** {header, body} on success, or an empty sequence on error. | |
771 | -- | |
772 | -- Comments: | |
773 | -- If ##post_data## is empty, then a normal GET request is done. If ##post_data## is non-empty | |
774 | -- then ##get_url## will perform a POST request and supply ##post_data## during the request. | |
775 | -- | |
776 | -- Example 1: | |
777 | -- | |
778 | -- url = "http://banners.wunderground.com/weathersticker/mini" & | |
779 | -- "Weather2_metric_cond/language/www/US/PA/Philadelphia.gif" | |
780 | -- | |
781 | -- temp = get_url(url) | |
782 | -- if length(temp)>=2 and length(temp[2])>0 then | |
783 | -- tempfp = open(TEMPDIR&"current_weather.gif","wb") | |
784 | -- puts(tempfp,temp[2]) | |
785 | -- close(tempfp) | |
786 | -- end if | |
787 | -- | |
788 | ||
789 | 3 | |
790 | object addrinfo, url_data | |
791 | ||
792 | 3 | url_data = parse(url) |
793 | 3 | if atom(url_data) then return 0 end if |
794 | ||
795 | 3 | addrinfo = host_by_name(url_data[URL_HOSTNAME]) |
796 | 3 | if atom(addrinfo) or length(addrinfo) < 3 or length(addrinfo[3]) = 0 then |
797 | 0 | return 0 |
798 | end if | |
799 | ||
800 | 3 | set_sendheader("POSTDATA", post_data) |
801 | ||
802 | 3 | if url_data[URL_PORT] = 0 then |
803 | -- url didn't specify a port, so default to 80 | |
804 | 2 | url_data[URL_PORT] = 80 |
805 | end if | |
806 | ||
807 | 3 | sequence data = {"",""} |
808 | 3 | if eu:compare(lower(url_data[URL_PROTOCOL]),"http") = 0 then |
809 | 3 | data = get_http(addrinfo[3][1], url_data[URL_HOSTNAME], |
810 | url_data[URL_PATH] & url_data[URL_QUERY_STRING], , url_data[URL_PORT]) | |
811 | end if | |
812 | ||
813 | 3 | return data |
814 | end function | |
815 | ||
816 | -- set the lines in the "proper" order for sending, not that the defaults will get sent. | |
817 | 1 | set_sendheader_default() |
818 |