Subversion Repositories Kolibri OS

Rev

Rev 4204 | Rev 4206 | 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]
4205 hidnplayr 60
        mov     [eax + http_msg.flags], FLAG_CONNECTED
4167 hidnplayr 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
 
4205 hidnplayr 416
        test    [ebp + http_msg.flags], FLAG_CONNECTED
417
        jz      .connection_closed
418
 
4162 hidnplayr 419
; Receive some data
4158 hidnplayr 420
        mcall   recv, [ebp + http_msg.socket], [ebp + http_msg.write_ptr], \
421
                      [ebp + http_msg.buffer_length], MSG_DONTWAIT
422
        cmp     eax, 0xffffffff
423
        je      .check_socket
424
        DEBUGF  1, "Received %u bytes\n", eax
425
 
4162 hidnplayr 426
; Update pointers
4158 hidnplayr 427
        mov     edi, [ebp + http_msg.write_ptr]
428
        add     [ebp + http_msg.write_ptr], eax
429
        sub     [ebp + http_msg.buffer_length], eax
430
        jz      .got_all_data
4162 hidnplayr 431
 
432
; If data is chunked, combine chunks into contiguous data.
433
        test    [ebp + http_msg.flags], FLAG_CHUNKED
434
        jnz     .chunk_loop
435
 
436
; Did we detect the header yet?
4158 hidnplayr 437
        test    [ebp + http_msg.flags], FLAG_GOT_HEADER
438
        jnz     .header_parsed
439
 
4203 hidnplayr 440
        push    eax
441
 
4162 hidnplayr 442
; We havent found the header yet, search for it..
4158 hidnplayr 443
        sub     eax, 4
4203 hidnplayr 444
        jl      .need_more_data_pop
4204 hidnplayr 445
        inc     eax
4158 hidnplayr 446
  .scan:
447
        ; scan for end of header (empty line)
448
        cmp     dword[edi], 0x0a0d0a0d                  ; end of header
449
        je      .end_of_header
450
        cmp     word[edi+2], 0x0a0a
451
        je      .end_of_header
452
        inc     edi
453
        dec     eax
454
        jnz     .scan
4203 hidnplayr 455
        jmp     .need_more_data_pop
4158 hidnplayr 456
 
457
  .end_of_header:
458
        add     edi, 4 - http_msg.data
459
        sub     edi, ebp
460
        mov     [ebp + http_msg.header_length], edi
461
        or      [ebp + http_msg.flags], FLAG_GOT_HEADER
462
        DEBUGF  1, "Header length: %u\n", edi
463
 
464
; Ok, we have found header:
465
        cmp     dword[ebp + http_msg.data], 'HTTP'
466
        jne     .invalid_header
467
        cmp     dword[ebp + http_msg.data+4], '/1.0'
468
        je      .http_1.0
469
        cmp     dword[ebp + http_msg.data+4], '/1.1'
470
        jne     .invalid_header
471
        or      [ebp + http_msg.flags], FLAG_HTTP11
472
  .http_1.0:
473
        cmp     byte[ebp + http_msg.data+8], ' '
474
        jne     .invalid_header
475
        DEBUGF  1, "Header seems valid.\n"
476
 
477
        lea     esi, [ebp + http_msg.data+9]
478
        xor     eax, eax
479
        xor     ebx, ebx
480
        mov     ecx, 3
481
  .statusloop:
482
        lodsb
483
        sub     al, '0'
484
        jb      .invalid_header
485
        cmp     al, 9
486
        ja      .invalid_header
487
        lea     ebx, [ebx + 4*ebx]
488
        shl     ebx, 1
489
        add     ebx, eax
490
        dec     ecx
491
        jnz     .statusloop
492
        mov     [ebp + http_msg.status], ebx
493
        DEBUGF  1, "Status: %u\n", ebx
494
 
495
; Now, convert all header names to lowercase.
496
; This way, it will be much easier to find certain header fields, later on.
497
 
498
        lea     esi, [ebp + http_msg.data]
