Subversion Repositories Kolibri OS

Rev

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