Subversion Repositories Kolibri OS

Rev

Rev 4222 | Rev 4241 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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. ;;   Proxy code written by CleverMouse                             ;;
  10. ;;                                                                 ;;
  11. ;;         GNU GENERAL PUBLIC LICENSE                              ;;
  12. ;;          Version 2, June 1991                                   ;;
  13. ;;                                                                 ;;
  14. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  15.  
  16. ; references:
  17. ; "HTTP made really easy", http://www.jmarshall.com/easy/http/
  18. ; "Hypertext Transfer Protocol -- HTTP/1.1", http://tools.ietf.org/html/rfc2616
  19.  
  20.  
  21.         URLMAXLEN       = 65535
  22.         BUFFERSIZE      = 4096
  23.         TIMEOUT         = 1000  ; in 1/100 s
  24.  
  25.         __DEBUG__       = 1
  26.         __DEBUG_LEVEL__ = 1
  27.  
  28.  
  29. format MS COFF
  30.  
  31. public @EXPORT as 'EXPORTS'
  32.  
  33. include '../../../struct.inc'
  34. include '../../../proc32.inc'
  35. include '../../../macros.inc'
  36. purge section,mov,add,sub
  37. include '../../../debug-fdo.inc'
  38.  
  39. include '../../../network.inc'
  40. include 'http.inc'
  41.  
  42. virtual at 0
  43.         http_msg http_msg
  44. end virtual
  45.  
  46. macro copy_till_zero {
  47.   @@:
  48.         lodsb
  49.         test    al, al
  50.         jz      @f
  51.         stosb
  52.         jmp     @r
  53.   @@:
  54. }
  55.  
  56. macro HTTP_init_buffer buffer, socketnum {
  57.  
  58.         mov     eax, buffer
  59.         push    socketnum
  60.         popd    [eax + http_msg.socket]
  61.         lea     esi, [eax + http_msg.data]
  62.         mov     [eax + http_msg.flags], FLAG_CONNECTED
  63.         mov     [eax + http_msg.write_ptr], esi
  64.         mov     [eax + http_msg.buffer_length], BUFFERSIZE -  http_msg.data
  65.         mov     [eax + http_msg.chunk_ptr], 0
  66.  
  67.         mov     [eax + http_msg.status], 0
  68.         mov     [eax + http_msg.header_length], 0
  69.         mov     [eax + http_msg.content_length], 0
  70.         mov     [eax + http_msg.content_received], 0
  71.  
  72.         push    eax ebp
  73.         mov     ebp, eax
  74.         mcall   29, 9
  75.         mov     [ebp + http_msg.timestamp], eax
  76.         pop     ebp eax
  77. }
  78.  
  79. section '.flat' code readable align 16
  80.  
  81. ;;===========================================================================;;
  82. lib_init: ;//////////////////////////////////////////////////////////////////;;
  83. ;;---------------------------------------------------------------------------;;
  84. ;? Library entry point (called after library load)                           ;;
  85. ;;---------------------------------------------------------------------------;;
  86. ;> eax = pointer to memory allocation routine                                ;;
  87. ;> ebx = pointer to memory freeing routine                                   ;;
  88. ;> ecx = pointer to memory reallocation routine                              ;;
  89. ;> edx = pointer to library loading routine                                  ;;
  90. ;;---------------------------------------------------------------------------;;
  91. ;< eax = 1 (fail) / 0 (ok)                                                   ;;
  92. ;;===========================================================================;;
  93.         mov     [mem.alloc], eax
  94.         mov     [mem.free], ebx
  95.         mov     [mem.realloc], ecx
  96.         mov     [dll.load], edx
  97.  
  98.         invoke  dll.load, @IMPORT
  99.         test    eax, eax
  100.         jnz     .error
  101.  
  102. ; load proxy settings
  103.         pusha
  104.         invoke  ini.get_str, inifile, sec_proxy, key_proxy, proxyAddr, 256, proxyAddr
  105.         invoke  ini.get_int, inifile, sec_proxy, key_proxyport, 80
  106.         mov     [proxyPort], eax
  107.         invoke  ini.get_str, inifile, sec_proxy, key_user, proxyUser, 256, proxyUser
  108.         invoke  ini.get_str, inifile, sec_proxy, key_password, proxyPassword, 256, proxyPassword
  109.         popa
  110.  
  111.         DEBUGF  1, "HTTP library: init OK\n"
  112.  
  113.         xor     eax, eax
  114.         ret
  115.  
  116.   .error:
  117.         DEBUGF  1, "ERROR loading libraries\n"
  118.  
  119.         xor     eax, eax
  120.         inc     eax
  121.  
  122.         ret
  123.  
  124.  
  125.  
  126.  
  127. ;;================================================================================================;;
  128. proc HTTP_get URL, add_header ;///////////////////////////////////////////////////////////////////;;
  129. ;;------------------------------------------------------------------------------------------------;;
  130. ;? Initiates a HTTP connection, using 'GET' method.                                               ;;
  131. ;;------------------------------------------------------------------------------------------------;;
  132. ;> URL          = pointer to ASCIIZ URL                                                           ;;
  133. ;> add_header   = pointer to additional header parameters (ASCIIZ), or null for none.             ;;
  134. ;;------------------------------------------------------------------------------------------------;;
  135. ;< eax = 0 (error) / buffer ptr                                                                   ;;
  136. ;;================================================================================================;;
  137. locals
  138.         hostname        dd ?
  139.         pageaddr        dd ?
  140.         sockaddr        dd ?
  141.         socketnum       dd ?
  142.         buffer          dd ?
  143.         port            dd ?
  144. endl
  145.  
  146.         pusha
  147.  
  148. ; split the URL into hostname and pageaddr
  149.         stdcall parse_url, [URL]
  150.         test    eax, eax
  151.         jz      .error
  152.         mov     [hostname], eax
  153.         mov     [pageaddr], ebx
  154.         mov     [port], ecx
  155.  
  156. ; Connect to the other side.
  157.         stdcall open_connection, [hostname], [port]
  158.         test    eax, eax
  159.         jz      .error
  160.         mov     [socketnum], eax
  161.  
  162. ; Create the HTTP request.
  163.         invoke  mem.alloc, BUFFERSIZE
  164.         test    eax, eax
  165.         jz      .error
  166.         mov     [buffer], eax
  167.         mov     edi, eax
  168.         DEBUGF  1, "Buffer has been allocated.\n"
  169.  
  170.         mov     esi, str_get
  171.         copy_till_zero
  172.  
  173. ; If we are using a proxy, send complete URL, otherwise send only page address.
  174.         cmp     [proxyAddr], 0
  175.         je      .no_proxy
  176.         mov     esi, str_http           ; prepend 'http://'
  177.         copy_till_zero
  178.         mov     esi, [hostname]
  179.         copy_till_zero
  180.   .no_proxy:
  181.         mov     esi, [pageaddr]
  182.         copy_till_zero
  183.  
  184.         mov     esi, str_http11
  185.         mov     ecx, str_http11.length
  186.         rep     movsb
  187.  
  188.         mov     esi, [hostname]
  189.         copy_till_zero
  190.  
  191.         mov     esi, [add_header]
  192.         test    esi, esi
  193.         jz      @f
  194.         copy_till_zero
  195.   @@:
  196.  
  197.         cmp     byte[proxyUser], 0
  198.         je      @f
  199.         call    append_proxy_auth_header
  200.   @@:
  201.  
  202.         mov     esi, str_close
  203.         mov     ecx, str_close.length
  204.         rep     movsb
  205.  
  206.         mov     byte[edi], 0
  207.         DEBUGF  1, "Request:\n%s", [buffer]
  208.  
  209. ; Free unused memory
  210.         push    edi
  211.         invoke  mem.free, [pageaddr]
  212.         invoke  mem.free, [hostname]
  213.         pop     esi
  214.  
  215. ; Send the request
  216.         sub     esi, [buffer]   ; length
  217.         xor     edi, edi        ; flags
  218.         mcall   send, [socketnum], [buffer]
  219.         test    eax, eax
  220.         jz      .error
  221.         DEBUGF  1, "Request has been sent to server.\n"
  222.  
  223.         HTTP_init_buffer [buffer], [socketnum]
  224.  
  225.         popa
  226.         mov     eax, [buffer]   ; return buffer ptr
  227.         ret
  228.  
  229.   .error:
  230.         DEBUGF  1, "Error!\n"
  231.         popa
  232.         xor     eax, eax        ; return 0 = error
  233.         ret
  234.  
  235. endp
  236.  
  237.  
  238.  
  239. ;;================================================================================================;;
  240. proc HTTP_head URL, add_header ;//////////////////////////////////////////////////////////////////;;
  241. ;;------------------------------------------------------------------------------------------------;;
  242. ;? Initiates a HTTP connection, using 'HEAD' method.                                              ;;
  243. ;;------------------------------------------------------------------------------------------------;;
  244. ;> URL          = pointer to ASCIIZ URL                                                           ;;
  245. ;> add_header   = pointer to additional header parameters (ASCIIZ), or null for none.             ;;
  246. ;;------------------------------------------------------------------------------------------------;;
  247. ;< eax = 0 (error) / buffer ptr                                                                   ;;
  248. ;;================================================================================================;;
  249. locals
  250.         hostname        dd ?
  251.         pageaddr        dd ?
  252.         sockaddr        dd ?
  253.         socketnum       dd ?
  254.         buffer          dd ?
  255.         port            dd ?
  256. endl
  257.  
  258.         pusha
  259. ; split the URL into hostname and pageaddr
  260.         stdcall parse_url, [URL]
  261.         test    eax, eax
  262.         jz      .error
  263.         mov     [hostname], eax
  264.         mov     [pageaddr], ebx
  265.         mov     [port], ecx
  266.  
  267. ; Connect to the other side.
  268.         stdcall open_connection, [hostname], [port]
  269.         test    eax, eax
  270.         jz      .error
  271.         mov     [socketnum], eax
  272.  
  273. ; Create the HTTP request.
  274.         invoke  mem.alloc, BUFFERSIZE
  275.         test    eax, eax
  276.         jz      .error
  277.         mov     [buffer], eax
  278.         mov     edi, eax
  279.         DEBUGF  1, "Buffer has been allocated.\n"
  280.  
  281.         mov     esi, str_head
  282.         copy_till_zero
  283.  
  284. ; If we are using a proxy, send complete URL, otherwise send only page address.
  285.         cmp     [proxyAddr], 0
  286.         je      .no_proxy
  287.         mov     esi, str_http           ; prepend 'http://'
  288.         copy_till_zero
  289.         mov     esi, [hostname]
  290.         copy_till_zero
  291.   .no_proxy:
  292.         mov     esi, [pageaddr]
  293.         copy_till_zero
  294.  
  295.         mov     esi, str_http11
  296.         mov     ecx, str_http11.length
  297.         rep     movsb
  298.  
  299.         mov     esi, [hostname]
  300.         copy_till_zero
  301.  
  302.         mov     esi, [add_header]
  303.         test    esi, esi
  304.         jz      @f
  305.         copy_till_zero
  306.   @@:
  307.  
  308.         cmp     byte[proxyUser], 0
  309.         je      @f
  310.         call    append_proxy_auth_header
  311.   @@:
  312.  
  313.         mov     esi, str_close
  314.         mov     ecx, str_close.length
  315.         rep     movsb
  316.  
  317.         mov     byte[edi], 0
  318.         DEBUGF  1, "Request:\n%s", [buffer]
  319.  
  320.  
  321. ; Free unused memory
  322.         push    edi
  323.         invoke  mem.free, [pageaddr]
  324.         invoke  mem.free, [hostname]
  325.         pop     esi
  326.  
  327. ; Send the request
  328.         sub     esi, [buffer]   ; length
  329.         xor     edi, edi        ; flags
  330.         mcall   send, [socketnum], [buffer]
  331.         test    eax, eax
  332.         jz      .error
  333.         DEBUGF  1, "Request has been sent to server.\n"
  334.  
  335.         HTTP_init_buffer [buffer], [socketnum]
  336.  
  337.         popa
  338.         mov     eax, [buffer]
  339.         ret                     ; return buffer ptr
  340.  
  341.   .error:
  342.         DEBUGF  1, "Error!\n"
  343.         popa
  344.         xor     eax, eax        ; return 0 = error
  345.         ret
  346.  
  347. endp
  348.  
  349.  
  350. ;;================================================================================================;;
  351. proc HTTP_post URL, add_header, content_type, content_length ;////////////////////////////////////;;
  352. ;;------------------------------------------------------------------------------------------------;;
  353. ;? Initiates a HTTP connection, using 'GET' method.                                               ;;
  354. ;;------------------------------------------------------------------------------------------------;;
  355. ;> URL                  = pointer to ASCIIZ URL                                                   ;;
  356. ;> add_header           = pointer to additional header parameters (ASCIIZ), or null for none.     ;;
  357. ;> content_type         = pointer to ASCIIZ string containing content type                        ;;
  358. ;> content_length       = length of content (in bytes)                                            ;;
  359. ;;------------------------------------------------------------------------------------------------;;
  360. ;< eax = 0 (error) / buffer ptr                                                                   ;;
  361. ;;================================================================================================;;
  362. locals
  363.         hostname        dd ?
  364.         pageaddr        dd ?
  365.         sockaddr        dd ?
  366.         socketnum       dd ?
  367.         buffer          dd ?
  368.         port            dd ?
  369. endl
  370.  
  371.         pusha
  372. ; split the URL into hostname and pageaddr
  373.         stdcall parse_url, [URL]
  374.         test    eax, eax
  375.         jz      .error
  376.         mov     [hostname], eax
  377.         mov     [pageaddr], ebx
  378.         mov     [port], ecx
  379.  
  380. ; Connect to the other side.
  381.         stdcall open_connection, [hostname], [port]
  382.         test    eax, eax
  383.         jz      .error
  384.         mov     [socketnum], eax
  385.  
  386. ; Create the HTTP request.
  387.         invoke  mem.alloc, BUFFERSIZE
  388.         test    eax, eax
  389.         jz      .error
  390.         mov     [buffer], eax
  391.         mov     edi, eax
  392.         DEBUGF  1, "Buffer has been allocated.\n"
  393.  
  394.         mov     esi, str_post
  395.         copy_till_zero
  396.  
  397. ; If we are using a proxy, send complete URL, otherwise send only page address.
  398.         cmp     [proxyAddr], 0
  399.         je      .no_proxy
  400.         mov     esi, str_http           ; prepend 'http://'
  401.         copy_till_zero
  402.         mov     esi, [hostname]
  403.         copy_till_zero
  404.   .no_proxy:
  405.         mov     esi, [pageaddr]
  406.         copy_till_zero
  407.  
  408.         mov     esi, str_http11
  409.         mov     ecx, str_http11.length
  410.         rep     movsb
  411.  
  412.         mov     esi, [hostname]
  413.         copy_till_zero
  414.  
  415.         mov     esi, str_post_cl
  416.         mov     ecx, str_post_cl.length
  417.         rep     movsb
  418.  
  419.         mov     eax, [content_length]
  420.         call    eax_ascii_dec
  421.  
  422.         mov     esi, str_post_ct
  423.         mov     ecx, str_post_ct.length
  424.         rep     movsb
  425.  
  426.         mov     esi, [content_type]
  427.         copy_till_zero
  428.  
  429.         mov     esi, [add_header]
  430.         test    esi, esi
  431.         jz      @f
  432.         copy_till_zero
  433.   @@:
  434.  
  435.         cmp     byte[proxyUser], 0
  436.         je      @f
  437.         call    append_proxy_auth_header
  438.   @@:
  439.  
  440.         mov     esi, str_close
  441.         mov     ecx, str_close.length
  442.         rep     movsb
  443.  
  444.         mov     byte[edi], 0
  445.         DEBUGF  1, "Request:\n%s", [buffer]
  446.  
  447. ; Free unused memory
  448.         push    edi
  449.         invoke  mem.free, [pageaddr]
  450.         invoke  mem.free, [hostname]
  451.         pop     esi
  452.  
  453. ; Send the request
  454.         sub     esi, [buffer]   ; length
  455.         xor     edi, edi        ; flags
  456.         mcall   send, [socketnum], [buffer]
  457.         test    eax, eax
  458.         jz      .error
  459.         DEBUGF  1, "Request has been sent to server.\n"
  460.  
  461.         HTTP_init_buffer [buffer], [socketnum]
  462.  
  463.         popa
  464.         mov     eax, [buffer]
  465.         ret                     ; return buffer ptr
  466.  
  467.   .error:
  468.         DEBUGF  1, "Error!\n"
  469.         popa
  470.         xor     eax, eax        ; return 0 = error
  471.         ret
  472.  
  473. endp
  474.  
  475.  
  476.  
  477. ;;================================================================================================;;
  478. proc HTTP_process identifier ;////////////////////////////////////////////////////////////////////;;
  479. ;;------------------------------------------------------------------------------------------------;;
  480. ;? Receive data from the server, parse headers and put data in receive buffer.                    ;;
  481. ;? To complete a transfer, this procedure must be called over and over again untill it returns 0. ;;
  482. ;;------------------------------------------------------------------------------------------------;;
  483. ;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
  484. ;;------------------------------------------------------------------------------------------------;;
  485. ;< eax = -1 (not finished) / 0 finished                                                           ;;
  486. ;;================================================================================================;;
  487.         pusha
  488.         mov     ebp, [identifier]
  489.  
  490. ; If the connection is closed, return immediately
  491.         test    [ebp + http_msg.flags], FLAG_CONNECTED
  492.         jz      .connection_closed
  493.  
  494. ; Receive some data
  495.         mcall   recv, [ebp + http_msg.socket], [ebp + http_msg.write_ptr], \
  496.                       [ebp + http_msg.buffer_length], MSG_DONTWAIT
  497.         cmp     eax, 0xffffffff
  498.         je      .check_socket
  499.  
  500.         test    eax, eax
  501.         jz      .server_closed
  502.         DEBUGF  1, "Received %u bytes\n", eax
  503.  
  504. ; Update timestamp
  505.         push    eax
  506.         mcall   29, 9
  507.         mov     [ebp + http_msg.timestamp], eax
  508.         pop     eax
  509.  
  510. ; Update pointers
  511.         mov     edi, [ebp + http_msg.write_ptr]
  512.         add     [ebp + http_msg.write_ptr], eax
  513.         sub     [ebp + http_msg.buffer_length], eax
  514.         jz      .got_all_data
  515.  
  516. ; If data is chunked, combine chunks into contiguous data.
  517.         test    [ebp + http_msg.flags], FLAG_CHUNKED
  518.         jnz     .chunk_loop
  519.  
  520. ; Did we detect the (final) header yet?
  521.         test    [ebp + http_msg.flags], FLAG_GOT_HEADER
  522.         jnz     .header_parsed
  523.  
  524. ; We havent found the (final) header yet, search for it..
  525.   .scan_again:
  526.         ; eax = total number of bytes received so far
  527.         mov     eax, [ebp + http_msg.write_ptr]
  528.         sub     eax, http_msg.data
  529.         sub     eax, ebp
  530.         sub     eax, [ebp + http_msg.header_length]
  531.         ; edi is ptr to begin of header
  532.         lea     edi, [ebp + http_msg.data]
  533.         add     edi, [ebp + http_msg.header_length]
  534.         ; put it in esi for next proc too
  535.         mov     esi, edi
  536.         sub     eax, 3
  537.         jle     .need_more_data
  538.   .scan_loop:
  539.         ; scan for end of header (empty line)
  540.         cmp     dword[edi], 0x0a0d0a0d                  ; end of header
  541.         je      .end_of_header
  542.         cmp     word[edi+2], 0x0a0a                     ; notice the use of offset + 2, to calculate header length correctly :)
  543.         je      .end_of_header
  544.         inc     edi
  545.         dec     eax
  546.         jnz     .scan_loop
  547.         jmp     .need_more_data
  548.  
  549.   .end_of_header:
  550.         add     edi, 4 - http_msg.data
  551.         sub     edi, ebp
  552.         mov     [ebp + http_msg.header_length], edi     ; If this isnt the final header, we'll use this as an offset to find real header.
  553.         DEBUGF  1, "Header length: %u\n", edi
  554.  
  555. ; Ok, we have found header:
  556.         cmp     dword[esi], 'HTTP'
  557.         jne     .invalid_header
  558.         cmp     dword[esi+4], '/1.0'
  559.         je      .http_1.0
  560.         cmp     dword[esi+4], '/1.1'
  561.         jne     .invalid_header
  562.         or      [ebp + http_msg.flags], FLAG_HTTP11
  563.   .http_1.0:
  564.         cmp     byte[esi+8], ' '
  565.         jne     .invalid_header
  566.  
  567.         add     esi, 9
  568.         xor     eax, eax
  569.         xor     ebx, ebx
  570.         mov     ecx, 3
  571.   .statusloop:
  572.         lodsb
  573.         sub     al, '0'
  574.         jb      .invalid_header
  575.         cmp     al, 9
  576.         ja      .invalid_header
  577.         lea     ebx, [ebx + 4*ebx]
  578.         shl     ebx, 1
  579.         add     ebx, eax
  580.         dec     ecx
  581.         jnz     .statusloop
  582.  
  583. ; Ignore "100 - Continue" headers
  584.         cmp     ebx, 100
  585.         je      .scan_again
  586.  
  587.         DEBUGF  1, "Status: %u\n", ebx
  588.         mov     [ebp + http_msg.status], ebx
  589.         or      [ebp + http_msg.flags], FLAG_GOT_HEADER
  590.  
  591. ; Now, convert all header names to lowercase.
  592. ; This way, it will be much easier to find certain header fields, later on.
  593.  
  594.         lea     esi, [ebp + http_msg.data]
  595.         mov     ecx, [ebp + http_msg.header_length]
  596.   .need_newline:
  597.         inc     esi
  598.         dec     ecx
  599.         jz      .convert_done
  600.         cmp     byte[esi], 10
  601.         jne     .need_newline
  602. ; Ok, we have a newline, a line beginning with space or tabs has no header fields.
  603.  
  604.         inc     esi
  605.         dec     ecx
  606.         jz      .convert_done
  607.         cmp     byte[esi], ' '
  608.         je      .need_newline
  609.         cmp     byte[esi], 9    ; horizontal tab
  610.         je      .need_newline
  611.         jmp     .convert_loop
  612.   .next_char:
  613.         inc     esi
  614.         dec     ecx
  615.         jz      .convert_done
  616.   .convert_loop:
  617.         cmp     byte[esi], ':'
  618.         je      .need_newline
  619.         cmp     byte[esi], 'A'
  620.         jb      .next_char
  621.         cmp     byte[esi], 'Z'
  622.         ja      .next_char
  623.         or      byte[esi], 0x20 ; convert to lowercase
  624.         jmp     .next_char
  625.   .convert_done:
  626.         mov     byte[esi-1], 0
  627.         lea     esi, [ebp + http_msg.data]
  628.         DEBUGF  1, "Header names converted to lowercase:\n%s\n", esi
  629.  
  630. ; Check for content-length header field.
  631.         stdcall HTTP_find_header_field, ebp, str_cl
  632.         test    eax, eax
  633.         jz      .no_content
  634.         or      [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
  635.  
  636.         xor     edx, edx
  637.   .cl_loop:
  638.         movzx   ebx, byte[eax]
  639.         inc     eax
  640.         cmp     bl, 10
  641.         je      .cl_ok
  642.         cmp     bl, 13
  643.         je      .cl_ok
  644.         cmp     bl, ' '
  645.         je      .cl_ok
  646.         sub     bl, '0'
  647.         jb      .invalid_header
  648.         cmp     bl, 9
  649.         ja      .invalid_header
  650.         lea     edx, [edx + edx*4]      ; edx = edx*10
  651.         shl     edx, 1                  ;
  652.         add     edx, ebx
  653.         jmp     .cl_loop
  654.  
  655.   .cl_ok:
  656.         mov     [ebp + http_msg.content_length], edx
  657.         DEBUGF  1, "Content-length: %u\n", edx
  658.  
  659. ; Resize buffer according to content-length.
  660.         add     edx, [ebp + http_msg.header_length]
  661.         add     edx, http_msg.data
  662.  
  663.         mov     ecx, edx
  664.         sub     ecx, [ebp + http_msg.write_ptr]
  665.         mov     [ebp + http_msg.buffer_length], ecx
  666.  
  667.         invoke  mem.realloc, ebp, edx
  668.         or      eax, eax
  669.         jz      .no_ram
  670.  
  671.   .not_chunked:
  672.         mov     eax, [ebp + http_msg.write_ptr]
  673.         sub     eax, [ebp + http_msg.header_length]
  674.         sub     eax, http_msg.data
  675.         sub     eax, ebp
  676.         jmp     .header_parsed  ; hooray!
  677.  
  678.   .no_content:
  679.         DEBUGF  1, "Content-length not found.\n"
  680.  
  681. ; We didnt find 'content-length', maybe server is using chunked transfer encoding?
  682. ; Try to find 'transfer-encoding' header.
  683.         stdcall HTTP_find_header_field, ebp, str_te
  684.         test    eax, eax
  685.         jz      .not_chunked
  686.  
  687.         mov     ebx, dword[eax]
  688.         or      ebx, 0x20202020
  689.         cmp     ebx, 'chun'
  690.         jne     .not_chunked
  691.         mov     ebx, dword[eax+4]
  692.         or      ebx, 0x00202020
  693.         and     ebx, 0x00ffffff
  694.         cmp     ebx, 'ked'
  695.         jne     .not_chunked
  696.  
  697.         or      [ebp + http_msg.flags], FLAG_CHUNKED
  698.         DEBUGF  1, "Transfer type is: chunked\n"
  699.  
  700. ; Set chunk pointer where first chunk should begin.
  701.         lea     eax, [ebp + http_msg.data]
  702.         add     eax, [ebp + http_msg.header_length]
  703.         mov     [ebp + http_msg.chunk_ptr], eax
  704.  
  705.   .chunk_loop:
  706.         mov     ecx, [ebp + http_msg.write_ptr]
  707.         sub     ecx, [ebp + http_msg.chunk_ptr]
  708.         jb      .need_more_data_chunked         ; TODO: use this ecx !!!
  709.  
  710. ; Chunkline starts here, convert the ASCII hex number into ebx
  711.         mov     esi, [ebp + http_msg.chunk_ptr]
  712.         xor     ebx, ebx
  713.   .chunk_hexloop:
  714.         lodsb
  715.         sub     al, '0'
  716.         jb      .chunk_
  717.         cmp     al, 9
  718.         jbe     .chunk_hex
  719.         sub     al, 'A' - '0' - 10
  720.         jb      .chunk_
  721.         cmp     al, 15
  722.         jbe     .chunk_hex
  723.         sub     al, 'a' - 'A'
  724.         cmp     al, 15
  725.         ja      .chunk_
  726.   .chunk_hex:
  727.         shl     ebx, 4
  728.         add     bl, al
  729.         jmp     .chunk_hexloop
  730.   .chunk_:
  731.         DEBUGF  1, "got chunk of %u bytes\n", ebx
  732. ;;        cmp     esi, [ebp + http_msg.chunk_ptr]
  733. ;;        je
  734. ; If chunk size is 0, all chunks have been received.
  735.         test    ebx, ebx
  736.         jz      .got_all_data_chunked           ; last chunk, hooray! FIXME: what if it wasnt a valid hex number???
  737.  
  738. ; Chunkline ends with a CR, LF or simply LF
  739.   .end_of_chunkline?:
  740.         cmp     al, 10
  741.         je      .end_of_chunkline
  742.         lodsb
  743.         cmp     edi, [ebp + http_msg.write_ptr]
  744.         jb      .end_of_chunkline?
  745.         jmp     .need_more_data
  746.  
  747.   .end_of_chunkline:
  748. ; Update chunk ptr, and remember old one
  749.         mov     edi, [ebp + http_msg.chunk_ptr]
  750.         add     [ebp + http_msg.chunk_ptr], ebx
  751. ; Realloc buffer, make it 'chunksize' bigger.
  752.         mov     eax, [ebp + http_msg.buffer_length]
  753.         add     eax, ebx
  754.         invoke  mem.realloc, ebp, eax
  755.         or      eax, eax
  756.         jz      .no_ram
  757.         add     [ebp + http_msg.buffer_length], ebx
  758.  
  759. ; Update write ptr
  760.         mov     eax, esi
  761.         sub     eax, edi
  762.         sub     [ebp + http_msg.write_ptr], eax
  763.  
  764. ; Now move all received data to the left (remove chunk header).
  765. ; Update content_length accordingly.
  766.         mov     ecx, [ebp + http_msg.write_ptr]
  767.         sub     ecx, esi
  768.         add     [ebp + http_msg.content_received], ecx
  769.         rep     movsb
  770.         jmp     .chunk_loop
  771.  
  772. ; Check if we got all the data.
  773.   .header_parsed:
  774.         add     [ebp + http_msg.content_received], eax
  775.         test    [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
  776.         jz      .need_more_data_and_space
  777.         mov     eax, [ebp + http_msg.content_received]
  778.         cmp     eax, [ebp + http_msg.content_length]
  779.         jae     .got_all_data
  780.         jmp     .need_more_data
  781.  
  782.   .need_more_data_and_space:
  783.         mov     eax, [ebp + http_msg.write_ptr]
  784.         add     eax, BUFFERSIZE
  785.         sub     eax, ebp
  786.         invoke  mem.realloc, ebp, eax
  787.         or      eax, eax
  788.         jz      .no_ram
  789.         mov     [ebp + http_msg.buffer_length], BUFFERSIZE
  790.  
  791.   .need_more_data:
  792.         popa
  793.         xor     eax, eax
  794.         dec     eax
  795.         ret
  796.  
  797.   .need_more_data_chunked:
  798.         add     [ebp + http_msg.content_received], eax
  799.         popa
  800.         xor     eax, eax
  801.         dec     eax
  802.         ret
  803.  
  804.   .got_all_data_chunked:
  805.         mov     eax, [ebp + http_msg.chunk_ptr]
  806.         sub     eax, [ebp + http_msg.header_length]
  807.         sub     eax, http_msg.data
  808.         sub     eax, ebp
  809.         mov     [ebp + http_msg.content_length], eax
  810.         mov     [ebp + http_msg.content_received], eax
  811.   .got_all_data:
  812.         DEBUGF  1, "We got all the data! (%u bytes)\n", [ebp + http_msg.content_received]
  813.         or      [ebp + http_msg.flags], FLAG_GOT_ALL_DATA
  814.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  815.         mcall   close, [ebp + http_msg.socket]
  816.         popa
  817.         xor     eax, eax
  818.         ret
  819.  
  820.   .check_socket:
  821.         cmp     ebx, EWOULDBLOCK
  822.         jne     .socket_error
  823.  
  824.         mcall   29, 9
  825.         sub     eax, TIMEOUT
  826.         cmp     eax, [ebp + http_msg.timestamp]
  827.         jb      .need_more_data
  828.         DEBUGF  1, "ERROR: timeout\n"
  829.         or      [ebp + http_msg.flags], FLAG_TIMEOUT_ERROR
  830.         jmp     .disconnect
  831.  
  832.   .server_closed:
  833.         DEBUGF  1, "server closed connection, transfer complete?\n"
  834.         test    [ebp + http_msg.flags], FLAG_GOT_HEADER
  835.         jz      .server_error
  836.         test    [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
  837.         jz      .got_all_data
  838.  
  839.   .server_error:
  840.         pop     eax
  841.         DEBUGF  1, "ERROR: server closed connection unexpectedly\n"
  842.         or      [ebp + http_msg.flags], FLAG_TRANSFER_FAILED
  843.         jmp     .disconnect
  844.  
  845.   .invalid_header:
  846.         pop     eax
  847.         DEBUGF  1, "ERROR: invalid header\n"
  848.         or      [ebp + http_msg.flags], FLAG_INVALID_HEADER
  849.         jmp     .disconnect
  850.  
  851.   .no_ram:
  852.         DEBUGF  1, "ERROR: out of RAM\n"
  853.         or      [ebp + http_msg.flags], FLAG_NO_RAM
  854.         jmp     .disconnect
  855.  
  856.   .socket_error:
  857.         DEBUGF  1, "ERROR: socket error %u\n", ebx
  858.         or      [ebp + http_msg.flags], FLAG_SOCKET_ERROR
  859.   .disconnect:
  860.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  861.         mcall   close, [ebp + http_msg.socket]
  862.   .connection_closed:
  863.         popa
  864.         xor     eax, eax
  865.         ret
  866.  
  867. endp
  868.  
  869.  
  870.  
  871.  
  872. ;;================================================================================================;;
  873. proc HTTP_free identifier ;///////////////////////////////////////////////////////////////////////;;
  874. ;;------------------------------------------------------------------------------------------------;;
  875. ;? Free the http_msg structure                                                                    ;;
  876. ;;------------------------------------------------------------------------------------------------;;
  877. ;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
  878. ;;------------------------------------------------------------------------------------------------;;
  879. ;< none                                                                                           ;;
  880. ;;================================================================================================;;
  881.  
  882.         pusha
  883.         mov     ebp, [identifier]
  884.  
  885.         test    [ebp + http_msg.flags], FLAG_CONNECTED
  886.         jz      .not_connected
  887.  
  888.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  889.         mcall   close, [ebp + http_msg.socket]
  890.  
  891.   .not_connected:
  892.         invoke  mem.free, ebp
  893.  
  894.         popa
  895.         ret
  896.  
  897. endp
  898.  
  899.  
  900.  
  901. ;;================================================================================================;;
  902. proc HTTP_stop identifier ;///////////////////////////////////////////////////////////////////////;;
  903. ;;------------------------------------------------------------------------------------------------;;
  904. ;? Stops the open connection                                                                      ;;
  905. ;;------------------------------------------------------------------------------------------------;;
  906. ;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
  907. ;;------------------------------------------------------------------------------------------------;;
  908. ;< none                                                                                           ;;
  909. ;;================================================================================================;;
  910.  
  911.         pusha
  912.         mov     ebp, [identifier]
  913.  
  914.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  915.         mcall   close, [ebp + http_msg.socket]
  916.  
  917.         popa
  918.         ret
  919.  
  920. endp
  921.  
  922.  
  923.  
  924. ;;================================================================================================;;
  925. proc HTTP_find_header_field identifier, headername ;//////////////////////////////////////////////;;
  926. ;;------------------------------------------------------------------------------------------------;;
  927. ;? Find a header field in the received HTTP header                                                ;;
  928. ;;------------------------------------------------------------------------------------------------;;
  929. ;> identifier   = ptr to http_msg struct                                                          ;;
  930. ;> headername   = ptr to ASCIIZ string containg field you want to find (must be in lowercase)     ;;
  931. ;;------------------------------------------------------------------------------------------------;;
  932. ;< eax = 0 (error) / ptr to content of the HTTP header field                                      ;;
  933. ;;================================================================================================;;
  934.         push    ebx ecx edx esi edi
  935.  
  936.         DEBUGF  1, "Find header field: %s\n", [headername]
  937.  
  938.         mov     ebx, [identifier]
  939.         test    [ebx + http_msg.flags], FLAG_GOT_HEADER
  940.         jz      .fail
  941.  
  942.         lea     edx, [ebx + http_msg.data]
  943.         mov     ecx, edx
  944.         add     ecx, [ebx + http_msg.header_length]
  945.  
  946.   .restart:
  947.         mov     esi, [headername]
  948.         mov     edi, edx
  949.   .loop:
  950.         cmp     edi, ecx
  951.         jae     .fail
  952.         lodsb
  953.         scasb
  954.         je      .loop
  955.         test    al, al
  956.         jz      .done?
  957.   .next:
  958.         inc     edx
  959.         jmp     .restart
  960.  
  961.   .not_done:
  962.         inc     edi
  963.   .done?:
  964.         cmp     byte[edi-1], ':'
  965.         je      .almost_done
  966.         cmp     byte[edi-1], ' '
  967.         je      .not_done
  968.         cmp     byte[edi-1], 9  ; tab
  969.         je      .not_done
  970.  
  971.         jmp     .next
  972.  
  973.   .almost_done:                 ; FIXME: buffer overflow?
  974.         dec     edi
  975.         DEBUGF  1, "Found header field\n"
  976.   .spaceloop:
  977.         inc     edi
  978.         cmp     byte[edi], ' '
  979.         je      .spaceloop
  980.         cmp     byte[edi], 9    ; tab
  981.         je      .spaceloop
  982.  
  983.         mov     eax, edi
  984.         pop     edi esi edx ecx ebx
  985.         ret
  986.  
  987.   .fail:
  988.         pop     edi esi edx ecx ebx
  989.         xor     eax, eax
  990.         ret
  991.  
  992. endp
  993.  
  994.  
  995.  
  996. ;;================================================================================================;;
  997. proc URI_escape URI ;/////////////////////////////////////////////////////////////////////////////;;
  998. ;;------------------------------------------------------------------------------------------------;;
  999. ;?                                                                                                ;;
  1000. ;;------------------------------------------------------------------------------------------------;;
  1001. ;> URI = ptr to ASCIIZ URI                                                                        ;;
  1002. ;;------------------------------------------------------------------------------------------------;;
  1003. ;< eax = 0 (error) / ptr to ASCIIZ URI                                                            ;;
  1004. ;;================================================================================================;;
  1005.  
  1006.         pusha
  1007.  
  1008.         invoke  mem.alloc, URLMAXLEN
  1009.         test    eax, eax
  1010.         jz      .error
  1011.         mov     [esp + 7 * 4], eax              ; return ptr in eax
  1012.         mov     esi, [URI]
  1013.         mov     edi, eax
  1014.         xor     ebx, ebx
  1015.         xor     ecx, ecx
  1016.   .loop:
  1017.         lodsb
  1018.         test    al, al
  1019.         jz      .done
  1020.  
  1021.         mov     cl, al
  1022.         and     cl, 0x1f
  1023.         mov     bl, al
  1024.         shr     bl, 5
  1025.         bt      dword[bits_must_escape + ebx], ecx
  1026.         jc      .escape
  1027.  
  1028.         stosb
  1029.         jmp     .loop
  1030.  
  1031.   .escape:
  1032.         mov     al, '%'
  1033.         stosb
  1034.         mov     bl, byte[esi-1]
  1035.         shr     bl, 4
  1036.         mov     al, byte[str_hex + ebx]
  1037.         stosb
  1038.         mov     bl, byte[esi-1]
  1039.         and     bl, 0x0f
  1040.         mov     al, byte[str_hex + ebx]
  1041.         stosb
  1042.         jmp     .loop
  1043.  
  1044.  
  1045.   .done:
  1046.         stosb
  1047.  
  1048.         popa
  1049.         ret
  1050.  
  1051.   .error:
  1052.         popa
  1053.         xor     eax, eax
  1054.         ret
  1055.  
  1056. endp
  1057.  
  1058.  
  1059.  
  1060. ;;================================================================================================;;
  1061. proc URI_unescape URI ;///////////////////////////////////////////////////////////////////////////;;
  1062. ;;------------------------------------------------------------------------------------------------;;
  1063. ;?                                                                                                ;;
  1064. ;;------------------------------------------------------------------------------------------------;;
  1065. ;> URI = ptr to ASCIIZ URI                                                                        ;;
  1066. ;;------------------------------------------------------------------------------------------------;;
  1067. ;< eax = 0 (error) / ptr to ASCIIZ URI                                                            ;;
  1068. ;;================================================================================================;;
  1069.  
  1070.         pusha
  1071.  
  1072.         invoke  mem.alloc, URLMAXLEN
  1073.         test    eax, eax
  1074.         jz      .error
  1075.         mov     [esp + 7 * 4], eax              ; return ptr in eax
  1076.         mov     esi, [URI]
  1077.         mov     edi, eax
  1078.   .loop:
  1079.         lodsb
  1080.         test    al, al
  1081.         jz      .done
  1082.  
  1083.         cmp     al, '%'
  1084.         je      .unescape
  1085.  
  1086.         stosb
  1087.         jmp     .loop
  1088.  
  1089.   .unescape:
  1090.         xor     ebx, ebx
  1091.         xor     ecx, ecx
  1092.   .unescape_nibble:
  1093.         lodsb
  1094.         sub     al, '0'
  1095.         jb      .fail
  1096.         cmp     al, 9
  1097.         jbe     .nibble_ok
  1098.         sub     al, 'A' - '0' - 10
  1099.         jb      .fail
  1100.         cmp     al, 15
  1101.         jbe     .nibble_ok
  1102.         sub     al, 'a' - 'A'
  1103.         cmp     al, 15
  1104.         ja      .fail
  1105.   .nibble_ok:
  1106.         shl     bl, 8
  1107.         or      bl, al
  1108.         dec     ecx
  1109.         jc      .unescape_nibble
  1110.         mov     al, bl
  1111.         stosb
  1112.         jmp     .loop
  1113.  
  1114.   .fail:
  1115.         DEBUGF  1, "ERROR: invalid URI!\n"
  1116.         jmp     .loop
  1117.  
  1118.   .done:
  1119.         stosb
  1120.  
  1121.         popa
  1122.         ret
  1123.  
  1124.   .error:
  1125.         popa
  1126.         xor     eax, eax
  1127.         ret
  1128.  
  1129. endp
  1130.  
  1131.  
  1132.  
  1133.  
  1134.  
  1135. ;;================================================================================================;;
  1136. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1137. ;;================================================================================================;;
  1138. ;! Internal procedures section                                                                    ;;
  1139. ;;                                                                                                ;;
  1140. ;; NOTICE: These procedures do not follow stdcall conventions and thus may destroy any register.  ;;
  1141. ;;================================================================================================;;
  1142. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1143. ;;================================================================================================;;
  1144.  
  1145.  
  1146.  
  1147.  
  1148. ;;================================================================================================;;
  1149. proc open_connection hostname, port ;/////////////////////////////////////////////////////////////;;
  1150. ;;------------------------------------------------------------------------------------------------;;
  1151. ;? Connects to a HTTP server                                                                      ;;
  1152. ;;------------------------------------------------------------------------------------------------;;
  1153. ;> hostname     = ptr to ASCIIZ hostname                                                          ;;
  1154. ;> port         = port (x86 byte order)                                                           ;;
  1155. ;;------------------------------------------------------------------------------------------------;;
  1156. ;< eax = 0 (error) / socketnum                                                                    ;;
  1157. ;;================================================================================================;;
  1158.  
  1159. locals
  1160.         sockaddr        dd ?
  1161.         socketnum       dd ?
  1162. endl
  1163.  
  1164.         cmp     [proxyAddr], 0
  1165.         je      .no_proxy
  1166.  
  1167.         mov     [hostname], proxyAddr
  1168.  
  1169.         push    [proxyPort]
  1170.         pop     [port]
  1171.   .no_proxy:
  1172.  
  1173. ; Resolve the hostname
  1174.         DEBUGF  1, "Resolving hostname\n"
  1175.         push    esp     ; reserve stack place
  1176.         push    esp     ; fourth parameter
  1177.         push    0       ; third parameter
  1178.         push    0       ; second parameter
  1179.         push    [hostname]
  1180.         call    [getaddrinfo]
  1181.         pop     esi
  1182.         test    eax, eax
  1183.         jnz     .error1
  1184.  
  1185. ; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
  1186.         mov     esi, [esi + addrinfo.ai_addr]
  1187.         mov     [sockaddr], esi
  1188.         mov     eax, [esi + sockaddr_in.sin_addr]
  1189.         test    eax, eax
  1190.         jz      .error2
  1191.  
  1192.         DEBUGF  1, "Server ip=%u.%u.%u.%u\n", \
  1193.         [esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
  1194.         [esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
  1195.  
  1196.         mov     [esi + sockaddr_in.sin_family], AF_INET4
  1197.         mov     eax, [port]
  1198.         xchg    al, ah
  1199.         mov     [esi + sockaddr_in.sin_port], ax
  1200.  
  1201. ; Connect to the server.
  1202.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  1203.         test    eax, eax
  1204.         jz      .error2
  1205.         mov     [socketnum], eax
  1206.         DEBUGF  1, "Socket: 0x%x\n", eax
  1207.  
  1208.         mcall   connect, [socketnum], [sockaddr], 18
  1209.         test    eax, eax
  1210.         jnz     .error2
  1211.         DEBUGF  1, "Socket is now connected.\n"
  1212.  
  1213. ; free allocated memory
  1214.         push    [sockaddr]
  1215.         call    [freeaddrinfo]
  1216.  
  1217.         mov     eax, [socketnum]
  1218.         ret
  1219.  
  1220.   .error2:
  1221.  
  1222. ; free allocated memory
  1223.         push    [sockaddr]
  1224.         call    [freeaddrinfo]
  1225.  
  1226.   .error1:
  1227.         xor     eax, eax
  1228.         ret
  1229.  
  1230. endp
  1231.  
  1232.  
  1233. ;;================================================================================================;;
  1234. proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
  1235. ;;------------------------------------------------------------------------------------------------;;
  1236. ;? Split a given URL into hostname and pageaddr                                                   ;;
  1237. ;;------------------------------------------------------------------------------------------------;;
  1238. ;> URL = ptr to ASCIIZ URL                                                                        ;;
  1239. ;;------------------------------------------------------------------------------------------------;;
  1240. ;< eax = 0 (error) / ptr to ASCIIZ hostname                                                       ;;
  1241. ;< ebx = ptr to ASCIIZ pageaddr                                                                   ;;
  1242. ;< ecx = port number                                                                              ;;
  1243. ;;================================================================================================;;
  1244.  
  1245. locals
  1246.         urlsize         dd ?
  1247.         hostname        dd ?
  1248.         pageaddr        dd ?
  1249.         port            dd ?
  1250. endl
  1251.  
  1252.         DEBUGF  1, "parsing URL: %s\n", [URL]
  1253.  
  1254. ; remove any leading protocol text
  1255.         mov     esi, [URL]
  1256.         mov     ecx, URLMAXLEN
  1257.         mov     ax, '//'
  1258.   .loop1:
  1259.         cmp     byte[esi], 0            ; end of URL?
  1260.         je      .url_ok                 ; yep, so not found
  1261.         cmp     [esi], ax
  1262.         je      .skip_proto
  1263.         inc     esi
  1264.         dec     ecx
  1265.         jnz     .loop1
  1266.         jmp     .invalid
  1267.  
  1268.   .skip_proto:
  1269.         inc     esi                     ; skip the two '/'
  1270.         inc     esi
  1271.         mov     [URL], esi              ; update pointer so it skips protocol
  1272.         jmp     .loop1                  ; we still need to find the length of the URL
  1273.  
  1274.   .url_ok:
  1275.         sub     esi, [URL]              ; calculate total length of URL
  1276.         mov     [urlsize], esi
  1277.  
  1278.  
  1279. ; now look for page delimiter - it's a '/' character
  1280.         mov     ecx, esi                ; URL length
  1281.         mov     edi, [URL]
  1282.         mov     al, '/'
  1283.         repne   scasb
  1284.         jne     @f
  1285.         dec     edi                     ; return one char, '/' must be part of the pageaddr
  1286.         inc     ecx                     ;
  1287.   @@:
  1288.         push    ecx edi                 ; remember the pointer and length of pageaddr
  1289.  
  1290.  
  1291. ; Create new buffer and put hostname in it.
  1292.         mov     ecx, edi
  1293.         sub     ecx, [URL]
  1294.         inc     ecx                     ; we will add a 0 byte at the end
  1295.         invoke  mem.alloc, ecx
  1296.         or      eax, eax
  1297.         jz      .no_mem
  1298.  
  1299.         mov     [hostname], eax         ; copy hostname to buffer
  1300.         mov     edi, eax
  1301.         mov     esi, [URL]
  1302.         dec     ecx
  1303.         rep     movsb
  1304.         xor     al, al
  1305.         stosb
  1306.  
  1307. ; Check if user provided a port, and convert it if so.
  1308.         mov     esi, [hostname]
  1309.         mov     [port], 80              ; default port if user didnt provide one
  1310.   .portloop:
  1311.         lodsb
  1312.         test    al, al
  1313.         jz      .no_port
  1314.         cmp     al, ':'
  1315.         jne     .portloop
  1316.  
  1317.         push    esi
  1318.         call    ascii_dec_ebx
  1319.         pop     edi
  1320.         cmp     byte[esi-1], 0
  1321.         jne     .invalid
  1322.         cmp     [proxyAddr], 0          ; remove port number from hostname
  1323.         jne     @f                      ; unless when we are using proxy
  1324.         mov     byte[edi-1], 0
  1325.   @@:
  1326.         test    ebx, ebx
  1327.         je      .invalid
  1328.         cmp     ebx, 0xffff
  1329.         ja      .invalid
  1330.         mov     [port], ebx
  1331.   .no_port:
  1332.  
  1333.  
  1334. ; Did user provide a pageaddr?
  1335.         mov     [pageaddr], str_slash   ; assume there is no pageaddr
  1336.         pop     esi ecx
  1337.         test    ecx, ecx
  1338.         jz      .no_page
  1339.  
  1340. ; Create new buffer and put pageaddr into it.
  1341.         inc     ecx                     ; we will add a 0 byte at the end
  1342.         invoke  mem.alloc, ecx
  1343.         or      eax, eax
  1344.         jz      .no_mem
  1345.  
  1346.         mov     [pageaddr], eax         ; copy pageaddr to buffer
  1347.         mov     edi, eax
  1348.         dec     ecx
  1349.         rep     movsb
  1350.         xor     al, al
  1351.         stosb
  1352.  
  1353.   .no_page:
  1354.         mov     eax, [hostname]
  1355.         mov     ebx, [pageaddr]
  1356.         mov     ecx, [port]
  1357.  
  1358.         DEBUGF  1, "hostname: %s\n", eax
  1359.         DEBUGF  1, "pageaddr: %s\n", ebx
  1360.         DEBUGF  1, "port: %u\n", ecx
  1361.  
  1362.         ret
  1363.  
  1364.   .no_mem:
  1365.         DEBUGF  1, "Out of memory!\n"
  1366.         xor     eax, eax
  1367.         ret
  1368.  
  1369.   .invalid:
  1370.         DEBUGF  1, "Invalid URL!\n"
  1371.         xor     eax, eax
  1372.         ret
  1373.  
  1374. endp
  1375.  
  1376.  
  1377.  
  1378.  
  1379.  
  1380. ;;================================================================================================;;
  1381. proc append_proxy_auth_header ;///////////////////////////////////////////////////////////////////;;
  1382. ;;------------------------------------------------------------------------------------------------;;
  1383. ;? Appends the proxy authentication header                                                        ;;
  1384. ;;------------------------------------------------------------------------------------------------;;
  1385. ;> /                                                                                              ;;
  1386. ;;------------------------------------------------------------------------------------------------;;
  1387. ;< /                                                                                              ;;
  1388. ;;================================================================================================;;
  1389.         mov     esi, str_proxy_auth
  1390.         mov     ecx, str_proxy_auth.length
  1391.         rep     movsb
  1392. ; base64-encode string <user>:<password>
  1393.         mov     esi, proxyUser
  1394.  
  1395. apah000:
  1396.         lodsb
  1397.         test    al, al
  1398.         jz      apah001
  1399.         call    encode_base64_byte
  1400.         jmp     apah000
  1401.  
  1402. apah001:
  1403.         mov     al, ':'
  1404.         call    encode_base64_byte
  1405.         mov     esi, proxyPassword
  1406.  
  1407. apah002:
  1408.         lodsb
  1409.         test    al, al
  1410.         jz      apah003
  1411.         call    encode_base64_byte
  1412.         jmp     apah002
  1413.  
  1414. apah003:
  1415.         call    encode_base64_final
  1416.         ret
  1417.  
  1418. encode_base64_byte:
  1419.         inc     ecx
  1420.         shl     edx, 8
  1421.         mov     dl, al
  1422.         cmp     ecx, 3
  1423.         je      ebb001
  1424.         ret
  1425.  
  1426. ebb001:
  1427.         shl     edx, 8
  1428.         inc     ecx
  1429.  
  1430. ebb002:
  1431.         rol     edx, 6
  1432.         xor     eax, eax
  1433.         xchg    al, dl
  1434.         mov     al, [base64_table+eax]
  1435.         stosb
  1436.         loop    ebb002
  1437.         ret
  1438.  
  1439. encode_base64_final:
  1440.         mov     al, 0
  1441.         test    ecx, ecx
  1442.         jz      ebf000
  1443.         call    encode_base64_byte
  1444.         test    ecx, ecx
  1445.         jz      ebf001
  1446.         call    encode_base64_byte
  1447.         mov     byte [edi-2], '='
  1448.  
  1449. ebf001:
  1450.         mov     byte [edi-1], '='
  1451.  
  1452. ebf000:
  1453.         ret
  1454.  
  1455. endp
  1456.  
  1457.  
  1458. ;;================================================================================================;;
  1459. proc eax_ascii_dec ;//////////////////////////////////////////////////////////////////////////////;;
  1460. ;;------------------------------------------------------------------------------------------------;;
  1461. ;? Convert eax to ASCII decimal number                                                            ;;
  1462. ;;------------------------------------------------------------------------------------------------;;
  1463. ;> eax = number                                                                                   ;;
  1464. ;> edi = ptr where to write ASCII decimal number                                                  ;;
  1465. ;;------------------------------------------------------------------------------------------------;;
  1466. ;< /                                                                                              ;;
  1467. ;;================================================================================================;;
  1468.  
  1469.         push    -'0'
  1470.         mov     ecx, 10
  1471.   .loop:
  1472.         xor     edx, edx
  1473.         div     ecx
  1474.         add     dl, '0'
  1475.         push    edx
  1476.         test    eax, eax
  1477.         jnz     .loop
  1478.  
  1479.   .loop2:
  1480.         pop     eax
  1481.         add     al, '0'
  1482.         jz      .done
  1483.         stosb
  1484.         jmp     .loop2
  1485.   .done:
  1486.  
  1487.         ret
  1488.  
  1489. endp
  1490.  
  1491.  
  1492. ;;================================================================================================;;
  1493. proc ascii_dec_ebx ;//////////////////////////////////////////////////////////////////////////////;;
  1494. ;;------------------------------------------------------------------------------------------------;;
  1495. ;? Convert ASCII decimal number to ebx                                                            ;;
  1496. ;;------------------------------------------------------------------------------------------------;;
  1497. ;> esi = ptr where to read ASCII decimal number                                                   ;;
  1498. ;;------------------------------------------------------------------------------------------------;;
  1499. ;> ebx = number                                                                                   ;;
  1500. ;;================================================================================================;;
  1501.  
  1502.         xor     eax, eax
  1503.         xor     ebx, ebx
  1504.   .loop:
  1505.         lodsb
  1506.         sub     al, '0'
  1507.         jb      .done
  1508.         cmp     al, 9
  1509.         ja      .done
  1510.         lea     ebx, [ebx + 4*ebx]
  1511.         shl     ebx, 1
  1512.         add     ebx, eax
  1513.         jmp     .loop
  1514.   .done:
  1515.  
  1516.         ret
  1517.  
  1518. endp
  1519.  
  1520.  
  1521. ;;================================================================================================;;
  1522. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1523. ;;================================================================================================;;
  1524. ;! Imported functions section                                                                     ;;
  1525. ;;================================================================================================;;
  1526. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1527. ;;================================================================================================;;
  1528.  
  1529.  
  1530. align 16
  1531. @IMPORT:
  1532.  
  1533. library \
  1534.         libini, 'libini.obj', \
  1535.         network, 'network.obj'
  1536.  
  1537. import  libini, \
  1538.         ini.get_str, 'ini_get_str', \
  1539.         ini.get_int, 'ini_get_int'
  1540.  
  1541. import  network,\
  1542.         getaddrinfo, 'getaddrinfo',\
  1543.         freeaddrinfo,  'freeaddrinfo',\
  1544.         inet_ntoa, 'inet_ntoa'
  1545.  
  1546. ;;===========================================================================;;
  1547. ;;///////////////////////////////////////////////////////////////////////////;;
  1548. ;;===========================================================================;;
  1549. ;! Exported functions section                                                ;;
  1550. ;;===========================================================================;;
  1551. ;;///////////////////////////////////////////////////////////////////////////;;
  1552. ;;===========================================================================;;
  1553.  
  1554.  
  1555. align 4
  1556. @EXPORT:
  1557. export  \
  1558.         lib_init                , 'lib_init'            , \
  1559.         0x00010001              , 'version'             , \
  1560.         HTTP_get                , 'get'                 , \
  1561.         HTTP_head               , 'head'                , \
  1562.         HTTP_post               , 'post'                , \
  1563.         HTTP_find_header_field  , 'find_header_field'   , \
  1564.         HTTP_process            , 'process'             , \
  1565.         HTTP_free               , 'free'                , \
  1566.         HTTP_stop               , 'stop'                , \
  1567.         URI_escape              , 'escape'              , \
  1568.         URI_unescape            , 'unescape'
  1569.  
  1570. ;        HTTP_put                , 'put'                 , \
  1571. ;        HTTP_delete             , 'delete'              , \
  1572. ;        HTTP_trace              , 'trace'               , \
  1573. ;        HTTP_connect            , 'connect'             , \
  1574.  
  1575.  
  1576.  
  1577. section '.data' data readable writable align 16
  1578.  
  1579. inifile         db '/sys/settings/network.ini', 0
  1580.  
  1581. sec_proxy:
  1582. key_proxy       db 'proxy', 0
  1583. key_proxyport   db 'port', 0
  1584. key_user        db 'user', 0
  1585. key_password    db 'password', 0
  1586.  
  1587. str_http11      db ' HTTP/1.1', 13, 10, 'Host: '
  1588.   .length       = $ - str_http11
  1589. str_post_cl     db 13, 10, 'Content-Length: '
  1590.   .length       = $ - str_post_cl
  1591. str_post_ct     db 13, 10, 'Content-Type: '
  1592.   .length       = $ - str_post_ct
  1593. str_close       db 13, 10, 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
  1594.   .length       = $ - str_close
  1595. str_proxy_auth  db 13, 10, 'Proxy-Authorization: Basic '
  1596.   .length       = $ - str_proxy_auth
  1597.  
  1598. str_http        db 'http://', 0
  1599.  
  1600. base64_table    db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  1601.                 db '0123456789+/'
  1602.  
  1603. str_cl          db 'content-length', 0
  1604. str_slash       db '/', 0
  1605. str_te          db 'transfer-encoding', 0
  1606. str_get         db 'GET ', 0
  1607. str_head        db 'HEAD ', 0
  1608. str_post        db 'POST ', 0
  1609.  
  1610. bits_must_escape:
  1611. dd      0xffffffff                                                      ; 00-1F
  1612. dd      1 shl 0 + 1 shl 2 + 1 shl 3 + 1 shl 5 + 1 shl 28 + 1 shl 30     ; "#%<>
  1613. dd      1 shl 27 + 1 shl 28 + 1 shl 29 + 1 shl 30                       ;[\]^
  1614. dd      1 shl 0 + 1 shl 27 + 1 shl 28 + 1 shl 29 + 1 shl 31             ;`{|} DEL
  1615.  
  1616. dd      0xffffffff
  1617. dd      0xffffffff
  1618. dd      0xffffffff
  1619. dd      0xffffffff
  1620.  
  1621. str_hex:
  1622. db '0123456789ABCDEF'
  1623.  
  1624. include_debug_strings
  1625.  
  1626. ; uninitialized data
  1627. mem.alloc       dd ?
  1628. mem.free        dd ?
  1629. mem.realloc     dd ?
  1630. dll.load        dd ?
  1631.  
  1632. proxyAddr       rb 256
  1633. proxyUser       rb 256
  1634. proxyPassword   rb 256
  1635. proxyPort       dd ?