Subversion Repositories Kolibri OS

Rev

Rev 4202 | Rev 4204 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4158 hidnplayr 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                                 ;;
3
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved.    ;;
4
;; Distributed under terms of the GNU General Public License       ;;
5
;;                                                                 ;;
6
;;  HTTP library for KolibriOS                                     ;;
7
;;                                                                 ;;
8
;;   Written by hidnplayr@kolibrios.org                            ;;
9
;;                                                                 ;;
10
;;         GNU GENERAL PUBLIC LICENSE                              ;;
11
;;          Version 2, June 1991                                   ;;
12
;;                                                                 ;;
13
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14
 
15
; references:
16
; "HTTP made really easy", http://www.jmarshall.com/easy/http/
17
; "Hypertext Transfer Protocol -- HTTP/1.1", http://tools.ietf.org/html/rfc2616
18
 
19
 
20
        URLMAXLEN       = 65535
21
        BUFFERSIZE      = 4096
22
 
23
        __DEBUG__       = 1
24
        __DEBUG_LEVEL__ = 1
25
 
26
 
27
format MS COFF
28
 
29
public @EXPORT as 'EXPORTS'
30
 
31
include '../../../struct.inc'
32
include '../../../proc32.inc'
33
include '../../../macros.inc'
34
purge section,mov,add,sub
35
include '../../../debug-fdo.inc'
36
 
37
include '../../../network.inc'
38
include 'http.inc'
39
 
40
virtual at 0
41
        http_msg http_msg
42
end virtual
43
 
44
macro copy_till_zero {
45
  @@:
46
        lodsb
47
        test    al, al
48
        jz      @f
49
        stosb
50
        jmp     @r
51
  @@:
52
}
53
 
4167 hidnplayr 54
macro HTTP_init_buffer buffer, socketnum {
55
 
56
        mov     eax, buffer
57
        push    socketnum
58
        popd    [eax + http_msg.socket]
59
        lea     esi, [eax + http_msg.data]
60
        mov     [eax + http_msg.flags], 0
61
        mov     [eax + http_msg.write_ptr], esi
62
        mov     [eax + http_msg.buffer_length], BUFFERSIZE -  http_msg.data
63
        mov     [eax + http_msg.chunk_ptr], 0
64
 
65
        mov     [eax + http_msg.status], 0
66
        mov     [eax + http_msg.header_length], 0
67
        mov     [eax + http_msg.content_length], 0
4168 hidnplayr 68
        mov     [eax + http_msg.content_received], 0
4167 hidnplayr 69
}
70
 
4158 hidnplayr 71
section '.flat' code readable align 16
72
 
73
;;===========================================================================;;
74
lib_init: ;//////////////////////////////////////////////////////////////////;;
75
;;---------------------------------------------------------------------------;;
76
;? Library entry point (called after library load)                           ;;
77
;;---------------------------------------------------------------------------;;
78
;> eax = pointer to memory allocation routine                                ;;
79
;> ebx = pointer to memory freeing routine                                   ;;
80
;> ecx = pointer to memory reallocation routine                              ;;
81
;> edx = pointer to library loading routine                                  ;;
82
;;---------------------------------------------------------------------------;;
83
;< eax = 1 (fail) / 0 (ok) (library initialization result)                   ;;
84
;;===========================================================================;;
85
        mov     [mem.alloc], eax
86
        mov     [mem.free], ebx
87
        mov     [mem.realloc], ecx
88
        mov     [dll.load], edx
89
 
90
        invoke  dll.load, @IMPORT
91
        or      eax, eax
92
        jz      .ok
93
 
94
; load proxy settings
95
        invoke  ini.get_str, inifile, sec_proxy, key_proxy, proxyAddr, 256, proxyAddr
96
        invoke  ini.get_int, inifile, sec_proxy, key_proxyport, 80
97
        mov     [proxyPort], eax
98
        invoke  ini.get_str, inifile, sec_proxy, key_user, proxyUser, 256, proxyUser
99
        invoke  ini.get_str, inifile, sec_proxy, key_password, proxyPassword, 256, proxyPassword
100
 
101
        xor     eax, eax
102
        inc     eax
103
        ret
104
 
105
  .ok:
106
        xor     eax, eax
107
        ret
108
 
109
 
110
 
111
 
112
 
113
;;================================================================================================;;
114
proc HTTP_get URL ;///////////////////////////////////////////////////////////////////////////////;;
115
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 116
;? Initiates a HTTP connection, using 'GET' method.                                               ;;
4158 hidnplayr 117
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 118
;> URL = pointer to ASCIIZ URL                                                                    ;;
4158 hidnplayr 119
;;------------------------------------------------------------------------------------------------;;
120
;< eax = 0 (error) / buffer ptr                                                                   ;;
121
;;================================================================================================;;
122
locals
123
        hostname        dd ?
124
        pageaddr        dd ?
125
        sockaddr        dd ?
126
        socketnum       dd ?
127
        buffer          dd ?
4167 hidnplayr 128
        port            dd ?
4158 hidnplayr 129
endl
130
 
131
; split the URL into hostname and pageaddr
132
        stdcall parse_url, [URL]
133
        test    eax, eax
134
        jz      .error
135
        mov     [hostname], eax
136
        mov     [pageaddr], ebx
137
 
138
; Do we need to use a proxy?
139
        cmp     [proxyAddr], 0
140
        jne     .proxy_done
141
 
142
  .proxy_done:
143
 
