Subversion Repositories Kolibri OS

Rev

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

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