499
        mov     ecx, [ebp + http_msg.header_length]
500
  .need_newline:
501
        inc     esi
502
        dec     ecx
503
        jz      .convert_done
504
        cmp     byte[esi], 10
505
        jne     .need_newline
506
; Ok, we have a newline, a line beginning with space or tabs has no header fields.
507
 
508
        inc     esi
509
        dec     ecx
510
        jz      .convert_done
511
        cmp     byte[esi], ' '
512
        je      .need_newline
513
        cmp     byte[esi], 9    ; horizontal tab
514
        je      .need_newline
515
        jmp     .convert_loop
516
  .next_char:
517
        inc     esi
518
        dec     ecx
519
        jz      .convert_done
520
  .convert_loop:
521
        cmp     byte[esi], ':'
522
        je      .need_newline
523
        cmp     byte[esi], 'A'
524
        jb      .next_char
525
        cmp     byte[esi], 'Z'
526
        ja      .next_char
527
        or      byte[esi], 0x20 ; convert to lowercase
528
        jmp     .next_char
529
  .convert_done:
530
        mov     byte[esi-1], 0
531
        lea     esi, [ebp + http_msg.data]
532
        DEBUGF  1, "Header names converted to lowercase:\n%s\n", esi
533
 
534
; Check for content-length header field.
535
        stdcall find_header_field, ebp, str_cl
536
        test    eax, eax
537
        jz      .no_content
