Subversion Repositories Kolibri OS

Rev

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