4167 hidnplayr 144
;;;;
145
        mov     [port], 80      ;;;; FIXME
146
 
147
; Connect to the other side.
148
        stdcall open_connection, [hostname], [port]
4158 hidnplayr 149
        test    eax, eax
4167 hidnplayr 150
        jz      .error
151
        mov     [socketnum], eax
4158 hidnplayr 152
 
4167 hidnplayr 153
; Create the HTTP request.
154
        invoke  mem.alloc, BUFFERSIZE
4158 hidnplayr 155
        test    eax, eax
156
        jz      .error
4167 hidnplayr 157
        mov     [buffer], eax
158
        mov     edi, eax
159
        DEBUGF  1, "Buffer has been allocated.\n"
4158 hidnplayr 160
 
4167 hidnplayr 161
        mov     esi, str_get
162
        copy_till_zero
4158 hidnplayr 163
 
4167 hidnplayr 164
        mov     esi, [pageaddr]
165
        copy_till_zero
4158 hidnplayr 166
 
4167 hidnplayr 167
        mov     esi, str_http11
168
        mov     ecx, str_http11.length
169
        rep     movsb
170
 
171
        mov     esi, [hostname]
172
        copy_till_zero
173
 
174
        mov     esi, str_close
175
        mov     ecx, str_close.length
176
        rep     movsb
177
 
178
        mov     byte[edi], 0
179
        DEBUGF  1, "Request:\n%s", [buffer]
180
 
181
; Send the request
182
        mov     esi, edi
183
        sub     esi, [buffer]   ; length
184
        xor     edi, edi        ; flags
185
 
186
        mcall   send, [socketnum], [buffer]
4158 hidnplayr 187
        test    eax, eax
188
        jz      .error
4167 hidnplayr 189
        DEBUGF  1, "Request has been sent to server.\n"
4158 hidnplayr 190
 
4167 hidnplayr 191
        HTTP_init_buffer [buffer], [socketnum]
192
 
193
        ret                     ; return buffer ptr
194
 
195
  .error:
196
        DEBUGF  1, "Error!\n"
197
        xor     eax, eax        ; return 0 = error
198
        ret
199
 
200
endp
201
 
202
 
203
 
204
;;================================================================================================;;
4202 hidnplayr 205
proc HTTP_head URL ;//////////////////////////////////////////////////////////////////////////////;;
4167 hidnplayr 206
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 207
;? Initiates a HTTP connection, using 'HEAD' method.                                              ;;
4167 hidnplayr 208
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 209
;> URL = pointer to ASCIIZ URL                                                                    ;;
4167 hidnplayr 210
;;------------------------------------------------------------------------------------------------;;
211
;< eax = 0 (error) / buffer ptr                                                                   ;;
212
;;================================================================================================;;
213
locals
214
        hostname        dd ?
215
        pageaddr        dd ?
216
        sockaddr        dd ?
217
        socketnum       dd ?
218
        buffer          dd ?
219
        port            dd ?
220
endl
221
 
222
; split the URL into hostname and pageaddr
223
        stdcall parse_url, [URL]
4158 hidnplayr 224
        test    eax, eax
4167 hidnplayr 225
        jz      .error
226
        mov     [hostname], eax
227
        mov     [pageaddr], ebx
4158 hidnplayr 228
 
4167 hidnplayr 229
; Do we need to use a proxy?
230
        cmp     [proxyAddr], 0
231
        jne     .proxy_done
4158 hidnplayr 232
 
4167 hidnplayr 233
        ; TODO: set hostname to that of the
234
  .proxy_done:
235
 
236
;;;;
237
        mov     [port], 80      ;;;; FIXME
238
 
239
; Connect to the other side.
240
        stdcall open_connection, [hostname], [port]
241
        test    eax, eax
242
        jz      .error
243
        mov     [socketnum], eax
244
 
4158 hidnplayr 245
; Create the HTTP request.
246
        invoke  mem.alloc, BUFFERSIZE
247
        test    eax, eax
248
        jz      .error
249
        mov     [buffer], eax
4167 hidnplayr 250
        mov     edi, eax
4158 hidnplayr 251
        DEBUGF  1, "Buffer has been allocated.\n"
252
 
4167 hidnplayr 253
        mov     esi, str_head
4158 hidnplayr 254
        copy_till_zero
255
 
4167 hidnplayr 256
        mov     esi, [pageaddr]
257
        copy_till_zero
258
 
4158 hidnplayr 259
        mov     esi, str_http11
260
        mov     ecx, str_http11.length
261
        rep     movsb
262
 
263
        mov     esi, [hostname]
264
        copy_till_zero
265
 
266
        mov     esi, str_close
267
        mov     ecx, str_close.length
268
        rep     movsb
269
 
270
        mov     byte[edi], 0
271
        DEBUGF  1, "Request:\n%s", [buffer]
272
 
4167 hidnplayr 273
; Send the request
4158 hidnplayr 274
        mov     esi, edi
275
        sub     esi, [buffer]   ; length
276
        xor     edi, edi        ; flags
277
 
278
        mcall   send, [socketnum], [buffer]
279
        test    eax, eax
280
        jz      .error
281
        DEBUGF  1, "Request has been sent to server.\n"
282
 
4167 hidnplayr 283
        HTTP_init_buffer [buffer], [socketnum]
4158 hidnplayr 284
 
4167 hidnplayr 285
        ret                     ; return buffer ptr
4158 hidnplayr 286
 
4167 hidnplayr 287
  .error:
