1 /******************************************************************************
2 *
3 * Copyright (C) 2004-2008, The Gentee Group. All rights reserved.
4 * This file is part of the Gentee open source project - http://www.gentee.com.
5 *
6 * THIS FILE IS PROVIDED UNDER THE TERMS OF THE GENTEE LICENSE ("AGREEMENT").
7 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE CONSTITUTES RECIPIENTS
8 * ACCEPTANCE OF THE AGREEMENT.
9 *
10 * Author: Alexey Krivonogov ( gentee )
11 *
12 ******************************************************************************/
13
14 /*-----------------------------------------------------------------------------
15 * Id: http L "HTTP"
16 *
17 * Summary: HTTP protocol. You must call
18 #a(inet_init) function before using this library. For using this
19 library, it is
20 required to specify the file http.g (from lib\http
21 subfolder) with include command. #srcg[
22 |include : $"...\gentee\lib\http\http.g"]
23 *
24 * List: *,http_get,http_getfile,http_head,http_post,
25 *@Common internet functions,inet_close,inet_error,inet_init,
26 inet_proxy,inet_proxyenable,inetnotify_func,
27 *@URL strings,str_iencoding,str_ihead,str_ihttpinfo,str_iurl,
28 *
29 -----------------------------------------------------------------------------*/
30
31
32 include : $"..\socket\internet.g"
33
34 func str http_cmd( str ret, str cmd host path more, uint isproxy )
35 {
36 return ret = "\(cmd) \(?( isproxy, "http://\(host)/\(path)","/\(path)")) HTTP/1.0
37 User-Agent: \( inet_useragent )
38 Accept: */*
39 Host: \(host)
40 \(more)\l"
41 }
42 /**/
43
44 /*-----------------------------------------------------------------------------
45 * Id: http_get F
46 *
47 * Summary: Getting data via the HTTP protocol. The method sends a GET request
48 to the specified URL and writes data it receives to the databuf
49 buffer.
50 *
51 * Params: url - The URL address data is received from.
52 databuf - The buffer for getting data.
53 notify - The #a(inetnotify_func,function) for getting /
54 notifications. It can be 0.
55 flag - Flags. $$[httpflag]
56 *
57 * Return: #lng/retf#
58 *
59 -----------------------------------------------------------------------------*/
60
61 func uint http_get( str url, buf databuf, uint notify, uint flag )
62 {
63 uint ret i isfile data dif
64 str request host path range stemp
65 socket sock
66 httpinfo hi
67 inetnotify ni
68 buf fbuf
69 file fdwn
70 finfo fi
71
72 subfunc uint nfy( uint code )
73 {
74 if !notify : return 1
75
76 if !notify->func( code, ni )
77 {
78 ineterror = $ERRINET_USERBREAK
79 ret = 0
80 return 0
81 }
82 return 1
83 }
84 ineterror = 0
85 isfile = flag & $HTTPF_FILE
86 url.replacech( stemp = url, ' ', "%20" )
87 if !isfile : databuf.use = 0
88
89 ni.url = url
90
91 nfy( $NFYINET_CONNECT )
92 if !sock.urlconnect( url, host, path )
93 {
94 nfy( $NFYINET_ERROR )
95 return 0
96 }
97 if !nfy( $NFYINET_SEND ) : goto end
98 if isfile && flag & $HTTPF_CONTINUE
99 {
100 getfileinfo( databuf->str, fi )
101 if fi.sizelo : range = "Range: bytes=\(fi.sizelo)-\l"
102 }
103
104 http_cmd( request, "GET", host, path, range, sock.isproxy())
105
106 if !sock.send( request ) : goto end
107
108 data = ?( isfile, &fbuf, &databuf )
109 data as buf
110
111 if !sock.recv( data ) : goto end
112
113 if !"HTTP".eqlenign( data.ptr()) || !ni.head.ihead( data ).ihttpinfo( hi )
114 {
115 ineterror = $ERRINET_HTTPDATA
116 goto end
117 }
118 ni.param = &hi
119 if !nfy( $NFYINET_HEAD ) : goto end
120
121 if flag & $HTTPF_REDIRECT && *hi.location
122 {
123 data.clear()
124 sock.close()
125
126 ni.sparam = hi.location
127 if !nfy( $NFYINET_REDIRECT ) : goto end
128
129 return http_get( hi.location, databuf, notify, flag )
130 }
131 if isfile
132 {
133 data.expand( 0x20000 )
134 /*if !( fhandle = open( databuf->str, ?( flag & $HTTPF_CONTINUE,
135 $OP_ALWAYS, $OP_CREATE )))*/
136 if !( fdwn.open( databuf->str, ?( flag & $HTTPF_CONTINUE,
137 $OP_ALWAYS, $OP_CREATE ) ) )
138 {
139 ni.sparam = databuf->str
140 ineterror = $ERRINET_OPENFILE
141 nfy( $NFYINET_ERROR )
142 goto end
143 }
144 }
145 else
146 {
147 if uint( hi.size ) : data.expand( uint( hi.size ) + 0x7FFF )
148 }
149 if *range
150 {
151 fdwn.setpos( 0, $FILE_END )
152 ni.param = fi.sizelo + *data
153 }
154 else : ni.param = *data
155
156 do
157 {
158 i = *data
159 if !nfy( $NFYINET_GET ) : goto end
160 sock.recv( data )
161 dif = *data - i
162 ni.param += dif
163
164 if isfile && ( *data >= 0x7FFF || !dif )
165 {
166 if !( fdwn.write( data ))
167 {
168 ni.sparam = databuf->str
169 ineterror = $ERRINET_WRITEFILE
170 nfy( $NFYINET_ERROR )
171 goto end
172 }
173 data.use = 0
174 }
175 } while dif
176
177 nfy( $NFYINET_END )
178
179 ret = 1
180 label end
181 if flag & $HTTPF_STR : data += byte( 0 )
182 if fdwn.fopen
183 {
184 if flag & $HTTPF_SETTIME && hi.dt.day
185 {
186 filetime ft
187
188 datetimetoftime( hi.dt, ft )
189 fdwn.settime( ft )
190 }
191 fdwn.close()
192 }
193 if !ret && ineterror : nfy( $NFYINET_ERROR )
194
195 sock.close( )
196 return ret
197 }
198
199 /*-----------------------------------------------------------------------------
200 * Id: http_getfile F
201 *
202 * Summary: Downloading a file via the HTTP protocol. The method sends a GET
203 request to the specified URL and writes data it receives to
204 the specified file.
205 *
206 * Params: url - The URL address for downloading.
207 filename - The name of the file for writing.
208 notify - The #a(inetnotify_func,function) for getting /
209 notifications. It can be 0.
210 flag - Flags. $$[httpflag]
211 *
212 * Return: #lng/retf#
213 *
214 -----------------------------------------------------------------------------*/
215
216 func uint http_getfile( str url, str filename, uint notify, uint flag )
217 {
218 flag &= 0xFF0F
219 return http_get( url, filename->buf, notify, flag | $HTTPF_FILE )
220 }
221
222 /*-----------------------------------------------------------------------------
223 * Id: http_head F
224 *
225 * Summary: Getting a header via the HTTP protocol. The method sends a HEAD
226 request to the specified URL address and partially parses the
227 received data.
228 *
229 * Params: url - The URL address for getting the header.
230 head - The string for getting the text of the header.
231 hi - The variable of the #a( thttpinfo ) type for getting /
232 information about the header.
233 *
234 * Return: #lng/retf#
235 *
236 -----------------------------------------------------------------------------*/
237
238 func uint http_head( str url, str head, httpinfo hi )
239 {
240 uint ret
241 str request host path
242 socket sock
243
244 head.clear()
245 if !sock.urlconnect( url, host, path ) : return 0
246
247 http_cmd( request, "HEAD", host, path, "", sock.isproxy( ))
248
249 if !sock.send( request ) : goto end
250 if !sock.recv( head->buf ) : goto end
251 head->buf.del( 0, 1 )
252 head->buf += byte( 0 )
253 ret = 1
254
255 if &hi : head.ihttpinfo( hi )
256 label end
257 sock.close( )
258 return ret
259 }
260
261 /*-----------------------------------------------------------------------------
262 ** Id: http_post F
263 *
264 * Summary: Sending data via the HTTP protocol. The method sends a POST
265 request with the specified string to the specified URL address.
266 It is used to fill out forms automatically.
267 *
268 * Params: url - The URL address where the data will be sent.
269 data - The string with the data being sent. Before the data is /
270 sent, request strings with parameters should be recoded /
271 with the help of the #a(str_iencoding) method.
272 result - The string for getting a response from the server.
273 notify - The #a(inetnotify_func, function ) for getting /
274 notifications. It can be 0.
275 *
276 * Return: #lng/retf#
277 *
278 -----------------------------------------------------------------------------*/
279
280 func uint http_post( str url, str data, str result, uint notify )
281 {
282 str host path request
283 socket sock
284 uint i dif ret
285 inetnotify ni
286
287 subfunc uint nfy( uint code )
288 {
289 if !notify : return 1
290
291 if !notify->func( code, ni )
292 {
293 ineterror = $ERRINET_USERBREAK
294 ret = 0
295 return 0
296 }
297 return 1
298 }
299 ineterror = 0
300 ni.url = url
301
302 nfy( $NFYINET_CONNECT )
303 if !sock.urlconnect( url, host, path )
304 {
305 nfy( $NFYINET_ERROR )
306 return 0
307 }
308 http_cmd( request, "POST", host, path, "Content-Type: application/x-www-form-urlencoded
309 Content-Length: \(*data)\l\l\(data)", sock.isproxy( ))
310
311 if !nfy( $NFYINET_POST ) : goto end
312
313 if !sock.send( request ) : goto end
314
315 result->buf.clear()
316 ni.param = 0
317 do
318 {
319 i = *result->buf
320 if !nfy( $NFYINET_GET ) : goto end
321 sock.recv( result->buf )
322 dif = *result->buf - i
323 ni.param += dif
324 } while dif
325
326 result->buf += byte( 0 )
327
328 nfy( $NFYINET_END )
329 ret = 1
330 label end
331 sock.close( )
332 return ret
333 }
334
335 //Range: bytes=0-
336 //Referer:
337 //Agent: