Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                                 ;;
  3. ;; Copyright (C) KolibriOS team 2004-2013. All rights reserved.    ;;
  4. ;; Distributed under terms of the GNU General Public License       ;;
  5. ;;                                                                 ;;
  6. ;;  HTTP library for KolibriOS                                     ;;
  7. ;;                                                                 ;;
  8. ;;   Written by hidnplayr@kolibrios.org                            ;;
  9. ;;                                                                 ;;
  10. ;;         GNU GENERAL PUBLIC LICENSE                              ;;
  11. ;;          Version 2, June 1991                                   ;;
  12. ;;                                                                 ;;
  13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14.  
  15. ; references:
  16. ; "HTTP made really easy", http://www.jmarshall.com/easy/http/
  17. ; "Hypertext Transfer Protocol -- HTTP/1.1", http://tools.ietf.org/html/rfc2616
  18.  
  19.  
  20.         URLMAXLEN       = 65535
  21.         BUFFERSIZE      = 4096
  22.  
  23.         __DEBUG__       = 1
  24.         __DEBUG_LEVEL__ = 1
  25.  
  26.  
  27. format MS COFF
  28.  
  29. public @EXPORT as 'EXPORTS'
  30.  
  31. include '../../../struct.inc'
  32. include '../../../proc32.inc'
  33. include '../../../macros.inc'
  34. purge section,mov,add,sub
  35. include '../../../debug-fdo.inc'
  36.  
  37. include '../../../network.inc'
  38. include 'http.inc'
  39.  
  40. virtual at 0
  41.         http_msg http_msg
  42. end virtual
  43.  
  44. macro copy_till_zero {
  45.   @@:
  46.         lodsb
  47.         test    al, al
  48.         jz      @f
  49.         stosb
  50.         jmp     @r
  51.   @@:
  52. }
  53.  
  54. macro HTTP_init_buffer buffer, socketnum {
  55.  
  56.         mov     eax, buffer
  57.         push    socketnum
  58.         popd    [eax + http_msg.socket]
  59.         lea     esi, [eax + http_msg.data]
  60.         mov     [eax + http_msg.flags], 0
  61.         mov     [eax + http_msg.write_ptr], esi
  62.         mov     [eax + http_msg.buffer_length], BUFFERSIZE -  http_msg.data
  63.         mov     [eax + http_msg.chunk_ptr], 0
  64.  
  65.         mov     [eax + http_msg.status], 0
  66.         mov     [eax + http_msg.header_length], 0
  67.         mov     [eax + http_msg.content_length], 0
  68. }
  69.  
  70. section '.flat' code readable align 16
  71.  
  72. ;;===========================================================================;;
  73. lib_init: ;//////////////////////////////////////////////////////////////////;;
  74. ;;---------------------------------------------------------------------------;;
  75. ;? Library entry point (called after library load)                           ;;
  76. ;;---------------------------------------------------------------------------;;
  77. ;> eax = pointer to memory allocation routine                                ;;
  78. ;> ebx = pointer to memory freeing routine                                   ;;
  79. ;> ecx = pointer to memory reallocation routine                              ;;
  80. ;> edx = pointer to library loading routine                                  ;;
  81. ;;---------------------------------------------------------------------------;;
  82. ;< eax = 1 (fail) / 0 (ok) (library initialization result)                   ;;
  83. ;;===========================================================================;;
  84.         mov     [mem.alloc], eax
  85.         mov     [mem.free], ebx
  86.         mov     [mem.realloc], ecx
  87.         mov     [dll.load], edx
  88.  
  89.         invoke  dll.load, @IMPORT
  90.         or      eax, eax
  91.         jz      .ok
  92.  
  93. ; load proxy settings
  94.         invoke  ini.get_str, inifile, sec_proxy, key_proxy, proxyAddr, 256, proxyAddr
  95.         invoke  ini.get_int, inifile, sec_proxy, key_proxyport, 80
  96.         mov     [proxyPort], eax
  97.         invoke  ini.get_str, inifile, sec_proxy, key_user, proxyUser, 256, proxyUser
  98.         invoke  ini.get_str, inifile, sec_proxy, key_password, proxyPassword, 256, proxyPassword
  99.  
  100.         xor     eax, eax
  101.         inc     eax
  102.         ret
  103.  
  104.   .ok:
  105.         xor     eax, eax
  106.         ret
  107.  
  108.  
  109.  
  110.  
  111.  
  112. ;;================================================================================================;;
  113. proc HTTP_get URL ;///////////////////////////////////////////////////////////////////////////////;;
  114. ;;------------------------------------------------------------------------------------------------;;
  115. ;?                                                                                                ;;
  116. ;;------------------------------------------------------------------------------------------------;;
  117. ;> _                                                                                              ;;
  118. ;;------------------------------------------------------------------------------------------------;;
  119. ;< eax = 0 (error) / buffer ptr                                                                   ;;
  120. ;;================================================================================================;;
  121. locals
  122.         hostname        dd ?
  123.         pageaddr        dd ?
  124.         sockaddr        dd ?
  125.         socketnum       dd ?
  126.         buffer          dd ?
  127.         port            dd ?
  128. endl
  129.  
  130. ; split the URL into hostname and pageaddr
  131.         stdcall parse_url, [URL]
  132.         test    eax, eax
  133.         jz      .error
  134.         mov     [hostname], eax
  135.         mov     [pageaddr], ebx
  136.  
  137. ; Do we need to use a proxy?
  138.         cmp     [proxyAddr], 0
  139.         jne     .proxy_done
  140.  
  141.   .proxy_done:
  142.  
  143. ;;;;
  144.         mov     [port], 80      ;;;; FIXME
  145.  
  146. ; Connect to the other side.
  147.         stdcall open_connection, [hostname], [port]
  148.         test    eax, eax
  149.         jz      .error
  150.         mov     [socketnum], eax
  151.  
  152. ; Create the HTTP request.
  153.         invoke  mem.alloc, BUFFERSIZE
  154.         test    eax, eax
  155.         jz      .error
  156.         mov     [buffer], eax
  157.         mov     edi, eax
  158.         DEBUGF  1, "Buffer has been allocated.\n"
  159.  
  160.         mov     esi, str_get
  161.         copy_till_zero
  162.  
  163.         mov     esi, [pageaddr]
  164.         copy_till_zero
  165.  
  166.         mov     esi, str_http11
  167.         mov     ecx, str_http11.length
  168.         rep     movsb
  169.  
  170.         mov     esi, [hostname]
  171.         copy_till_zero
  172.  
  173.         mov     esi, str_close
  174.         mov     ecx, str_close.length
  175.         rep     movsb
  176.  
  177.         mov     byte[edi], 0
  178.         DEBUGF  1, "Request:\n%s", [buffer]
  179.  
  180. ; Send the request
  181.         mov     esi, edi
  182.         sub     esi, [buffer]   ; length
  183.         xor     edi, edi        ; flags
  184.  
  185.         mcall   send, [socketnum], [buffer]
  186.         test    eax, eax
  187.         jz      .error
  188.         DEBUGF  1, "Request has been sent to server.\n"
  189.  
  190.         HTTP_init_buffer [buffer], [socketnum]
  191.  
  192.         ret                     ; return buffer ptr
  193.  
  194.   .error:
  195.         DEBUGF  1, "Error!\n"
  196.         xor     eax, eax        ; return 0 = error
  197.         ret
  198.  
  199. endp
  200.  
  201.  
  202.  
  203. ;;================================================================================================;;
  204. proc HTTP_head URL ;///////////////////////////////////////////////////////////////////////////////;;
  205. ;;------------------------------------------------------------------------------------------------;;
  206. ;?                                                                                                ;;
  207. ;;------------------------------------------------------------------------------------------------;;
  208. ;> _                                                                                              ;;
  209. ;;------------------------------------------------------------------------------------------------;;
  210. ;< eax = 0 (error) / buffer ptr                                                                   ;;
  211. ;;================================================================================================;;
  212. locals
  213.         hostname        dd ?
  214.         pageaddr        dd ?
  215.         sockaddr        dd ?
  216.         socketnum       dd ?
  217.         buffer          dd ?
  218.         port            dd ?
  219. endl
  220.  
  221. ; split the URL into hostname and pageaddr
  222.         stdcall parse_url, [URL]
  223.         test    eax, eax
  224.         jz      .error
  225.         mov     [hostname], eax
  226.         mov     [pageaddr], ebx
  227.  
  228. ; Do we need to use a proxy?
  229.         cmp     [proxyAddr], 0
  230.         jne     .proxy_done
  231.  
  232.         ; TODO: set hostname to that of the
  233.   .proxy_done:
  234.  
  235. ;;;;
  236.         mov     [port], 80      ;;;; FIXME
  237.  
  238. ; Connect to the other side.
  239.         stdcall open_connection, [hostname], [port]
  240.         test    eax, eax
  241.         jz      .error
  242.         mov     [socketnum], eax
  243.  
  244. ; Create the HTTP request.
  245.         invoke  mem.alloc, BUFFERSIZE
  246.         test    eax, eax
  247.         jz      .error
  248.         mov     [buffer], eax
  249.         mov     edi, eax
  250.         DEBUGF  1, "Buffer has been allocated.\n"
  251.  
  252.         mov     esi, str_head
  253.         copy_till_zero
  254.  
  255.         mov     esi, [pageaddr]
  256.         copy_till_zero
  257.  
  258.         mov     esi, str_http11
  259.         mov     ecx, str_http11.length
  260.         rep     movsb
  261.  
  262.         mov     esi, [hostname]
  263.         copy_till_zero
  264.  
  265.         mov     esi, str_close
  266.         mov     ecx, str_close.length
  267.         rep     movsb
  268.  
  269.         mov     byte[edi], 0
  270.         DEBUGF  1, "Request:\n%s", [buffer]
  271.  
  272. ; Send the request
  273.         mov     esi, edi
  274.         sub     esi, [buffer]   ; length
  275.         xor     edi, edi        ; flags
  276.  
  277.         mcall   send, [socketnum], [buffer]
  278.         test    eax, eax
  279.         jz      .error
  280.         DEBUGF  1, "Request has been sent to server.\n"
  281.  
  282.         HTTP_init_buffer [buffer], [socketnum]
  283.  
  284.         ret                     ; return buffer ptr
  285.  
  286.   .error:
  287.         DEBUGF  1, "Error!\n"
  288.         xor     eax, eax        ; return 0 = error
  289.         ret
  290.  
  291. endp
  292.  
  293.  
  294. ;;================================================================================================;;
  295. proc HTTP_post URL, content, content_type, content_length ;///////////////////////////////////////;;
  296. ;;------------------------------------------------------------------------------------------------;;
  297. ;?                                                                                                ;;
  298. ;;------------------------------------------------------------------------------------------------;;
  299. ;> _                                                                                              ;;
  300. ;;------------------------------------------------------------------------------------------------;;
  301. ;< eax = 0 (error) / buffer ptr                                                                   ;;
  302. ;;================================================================================================;;
  303. locals
  304.         hostname        dd ?
  305.         pageaddr        dd ?
  306.         sockaddr        dd ?
  307.         socketnum       dd ?
  308.         buffer          dd ?
  309.         port            dd ?
  310. endl
  311.  
  312. ; split the URL into hostname and pageaddr
  313.         stdcall parse_url, [URL]
  314.         test    eax, eax
  315.         jz      .error
  316.         mov     [hostname], eax
  317.         mov     [pageaddr], ebx
  318.  
  319. ; Do we need to use a proxy?
  320.         cmp     [proxyAddr], 0
  321.         jne     .proxy_done
  322.  
  323.         ; TODO: set hostname to that of the
  324.   .proxy_done:
  325.  
  326. ;;;;
  327.         mov     [port], 80      ;;;; FIXME
  328.  
  329. ; Connect to the other side.
  330.         stdcall open_connection, [hostname], [port]
  331.         test    eax, eax
  332.         jz      .error
  333.         mov     [socketnum], eax
  334.  
  335. ; Create the HTTP request.
  336.         invoke  mem.alloc, BUFFERSIZE
  337.         test    eax, eax
  338.         jz      .error
  339.         mov     [buffer], eax
  340.         mov     edi, eax
  341.         DEBUGF  1, "Buffer has been allocated.\n"
  342.  
  343.         mov     esi, str_post
  344.         copy_till_zero
  345.  
  346.         mov     esi, [pageaddr]
  347.         copy_till_zero
  348.  
  349.         mov     esi, str_http11
  350.         mov     ecx, str_http11.length
  351.         rep     movsb
  352.  
  353.         mov     esi, [hostname]
  354.         copy_till_zero
  355.  
  356.         mov     esi, str_post_cl
  357.         mov     ecx, str_post_cl.length
  358.         rep     movsb
  359.  
  360.         mov     eax, [content_length]
  361.         call    ascii_dec
  362.  
  363.         mov     esi, str_post_ct
  364.         mov     ecx, str_post_ct.length
  365.         rep     movsb
  366.  
  367.         mov     esi, [content_type]
  368.         rep     movsb
  369.  
  370.         mov     esi, str_close
  371.         mov     ecx, str_close.length
  372.         rep     movsb
  373.  
  374.         mov     byte[edi], 0
  375.         DEBUGF  1, "Request:\n%s", [buffer]
  376.  
  377. ; Send the request
  378.         mov     esi, edi
  379.         sub     esi, [buffer]   ; length
  380.         xor     edi, edi        ; flags
  381.         mcall   send, [socketnum], [buffer]
  382.         test    eax, eax
  383.         jz      .error
  384.         DEBUGF  1, "Request has been sent to server.\n"
  385.  
  386.         mcall   send, [socketnum], [content], [content_length]
  387.         test    eax, eax
  388.         jz      .error
  389.         DEBUGF  1, "Data has been sent to server.\n"
  390.  
  391.         HTTP_init_buffer [buffer], [socketnum]
  392. ;        mov     eax, [buffer]
  393.  
  394.         ret                     ; return buffer ptr
  395.  
  396.   .error:
  397.         DEBUGF  1, "Error!\n"
  398.         xor     eax, eax        ; return 0 = error
  399.         ret
  400.  
  401. endp
  402.  
  403.  
  404.  
  405. ;;================================================================================================;;
  406. proc HTTP_process identifier ;////////////////////////////////////////////////////////////////////;;
  407. ;;------------------------------------------------------------------------------------------------;;
  408. ;?                                                                                                ;;
  409. ;;------------------------------------------------------------------------------------------------;;
  410. ;> _                                                                                              ;;
  411. ;;------------------------------------------------------------------------------------------------;;
  412. ;< eax = -1 (not finished) / 0 finished                                                           ;;
  413. ;;================================================================================================;;
  414.         pusha
  415.         mov     ebp, [identifier]
  416.  
  417. ; Receive some data
  418.         mcall   recv, [ebp + http_msg.socket], [ebp + http_msg.write_ptr], \
  419.                       [ebp + http_msg.buffer_length], MSG_DONTWAIT
  420.         cmp     eax, 0xffffffff
  421.         je      .check_socket
  422.         DEBUGF  1, "Received %u bytes\n", eax
  423.  
  424. ; Update pointers
  425.         mov     edi, [ebp + http_msg.write_ptr]
  426.         add     [ebp + http_msg.write_ptr], eax
  427.         sub     [ebp + http_msg.buffer_length], eax
  428.         jz      .got_all_data
  429.  
  430. ; If data is chunked, combine chunks into contiguous data.
  431.         test    [ebp + http_msg.flags], FLAG_CHUNKED
  432.         jnz     .chunk_loop
  433.  
  434. ; Did we detect the header yet?
  435.         test    [ebp + http_msg.flags], FLAG_GOT_HEADER
  436.         jnz     .header_parsed
  437.  
  438. ; We havent found the header yet, search for it..
  439.         sub     eax, 4
  440.         jl      .need_more_data
  441.   .scan:
  442.         ; scan for end of header (empty line)
  443.         cmp     dword[edi], 0x0a0d0a0d                  ; end of header
  444.         je      .end_of_header
  445.         cmp     word[edi+2], 0x0a0a
  446.         je      .end_of_header
  447.         inc     edi
  448.         dec     eax
  449.         jnz     .scan
  450.  
  451.   .end_of_header:
  452.         add     edi, 4 - http_msg.data
  453.         sub     edi, ebp
  454.         mov     [ebp + http_msg.header_length], edi
  455.         or      [ebp + http_msg.flags], FLAG_GOT_HEADER
  456.         DEBUGF  1, "Header length: %u\n", edi
  457.  
  458. ; Ok, we have found header:
  459.         cmp     dword[ebp + http_msg.data], 'HTTP'
  460.         jne     .invalid_header
  461.         cmp     dword[ebp + http_msg.data+4], '/1.0'
  462.         je      .http_1.0
  463.         cmp     dword[ebp + http_msg.data+4], '/1.1'
  464.         jne     .invalid_header
  465.         or      [ebp + http_msg.flags], FLAG_HTTP11
  466.   .http_1.0:
  467.         cmp     byte[ebp + http_msg.data+8], ' '
  468.         jne     .invalid_header
  469.         DEBUGF  1, "Header seems valid.\n"
  470.  
  471.         lea     esi, [ebp + http_msg.data+9]
  472.         xor     eax, eax
  473.         xor     ebx, ebx
  474.         mov     ecx, 3
  475.   .statusloop:
  476.         lodsb
  477.         sub     al, '0'
  478.         jb      .invalid_header
  479.         cmp     al, 9
  480.         ja      .invalid_header
  481.         lea     ebx, [ebx + 4*ebx]
  482.         shl     ebx, 1
  483.         add     ebx, eax
  484.         dec     ecx
  485.         jnz     .statusloop
  486.         mov     [ebp + http_msg.status], ebx
  487.         DEBUGF  1, "Status: %u\n", ebx
  488.  
  489. ; Now, convert all header names to lowercase.
  490. ; This way, it will be much easier to find certain header fields, later on.
  491.  
  492.         lea     esi, [ebp + http_msg.data]
  493.         mov     ecx, [ebp + http_msg.header_length]
  494.   .need_newline:
  495.         inc     esi
  496.         dec     ecx
  497.         jz      .convert_done
  498.         cmp     byte[esi], 10
  499.         jne     .need_newline
  500. ; Ok, we have a newline, a line beginning with space or tabs has no header fields.
  501.  
  502.         inc     esi
  503.         dec     ecx
  504.         jz      .convert_done
  505.         cmp     byte[esi], ' '
  506.         je      .need_newline
  507.         cmp     byte[esi], 9    ; horizontal tab
  508.         je      .need_newline
  509.         jmp     .convert_loop
  510.   .next_char:
  511.         inc     esi
  512.         dec     ecx
  513.         jz      .convert_done
  514.   .convert_loop:
  515.         cmp     byte[esi], ':'
  516.         je      .need_newline
  517.         cmp     byte[esi], 'A'
  518.         jb      .next_char
  519.         cmp     byte[esi], 'Z'
  520.         ja      .next_char
  521.         or      byte[esi], 0x20 ; convert to lowercase
  522.         jmp     .next_char
  523.   .convert_done:
  524.         mov     byte[esi-1], 0
  525.         lea     esi, [ebp + http_msg.data]
  526.         DEBUGF  1, "Header names converted to lowercase:\n%s\n", esi
  527.  
  528. ; Check for content-length header field.
  529.         stdcall find_header_field, ebp, str_cl
  530.         test    eax, eax
  531.         jz      .no_content
  532.         or      [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
  533.  
  534.         xor     edx, edx
  535.   .cl_loop:
  536.         movzx   ebx, byte[eax]
  537.         inc     eax
  538.         cmp     bl, 10
  539.         je      .cl_ok
  540.         cmp     bl, 13
  541.         je      .cl_ok
  542.         cmp     bl, ' '
  543.         je      .cl_ok
  544.         sub     bl, '0'
  545.         jb      .invalid_header
  546.         cmp     bl, 9
  547.         ja      .invalid_header
  548.         lea     edx, [edx + edx*4]      ; edx = edx*10
  549.         shl     edx, 1                  ;
  550.         add     edx, ebx
  551.         jmp     .cl_loop
  552.  
  553.   .cl_ok:
  554.         mov     [ebp + http_msg.content_length], edx
  555.         DEBUGF  1, "Content-length: %u\n", edx
  556.  
  557. ; Resize buffer according to content-length.
  558.         mov     eax, [ebp + http_msg.header_length]
  559.         add     eax, [ebp + http_msg.content_length]
  560.         add     eax, http_msg.data
  561.  
  562.         mov     ecx, eax
  563.         sub     ecx, [ebp + http_msg.write_ptr]
  564.         mov     [ebp + http_msg.buffer_length], ecx
  565.  
  566.         invoke  mem.realloc, ebp, eax
  567.         or      eax, eax
  568.         jz      .no_ram
  569.         jmp     .header_parsed  ; hooray!
  570.  
  571.   .no_content:
  572.         DEBUGF  1, "Content-length not found.\n"
  573.  
  574. ; We didnt find 'content-length', maybe server is using chunked transfer encoding?
  575. ; Try to find 'transfer-encoding' header.
  576.         stdcall find_header_field, ebp, str_te
  577.         test    eax, eax
  578.         jz      .invalid_header
  579.  
  580.         mov     ebx, dword[eax]
  581.         or      ebx, 0x20202020
  582.         cmp     ebx, 'chun'
  583.         jne     .invalid_header
  584.         mov     ebx, dword[eax+4]
  585.         or      ebx, 0x00202020
  586.         and     ebx, 0x00ffffff
  587.         cmp     ebx, 'ked'
  588.         jne     .invalid_header
  589.  
  590.         or      [ebp + http_msg.flags], FLAG_CHUNKED
  591.         DEBUGF  1, "Transfer type is: chunked\n"
  592.  
  593. ; Set chunk pointer where first chunk should begin.
  594.         lea     eax, [ebp + http_msg.data]
  595.         add     eax, [ebp + http_msg.header_length]
  596.         mov     [ebp + http_msg.chunk_ptr], eax
  597.  
  598.   .chunk_loop:
  599.         mov     ecx, [ebp + http_msg.write_ptr]
  600.         sub     ecx, [ebp + http_msg.chunk_ptr]
  601.         jb      .need_more_data_chunked
  602.  
  603. ; TODO: make sure we have the complete chunkline header
  604.         mov     esi, [ebp + http_msg.chunk_ptr]
  605.         xor     ebx, ebx
  606.   .chunk_hexloop:
  607.         lodsb
  608.         sub     al, '0'
  609.         jb      .chunk_
  610.         cmp     al, 9
  611.         jbe     .chunk_hex
  612.         sub     al, 'A' - '0' - 10
  613.         jb      .chunk_
  614.         cmp     al, 15
  615.         jbe     .chunk_hex
  616.         sub     al, 'a' - 'A'
  617.         cmp     al, 15
  618.         ja      .chunk_
  619.   .chunk_hex:
  620.         shl     ebx, 4
  621.         add     bl, al
  622.         jmp     .chunk_hexloop
  623.   .chunk_:
  624.         DEBUGF  1, "got chunk of %u bytes\n", ebx
  625. ; If chunk size is 0, all chunks have been received.
  626.         test    ebx, ebx
  627.         jz      .got_all_data_chunked           ; last chunk, hooray! FIXME: what if it wasnt a valid hex number???
  628.         mov     edi, [ebp + http_msg.chunk_ptr] ; we'll need this in about 25 lines...
  629.         add     [ebp + http_msg.chunk_ptr], ebx
  630.  
  631. ; Chunkline ends with a CR, LF or simply LF
  632.   .end_of_chunkline?:           ; FIXME: buffer overflow possible!
  633.         cmp     al, 10
  634.         je      .end_of_chunkline
  635.         lodsb
  636.         jmp     .end_of_chunkline?
  637.  
  638.   .end_of_chunkline:
  639. ; Realloc buffer, make it 'chunksize' bigger.
  640.         mov     eax, [ebp + http_msg.buffer_length]
  641.         add     eax, ebx
  642.         invoke  mem.realloc, ebp, eax
  643.         or      eax, eax
  644.         jz      .no_ram
  645.         add     [ebp + http_msg.buffer_length], ebx
  646.  
  647. ; Update write ptr
  648.         mov     eax, esi
  649.         sub     eax, edi
  650.         sub     [ebp + http_msg.write_ptr], eax
  651.  
  652. ; Now move all received data to the left (remove chunk header).
  653. ; Update content_length accordingly.
  654.         mov     ecx, [ebp + http_msg.write_ptr]
  655.         sub     ecx, esi
  656.         add     [ebp + http_msg.content_length], ecx
  657.         rep     movsb
  658.         jmp     .chunk_loop
  659.  
  660. ; Check if we got all the data.
  661.   .header_parsed:
  662.         mov     eax, [ebp + http_msg.header_length]
  663.         add     eax, [ebp + http_msg.content_length]
  664.         cmp     eax, [ebp + http_msg.buffer_length]
  665.         je      .got_all_data
  666.   .need_more_data:
  667.         popa
  668.         xor     eax, eax
  669.         dec     eax
  670.         ret
  671.  
  672.   .need_more_data_chunked:
  673.         add     [ebp + http_msg.content_length], eax
  674.         popa
  675.         xor     eax, eax
  676.         dec     eax
  677.         ret
  678.  
  679.   .got_all_data_chunked:
  680.         mov     eax, [ebp + http_msg.chunk_ptr]
  681.         sub     eax, [ebp + http_msg.header_length]
  682.         sub     eax, http_msg.data
  683.         sub     eax, ebp
  684.         mov     [ebp + http_msg.content_length], eax
  685.   .got_all_data:
  686.         DEBUGF  1, "We got all the data! (%u bytes)\n", [ebp + http_msg.content_length]
  687.         or      [ebp + http_msg.flags], FLAG_GOT_DATA
  688.         mcall   close, [ebp + http_msg.socket]
  689.         popa
  690.         xor     eax, eax
  691.         ret
  692.  
  693.   .check_socket:
  694.         cmp     ebx, EWOULDBLOCK
  695.         je      .need_more_data
  696.         DEBUGF  1, "ERROR: socket error %u\n", ebx
  697.  
  698.         or      [ebp + http_msg.flags], FLAG_SOCKET_ERROR
  699.         popa
  700.         xor     eax, eax
  701.         ret
  702.  
  703.   .invalid_header:
  704.         DEBUGF  1, "ERROR: invalid header\n"
  705.         or      [ebp + http_msg.flags], FLAG_INVALID_HEADER
  706.         popa
  707.         xor     eax, eax
  708.         ret
  709.  
  710.   .no_ram:
  711.         DEBUGF  1, "ERROR: out of RAM\n"
  712.         or      [ebp + http_msg.flags], FLAG_NO_RAM
  713.         popa
  714.         xor     eax, eax
  715.         ret
  716.  
  717. endp
  718.  
  719.  
  720.  
  721. ;;================================================================================================;;
  722. proc find_header_field identifier, headername ;///////////////////////////////////////////////////;;
  723. ;;------------------------------------------------------------------------------------------------;;
  724. ;?                                                                                                ;;
  725. ;;------------------------------------------------------------------------------------------------;;
  726. ;> _                                                                                              ;;
  727. ;;------------------------------------------------------------------------------------------------;;
  728. ;< eax = -1 (error) / 0                                                                           ;;
  729. ;;================================================================================================;;
  730.         push    ebx ecx edx esi edi
  731.  
  732.         DEBUGF  1, "Find header field: %s\n", [headername]
  733.  
  734.         mov     ebx, [identifier]
  735.         lea     edx, [ebx + http_msg.data]
  736.         mov     ecx, edx
  737.         add     ecx, [ebx + http_msg.header_length]
  738.  
  739.   .restart:
  740.         mov     esi, [headername]
  741.         mov     edi, edx
  742.   .loop:
  743.         cmp     edi, ecx
  744.         jae     .fail
  745.         lodsb
  746.         scasb
  747.         je      .loop
  748.         test    al, al
  749.         jz      .done?
  750.   .next:
  751.         inc     edx
  752.         jmp     .restart
  753.  
  754.   .not_done:
  755.         inc     edi
  756.   .done?:
  757.         cmp     byte[edi-1], ':'
  758.         je      .almost_done
  759.         cmp     byte[edi-1], ' '
  760.         je      .not_done
  761.         cmp     byte[edi-1], 9  ; tab
  762.         je      .not_done
  763.  
  764.         jmp     .next
  765.  
  766.   .almost_done:                 ; FIXME: buffer overflow?
  767.         dec     edi
  768.         DEBUGF  1, "Found header field\n"
  769.   .spaceloop:
  770.         inc     edi
  771.         cmp     byte[edi], ' '
  772.         je      .spaceloop
  773.         cmp     byte[edi], 9    ; tab
  774.         je      .spaceloop
  775.  
  776.         mov     eax, edi
  777.         pop     edi esi edx ecx ebx
  778.         ret
  779.  
  780.   .fail:
  781.         pop     edi esi edx ecx ebx
  782.         xor     eax, eax
  783.         ret
  784.  
  785. endp
  786.  
  787.  
  788. ; internal procedures start here:
  789.  
  790. ;;================================================================================================;;
  791. proc open_connection hostname, port ;/////////////////////////////////////////////////////////////;;
  792. ;;------------------------------------------------------------------------------------------------;;
  793. ;?                                                                                                ;;
  794. ;;------------------------------------------------------------------------------------------------;;
  795. ;> _                                                                                              ;;
  796. ;;------------------------------------------------------------------------------------------------;;
  797. ;< eax = -1 (error) / 0                                                                           ;;
  798. ;;================================================================================================;;
  799.  
  800. locals
  801.         sockaddr        dd ?
  802.         socketnum       dd ?
  803. endl
  804.  
  805. ; Resolve the hostname
  806.         DEBUGF  1, "Resolving hostname\n"
  807.         push    esp     ; reserve stack place
  808.         push    esp     ; fourth parameter
  809.         push    0       ; third parameter
  810.         push    0       ; second parameter
  811.         push    [hostname]
  812.         call    [getaddrinfo]
  813.         pop     esi
  814.         test    eax, eax
  815.         jnz     .error1
  816.  
  817. ; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
  818.         mov     esi, [esi + addrinfo.ai_addr]
  819.         mov     [sockaddr], esi
  820.         mov     eax, [esi + sockaddr_in.sin_addr]
  821.         test    eax, eax
  822.         jz      .error2
  823.  
  824.         DEBUGF  1, "Server ip=%u.%u.%u.%u\n", \
  825.         [esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
  826.         [esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
  827.  
  828.         mov     [esi + sockaddr_in.sin_family], AF_INET4
  829.         mov     eax, [port]
  830.         xchg    al, ah
  831.         mov     [esi + sockaddr_in.sin_port], ax
  832.  
  833. ; Connect to the server.
  834.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  835.         test    eax, eax
  836.         jz      .error2
  837.         mov     [socketnum], eax
  838.         DEBUGF  1, "Socket: 0x%x\n", eax
  839.  
  840.         mcall   connect, [socketnum], [sockaddr], 18
  841.         test    eax, eax
  842.         jnz     .error2
  843.         DEBUGF  1, "Socket is now connected.\n"
  844.  
  845. ; free allocated memory
  846.         push    [sockaddr]
  847.         call    [freeaddrinfo]
  848.  
  849.         mov     eax, [socketnum]
  850.         ret
  851.  
  852.   .error2:
  853.  
  854. ; free allocated memory
  855.         push    [sockaddr]
  856.         call    [freeaddrinfo]
  857.  
  858.   .error1:
  859.         xor     eax, eax
  860.         ret
  861.  
  862. endp
  863.  
  864.  
  865. ;;================================================================================================;;
  866. proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
  867. ;;------------------------------------------------------------------------------------------------;;
  868. ;?                                                                                                ;;
  869. ;;------------------------------------------------------------------------------------------------;;
  870. ;> _                                                                                              ;;
  871. ;;------------------------------------------------------------------------------------------------;;
  872. ;< eax = -1 (error) / 0                                                                           ;;
  873. ;;================================================================================================;;
  874.  
  875. locals
  876.         urlsize         dd ?
  877.         hostname        dd ?
  878.         pageaddr        dd ?
  879. endl
  880.  
  881.         DEBUGF  1, "parsing URL: %s\n", [URL]
  882.  
  883. ; remove any leading protocol text
  884.         mov     esi, [URL]
  885.         mov     ecx, URLMAXLEN
  886.         mov     ax, '//'
  887.   .loop1:
  888.         cmp     byte[esi], 0            ; end of URL?
  889.         je      .url_ok                 ; yep, so not found
  890.         cmp     [esi], ax
  891.         je      .skip_proto
  892.         inc     esi
  893.         dec     ecx
  894.         jnz     .loop1
  895.  
  896.         DEBUGF  1, "Invalid URL\n"
  897.         xor     eax, eax
  898.         ret
  899.  
  900.   .skip_proto:
  901.         inc     esi                     ; skip the two '/'
  902.         inc     esi
  903.         mov     [URL], esi              ; update pointer so it skips protocol
  904.         jmp     .loop1                  ; we still need to find the length of the URL
  905.  
  906.   .url_ok:
  907.         sub     esi, [URL]              ; calculate total length of URL
  908.         mov     [urlsize], esi
  909.  
  910.  
  911. ; now look for page delimiter - it's a '/' character
  912.         mov     ecx, esi                ; URL length
  913.         mov     edi, [URL]
  914.         mov     al, '/'
  915.         repne   scasb
  916.         jne     @f
  917.         dec     edi                     ; return one char, '/' must be part of the pageaddr
  918.         inc     ecx                     ;
  919.   @@:
  920.         push    ecx edi                 ; remember the pointer and length of pageaddr
  921.  
  922.         mov     ecx, edi
  923.         sub     ecx, [URL]
  924.         inc     ecx                     ; we will add a 0 byte at the end
  925.         invoke  mem.alloc, ecx
  926.         or      eax, eax
  927.         jz      .no_mem
  928.  
  929.         mov     [hostname], eax         ; copy hostname to buffer
  930.         mov     edi, eax
  931.         mov     esi, [URL]
  932.         dec     ecx
  933.         rep     movsb
  934.         xor     al, al
  935.         stosb
  936.  
  937.         mov     [pageaddr], str_slash   ; assume there is no pageaddr
  938.         pop     esi ecx
  939.         test    ecx, ecx
  940.         jz      .no_page
  941.         inc     ecx                     ; we will add a 0 byte at the end
  942.         invoke  mem.alloc, ecx
  943.         or      eax, eax
  944.         jz      .no_mem
  945.  
  946.         mov     [pageaddr], eax         ; copy pageaddr to buffer
  947.         mov     edi, eax
  948.         dec     ecx
  949.         rep     movsb
  950.         xor     al, al
  951.         stosb
  952.   .no_page:
  953.  
  954.         mov     eax, [hostname]
  955.         mov     ebx, [pageaddr]
  956.  
  957.         DEBUGF  1, "hostname: %s\n", eax
  958.         DEBUGF  1, "pageaddr: %s\n", ebx
  959.  
  960.         ret
  961.  
  962.   .no_mem:
  963.         xor     eax, eax
  964.         ret
  965.  
  966. endp
  967.  
  968.  
  969. ; in: eax = number
  970. ;     edi = ptr where to store ascii
  971. ascii_dec:
  972.  
  973.         mov     ecx, 10
  974.   .loop:
  975.         xor     edx, edx
  976.         div     ecx
  977.         add     dl, '0'
  978.         mov     byte[edi], dl
  979.         inc     edi
  980.         test    eax, eax
  981.         jnz     .loop
  982.  
  983.         ret
  984.  
  985.  
  986. ;;================================================================================================;;
  987. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  988. ;;================================================================================================;;
  989. ;! Imported functions section                                                                     ;;
  990. ;;================================================================================================;;
  991. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  992. ;;================================================================================================;;
  993.  
  994.  
  995. align 16
  996. @IMPORT:
  997.  
  998. library \
  999.         libini, 'libini.obj', \
  1000.         network, 'network.obj'
  1001.  
  1002. import  libini, \
  1003.         ini.get_str, 'ini_get_str', \
  1004.         ini.get_int, 'ini_get_int'
  1005.  
  1006. import  network,\
  1007.         getaddrinfo, 'getaddrinfo',\
  1008.         freeaddrinfo,  'freeaddrinfo',\
  1009.         inet_ntoa, 'inet_ntoa'
  1010.  
  1011. ;;===========================================================================;;
  1012. ;;///////////////////////////////////////////////////////////////////////////;;
  1013. ;;===========================================================================;;
  1014. ;! Exported functions section                                                ;;
  1015. ;;===========================================================================;;
  1016. ;;///////////////////////////////////////////////////////////////////////////;;
  1017. ;;===========================================================================;;
  1018.  
  1019.  
  1020. align 4
  1021. @EXPORT:
  1022. export  \
  1023.         lib_init                , 'lib_init'            , \
  1024.         0x00010001              , 'version'             , \
  1025.         HTTP_get                , 'get'                 , \
  1026.         HTTP_head               , 'head'                , \
  1027.         HTTP_post               , 'post'                , \
  1028.         find_header_field       , 'find_header_field'   , \
  1029.         HTTP_process            , 'process'
  1030.  
  1031. ;        HTTP_put                , 'put'                 , \
  1032. ;        HTTP_delete             , 'delete'              , \
  1033. ;        HTTP_trace              , 'trace'               , \
  1034. ;        HTTP_connect            , 'connect'             , \
  1035.  
  1036.  
  1037.  
  1038. section '.data' data readable writable align 16
  1039.  
  1040. inifile         db '/sys/settings/network.ini', 0
  1041.  
  1042. sec_proxy:
  1043. key_proxy       db 'proxy', 0
  1044. key_proxyport   db 'port', 0
  1045. key_user        db 'user', 0
  1046. key_password    db 'password', 0
  1047.  
  1048. str_http11      db ' HTTP/1.1', 13, 10, 'Host: '
  1049.   .length       = $ - str_http11
  1050. str_post_cl     db 13, 10, 'Content-Length: '
  1051.   .length       = $ - str_post_cl
  1052. str_post_ct     db 13, 10, 'Content-Type: '
  1053.   .length       = $ - str_post_ct
  1054. str_close       db 13, 10, 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
  1055.   .length       = $ - str_close
  1056. str_proxy_auth  db 13, 10, 'Proxy-Authorization: Basic '
  1057.   .length       = $ - str_proxy_auth
  1058.  
  1059. base64_table    db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  1060.                 db '0123456789+/'
  1061.  
  1062. str_cl          db 'content-length', 0
  1063. str_slash       db '/', 0
  1064. str_te          db 'transfer-encoding', 0
  1065. str_get         db 'GET ', 0
  1066. str_head        db 'HEAD ', 0
  1067. str_post        db 'POST ', 0
  1068.  
  1069. include_debug_strings
  1070.  
  1071. ; uninitialized data
  1072. mem.alloc       dd ?
  1073. mem.free        dd ?
  1074. mem.realloc     dd ?
  1075. dll.load        dd ?
  1076.  
  1077. proxyAddr       rb 256
  1078. proxyUser       rb 256
  1079. proxyPassword   rb 256
  1080. proxyPort       dd ?