288
        DEBUGF  1, "Error!\n"
289
        xor     eax, eax        ; return 0 = error
290
        ret
291
 
292
endp
293
 
294
 
295
;;================================================================================================;;
4168 hidnplayr 296
proc HTTP_post URL, content_type, content_length ;////////////////////////////////////////////////;;
4167 hidnplayr 297
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 298
;? Initiates a HTTP connection, using 'GET' method.                                               ;;
4167 hidnplayr 299
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 300
;> URL                  = pointer to ASCIIZ URL                                                   ;;
301
;> content_type         = pointer to ASCIIZ string containing content type                        ;;
302
;> content_length       = length of content (in bytes)                                            ;;
4167 hidnplayr 303
;;------------------------------------------------------------------------------------------------;;
304
;< eax = 0 (error) / buffer ptr                                                                   ;;
305
;;================================================================================================;;
306
locals
307
        hostname        dd ?
308
        pageaddr        dd ?
309
        sockaddr        dd ?
310
        socketnum       dd ?
311
        buffer          dd ?
312
        port            dd ?
313
endl
314
 
315
; split the URL into hostname and pageaddr
316
        stdcall parse_url, [URL]
317
        test    eax, eax
318
        jz      .error
319
        mov     [hostname], eax
320
        mov     [pageaddr], ebx
321
 
322
; Do we need to use a proxy?
323
        cmp     [proxyAddr], 0
324
        jne     .proxy_done
325
 
326
        ; TODO: set hostname to that of the
327
  .proxy_done:
328
 
329
;;;;
330
        mov     [port], 80      ;;;; FIXME
331
 
332
; Connect to the other side.
333
        stdcall open_connection, [hostname], [port]
334
        test    eax, eax
335
        jz      .error
336
        mov     [socketnum], eax
337
 
338
; Create the HTTP request.
339
        invoke  mem.alloc, BUFFERSIZE
340
        test    eax, eax
341
        jz      .error
342
        mov     [buffer], eax
343
        mov     edi, eax
344
        DEBUGF  1, "Buffer has been allocated.\n"
345
 
346
        mov     esi, str_post
347
        copy_till_zero
348
 
349
        mov     esi, [pageaddr]
350
        copy_till_zero
351
 
352
        mov     esi, str_http11
353
        mov     ecx, str_http11.length
354
        rep     movsb
355
 
356
        mov     esi, [hostname]
357
        copy_till_zero
358
 
359
        mov     esi, str_post_cl
360
        mov     ecx, str_post_cl.length
361
        rep     movsb
362
 
363
        mov     eax, [content_length]
364
        call    ascii_dec
365
 
366
        mov     esi, str_post_ct
367
        mov     ecx, str_post_ct.length
368
        rep     movsb
369
 
370
        mov     esi, [content_type]
371
        rep     movsb
372
 
373
        mov     esi, str_close
374
        mov     ecx, str_close.length
375
        rep     movsb
376
 
377
        mov     byte[edi], 0
378
        DEBUGF  1, "Request:\n%s", [buffer]
379
 
380
; Send the request
381
        mov     esi, edi
382
        sub     esi, [buffer]   ; length
383
        xor     edi, edi        ; flags
384
        mcall   send, [socketnum], [buffer]
385
        test    eax, eax
386
        jz      .error
387
        DEBUGF  1, "Request has been sent to server.\n"
388
 
389
        HTTP_init_buffer [buffer], [socketnum]
390
;        mov     eax, [buffer]
391
 
4158 hidnplayr 392
        ret                     ; return buffer ptr
393
 
394
  .error:
395
        DEBUGF  1, "Error!\n"
396
        xor     eax, eax        ; return 0 = error
397
        ret
398
 
399
endp
400
 
401
 
402
 
403
;;================================================================================================;;
404
proc HTTP_process identifier ;////////////////////////////////////////////////////////////////////;;
405
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 406
;? Receive data from the server, parse headers and put data in receive buffer.                    ;;
407
;? To complete a transfer, this procedure must be called over and over again untill it returns 0. ;;
4158 hidnplayr 408
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 409
;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
4158 hidnplayr 410
;;------------------------------------------------------------------------------------------------;;
411
;< eax = -1 (not finished) / 0 finished                                                           ;;
412
;;================================================================================================;;
413
        pusha
414
        mov     ebp, [identifier]
4162 hidnplayr 415
 
416
; Receive some data
4158 hidnplayr 417
        mcall   recv, [ebp + http_msg.socket], [ebp + http_msg.write_ptr], \
418
                      [ebp + http_msg.buffer_length], MSG_DONTWAIT
419
        cmp     eax, 0xffffffff
420
        je      .check_socket
421
        DEBUGF  1, "Received %u bytes\n", eax
422
 
4162 hidnplayr 423
; Update pointers
4158 hidnplayr 424
        mov     edi, [ebp + http_msg.write_ptr]
425
        add     [ebp + http_msg.write_ptr], eax
426
        sub     [ebp + http_msg.buffer_length], eax
427
        jz      .got_all_data
4162 hidnplayr 428
 
429
; If data is chunked, combine chunks into contiguous data.
430
        test    [ebp + http_msg.flags], FLAG_CHUNKED
431
        jnz     .chunk_loop
432
 
433
; Did we detect the header yet?
4158 hidnplayr 434
        test    [ebp + http_msg.flags], FLAG_GOT_HEADER
435
        jnz     .header_parsed
436
 
4203 hidnplayr 437
        push    eax
