Subversion Repositories Kolibri OS

Rev

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