538
        or      [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
539
 
540
        xor     edx, edx
541
  .cl_loop:
542
        movzx   ebx, byte[eax]
543
        inc     eax
544
        cmp     bl, 10
545
        je      .cl_ok
546
        cmp     bl, 13
547
        je      .cl_ok
548
        cmp     bl, ' '
549
        je      .cl_ok
550
        sub     bl, '0'
551
        jb      .invalid_header
552
        cmp     bl, 9
553
        ja      .invalid_header
554
        lea     edx, [edx + edx*4]      ; edx = edx*10
555
        shl     edx, 1                  ;
556
        add     edx, ebx
557
        jmp     .cl_loop
558
 
559
  .cl_ok:
560
        mov     [ebp + http_msg.content_length], edx
561
        DEBUGF  1, "Content-length: %u\n", edx
562
 
563
; Resize buffer according to content-length.
4168 hidnplayr 564
        add     edx, [ebp + http_msg.header_length]
565
        add     edx, http_msg.data
4158 hidnplayr 566
 
4168 hidnplayr 567
        mov     ecx, edx
4158 hidnplayr 568
        sub     ecx, [ebp + http_msg.write_ptr]
569
        mov     [ebp + http_msg.buffer_length], ecx
570
 
4168 hidnplayr 571
        invoke  mem.realloc, ebp, edx
4158 hidnplayr 572
        or      eax, eax
4203 hidnplayr 573
        jz      .no_ram_pop
574
 
575
        pop     eax
4168 hidnplayr 576
        sub     eax, [ebp + http_msg.header_length]
4158 hidnplayr 577
        jmp     .header_parsed  ; hooray!
578
 
579
  .no_content:
580
        DEBUGF  1, "Content-length not found.\n"
581
 
582
; We didnt find 'content-length', maybe server is using chunked transfer encoding?
583
; Try to find 'transfer-encoding' header.
584
        stdcall find_header_field, ebp, str_te
585
        test    eax, eax
586
        jz      .invalid_header
587
 
588
        mov     ebx, dword[eax]
4162 hidnplayr 589
        or      ebx, 0x20202020
4158 hidnplayr 590
        cmp     ebx, 'chun'
591
        jne     .invalid_header
592
        mov     ebx, dword[eax+4]
4162 hidnplayr 593
        or      ebx, 0x00202020
594
        and     ebx, 0x00ffffff
4158 hidnplayr 595
        cmp     ebx, 'ked'
596
        jne     .invalid_header
597
 
598
        or      [ebp + http_msg.flags], FLAG_CHUNKED
599
        DEBUGF  1, "Transfer type is: chunked\n"
600
 
4203 hidnplayr 601
        pop     eax
602
 
4158 hidnplayr 603
; Set chunk pointer where first chunk should begin.
4162 hidnplayr 604
        lea     eax, [ebp + http_msg.data]
605
        add     eax, [ebp + http_msg.header_length]
4158 hidnplayr 606
        mov     [ebp + http_msg.chunk_ptr], eax
607
 
4162 hidnplayr 608
  .chunk_loop:
4158 hidnplayr 609
        mov     ecx, [ebp + http_msg.write_ptr]
610
        sub     ecx, [ebp + http_msg.chunk_ptr]
4202 hidnplayr 611
        jb      .need_more_data_chunked         ; TODO: use this ecx !!!
4158 hidnplayr 612
 
4202 hidnplayr 613
; Chunkline starts here, convert the ASCII hex number into ebx
4158 hidnplayr 614
        mov     esi, [ebp + http_msg.chunk_ptr]
615
        xor     ebx, ebx
616
  .chunk_hexloop:
617
        lodsb
618
        sub     al, '0'
619
        jb      .chunk_
620
        cmp     al, 9
621
        jbe     .chunk_hex
4162 hidnplayr 622
        sub     al, 'A' - '0' - 10
4158 hidnplayr 623
        jb      .chunk_
4162 hidnplayr 624
        cmp     al, 15
4158 hidnplayr 625
        jbe     .chunk_hex
626
        sub     al, 'a' - 'A'
4162 hidnplayr 627
        cmp     al, 15
4158 hidnplayr 628
        ja      .chunk_
629
  .chunk_hex:
630
        shl     ebx, 4
631
        add     bl, al
632
        jmp     .chunk_hexloop
633
  .chunk_:
634
        DEBUGF  1, "got chunk of %u bytes\n", ebx
4202 hidnplayr 635
;;        cmp     esi, [ebp + http_msg.chunk_ptr]
636
;;        je
4158 hidnplayr 637
; If chunk size is 0, all chunks have been received.
638
        test    ebx, ebx
4162 hidnplayr 639
        jz      .got_all_data_chunked           ; last chunk, hooray! FIXME: what if it wasnt a valid hex number???
4158 hidnplayr 640
 
641
; Chunkline ends with a CR, LF or simply LF
4202 hidnplayr 642
  .end_of_chunkline?:
4158 hidnplayr 643
        cmp     al, 10
644
        je      .end_of_chunkline
645
        lodsb
4202 hidnplayr 646
        cmp     edi, [ebp + http_msg.write_ptr]
647
        jb      .end_of_chunkline?
648
        jmp     .need_more_data
4158 hidnplayr 649
 
650
  .end_of_chunkline:
4202 hidnplayr 651
; Update chunk ptr, and remember old one
652
        mov     edi, [ebp + http_msg.chunk_ptr]
653
        add     [ebp + http_msg.chunk_ptr], ebx
4162 hidnplayr 654
; Realloc buffer, make it 'chunksize' bigger.
655
        mov     eax, [ebp + http_msg.buffer_length]
656
        add     eax, ebx
657
        invoke  mem.realloc, ebp, eax
658
        or      eax, eax
659
        jz      .no_ram
660
        add     [ebp + http_msg.buffer_length], ebx
661
 
662
; Update write ptr
663
        mov     eax, esi
664
        sub     eax, edi
665
        sub     [ebp + http_msg.write_ptr], eax
666
 
4158 hidnplayr 667
; Now move all received data to the left (remove chunk header).
4162 hidnplayr 668
; Update content_length accordingly.
4158 hidnplayr 669
        mov     ecx, [ebp + http_msg.write_ptr]
670
        sub     ecx, esi
4168 hidnplayr 671
        add     [ebp + http_msg.content_received], ecx
4158 hidnplayr 672
        rep     movsb
4162 hidnplayr 673
        jmp     .chunk_loop
4158 hidnplayr 674
 
675
; Check if we got all the data.
4162 hidnplayr 676
  .header_parsed:
4168 hidnplayr 677
        add     [ebp + http_msg.content_received], eax
678
        mov     eax, [ebp + http_msg.content_length]
679
        cmp     eax, [ebp + http_msg.content_received]
680
        jae     .got_all_data
4203 hidnplayr 681
        jmp     .need_more_data
682
  .need_more_data_pop:
683
        pop     eax
4162 hidnplayr 684
  .need_more_data:
685
        popa
686
        xor     eax, eax
687
        dec     eax
688
        ret
4158 hidnplayr 689
 
4162 hidnplayr 690
  .need_more_data_chunked:
4168 hidnplayr 691
        add     [ebp + http_msg.content_received], eax
4158 hidnplayr 692
        popa
693
        xor     eax, eax
694
        dec     eax
695
        ret
696
 
4162 hidnplayr 697
  .got_all_data_chunked:
698
        mov     eax, [ebp + http_msg.chunk_ptr]
699
        sub     eax, [ebp + http_msg.header_length]
700
        sub     eax, http_msg.data
701
        sub     eax, ebp
702
        mov     [ebp + http_msg.content_length], eax
4168 hidnplayr 703
        mov     [ebp + http_msg.content_received], eax
4158 hidnplayr 704
  .got_all_data:
4162 hidnplayr 705
        DEBUGF  1, "We got all the data! (%u bytes)\n", [ebp + http_msg.content_length]
4205 hidnplayr 706
        or      [ebp + http_msg.flags], FLAG_GOT_ALL_DATA
707
        and     [ebp + http_msg.flags], not FLAG_CONNECTED
4158 hidnplayr 708
        mcall   close, [ebp + http_msg.socket]
709
        popa
710
        xor     eax, eax
711
        ret
712
 
713
  .check_socket:
714
        cmp     ebx, EWOULDBLOCK
4162 hidnplayr 715
        je      .need_more_data
4158 hidnplayr 716
        DEBUGF  1, "ERROR: socket error %u\n", ebx
717
 
718
        or      [ebp + http_msg.flags], FLAG_SOCKET_ERROR
4205 hidnplayr 719
        and     [ebp + http_msg.flags], not FLAG_CONNECTED
720
        mcall   close, [ebp + http_msg.socket]
721
  .connection_closed:
4158 hidnplayr 722
        popa
723
        xor     eax, eax
724
        ret
725
 
726
  .invalid_header:
4203 hidnplayr 727
        pop     eax
4158 hidnplayr 728
        DEBUGF  1, "ERROR: invalid header\n"
729
        or      [ebp + http_msg.flags], FLAG_INVALID_HEADER
4205 hidnplayr 730
        and     [ebp + http_msg.flags], not FLAG_CONNECTED
731
        mcall   close, [ebp + http_msg.socket]
4158 hidnplayr 732
        popa
733
        xor     eax, eax
734
        ret
735
 
4203 hidnplayr 736
  .no_ram_pop:
737
        pop     eax
4158 hidnplayr 738
  .no_ram:
739
        DEBUGF  1, "ERROR: out of RAM\n"
740
        or      [ebp + http_msg.flags], FLAG_NO_RAM
4205 hidnplayr 741
        and     [ebp + http_msg.flags], not FLAG_CONNECTED
742
        mcall   close, [ebp + http_msg.socket]
4158 hidnplayr 743
        popa
744
        xor     eax, eax
745
        ret
746
 
747
endp
748
 
749
 
750
 
4205 hidnplayr 751
 
4158 hidnplayr 752
;;================================================================================================;;
4205 hidnplayr 753
proc HTTP_free identifier ;///////////////////////////////////////////////////////////////////////;;
754
;;------------------------------------------------------------------------------------------------;;
755
;? Free the http_msg structure                                                                    ;;
756
;;------------------------------------------------------------------------------------------------;;
757
;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
758
;;------------------------------------------------------------------------------------------------;;
759
;< none                                                                                           ;;
760
;;================================================================================================;;
761
 
762
        pusha
763
        mov     ebp, [identifier]
764
 
765
        test    [ebp + http_msg.flags], FLAG_CONNECTED
766
        jz      .not_connected
767
 
768
        and     [ebp + http_msg.flags], not FLAG_CONNECTED
769
        mcall   close, [ebp + http_msg.socket]
770
 
771
  .not_connected:
772
        invoke  mem.free, ebp
773
 
774
        popa
775
        ret
776
 
777
endp
778
 
779
 
780
 
781
;;================================================================================================;;
782
proc HTTP_stop identifier ;///////////////////////////////////////////////////////////////////////;;
783
;;------------------------------------------------------------------------------------------------;;
784
;? Stops the open connection                                                                      ;;
785
;;------------------------------------------------------------------------------------------------;;
786
;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
787
;;------------------------------------------------------------------------------------------------;;
788
;< none                                                                                           ;;
789
;;================================================================================================;;
790
 
791
        pusha
792
        mov     ebp, [identifier]
793
 
794
        and     [ebp + http_msg.flags], not FLAG_CONNECTED
795
        mcall   close, [ebp + http_msg.socket]
796
 
797
        popa
798
        ret
799
 
800
endp
801
 
802
 
803
 
804
;;================================================================================================;;
4158 hidnplayr 805
proc find_header_field identifier, headername ;///////////////////////////////////////////////////;;
806
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 807
;? Find a header field in the received HTTP header                                                ;;
4158 hidnplayr 808
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 809
;> identifier   = ptr to http_msg struct                                                          ;;
810
;> headername   = ptr to ASCIIZ string containg field you want to find (must be in lowercase)     ;;
4158 hidnplayr 811
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 812
;< eax = 0 (error) / ptr to content of the HTTP header field                                      ;;
4158 hidnplayr 813
;;================================================================================================;;
814
        push    ebx ecx edx esi edi
815
 
816
        DEBUGF  1, "Find header field: %s\n", [headername]
817
 
818
        mov     ebx, [identifier]
4202 hidnplayr 819
        test    [ebx + http_msg.flags], FLAG_GOT_HEADER
820
        jz      .fail
821
 
4158 hidnplayr 822
        lea     edx, [ebx + http_msg.data]
823
        mov     ecx, edx
824
        add     ecx, [ebx + http_msg.header_length]
825
 
826
  .restart:
827
        mov     esi, [headername]
828
        mov     edi, edx
829
  .loop:
830
        cmp     edi, ecx
831
        jae     .fail
832
        lodsb
833
        scasb
834
        je      .loop
835
        test    al, al
836
        jz      .done?
837
  .next:
838
        inc     edx
839
        jmp     .restart
840
 
841
  .not_done:
842
        inc     edi
843
  .done?:
844
        cmp     byte[edi-1], ':'
845
        je      .almost_done
846
        cmp     byte[edi-1], ' '
847
        je      .not_done
848
        cmp     byte[edi-1], 9  ; tab
849
        je      .not_done
850
 
851
        jmp     .next
852
 
853
  .almost_done:                 ; FIXME: buffer overflow?
854
        dec     edi
855
        DEBUGF  1, "Found header field\n"
856
  .spaceloop:
857
        inc     edi
858
        cmp     byte[edi], ' '
859
        je      .spaceloop
860
        cmp     byte[edi], 9    ; tab
861
        je      .spaceloop
862
 
863
        mov     eax, edi
864
        pop     edi esi edx ecx ebx
865
        ret
866
 
867
  .fail:
868
        pop     edi esi edx ecx ebx
869
        xor     eax, eax
870
        ret
871
 
872
endp
873
 
874
 
875
 
4202 hidnplayr 876
 
4205 hidnplayr 877
 
4167 hidnplayr 878
;;================================================================================================;;
4202 hidnplayr 879
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
880
;;================================================================================================;;
881
;! Internal procedures section                                                                    ;;
882
;;================================================================================================;;
883
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
884
;;================================================================================================;;
885
 
886
 
887
 
888
 
889
;;================================================================================================;;
4167 hidnplayr 890
proc open_connection hostname, port ;/////////////////////////////////////////////////////////////;;
891
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 892
;? Connects to a HTTP server                                                                      ;;
4167 hidnplayr 893
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 894
;> hostname     = ptr to ASCIIZ hostname                                                          ;;
895
;> port         = port (x86 byte order)                                                           ;;
4167 hidnplayr 896
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 897
;< eax = 0 (error) / socketnum                                                                    ;;
4167 hidnplayr 898
;;================================================================================================;;
4158 hidnplayr 899
 
4167 hidnplayr 900
locals
901
        sockaddr        dd ?
902
        socketnum       dd ?
903
endl
904
 
905
; Resolve the hostname
906
        DEBUGF  1, "Resolving hostname\n"
907
        push    esp     ; reserve stack place
908
        push    esp     ; fourth parameter
909
        push    0       ; third parameter
910
        push    0       ; second parameter
911
        push    [hostname]
912
        call    [getaddrinfo]
913
        pop     esi
914
        test    eax, eax
915
        jnz     .error1
916
 
917
; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
918
        mov     esi, [esi + addrinfo.ai_addr]
919
        mov     [sockaddr], esi
920
        mov     eax, [esi + sockaddr_in.sin_addr]
921
        test    eax, eax
922
        jz      .error2
923
 
924
        DEBUGF  1, "Server ip=%u.%u.%u.%u\n", \
925
        [esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
926
        [esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
927
 
928
        mov     [esi + sockaddr_in.sin_family], AF_INET4
929
        mov     eax, [port]
930
        xchg    al, ah
931
        mov     [esi + sockaddr_in.sin_port], ax
932
 
933
; Connect to the server.
934
        mcall   socket, AF_INET4, SOCK_STREAM, 0
935
        test    eax, eax
936
        jz      .error2
937
        mov     [socketnum], eax
938
        DEBUGF  1, "Socket: 0x%x\n", eax
939
 
940
        mcall   connect, [socketnum], [sockaddr], 18
941
        test    eax, eax
942
        jnz     .error2
943
        DEBUGF  1, "Socket is now connected.\n"
944
 
945
; free allocated memory
946
        push    [sockaddr]
947
        call    [freeaddrinfo]
948
 
949
        mov     eax, [socketnum]
950
        ret
951
 
952
  .error2:
953
 
954
; free allocated memory
955
        push    [sockaddr]
956
        call    [freeaddrinfo]
957
 
958
  .error1:
959
        xor     eax, eax
960
        ret
961
 
962
endp
963
 
964
 
4158 hidnplayr 965
;;================================================================================================;;
966
proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
967
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 968
;? Split a given URL into hostname and pageaddr                                                   ;;
4158 hidnplayr 969
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 970
;> URL = ptr to ASCIIZ URL                                                                        ;;
4158 hidnplayr 971
;;------------------------------------------------------------------------------------------------;;
4202 hidnplayr 972
;< eax = 0 (error) / ptr to ASCIIZ hostname                                                       ;;
973
;< ebx = ptr to ASCIIZ pageaddr                                                                   ;;
4158 hidnplayr 974
;;================================================================================================;;
975
 
976
locals
977
        urlsize         dd ?
978
        hostname        dd ?
979
        pageaddr        dd ?
980
endl
981
 
4161 hidnplayr 982
        DEBUGF  1, "parsing URL: %s\n", [URL]
4158 hidnplayr 983
 
984
; remove any leading protocol text
985
        mov     esi, [URL]
986
        mov     ecx, URLMAXLEN
987
        mov     ax, '//'
988
  .loop1:
989
        cmp     byte[esi], 0            ; end of URL?
990
        je      .url_ok                 ; yep, so not found
991
        cmp     [esi], ax
992
        je      .skip_proto
993
        inc     esi
994
        dec     ecx
995
        jnz     .loop1
996
 
4161 hidnplayr 997
        DEBUGF  1, "Invalid URL\n"
4158 hidnplayr 998
        xor     eax, eax
999
        ret
1000
 
1001
  .skip_proto:
1002
        inc     esi                     ; skip the two '/'
1003
        inc     esi
1004
        mov     [URL], esi              ; update pointer so it skips protocol
1005
        jmp     .loop1                  ; we still need to find the length of the URL
1006
 
1007
  .url_ok:
1008
        sub     esi, [URL]              ; calculate total length of URL
1009
        mov     [urlsize], esi
1010
 
1011
 
1012
; now look for page delimiter - it's a '/' character
1013
        mov     ecx, esi                ; URL length
1014
        mov     edi, [URL]
1015
        mov     al, '/'
1016
        repne   scasb
4161 hidnplayr 1017
        jne     @f
4158 hidnplayr 1018
        dec     edi                     ; return one char, '/' must be part of the pageaddr
1019
        inc     ecx                     ;
4161 hidnplayr 1020
  @@:
4158 hidnplayr 1021
        push    ecx edi                 ; remember the pointer and length of pageaddr
1022
 
1023
        mov     ecx, edi
1024
        sub     ecx, [URL]
1025
        inc     ecx                     ; we will add a 0 byte at the end
1026
        invoke  mem.alloc, ecx
1027
        or      eax, eax
1028
        jz      .no_mem
1029
 
1030
        mov     [hostname], eax         ; copy hostname to buffer
1031
        mov     edi, eax
1032
        mov     esi, [URL]
1033
        dec     ecx
1034
        rep     movsb
1035
        xor     al, al
1036
        stosb
1037
 
4161 hidnplayr 1038
        mov     [pageaddr], str_slash   ; assume there is no pageaddr
4158 hidnplayr 1039
        pop     esi ecx
1040
        test    ecx, ecx
1041
        jz      .no_page
1042
        inc     ecx                     ; we will add a 0 byte at the end
1043
        invoke  mem.alloc, ecx
1044
        or      eax, eax
1045
        jz      .no_mem
1046
 
1047
        mov     [pageaddr], eax         ; copy pageaddr to buffer
1048
        mov     edi, eax
1049
        dec     ecx
1050
        rep     movsb
1051
        xor     al, al
1052
        stosb
1053
  .no_page:
1054
 
1055
        mov     eax, [hostname]
1056
        mov     ebx, [pageaddr]
1057
 
1058
        DEBUGF  1, "hostname: %s\n", eax
1059
        DEBUGF  1, "pageaddr: %s\n", ebx
1060
 
1061
        ret
1062
 
1063
  .no_mem:
1064
        xor     eax, eax
1065
        ret
1066
 
1067
endp
1068
 
1069
 
4202 hidnplayr 1070
;;================================================================================================;;
1071
proc ascii_dec ;//////////////////////////////////////////////////////////////////////////////////;;
1072
;;------------------------------------------------------------------------------------------------;;
1073
;? Convert eax to ASCII decimal number                                                            ;;
1074
;;------------------------------------------------------------------------------------------------;;
1075
;> eax = number                                                                                   ;;
1076
;> edi = ptr where to write ASCII decimal number                                                  ;;
1077
;;------------------------------------------------------------------------------------------------;;
1078
;< /                                                                                              ;;
1079
;;================================================================================================;;
4158 hidnplayr 1080
 
4168 hidnplayr 1081
        push    -'0'
4167 hidnplayr 1082
        mov     ecx, 10
1083
  .loop:
1084
        xor     edx, edx
1085
        div     ecx
1086
        add     dl, '0'
4168 hidnplayr 1087
        push    edx
4167 hidnplayr 1088
        test    eax, eax
1089
        jnz     .loop
4158 hidnplayr 1090
 
4168 hidnplayr 1091
  .loop2:
1092
        pop     eax
1093
        add     al, '0'
1094
        jz      .done
1095
        stosb
1096
        jmp     .loop2
1097
  .done:
1098
 
4167 hidnplayr 1099
        ret
4158 hidnplayr 1100
 
4202 hidnplayr 1101
endp
4167 hidnplayr 1102
 
4202 hidnplayr 1103
 
4158 hidnplayr 1104
;;================================================================================================;;
1105
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
1106
;;================================================================================================;;
1107
;! Imported functions section                                                                     ;;
1108
;;================================================================================================;;
1109
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
1110
;;================================================================================================;;
1111
 
1112
 
1113
align 16
1114
@IMPORT:
1115
 
1116
library \
1117
        libini, 'libini.obj', \
1118
        network, 'network.obj'
1119
 
1120
import  libini, \
1121
        ini.get_str, 'ini_get_str', \
1122
        ini.get_int, 'ini_get_int'
1123
 
1124
import  network,\
1125
        getaddrinfo, 'getaddrinfo',\
1126
        freeaddrinfo,  'freeaddrinfo',\
1127
        inet_ntoa, 'inet_ntoa'
1128
 
1129
;;===========================================================================;;
1130
;;///////////////////////////////////////////////////////////////////////////;;
1131
;;===========================================================================;;
1132
;! Exported functions section                                                ;;
1133
;;===========================================================================;;
1134
;;///////////////////////////////////////////////////////////////////////////;;
1135
;;===========================================================================;;
1136
 
1137
 
1138
align 4
1139
@EXPORT:
1140
export  \
1141
        lib_init                , 'lib_init'            , \
1142
        0x00010001              , 'version'             , \
1143
        HTTP_get                , 'get'                 , \
4167 hidnplayr 1144
        HTTP_head               , 'head'                , \
1145
        HTTP_post               , 'post'                , \
4158 hidnplayr 1146
        find_header_field       , 'find_header_field'   , \
4205 hidnplayr 1147
        HTTP_process            , 'process'             , \
1148
        HTTP_free               , 'free'                , \
1149
        HTTP_stop               , 'stop'
4158 hidnplayr 1150
 
1151
;        HTTP_put                , 'put'                 , \
1152
;        HTTP_delete             , 'delete'              , \
1153
;        HTTP_trace              , 'trace'               , \
1154
;        HTTP_connect            , 'connect'             , \
1155
 
1156
 
1157
 
1158
section '.data' data readable writable align 16
1159
 
1160
inifile         db '/sys/settings/network.ini', 0
1161
 
1162
sec_proxy:
1163
key_proxy       db 'proxy', 0
1164
key_proxyport   db 'port', 0
1165
key_user        db 'user', 0
1166
key_password    db 'password', 0
1167
 
1168
str_http11      db ' HTTP/1.1', 13, 10, 'Host: '
1169
  .length       = $ - str_http11
4167 hidnplayr 1170
str_post_cl     db 13, 10, 'Content-Length: '
1171
  .length       = $ - str_post_cl
1172
str_post_ct     db 13, 10, 'Content-Type: '
1173
  .length       = $ - str_post_ct
4158 hidnplayr 1174
str_close       db 13, 10, 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
1175
  .length       = $ - str_close
1176
str_proxy_auth  db 13, 10, 'Proxy-Authorization: Basic '
1177
  .length       = $ - str_proxy_auth
1178
 
1179
base64_table    db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
1180
                db '0123456789+/'
1181
 
1182
str_cl          db 'content-length', 0
4161 hidnplayr 1183
str_slash       db '/', 0
4158 hidnplayr 1184
str_te          db 'transfer-encoding', 0
4167 hidnplayr 1185
str_get         db 'GET ', 0
1186
str_head        db 'HEAD ', 0
1187
str_post        db 'POST ', 0
4158 hidnplayr 1188
 
1189
include_debug_strings
1190
 
1191
; uninitialized data
1192
mem.alloc       dd ?
1193
mem.free        dd ?
1194
mem.realloc     dd ?
1195
dll.load        dd ?
1196
 
1197
proxyAddr       rb 256
1198
proxyUser       rb 256
1199
proxyPassword   rb 256
1200
proxyPort       dd ?