438
 
4162 hidnplayr 439
; We havent found the header yet, search for it..
4158 hidnplayr 440
        sub     eax, 4
4203 hidnplayr 441
        jl      .need_more_data_pop
4158 hidnplayr 442
  .scan:
443
        ; scan for end of header (empty line)
444
        cmp     dword[edi], 0x0a0d0a0d                  ; end of header
445
        je      .end_of_header
446
        cmp     word[edi+2], 0x0a0a
447
        je      .end_of_header
448
        inc     edi
449
        dec     eax
450
        jnz     .scan
4203 hidnplayr 451
        jmp     .need_more_data_pop
4158 hidnplayr 452
 
453
  .end_of_header:
454
        add     edi, 4 - http_msg.data
455
        sub     edi, ebp
456
        mov     [ebp + http_msg.header_length], edi
457
        or      [ebp + http_msg.flags], FLAG_GOT_HEADER
458
        DEBUGF  1, "Header length: %u\n", edi
459
 
460
; Ok, we have found header:
461
        cmp     dword[ebp + http_msg.data], 'HTTP'
462
        jne     .invalid_header
463
        cmp     dword[ebp + http_msg.data+4], '/1.0'
464
        je      .http_1.0
465
        cmp     dword[ebp + http_msg.data+4], '/1.1'
466
        jne     .invalid_header
467
        or      [ebp + http_msg.flags], FLAG_HTTP11
468
  .http_1.0:
469
        cmp     byte[ebp + http_msg.data+8], ' '
470
        jne     .invalid_header
471
        DEBUGF  1, "Header seems valid.\n"
472
 
473
        lea     esi, [ebp + http_msg.data+9]
474
        xor     eax, eax
475
        xor     ebx, ebx
476
        mov     ecx, 3
477
  .statusloop:
478
        lodsb
479
        sub     al, '0'
480
        jb      .invalid_header
481
        cmp     al, 9
482
        ja      .invalid_header
483
        lea     ebx, [ebx + 4*ebx]
484
        shl     ebx, 1
485
        add     ebx, eax
486
        dec     ecx
487
        jnz     .statusloop
488
        mov     [ebp + http_msg.status], ebx
489
        DEBUGF  1, "Status: %u\n", ebx
490
 
491
; Now, convert all header names to lowercase.
492
; This way, it will be much easier to find certain header fields, later on.
493
 
494
        lea     esi, [ebp + http_msg.data]
495
        mov     ecx, [ebp + http_msg.header_length]
496
  .need_newline:
497
        inc     esi
498
        dec     ecx
499
        jz      .convert_done
500
        cmp     byte[esi], 10
501
        jne     .need_newline
502
; Ok, we have a newline, a line beginning with space or tabs has no header fields.
503
 
504
        inc     esi
505
        dec     ecx
506
        jz      .convert_done
507
        cmp     byte[esi], ' '
508
        je      .need_newline
509
        cmp     byte[esi], 9    ; horizontal tab
510
        je      .need_newline
511
        jmp     .convert_loop
512
  .next_char:
513
        inc     esi
514
        dec     ecx
515
        jz      .convert_done
516
  .convert_loop:
517
        cmp     byte[esi], ':'
518
        je      .need_newline
519
        cmp     byte[esi], 'A'
520
        jb      .next_char
521
        cmp     byte[esi], 'Z'
522
        ja      .next_char
523
        or      byte[esi], 0x20 ; convert to lowercase
524
        jmp     .next_char
525
  .convert_done:
526
        mov     byte[esi-1], 0
527
        lea     esi, [ebp + http_msg.data]
528
        DEBUGF  1, "Header names converted to lowercase:\n%s\n", esi
529
 
530
; Check for content-length header field.
531
        stdcall find_header_field, ebp, str_cl
532
        test    eax, eax
533
        jz      .no_content
