Subversion Repositories Kolibri OS

Rev

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