Subversion Repositories Kolibri OS

Rev

Rev 4830 | Rev 4848 | 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.         pusha
  1135.  
  1136.         invoke  mem.alloc, URLMAXLEN
  1137.         test    eax, eax
  1138.         jz      .error
  1139.         mov     [esp + 7 * 4], eax              ; return ptr in eax
  1140.         mov     esi, [URI]
  1141.         mov     edi, eax
  1142.         xor     ebx, ebx
  1143.         xor     ecx, ecx
  1144.   .loop:
  1145.         lodsb
  1146.         test    al, al
  1147.         jz      .done
  1148.  
  1149.         mov     cl, al
  1150.         and     cl, 0x1f
  1151.         mov     bl, al
  1152.         shr     bl, 3
  1153.         and     bl, not 3
  1154.         bt      dword[bits_must_escape + ebx], ecx
  1155.         jc      .escape
  1156.  
  1157.         stosb
  1158.         jmp     .loop
  1159.  
  1160.   .escape:
  1161.         mov     al, '%'
  1162.         stosb
  1163.         mov     bl, byte[esi-1]
  1164.         shr     bl, 4
  1165.         mov     al, byte[str_hex + ebx]
  1166.         stosb
  1167.         mov     bl, byte[esi-1]
  1168.         and     bl, 0x0f
  1169.         mov     al, byte[str_hex + ebx]
  1170.         stosb
  1171.         jmp     .loop
  1172.  
  1173.  
  1174.   .done:
  1175.         stosb
  1176.         sub     edi, [esp + 7 * 4]
  1177.         dec     edi
  1178.         mov     [esp + 4 * 4], edi
  1179.  
  1180.         popa
  1181.         ret
  1182.  
  1183.   .error:
  1184.         popa
  1185.         xor     eax, eax
  1186.         ret
  1187.  
  1188. endp
  1189.  
  1190.  
  1191.  
  1192. ;;================================================================================================;;
  1193. proc HTTP_unescape URI ;//////////////////////////////////////////////////////////////////////////;;
  1194. ;;------------------------------------------------------------------------------------------------;;
  1195. ;?                                                                                                ;;
  1196. ;;------------------------------------------------------------------------------------------------;;
  1197. ;> URI = ptr to ASCIIZ URI                                                                        ;;
  1198. ;;------------------------------------------------------------------------------------------------;;
  1199. ;< eax = 0 (error) / ptr to ASCIIZ URI                                                            ;;
  1200. ;;================================================================================================;;
  1201.  
  1202.         pusha
  1203.  
  1204.         invoke  mem.alloc, URLMAXLEN
  1205.         test    eax, eax
  1206.         jz      .error
  1207.         mov     [esp + 7 * 4], eax              ; return ptr in eax
  1208.         mov     esi, [URI]
  1209.         mov     edi, eax
  1210.   .loop:
  1211.         lodsb
  1212.         test    al, al
  1213.         jz      .done
  1214.  
  1215.         cmp     al, '%'
  1216.         je      .unescape
  1217.  
  1218.         stosb
  1219.         jmp     .loop
  1220.  
  1221.   .unescape:
  1222.         xor     ebx, ebx
  1223.         xor     ecx, ecx
  1224.   .unescape_nibble:
  1225.         lodsb
  1226.         sub     al, '0'
  1227.         jb      .fail
  1228.         cmp     al, 9
  1229.         jbe     .nibble_ok
  1230.         sub     al, 'A' - '0' - 10
  1231.         jb      .fail
  1232.         cmp     al, 15
  1233.         jbe     .nibble_ok
  1234.         sub     al, 'a' - 'A'
  1235.         cmp     al, 15
  1236.         ja      .fail
  1237.   .nibble_ok:
  1238.         shl     bl, 8
  1239.         or      bl, al
  1240.         dec     ecx
  1241.         jc      .unescape_nibble
  1242.         mov     al, bl
  1243.         stosb
  1244.         jmp     .loop
  1245.  
  1246.   .fail:
  1247.         DEBUGF  1, "ERROR: invalid URI!\n"
  1248.         jmp     .loop
  1249.  
  1250.   .done:
  1251.         stosb
  1252.  
  1253.         popa
  1254.         ret
  1255.  
  1256.   .error:
  1257.         popa
  1258.         xor     eax, eax
  1259.         ret
  1260.  
  1261. endp
  1262.  
  1263.  
  1264.  
  1265.  
  1266.  
  1267. ;;================================================================================================;;
  1268. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1269. ;;================================================================================================;;
  1270. ;! Internal procedures section                                                                    ;;
  1271. ;;                                                                                                ;;
  1272. ;; NOTICE: These procedures do not follow stdcall conventions and thus may destroy any register.  ;;
  1273. ;;================================================================================================;;
  1274. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1275. ;;================================================================================================;;
  1276.  
  1277.  
  1278.  
  1279.  
  1280. ;;================================================================================================;;
  1281. proc open_connection hostname, port ;/////////////////////////////////////////////////////////////;;
  1282. ;;------------------------------------------------------------------------------------------------;;
  1283. ;? Connects to a HTTP server                                                                      ;;
  1284. ;;------------------------------------------------------------------------------------------------;;
  1285. ;> hostname     = ptr to ASCIIZ hostname                                                          ;;
  1286. ;> port         = port (x86 byte order)                                                           ;;
  1287. ;;------------------------------------------------------------------------------------------------;;
  1288. ;< eax = 0 (error) / socketnum                                                                    ;;
  1289. ;;================================================================================================;;
  1290.  
  1291. locals
  1292.         sockaddr        dd ?
  1293.         socketnum       dd ?
  1294. endl
  1295.  
  1296.         cmp     [proxyAddr], 0
  1297.         je      .no_proxy
  1298.  
  1299.         mov     [hostname], proxyAddr
  1300.  
  1301.         push    [proxyPort]
  1302.         pop     [port]
  1303.   .no_proxy:
  1304.  
  1305. ; Resolve the hostname
  1306.         DEBUGF  1, "Resolving hostname\n"
  1307.         push    esp     ; reserve stack place
  1308.         push    esp     ; fourth parameter
  1309.         push    0       ; third parameter
  1310.         push    0       ; second parameter
  1311.         push    [hostname]
  1312.         call    [getaddrinfo]
  1313.         pop     esi
  1314.         test    eax, eax
  1315.         jnz     .error1
  1316.  
  1317. ; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
  1318.         mov     esi, [esi + addrinfo.ai_addr]
  1319.         mov     [sockaddr], esi
  1320.         mov     eax, [esi + sockaddr_in.sin_addr]
  1321.         test    eax, eax
  1322.         jz      .error2
  1323.  
  1324.         DEBUGF  1, "Server ip=%u.%u.%u.%u\n", \
  1325.         [esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
  1326.         [esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
  1327.  
  1328.         mov     [esi + sockaddr_in.sin_family], AF_INET4
  1329.         mov     eax, [port]
  1330.         xchg    al, ah
  1331.         mov     [esi + sockaddr_in.sin_port], ax
  1332.  
  1333. ; Connect to the server.
  1334.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  1335.         test    eax, eax
  1336.         jz      .error2
  1337.         mov     [socketnum], eax
  1338.         DEBUGF  1, "Socket: 0x%x\n", eax
  1339.  
  1340.         mcall   connect, [socketnum], [sockaddr], 18
  1341.         test    eax, eax
  1342.         jnz     .error2
  1343.         DEBUGF  1, "Socket is now connected.\n"
  1344.  
  1345. ; free allocated memory
  1346.         push    [sockaddr]
  1347.         call    [freeaddrinfo]
  1348.  
  1349.         mov     eax, [socketnum]
  1350.         ret
  1351.  
  1352.   .error2:
  1353.  
  1354. ; free allocated memory
  1355.         push    [sockaddr]
  1356.         call    [freeaddrinfo]
  1357.  
  1358.   .error1:
  1359.         xor     eax, eax
  1360.         ret
  1361.  
  1362. endp
  1363.  
  1364.  
  1365. ;;================================================================================================;;
  1366. proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
  1367. ;;------------------------------------------------------------------------------------------------;;
  1368. ;? Split a given URL into hostname and pageaddr                                                   ;;
  1369. ;;------------------------------------------------------------------------------------------------;;
  1370. ;> URL = ptr to ASCIIZ URL                                                                        ;;
  1371. ;;------------------------------------------------------------------------------------------------;;
  1372. ;< eax = 0 (error) / ptr to ASCIIZ hostname                                                       ;;
  1373. ;< ebx = ptr to ASCIIZ pageaddr                                                                   ;;
  1374. ;< ecx = port number                                                                              ;;
  1375. ;;================================================================================================;;
  1376.  
  1377. locals
  1378.         urlsize         dd ?
  1379.         hostname        dd ?
  1380.         pageaddr        dd ?
  1381.         port            dd ?
  1382. endl
  1383.  
  1384.         DEBUGF  1, "parsing URL: %s\n", [URL]
  1385.  
  1386. ; remove any leading protocol text
  1387.         mov     esi, [URL]
  1388.         mov     ecx, URLMAXLEN
  1389.         mov     ax, '//'
  1390.   .loop1:
  1391.         cmp     byte[esi], 0            ; end of URL?
  1392.         je      .url_ok                 ; yep, so not found
  1393.         cmp     [esi], ax
  1394.         je      .skip_proto
  1395.         inc     esi
  1396.         dec     ecx
  1397.         jnz     .loop1
  1398.         jmp     .invalid
  1399.  
  1400.   .skip_proto:
  1401.         inc     esi                     ; skip the two '/'
  1402.         inc     esi
  1403.         mov     [URL], esi              ; update pointer so it skips protocol
  1404.         jmp     .loop1                  ; we still need to find the length of the URL
  1405.  
  1406.   .url_ok:
  1407.         sub     esi, [URL]              ; calculate total length of URL
  1408.         mov     [urlsize], esi
  1409.  
  1410.  
  1411. ; now look for page delimiter - it's a '/' character
  1412.         mov     ecx, esi                ; URL length
  1413.         mov     edi, [URL]
  1414.         mov     al, '/'
  1415.         repne   scasb
  1416.         jne     @f
  1417.         dec     edi                     ; return one char, '/' must be part of the pageaddr
  1418.         inc     ecx                     ;
  1419.   @@:
  1420.         push    ecx edi                 ; remember the pointer and length of pageaddr
  1421.  
  1422.  
  1423. ; Create new buffer and put hostname in it.
  1424.         mov     ecx, edi
  1425.         sub     ecx, [URL]
  1426.         inc     ecx                     ; we will add a 0 byte at the end
  1427.         invoke  mem.alloc, ecx
  1428.         or      eax, eax
  1429.         jz      .no_mem
  1430.  
  1431.         mov     [hostname], eax         ; copy hostname to buffer
  1432.         mov     edi, eax
  1433.         mov     esi, [URL]
  1434.         dec     ecx
  1435.         rep     movsb
  1436.         xor     al, al
  1437.         stosb
  1438.  
  1439. ; Check if user provided a port, and convert it if so.
  1440.         mov     esi, [hostname]
  1441.         mov     [port], 80              ; default port if user didnt provide one
  1442.   .portloop:
  1443.         lodsb
  1444.         test    al, al
  1445.         jz      .no_port
  1446.         cmp     al, ':'
  1447.         jne     .portloop
  1448.  
  1449.         push    esi
  1450.         call    ascii_dec_ebx
  1451.         pop     edi
  1452.         cmp     byte[esi-1], 0
  1453.         jne     .invalid
  1454.         cmp     [proxyAddr], 0          ; remove port number from hostname
  1455.         jne     @f                      ; unless when we are using proxy
  1456.         mov     byte[edi-1], 0
  1457.   @@:
  1458.         test    ebx, ebx
  1459.         je      .invalid
  1460.         cmp     ebx, 0xffff
  1461.         ja      .invalid
  1462.         mov     [port], ebx
  1463.   .no_port:
  1464.  
  1465.  
  1466. ; Did user provide a pageaddr?
  1467.         mov     [pageaddr], str_slash   ; assume there is no pageaddr
  1468.         pop     esi ecx
  1469.         test    ecx, ecx
  1470.         jz      .no_page
  1471.  
  1472. ; Create new buffer and put pageaddr into it.
  1473.         inc     ecx                     ; we will add a 0 byte at the end
  1474.         invoke  mem.alloc, ecx
  1475.         or      eax, eax
  1476.         jz      .no_mem
  1477.  
  1478.         mov     [pageaddr], eax         ; copy pageaddr to buffer
  1479.         mov     edi, eax
  1480.         dec     ecx
  1481.         rep     movsb
  1482.         xor     al, al
  1483.         stosb
  1484.  
  1485.   .no_page:
  1486.         mov     eax, [hostname]
  1487.         mov     ebx, [pageaddr]
  1488.         mov     ecx, [port]
  1489.  
  1490.         DEBUGF  1, "hostname: %s\n", eax
  1491.         DEBUGF  1, "pageaddr: %s\n", ebx
  1492.         DEBUGF  1, "port: %u\n", ecx
  1493.  
  1494.         ret
  1495.  
  1496.   .no_mem:
  1497.         DEBUGF  1, "Out of memory!\n"
  1498.         xor     eax, eax
  1499.         ret
  1500.  
  1501.   .invalid:
  1502.         DEBUGF  1, "Invalid URL!\n"
  1503.         xor     eax, eax
  1504.         ret
  1505.  
  1506. endp
  1507.  
  1508.  
  1509.  
  1510.  
  1511.  
  1512. ;;================================================================================================;;
  1513. proc append_proxy_auth_header ;///////////////////////////////////////////////////////////////////;;
  1514. ;;------------------------------------------------------------------------------------------------;;
  1515. ;? Appends the proxy authentication header                                                        ;;
  1516. ;;------------------------------------------------------------------------------------------------;;
  1517. ;> /                                                                                              ;;
  1518. ;;------------------------------------------------------------------------------------------------;;
  1519. ;< /                                                                                              ;;
  1520. ;;================================================================================================;;
  1521.         mov     esi, str_proxy_auth
  1522.         mov     ecx, str_proxy_auth.length
  1523.         rep     movsb
  1524. ; base64-encode string <user>:<password>
  1525.         mov     esi, proxyUser
  1526.  
  1527. apah000:
  1528.         lodsb
  1529.         test    al, al
  1530.         jz      apah001
  1531.         call    encode_base64_byte
  1532.         jmp     apah000
  1533.  
  1534. apah001:
  1535.         mov     al, ':'
  1536.         call    encode_base64_byte
  1537.         mov     esi, proxyPassword
  1538.  
  1539. apah002:
  1540.         lodsb
  1541.         test    al, al
  1542.         jz      apah003
  1543.         call    encode_base64_byte
  1544.         jmp     apah002
  1545.  
  1546. apah003:
  1547.         call    encode_base64_final
  1548.         ret
  1549.  
  1550. encode_base64_byte:
  1551.         inc     ecx
  1552.         shl     edx, 8
  1553.         mov     dl, al
  1554.         cmp     ecx, 3
  1555.         je      ebb001
  1556.         ret
  1557.  
  1558. ebb001:
  1559.         shl     edx, 8
  1560.         inc     ecx
  1561.  
  1562. ebb002:
  1563.         rol     edx, 6
  1564.         xor     eax, eax
  1565.         xchg    al, dl
  1566.         mov     al, [base64_table+eax]
  1567.         stosb
  1568.         loop    ebb002
  1569.         ret
  1570.  
  1571. encode_base64_final:
  1572.         mov     al, 0
  1573.         test    ecx, ecx
  1574.         jz      ebf000
  1575.         call    encode_base64_byte
  1576.         test    ecx, ecx
  1577.         jz      ebf001
  1578.         call    encode_base64_byte
  1579.         mov     byte [edi-2], '='
  1580.  
  1581. ebf001:
  1582.         mov     byte [edi-1], '='
  1583.  
  1584. ebf000:
  1585.         ret
  1586.  
  1587. endp
  1588.  
  1589.  
  1590. ;;================================================================================================;;
  1591. proc eax_ascii_dec ;//////////////////////////////////////////////////////////////////////////////;;
  1592. ;;------------------------------------------------------------------------------------------------;;
  1593. ;? Convert eax to ASCII decimal number                                                            ;;
  1594. ;;------------------------------------------------------------------------------------------------;;
  1595. ;> eax = number                                                                                   ;;
  1596. ;> edi = ptr where to write ASCII decimal number                                                  ;;
  1597. ;;------------------------------------------------------------------------------------------------;;
  1598. ;< /                                                                                              ;;
  1599. ;;================================================================================================;;
  1600.  
  1601.         push    -'0'
  1602.         mov     ecx, 10
  1603.   .loop:
  1604.         xor     edx, edx
  1605.         div     ecx
  1606.         push    edx
  1607.         test    eax, eax
  1608.         jnz     .loop
  1609.  
  1610.   .loop2:
  1611.         pop     eax
  1612.         add     al, '0'
  1613.         jz      .done
  1614.         stosb
  1615.         jmp     .loop2
  1616.   .done:
  1617.  
  1618.         ret
  1619.  
  1620. endp
  1621.  
  1622.  
  1623. ;;================================================================================================;;
  1624. proc ascii_dec_ebx ;//////////////////////////////////////////////////////////////////////////////;;
  1625. ;;------------------------------------------------------------------------------------------------;;
  1626. ;? Convert ASCII decimal number to ebx                                                            ;;
  1627. ;;------------------------------------------------------------------------------------------------;;
  1628. ;> esi = ptr where to read ASCII decimal number                                                   ;;
  1629. ;;------------------------------------------------------------------------------------------------;;
  1630. ;> ebx = number                                                                                   ;;
  1631. ;;================================================================================================;;
  1632.  
  1633.         xor     eax, eax
  1634.         xor     ebx, ebx
  1635.   .loop:
  1636.         lodsb
  1637.         sub     al, '0'
  1638.         jb      .done
  1639.         cmp     al, 9
  1640.         ja      .done
  1641.         lea     ebx, [ebx + 4*ebx]
  1642.         shl     ebx, 1
  1643.         add     ebx, eax
  1644.         jmp     .loop
  1645.   .done:
  1646.  
  1647.         ret
  1648.  
  1649. endp
  1650.  
  1651.  
  1652. ;;================================================================================================;;
  1653. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1654. ;;================================================================================================;;
  1655. ;! Imported functions section                                                                     ;;
  1656. ;;================================================================================================;;
  1657. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1658. ;;================================================================================================;;
  1659.  
  1660.  
  1661. align 16
  1662. @IMPORT:
  1663.  
  1664. library \
  1665.         libini, 'libini.obj', \
  1666.         network, 'network.obj'
  1667.  
  1668. import  libini, \
  1669.         ini.get_str, 'ini_get_str', \
  1670.         ini.get_int, 'ini_get_int'
  1671.  
  1672. import  network,\
  1673.         getaddrinfo, 'getaddrinfo',\
  1674.         freeaddrinfo,  'freeaddrinfo',\
  1675.         inet_ntoa, 'inet_ntoa'
  1676.  
  1677. ;;===========================================================================;;
  1678. ;;///////////////////////////////////////////////////////////////////////////;;
  1679. ;;===========================================================================;;
  1680. ;! Exported functions section                                                ;;
  1681. ;;===========================================================================;;
  1682. ;;///////////////////////////////////////////////////////////////////////////;;
  1683. ;;===========================================================================;;
  1684.  
  1685.  
  1686. align 4
  1687. @EXPORT:
  1688. export  \
  1689.         lib_init                , 'lib_init'            , \
  1690.         0x00010001              , 'version'             , \
  1691.         HTTP_get                , 'get'                 , \
  1692.         HTTP_head               , 'head'                , \
  1693.         HTTP_post               , 'post'                , \
  1694.         HTTP_find_header_field  , 'find_header_field'   , \
  1695.         HTTP_process            , 'process'             , \
  1696.         HTTP_free               , 'free'                , \
  1697.         HTTP_stop               , 'stop'                , \
  1698.         HTTP_escape             , 'escape'              , \
  1699.         HTTP_unescape           , 'unescape'
  1700.  
  1701. ;        HTTP_put                , 'put'                 , \
  1702. ;        HTTP_delete             , 'delete'              , \
  1703. ;        HTTP_trace              , 'trace'               , \
  1704. ;        HTTP_connect            , 'connect'             , \
  1705.  
  1706.  
  1707.  
  1708. section '.data' data readable writable align 16
  1709.  
  1710. inifile         db '/sys/settings/network.ini', 0
  1711.  
  1712. sec_proxy:
  1713. key_proxy       db 'proxy', 0
  1714. key_proxyport   db 'port', 0
  1715. key_user        db 'user', 0
  1716. key_password    db 'password', 0
  1717.  
  1718. str_http11      db ' HTTP/1.1', 13, 10, 'Host: '
  1719.   .length       = $ - str_http11
  1720. str_post_cl     db 13, 10, 'Content-Length: '
  1721.   .length       = $ - str_post_cl
  1722. str_post_ct     db 13, 10, 'Content-Type: '
  1723.   .length       = $ - str_post_ct
  1724. str_proxy_auth  db 13, 10, 'Proxy-Authorization: Basic '
  1725.   .length       = $ - str_proxy_auth
  1726. str_close       db 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
  1727.   .length       = $ - str_close
  1728.  
  1729. str_http        db 'http://', 0
  1730.  
  1731. base64_table    db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  1732.                 db '0123456789+/'
  1733.  
  1734. str_cl          db 'content-length', 0
  1735. str_slash       db '/', 0
  1736. str_te          db 'transfer-encoding', 0
  1737. str_get         db 'GET ', 0
  1738. str_head        db 'HEAD ', 0
  1739. str_post        db 'POST ', 0
  1740.  
  1741. bits_must_escape:
  1742. dd      0xffffffff                                                      ; 00-1F
  1743. dd      1 shl 0 + 1 shl 2 + 1 shl 3 + 1 shl 5 + 1 shl 28 + 1 shl 30     ; "#%<>
  1744. dd      1 shl 27 + 1 shl 28 + 1 shl 29 + 1 shl 30                       ;[\]^
  1745. dd      1 shl 0 + 1 shl 27 + 1 shl 28 + 1 shl 29 + 1 shl 31             ;`{|} DEL
  1746.  
  1747. dd      0xffffffff
  1748. dd      0xffffffff
  1749. dd      0xffffffff
  1750. dd      0xffffffff
  1751.  
  1752. str_hex:
  1753. db '0123456789ABCDEF'
  1754.  
  1755. include_debug_strings
  1756.  
  1757. ; uninitialized data
  1758. mem.alloc       dd ?
  1759. mem.free        dd ?
  1760. mem.realloc     dd ?
  1761. dll.load        dd ?
  1762.  
  1763. proxyAddr       rb 256
  1764. proxyUser       rb 256
  1765. proxyPassword   rb 256
  1766. proxyPort       dd ?