534
        or      [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
535
 
536
        xor     edx, edx
537
  .cl_loop:
538
        movzx   ebx, byte[eax]
539
        inc     eax
540
        cmp     bl, 10
541
        je      .cl_ok
542
        cmp     bl, 13
543
        je      .cl_ok
544
        cmp     bl, ' '
545
        je      .cl_ok
546
        sub     bl, '0'
547
        jb      .invalid_header
548
        cmp     bl, 9
549
        ja      .invalid_header
550
        lea     edx, [edx + edx*4]      ; edx = edx*10
551
        shl     edx, 1                  ;
552
        add     edx, ebx
553
        jmp     .cl_loop
554
 
555
  .cl_ok:
556
        mov     [ebp + http_msg.content_length], edx
557
        DEBUGF  1, "Content-length: %u\n", edx
558
 
559
; Resize buffer according to content-length.
4168 hidnplayr 560
        add     edx, [ebp + http_msg.header_length]
561
        add     edx, http_msg.data
4158 hidnplayr 562
 
4168 hidnplayr 563
        mov     ecx, edx
4158 hidnplayr 564
        sub     ecx, [ebp + http_msg.write_ptr]
565
        mov     [ebp + http_msg.buffer_length], ecx
566
 
4168 hidnplayr 567
        invoke  mem.realloc, ebp, edx
4158 hidnplayr 568
        or      eax, eax
4203 hidnplayr 569
        jz      .no_ram_pop
570
 
571
        pop     eax
4168 hidnplayr 572
        sub     eax, [ebp + http_msg.header_length]
4158 hidnplayr 573
        jmp     .header_parsed  ; hooray!
574
 
575
  .no_content:
576
        DEBUGF  1, "Content-length not found.\n"
577
 
578
; We didnt find 'content-length', maybe server is using chunked transfer encoding?
579
; Try to find 'transfer-encoding' header.
580
        stdcall find_header_field, ebp, str_te
581
        test    eax, eax
582
        jz      .invalid_header
583
 
584
        mov     ebx, dword[eax]
4162 hidnplayr 585
        or      ebx, 0x20202020
4158 hidnplayr 586
        cmp     ebx, 'chun'
587
        jne     .invalid_header
588
        mov     ebx, dword[eax+4]
4162 hidnplayr 589
        or      ebx, 0x00202020
590
        and     ebx, 0x00ffffff
4158 hidnplayr 591
        cmp     ebx, 'ked'
592
        jne     .invalid_header
593
 
594
        or      [ebp + http_msg.flags], FLAG_CHUNKED
595
        DEBUGF  1, "Transfer type is: chunked\n"
596
 
4203 hidnplayr 597
        pop     eax
598
 
4158 hidnplayr 599
; Set chunk pointer where first chunk should begin.
4162 hidnplayr 600
        lea     eax, [ebp + http_msg.data]
601
        add     eax, [ebp + http_msg.header_length]
4158 hidnplayr 602
        mov     [ebp + http_msg.chunk_ptr], eax
603
 
4162 hidnplayr 604
  .chunk_loop:
4158 hidnplayr 605
        mov     ecx, [ebp + http_msg.write_ptr]
606
        sub     ecx, [ebp + http_msg.chunk_ptr]
4202 hidnplayr 607
        jb      .need_more_data_chunked         ; TODO: use this ecx !!!
4158 hidnplayr 608
 
4202 hidnplayr 609
; Chunkline starts here, convert the ASCII hex number into ebx
4158 hidnplayr 610
        mov     esi, [ebp + http_msg.chunk_ptr]
611
        xor     ebx, ebx
612
  .chunk_hexloop:
613
        lodsb
614
        sub     al, '0'
615
        jb      .chunk_
616
        cmp     al, 9
617
        jbe     .chunk_hex
4162 hidnplayr 618
        sub     al, 'A' - '0' - 10
4158 hidnplayr 619
        jb      .chunk_
4162 hidnplayr 620
        cmp     al, 15
4158 hidnplayr 621
        jbe     .chunk_hex
622
        sub     al, 'a' - 'A'
4162 hidnplayr 623
        cmp     al, 15
4158 hidnplayr 624
        ja      .chunk_
625
  .chunk_hex:
626
        shl     ebx, 4
627
        add     bl, al
628
        jmp     .chunk_hexloop
629
  .chunk_:
630
        DEBUGF  1, "got chunk of %u bytes\n", ebx
4202 hidnplayr 631
;;        cmp     esi, [ebp + http_msg.chunk_ptr]
632
;;        je
4158 hidnplayr 633
; If chunk size is 0, all chunks have been received.
634
        test    ebx, ebx
4162 hidnplayr 635
        jz      .got_all_data_chunked           ; last chunk, hooray! FIXME: what if it wasnt a valid hex number???
4158 hidnplayr 636
 
637
; Chunkline ends with a CR, LF or simply LF
4202 hidnplayr 638
  .end_of_chunkline?:
4158 hidnplayr 639
        cmp     al, 10
640
        je      .end_of_chunkline
641
        lodsb
4202 hidnplayr 642
        cmp     edi, [ebp + http_msg.write_ptr]
643
        jb      .end_of_chunkline?
644
        jmp     .need_more_data
4158 hidnplayr 645
 
646
  .end_of_chunkline:
4202 hidnplayr 647
; Update chunk ptr, and remember old one
648
        mov     edi, [ebp + http_msg.chunk_ptr]
649
        add     [ebp + http_msg.chunk_ptr], ebx
4162 hidnplayr 650
; Realloc buffer, make it 'chunksize' bigger.
651
        mov     eax, [ebp + http_msg.buffer_length]
652
        add     eax, ebx
653
        invoke  mem.realloc, ebp, eax
654
        or      eax, eax
655
        jz      .no_ram
656
        add     [ebp + http_msg.buffer_length], ebx
657
 
658
; Update write ptr
659
        mov     eax, esi
660
        sub     eax, edi
661
        sub     [ebp + http_msg.write_ptr], eax
662
 
4158 hidnplayr 663
; Now move all received data to the left (remove chunk header).
4162 hidnplayr 664
; Update content_length accordingly.
4158 hidnplayr 665
        mov     ecx, [ebp + http_msg.write_ptr]
666
        sub     ecx, esi
4168 hidnplayr 667
        add     [ebp + http_msg.content_received], ecx
4158 hidnplayr 668
        rep     movsb
4162 hidnplayr 669
        jmp     .chunk_loop
4158 hidnplayr 670
 
671
; Check if we got all the data.
4162 hidnplayr 672
  .header_parsed:
4168 hidnplayr 673
        add     [ebp + http_msg.content_received], eax
674
        mov     eax, [ebp + http_msg.content_length]
675
        cmp     eax, [ebp + http_msg.content_received]
676
        jae     .got_all_data
4203 hidnplayr 677
        jmp     .need_more_data
678
  .need_more_data_pop:
679
        pop     eax
4162 hidnplayr 680
  .need_more_data:
681
        popa
682
        xor     eax, eax
683
        dec     eax
684
        ret
4158 hidnplayr 685
 
4162 hidnplayr 686
  .need_more_data_chunked:
4168 hidnplayr 687
        add     [ebp + http_msg.content_received], eax
4158 hidnplayr 688
        popa
689
        xor     eax, eax
690
        dec     eax
691
        ret
692
 
4162 hidnplayr 693
  .got_all_data_chunked:
694
        mov     eax, [ebp + http_msg.chunk_ptr]
695
        sub     eax, [ebp + http_msg.header_length]
696
        sub     eax, http_msg.data
697
        sub     eax, ebp
698
        mov     [ebp + http_msg.content_length], eax
4168 hidnplayr 699
        mov     [ebp + http_msg.content_received], eax
4158 hidnplayr 700
  .got_all_data:
4162 hidnplayr 701
        DEBUGF  1, "We got all the data! (%u bytes)\n", [ebp + http_msg.content_length]
4158 hidnplayr 702
        or      [ebp + http_msg.flags], FLAG_GOT_DATA
703
        mcall   close, [ebp + http_msg.socket]
704
        popa
705
        xor     eax, eax
706
        ret
707
 
708
  .check_socket:
709
        cmp     ebx, EWOULDBLOCK
4162 hidnplayr 710
        je      .need_more_data
4158 hidnplayr 711
        DEBUGF  1, "ERROR: socket error %u\n", ebx
712
 
713
        or      [ebp + http_msg.flags], FLAG_SOCKET_ERROR
714
        popa
715
        xor     eax, eax
716
        ret
717
 
718
  .invalid_header:
4203 hidnplayr 719
        pop     eax
4158 hidnplayr 720
        DEBUGF  1, "ERROR: invalid header\n"
721
        or      [ebp + http_msg.flags], FLAG_INVALID_HEADER
722
        popa
723
        xor     eax, eax
724
        ret
725
 
4203 hidnplayr 726
  .no_ram_pop:
727
        pop     eax
4158 hidnplayr 728
  .no_ram:
729
        DEBUGF  1, "ERROR: out of RAM\n"
730
        or      [ebp + http_msg.flags], FLAG_NO_RAM
731
        popa
732
        xor     eax, eax
733
        ret
734
 
735
endp
736
 
737
 
738
 
739
;;================================================================================================;;
740
proc find_header_field identifier, headername ;///////////////////////////////////////////////////;;
741
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 742
;? Find a header field in the received HTTP header                                                ;;
4158 hidnplayr 743
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 744
;> identifier   = ptr to http_msg struct                                                          ;;
745
;> headername   = ptr to ASCIIZ string containg field you want to find (must be in lowercase)     ;;
4158 hidnplayr 746
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 747
;< eax = 0 (error) / ptr to content of the HTTP header field                                      ;;
4158 hidnplayr 748
;;================================================================================================;;
749
        push    ebx ecx edx esi edi
750
 
751
        DEBUGF  1, "Find header field: %s\n", [headername]
752
 
753
        mov     ebx, [identifier]
4202 hidnplayr 754
        test    [ebx + http_msg.flags], FLAG_GOT_HEADER
755
        jz      .fail
756
 
4158 hidnplayr 757
        lea     edx, [ebx + http_msg.data]
758
        mov     ecx, edx
759
        add     ecx, [ebx + http_msg.header_length]
760
 
761
  .restart:
762
        mov     esi, [headername]
763
        mov     edi, edx
764
  .loop:
765
        cmp     edi, ecx
766
        jae     .fail
767
        lodsb
768
        scasb
769
        je      .loop
770
        test    al, al
771
        jz      .done?
772
  .next:
773
        inc     edx
774
        jmp     .restart
775
 
776
  .not_done:
777
        inc     edi
778
  .done?:
779
        cmp     byte[edi-1], ':'
780
        je      .almost_done
781
        cmp     byte[edi-1], ' '
782
        je      .not_done
783
        cmp     byte[edi-1], 9  ; tab
784
        je      .not_done
785
 
786
        jmp     .next
787
 
788
  .almost_done:                 ; FIXME: buffer overflow?
789
        dec     edi
790
        DEBUGF  1, "Found header field\n"
791
  .spaceloop:
792
        inc     edi
793
        cmp     byte[edi], ' '
794
        je      .spaceloop
795
        cmp     byte[edi], 9    ; tab
796
        je      .spaceloop
797
 
798
        mov     eax, edi
799
        pop     edi esi edx ecx ebx
800
        ret
801
 
802
  .fail:
803
        pop     edi esi edx ecx ebx
804
        xor     eax, eax
805
        ret
806
 
807
endp
808
 
809
 
810
 
4202 hidnplayr 811
 
4167 hidnplayr 812
;;================================================================================================;;
4202 hidnplayr 813
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
814
;;================================================================================================;;
815
;! Internal procedures section                                                                    ;;
816
;;================================================================================================;;
817
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
818
;;================================================================================================;;
819
 
820
 
821
 
822
 
823
;;================================================================================================;;
4167 hidnplayr 824
proc open_connection hostname, port ;/////////////////////////////////////////////////////////////;;
825
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 826
;? Connects to a HTTP server                                                                      ;;
4167 hidnplayr 827
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 828
;> hostname     = ptr to ASCIIZ hostname                                                          ;;
829
;> port         = port (x86 byte order)                                                           ;;
4167 hidnplayr 830
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 831
;< eax = 0 (error) / socketnum                                                                    ;;
4167 hidnplayr 832
;;================================================================================================;;
4158 hidnplayr 833
 
4167 hidnplayr 834
locals
835
        sockaddr        dd ?
836
        socketnum       dd ?
837
endl
838
 
839
; Resolve the hostname
840
        DEBUGF  1, "Resolving hostname\n"
841
        push    esp     ; reserve stack place
842
        push    esp     ; fourth parameter
843
        push    0       ; third parameter
844
        push    0       ; second parameter
845
        push    [hostname]
846
        call    [getaddrinfo]
847
        pop     esi
848
        test    eax, eax
849
        jnz     .error1
850
 
851
; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
852
        mov     esi, [esi + addrinfo.ai_addr]
853
        mov     [sockaddr], esi
854
        mov     eax, [esi + sockaddr_in.sin_addr]
855
        test    eax, eax
856
        jz      .error2
857
 
858
        DEBUGF  1, "Server ip=%u.%u.%u.%u\n", \
859
        [esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
860
        [esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
861
 
862
        mov     [esi + sockaddr_in.sin_family], AF_INET4
863
        mov     eax, [port]
864
        xchg    al, ah
865
        mov     [esi + sockaddr_in.sin_port], ax
866
 
867
; Connect to the server.
868
        mcall   socket, AF_INET4, SOCK_STREAM, 0
869
        test    eax, eax
870
        jz      .error2
871
        mov     [socketnum], eax
872
        DEBUGF  1, "Socket: 0x%x\n", eax
873
 
874
        mcall   connect, [socketnum], [sockaddr], 18
875
        test    eax, eax
876
        jnz     .error2
877
        DEBUGF  1, "Socket is now connected.\n"
878
 
879
; free allocated memory
880
        push    [sockaddr]
881
        call    [freeaddrinfo]
882
 
883
        mov     eax, [socketnum]
884
        ret
885
 
886
  .error2:
887
 
888
; free allocated memory
889
        push    [sockaddr]
890
        call    [freeaddrinfo]
891
 
892
  .error1:
893
        xor     eax, eax
894
        ret
895
 
896
endp
897
 
898
 
4158 hidnplayr 899
;;================================================================================================;;
900
proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
901
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 902
;? Split a given URL into hostname and pageaddr                                                   ;;
4158 hidnplayr 903
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 904
;> URL = ptr to ASCIIZ URL                                                                        ;;
4158 hidnplayr 905
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 906
;< eax = 0 (error) / ptr to ASCIIZ hostname                                                       ;;
907
;< ebx = ptr to ASCIIZ pageaddr                                                                   ;;
4158 hidnplayr 908
;;================================================================================================;;
909
 
910
locals
911
        urlsize         dd ?
912
        hostname        dd ?
913
        pageaddr        dd ?
914
endl
915
 
4161 hidnplayr 916
        DEBUGF  1, "parsing URL: %s\n", [URL]
4158 hidnplayr 917
 
918
; remove any leading protocol text
919
        mov     esi, [URL]
920
        mov     ecx, URLMAXLEN
921
        mov     ax, '//'
922
  .loop1:
923
        cmp     byte[esi], 0            ; end of URL?
924
        je      .url_ok                 ; yep, so not found
925
        cmp     [esi], ax
926
        je      .skip_proto
927
        inc     esi
928
        dec     ecx
929
        jnz     .loop1
930
 
4161 hidnplayr 931
        DEBUGF  1, "Invalid URL\n"
4158 hidnplayr 932
        xor     eax, eax
933
        ret
934
 
935
  .skip_proto:
936
        inc     esi                     ; skip the two '/'
937
        inc     esi
938
        mov     [URL], esi              ; update pointer so it skips protocol
939
        jmp     .loop1                  ; we still need to find the length of the URL
940
 
941
  .url_ok:
942
        sub     esi, [URL]              ; calculate total length of URL
943
        mov     [urlsize], esi
944
 
945
 
946
; now look for page delimiter - it's a '/' character
947
        mov     ecx, esi                ; URL length
948
        mov     edi, [URL]
949
        mov     al, '/'
950
        repne   scasb
4161 hidnplayr 951
        jne     @f
4158 hidnplayr 952
        dec     edi                     ; return one char, '/' must be part of the pageaddr
953
        inc     ecx                     ;
4161 hidnplayr 954
  @@:
4158 hidnplayr 955
        push    ecx edi                 ; remember the pointer and length of pageaddr
956
 
957
        mov     ecx, edi
958
        sub     ecx, [URL]
959
        inc     ecx                     ; we will add a 0 byte at the end
960
        invoke  mem.alloc, ecx
961
        or      eax, eax
962
        jz      .no_mem
963
 
964
        mov     [hostname], eax         ; copy hostname to buffer
965
        mov     edi, eax
966
        mov     esi, [URL]
967
        dec     ecx
968
        rep     movsb
969
        xor     al, al
970
        stosb
971
 
4161 hidnplayr 972
        mov     [pageaddr], str_slash   ; assume there is no pageaddr
4158 hidnplayr 973
        pop     esi ecx
974
        test    ecx, ecx
975
        jz      .no_page
976
        inc     ecx                     ; we will add a 0 byte at the end
977
        invoke  mem.alloc, ecx
978
        or      eax, eax
979
        jz      .no_mem
980
 
981
        mov     [pageaddr], eax         ; copy pageaddr to buffer
982
        mov     edi, eax
983
        dec     ecx
984
        rep     movsb
985
        xor     al, al
986
        stosb
987
  .no_page:
988
 
989
        mov     eax, [hostname]
990
        mov     ebx, [pageaddr]
991
 
992
        DEBUGF  1, "hostname: %s\n", eax
993
        DEBUGF  1, "pageaddr: %s\n", ebx
994
 
995
        ret
996
 
997
  .no_mem:
998
        xor     eax, eax
999
        ret
1000
 
1001
endp
1002
 
1003
 
4202 hidnplayr 1004
;;================================================================================================;;
1005
proc ascii_dec ;//////////////////////////////////////////////////////////////////////////////////;;
1006
;;------------------------------------------------------------------------------------------------;;
1007
;? Convert eax to ASCII decimal number                                                            ;;
1008
;;------------------------------------------------------------------------------------------------;;
1009
;> eax = number                                                                                   ;;
1010
;> edi = ptr where to write ASCII decimal number                                                  ;;
1011
;;------------------------------------------------------------------------------------------------;;
1012
;< /                                                                                              ;;
1013
;;================================================================================================;;
4158 hidnplayr 1014
 
4168 hidnplayr 1015
        push    -'0'
4167 hidnplayr 1016
        mov     ecx, 10
1017
  .loop:
1018
        xor     edx, edx
1019
        div     ecx
1020
        add     dl, '0'
4168 hidnplayr 1021
        push    edx
4167 hidnplayr 1022
        test    eax, eax
1023
        jnz     .loop
4158 hidnplayr 1024
 
4168 hidnplayr 1025
  .loop2:
1026
        pop     eax
1027
        add     al, '0'
1028
        jz      .done
1029
        stosb
1030
        jmp     .loop2
1031
  .done:
1032
 
4167 hidnplayr 1033
        ret
4158 hidnplayr 1034
 
4202 hidnplayr 1035
endp
4167 hidnplayr 1036
 
4202 hidnplayr 1037
 
4158 hidnplayr 1038
;;================================================================================================;;
1039
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
1040
;;================================================================================================;;
1041
;! Imported functions section                                                                     ;;
1042
;;================================================================================================;;
1043
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
1044
;;================================================================================================;;
1045
 
1046
 
1047
align 16
1048
@IMPORT:
1049
 
1050
library \
1051
        libini, 'libini.obj', \
1052
        network, 'network.obj'
1053
 
1054
import  libini, \
1055
        ini.get_str, 'ini_get_str', \
1056
        ini.get_int, 'ini_get_int'
1057
 
1058
import  network,\
1059
        getaddrinfo, 'getaddrinfo',\
1060
        freeaddrinfo,  'freeaddrinfo',\
1061
        inet_ntoa, 'inet_ntoa'
1062
 
1063
;;===========================================================================;;
1064
;;///////////////////////////////////////////////////////////////////////////;;
1065
;;===========================================================================;;
1066
;! Exported functions section                                                ;;
1067
;;===========================================================================;;
1068
;;///////////////////////////////////////////////////////////////////////////;;
1069
;;===========================================================================;;
1070
 
1071
 
1072
align 4
1073
@EXPORT:
1074
export  \
1075
        lib_init                , 'lib_init'            , \
1076
        0x00010001              , 'version'             , \
1077
        HTTP_get                , 'get'                 , \
4167 hidnplayr 1078
        HTTP_head               , 'head'                , \
1079
        HTTP_post               , 'post'                , \
4158 hidnplayr 1080
        find_header_field       , 'find_header_field'   , \
1081
        HTTP_process            , 'process'
1082
 
1083
;        HTTP_put                , 'put'                 , \
1084
;        HTTP_delete             , 'delete'              , \
1085
;        HTTP_trace              , 'trace'               , \
1086
;        HTTP_connect            , 'connect'             , \
1087
 
1088
 
1089
 
1090
section '.data' data readable writable align 16
1091
 
1092
inifile         db '/sys/settings/network.ini', 0
1093
 
1094
sec_proxy:
1095
key_proxy       db 'proxy', 0
1096
key_proxyport   db 'port', 0
1097
key_user        db 'user', 0
1098
key_password    db 'password', 0
1099
 
1100
str_http11      db ' HTTP/1.1', 13, 10, 'Host: '
1101
  .length       = $ - str_http11
4167 hidnplayr 1102
str_post_cl     db 13, 10, 'Content-Length: '
1103
  .length       = $ - str_post_cl
1104
str_post_ct     db 13, 10, 'Content-Type: '
1105
  .length       = $ - str_post_ct
4158 hidnplayr 1106
str_close       db 13, 10, 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
1107
  .length       = $ - str_close
1108
str_proxy_auth  db 13, 10, 'Proxy-Authorization: Basic '
1109
  .length       = $ - str_proxy_auth
1110
 
1111
base64_table    db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
1112
                db '0123456789+/'
1113
 
1114
str_cl          db 'content-length', 0
4161 hidnplayr 1115
str_slash       db '/', 0
4158 hidnplayr 1116
str_te          db 'transfer-encoding', 0
4167 hidnplayr 1117
str_get         db 'GET ', 0
1118
str_head        db 'HEAD ', 0
1119
str_post        db 'POST ', 0
4158 hidnplayr 1120
 
1121
include_debug_strings
1122
 
1123
; uninitialized data
1124
mem.alloc       dd ?
1125
mem.free        dd ?
1126
mem.realloc     dd ?
1127
dll.load        dd ?
1128
 
1129
proxyAddr       rb 256
1130
proxyUser       rb 256
1131
proxyPassword   rb 256
1132
proxyPort       dd ?