Add myself to CREDITS
[parrot-plumage:kid51-parrot-plumage.git] / Plumage / Downloader.pir
1 # Copyright (C) 2009, Parrot Foundation.
2 # $Id$
3
4 =head1 NAME
5
6 Plumage::Downloader - Fetch the contents of a url in various ways
7
8 =head1 SYNOPSIS
9
10 Plumage::Downloader::save_to_file($url[, $filename])
11
12 =head1 DESCRIPTION
13
14 Downloads files.
15
16 =cut
17
18 .namespace ['Plumage';'Downloader']
19
20 .sub 'load' :anon :load :init
21     .local pmc c
22     load_language 'parrot'
23     c = compreg 'parrot'
24     c.'export'('save_url_to_file')
25 .end
26 .include 'socket.pasm'
27
28 .sub 'parse_url'
29     # half-assed url parser; should use PGE and a URI grammar
30     .param string url
31     .local string host, path
32     $S0 = substr url, 0, 7
33     ne $S0, 'http://', no_trim
34     url = substr url, 7
35   no_trim:
36     $I0 = index url, '/'
37     host = substr url, 0, $I0
38     path = substr url, $I0
39     .return (host, path)
40 .end
41
42 .sub 'get_filename'
43     .param string path
44     .local string filename
45     $I0 = 0
46   slash_loop:
47     inc $I0
48     $I1 = index path, '/', $I0
49     eq $I1, -1, end_slash_loop
50     $I0 = $I1
51     goto slash_loop
52   end_slash_loop:
53     filename = substr path, $I0
54     .return (filename)
55 .end
56
57 .sub 'build_request'
58     .param string host
59     .param string path
60     .param string ua     :optional
61     .param int    has_ua :opt_flag
62     .local string request
63     .local pmc args
64     ne has_ua, 0, got_ua
65     ua = 'Parrot'
66   got_ua:
67     args = new 'FixedPMCArray'
68     args = 3
69     args[0] = path
70     args[1] = host
71     args[2] = ua
72     request = sprintf "GET %s HTTP/1.1\r\nHost: %s\r\nUser-agent: %s\r\n\r\n", args
73     .return (request)
74 .end
75
76 .sub 'parse_headers'
77     .param string buf
78     .local pmc lines, headers, it
79     .local string i, k, v
80     .local int returncode
81     lines = split "\r\n", buf
82     $S0 = shift lines
83     $P0 = split ' ', $S0
84     returncode = $P0[1]
85     headers = new 'Hash'
86     it = iter lines
87   header_loop:
88     unless it goto end_header_loop
89     i = shift it
90     $I0 = index i, ': '
91     k = substr i, 0, $I0
92     $I0 += 2
93     v = substr i, $I0
94     headers[k] = v
95     goto header_loop
96   end_header_loop:
97     .return (returncode, headers)
98 .end
99
100 .sub 'save_url_to_file'
101     .param string url
102     .param string filename :optional
103     .param int has_filename :opt_flag
104     .local string host, path
105
106     (host, path) = 'parse_url'(url)
107     if has_filename goto got_filename
108     filename = 'get_filename'(path)
109     ne filename, '', got_filename
110     die "Couldn't determine filename to save. Provide a filename."
111   got_filename:
112
113     .local pmc sock, address, fh, headers
114     .local string headerbuf, buf, request
115     .local int ret, len, got_headers, returncode
116
117     # create the socket handle
118     sock = new 'Socket'
119     sock.'socket'(.PIO_PF_INET, .PIO_SOCK_STREAM, .PIO_PROTO_TCP)
120     unless sock goto ERR
121
122     # Pack a sockaddr_in structure with IP and port
123     address = sock.'sockaddr'(host, 80)
124     ret = sock.'connect'(address)
125
126     request = 'build_request'(host, path, 'Plumage_HTTP')
127     ret = sock.'send'(request)
128     fh = new 'FileHandle'
129     fh.'open'(filename, 'w')
130     got_headers = 0
131     $I5 = 1
132 MORE:
133     #say $I5
134     inc $I5
135     buf = sock.'recv'()
136     ret = length buf
137     if ret <= 0 goto END
138     ne got_headers, 0, save_content
139     $I0 = index buf, "\r\n\r\n"
140     ne $I0, -1, found_content
141     concat headerbuf, buf
142     goto MORE
143   found_content:
144     $S0 = substr buf, 0, $I0
145     concat headerbuf, $S0
146     (returncode, headers) = 'parse_headers'(headerbuf)
147     $I1 = returncode / 100
148     eq $I1, 2, http_success
149     $S0 = returncode
150     $S0 = concat "HTTP error: ", $S0
151     die $S0
152   http_success:
153     $I0 += 4
154     buf = substr buf, $I0
155     got_headers = 1
156   save_content:
157     fh.'print'(buf)
158     goto MORE
159 ERR:
160     die "Socket error"
161     end
162 END:
163     fh.'close'()
164     close sock
165     end
166 .end
167
168 # Local Variables:
169 #   mode: pir
170 #   fill-column: 100
171 # End:
172 # vim: expandtab shiftwidth=4 ft=pir: