Subversion Repositories Kolibri OS

Rev

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