Subversion Repositories Kolibri OS

Rev

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