Subversion Repositories Kolibri OS

Rev

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

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