Subversion Repositories Kolibri OS

Rev

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

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