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 include : $"cookies.g"
34
35 func str http_cmd( str ret, str cmd host path more data, uint isproxy )
36 {
37 str scookies
38 if usecookies
39 {
40 scookies = mcookies.get( host, "/\(path)" )
41 if *scookies : scookies = "Cookie: \(scookies)\l"
42 }
43 ret = "\(cmd) \(?( isproxy, "http://\(host)/\(path)","/\(path)")) HTTP/1.0
44 User-Agent: \( inet_useragent )
45 Accept: */*
46 Host: \(host)
47 \(scookies)\(more)\l\(data)"
48 return ret
49 }
50
51 /*-----------------------------------------------------------------------------
52 * Id: http_get F
53 *
54 * Summary: Getting data via the HTTP protocol. The method sends a GET request
55 to the specified URL and writes data it receives to the databuf
56 buffer.
57 *
58 * Params: url - The URL address data is received from.
59 databuf - The buffer for getting data.
60 notify - The #a(inetnotify_func,function) for getting /
61 notifications. It can be 0.
62 flag - Flags. $$[httpflag]
63 *
64 * Return: #lng/retf#
65 *
66 -----------------------------------------------------------------------------*/
67
68 func uint http_get( str url, buf databuf, uint notify, uint flag, str otherpars )
69 {
70 uint ret i isfile data dif
71 str request host path range stemp
72 socket sock
73 httpinfo hi
74 inetnotify ni
75 buf fbuf
76 file fdwn
77 finfo fi
78
79 subfunc uint nfy( uint code )
80 {
81 if !notify : return 1
82
83 if !notify->func( code, ni )
84 {
85 ineterror = $ERRINET_USERBREAK
86 ret = 0
87 return 0
88 }
89 return 1
90 }
91 ineterror = 0
92 isfile = flag & $HTTPF_FILE
93 url.replacech( stemp = url, ' ', "%20" )
94 if !isfile : databuf.use = 0
95
96 ni.url = url
97
98 nfy( $NFYINET_CONNECT )
99 if !sock.urlconnect( url, host, path )
100 {
101 nfy( $NFYINET_ERROR )
102 return 0
103 }
104 if !nfy( $NFYINET_SEND ) : goto end
105 if isfile && flag & $HTTPF_CONTINUE
106 {
107 getfileinfo( databuf->str, fi )
108 if fi.sizelo : range = "Range: bytes=\(fi.sizelo)-\l"
109 }
110
111 str stmp = range
112 if &otherpars: stmp@otherpars
113 http_cmd( request, "GET", host, path, stmp, "", sock.isproxy() )
114
115 if !sock.send( request ) : goto end
116
117 data = ?( isfile, &fbuf, &databuf )
118 data as buf
119
120 if !sock.recv( data ) : goto end
121 str shead
122 if !"HTTP".eqlenign( data.ptr()) || ((shead=ni.head.ihead( data )) && !shead.ihttpinfo( hi ))
123 {
124 ineterror = $ERRINET_HTTPDATA
125 goto end
126 }
127 if usecookies : mcookies.parse( host, path, shead )
128 if &otherpars : otherpars = shead
129 ni.param = &hi
130 if !nfy( $NFYINET_HEAD ) : goto end
131
132 if flag & $HTTPF_REDIRECT && *hi.location
133 {
134 data.clear()
135 sock.close()
136
137 ni.sparam = hi.location
138 if !nfy( $NFYINET_REDIRECT ) : goto end
139
140 return http_get( hi.location, databuf, notify, flag, otherpars )
141 }
142 if isfile
143 {
144 data.expand( 0x20000 )
145 /*if !( fhandle = open( databuf->str, ?( flag & $HTTPF_CONTINUE,
146 $OP_ALWAYS, $OP_CREATE )))*/
147 if !( fdwn.open( databuf->str, ?( flag & $HTTPF_CONTINUE,
148 $OP_ALWAYS, $OP_CREATE ) ) )
149 {
150 ni.sparam = databuf->str
151 ineterror = $ERRINET_OPENFILE
152 nfy( $NFYINET_ERROR )
153 goto end
154 }
155 }
156 else
157 {
158 if uint( hi.size ) : data.expand( uint( hi.size ) + 0x7FFF )
159 }
160 if *range
161 {
162 fdwn.setpos( 0, $FILE_END )
163 ni.param = fi.sizelo + *data
164 }
165 else : ni.param = *data
166
167 do
168 {
169 i = *data
170 if !nfy( $NFYINET_GET ) : goto end
171 sock.recv( data )
172 dif = *data - i
173 ni.param += dif
174
175 if isfile && ( *data >= 0x7FFF || !dif )
176 {
177 if !( fdwn.write( data ))
178 {
179 ni.sparam = databuf->str
180 ineterror = $ERRINET_WRITEFILE
181 nfy( $NFYINET_ERROR )
182 goto end
183 }
184 data.use = 0
185 }
186 } while dif
187
188 nfy( $NFYINET_END )
189
190 ret = 1
191 label end
192 if flag & $HTTPF_STR : data += byte( 0 )
193 if fdwn.fopen
194 {
195 if flag & $HTTPF_SETTIME && hi.dt.day
196 {
197
198 filetime ft
199 datetimetoftime( hi.dt, ft, 0 )
200 fdwn.settime( ft )
201 }
202 fdwn.close()
203 }
204 if !ret && ineterror : nfy( $NFYINET_ERROR )
205
206 sock.close( )
207 return ret
208 }
209
210 func uint http_get( str url, buf databuf, uint notify, uint flag )
211 {
212 return http_get( url, databuf, notify, flag, 0->str )
213 }
214
215 /*-----------------------------------------------------------------------------
216 * Id: http_getfile F
217 *
218 * Summary: Downloading a file via the HTTP protocol. The method sends a GET
219 request to the specified URL and writes data it receives to
220 the specified file.
221 *
222 * Params: url - The URL address for downloading.
223 filename - The name of the file for writing.
224 notify - The #a(inetnotify_func,function) for getting /
225 notifications. It can be 0.
226 flag - Flags. $$[httpflag]
227 *
228 * Return: #lng/retf#
229 *
230 -----------------------------------------------------------------------------*/
231
232 func uint http_getfile( str url, str filename, uint notify, uint flag )
233 {
234 flag &= 0xFF0F
235 return http_get( url, filename->buf, notify, flag | $HTTPF_FILE )
236 }
237
238 /*-----------------------------------------------------------------------------
239 * Id: http_head F
240 *
241 * Summary: Getting a header via the HTTP protocol. The method sends a HEAD
242 request to the specified URL address and partially parses the
243 received data.
244 *
245 * Params: url - The URL address for getting the header.
246 head - The string for getting the text of the header.
247 hi - The variable of the #a( thttpinfo ) type for getting /
248 information about the header.
249 *
250 * Return: #lng/retf#
251 *
252 -----------------------------------------------------------------------------*/
253
254 func uint http_head( str url, str head, httpinfo hi )
255 {
256 uint ret
257 str request host path
258 socket sock
259
260 head.clear()
261 if !sock.urlconnect( url, host, path ) : return 0
262
263 http_cmd( request, "HEAD", host, path, "", "", sock.isproxy( ))
264
265 if !sock.send( request ) : goto end
266 if !sock.recv( head->buf ) : goto end
267 head->buf.del( 0, 1 )
268 head->buf += byte( 0 )
269 ret = 1
270 if usecookies : mcookies.parse( host, path, head )
271 if &hi : head.ihttpinfo( hi )
272 label end
273 sock.close( )
274 return ret
275 }
276
277 /*-----------------------------------------------------------------------------
278 ** Id: http_post F
279 *
280 * Summary: Sending data via the HTTP protocol. The method sends a POST
281 request with the specified string to the specified URL address.
282 It is used to fill out forms automatically.
283 *
284 * Params: url - The URL address where the data will be sent.
285 data - The string with the data being sent. Before the data is /
286 sent, request strings with parameters should be recoded /
287 with the help of the #a(str_iencoding) method.
288 result - The string for getting a response from the server.
289 notify - The #a(inetnotify_func, function ) for getting /
290 notifications. It can be 0.
291 *
292 * Return: #lng/retf#
293 *
294 -----------------------------------------------------------------------------*/
295
296 func uint http_post( str url, str data, str result, uint notify, str otherpars )
297 {
298 str host path request
299 socket sock
300 uint i dif ret
301 httpinfo hi
302 inetnotify ni
303
304 subfunc uint nfy( uint code )
305 {
306 if !notify : return 1
307
308 if !notify->func( code, ni )
309 {
310 ineterror = $ERRINET_USERBREAK
311 ret = 0
312 return 0
313 }
314 return 1
315 }
316 ineterror = 0
317 ni.url = url
318
319 nfy( $NFYINET_CONNECT )
320 if !sock.urlconnect( url, host, path )
321 {
322 nfy( $NFYINET_ERROR )
323 return 0
324 }
325
326 str stmp
327 if &otherpars: stmp = otherpars
328
329 stmp@"Content-Type: application/x-www-form-urlencoded
330 Content-Length: \(*data)\l"
331
332 http_cmd( request, "POST", host, path, stmp, data, sock.isproxy( ))
333
334 if !nfy( $NFYINET_POST ) : goto end
335
336 if !sock.send( request ) : goto end
337
338 result->buf.clear()
339 ni.param = 0
340 do
341 {
342 i = *result->buf
343 if !nfy( $NFYINET_GET ) : goto end
344 sock.recv( result->buf )
345 dif = *result->buf - i
346 ni.param += dif
347 } while dif
348
349 str shead
350 if !"HTTP".eqlenign( result->buf.ptr()) || ((shead=ni.head.ihead( result->buf )) && !shead.ihttpinfo( hi ))
351 {
352 ineterror = $ERRINET_HTTPDATA
353 goto end
354 }
355 if usecookies : mcookies.parse( host, path, shead )
356 if &otherpars : otherpars = shead
357
358 result->buf += byte( 0 )
359
360 nfy( $NFYINET_END )
361 ret = 1
362 label end
363 sock.close( )
364 return ret
365 }
366
367 func uint http_post( str url, str data, str result, uint notify )
368 {
369 return http_post( url, data, result, notify, "" )
370 }
371
372
373 /*func test<main>
374 {
375 inet_init()
376 cookies_init( "cookies.gt" )
377 http_getfile( "http://www.gentee.com/perfect-automation/downloads/pautomation-setup.zip", "pautomation-setup.zip", 0, $HTTPF_SETTIME )
378 cookies_deinit()
379 getch()
380 }
381 */
382
383