Subversion Repositories Kolibri OS

Rev

Rev 4204 | Rev 4206 | 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], FLAG_CONNECTED
  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. ;? Initiates a HTTP connection, using 'GET' method.                                               ;;
  117. ;;------------------------------------------------------------------------------------------------;;
  118. ;> URL = pointer to ASCIIZ URL                                                                    ;;
  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. ;? Initiates a HTTP connection, using 'HEAD' method.                                              ;;
  208. ;;------------------------------------------------------------------------------------------------;;
  209. ;> URL = pointer to ASCIIZ URL                                                                    ;;
  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. ;? Initiates a HTTP connection, using 'GET' method.                                               ;;
  299. ;;------------------------------------------------------------------------------------------------;;
  300. ;> URL                  = pointer to ASCIIZ URL                                                   ;;
  301. ;> content_type         = pointer to ASCIIZ string containing content type                        ;;
  302. ;> content_length       = length of content (in bytes)                                            ;;
  303. ;;------------------------------------------------------------------------------------------------;;
  304. ;< eax = 0 (error) / buffer ptr                                                                   ;;
  305. ;;================================================================================================;;
  306. locals
  307.         hostname        dd ?
  308.         pageaddr        dd ?
  309.         sockaddr        dd ?
  310.         socketnum       dd ?
  311.         buffer          dd ?
  312.         port            dd ?
  313. endl
  314.  
  315. ; split the URL into hostname and pageaddr
  316.         stdcall parse_url, [URL]
  317.         test    eax, eax
  318.         jz      .error
  319.         mov     [hostname], eax
  320.         mov     [pageaddr], ebx
  321.  
  322. ; Do we need to use a proxy?
  323.         cmp     [proxyAddr], 0
  324.         jne     .proxy_done
  325.  
  326.         ; TODO: set hostname to that of the
  327.   .proxy_done:
  328.  
  329. ;;;;
  330.         mov     [port], 80      ;;;; FIXME
  331.  
  332. ; Connect to the other side.
  333.         stdcall open_connection, [hostname], [port]
  334.         test    eax, eax
  335.         jz      .error
  336.         mov     [socketnum], eax
  337.  
  338. ; Create the HTTP request.
  339.         invoke  mem.alloc, BUFFERSIZE
  340.         test    eax, eax
  341.         jz      .error
  342.         mov     [buffer], eax
  343.         mov     edi, eax
  344.         DEBUGF  1, "Buffer has been allocated.\n"
  345.  
  346.         mov     esi, str_post
  347.         copy_till_zero
  348.  
  349.         mov     esi, [pageaddr]
  350.         copy_till_zero
  351.  
  352.         mov     esi, str_http11
  353.         mov     ecx, str_http11.length
  354.         rep     movsb
  355.  
  356.         mov     esi, [hostname]
  357.         copy_till_zero
  358.  
  359.         mov     esi, str_post_cl
  360.         mov     ecx, str_post_cl.length
  361.         rep     movsb
  362.  
  363.         mov     eax, [content_length]
  364.         call    ascii_dec
  365.  
  366.         mov     esi, str_post_ct
  367.         mov     ecx, str_post_ct.length
  368.         rep     movsb
  369.  
  370.         mov     esi, [content_type]
  371.         rep     movsb
  372.  
  373.         mov     esi, str_close
  374.         mov     ecx, str_close.length
  375.         rep     movsb
  376.  
  377.         mov     byte[edi], 0
  378.         DEBUGF  1, "Request:\n%s", [buffer]
  379.  
  380. ; Send the request
  381.         mov     esi, edi
  382.         sub     esi, [buffer]   ; length
  383.         xor     edi, edi        ; flags
  384.         mcall   send, [socketnum], [buffer]
  385.         test    eax, eax
  386.         jz      .error
  387.         DEBUGF  1, "Request has been sent to server.\n"
  388.  
  389.         HTTP_init_buffer [buffer], [socketnum]
  390. ;        mov     eax, [buffer]
  391.  
  392.         ret                     ; return buffer ptr
  393.  
  394.   .error:
  395.         DEBUGF  1, "Error!\n"
  396.         xor     eax, eax        ; return 0 = error
  397.         ret
  398.  
  399. endp
  400.  
  401.  
  402.  
  403. ;;================================================================================================;;
  404. proc HTTP_process identifier ;////////////////////////////////////////////////////////////////////;;
  405. ;;------------------------------------------------------------------------------------------------;;
  406. ;? Receive data from the server, parse headers and put data in receive buffer.                    ;;
  407. ;? To complete a transfer, this procedure must be called over and over again untill it returns 0. ;;
  408. ;;------------------------------------------------------------------------------------------------;;
  409. ;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
  410. ;;------------------------------------------------------------------------------------------------;;
  411. ;< eax = -1 (not finished) / 0 finished                                                           ;;
  412. ;;================================================================================================;;
  413.         pusha
  414.         mov     ebp, [identifier]
  415.  
  416.         test    [ebp + http_msg.flags], FLAG_CONNECTED
  417.         jz      .connection_closed
  418.  
  419. ; Receive some data
  420.         mcall   recv, [ebp + http_msg.socket], [ebp + http_msg.write_ptr], \
  421.                       [ebp + http_msg.buffer_length], MSG_DONTWAIT
  422.         cmp     eax, 0xffffffff
  423.         je      .check_socket
  424.         DEBUGF  1, "Received %u bytes\n", eax
  425.  
  426. ; Update pointers
  427.         mov     edi, [ebp + http_msg.write_ptr]
  428.         add     [ebp + http_msg.write_ptr], eax
  429.         sub     [ebp + http_msg.buffer_length], eax
  430.         jz      .got_all_data
  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.         push    eax
  441.  
  442. ; We havent found the header yet, search for it..
  443.         sub     eax, 4
  444.         jl      .need_more_data_pop
  445.         inc     eax
  446.   .scan:
  447.         ; scan for end of header (empty line)
  448.         cmp     dword[edi], 0x0a0d0a0d                  ; end of header
  449.         je      .end_of_header
  450.         cmp     word[edi+2], 0x0a0a
  451.         je      .end_of_header
  452.         inc     edi
  453.         dec     eax
  454.         jnz     .scan
  455.         jmp     .need_more_data_pop
  456.  
  457.   .end_of_header:
  458.         add     edi, 4 - http_msg.data
  459.         sub     edi, ebp
  460.         mov     [ebp + http_msg.header_length], edi
  461.         or      [ebp + http_msg.flags], FLAG_GOT_HEADER
  462.         DEBUGF  1, "Header length: %u\n", edi
  463.  
  464. ; Ok, we have found header:
  465.         cmp     dword[ebp + http_msg.data], 'HTTP'
  466.         jne     .invalid_header
  467.         cmp     dword[ebp + http_msg.data+4], '/1.0'
  468.         je      .http_1.0
  469.         cmp     dword[ebp + http_msg.data+4], '/1.1'
  470.         jne     .invalid_header
  471.         or      [ebp + http_msg.flags], FLAG_HTTP11
  472.   .http_1.0:
  473.         cmp     byte[ebp + http_msg.data+8], ' '
  474.         jne     .invalid_header
  475.         DEBUGF  1, "Header seems valid.\n"
  476.  
  477.         lea     esi, [ebp + http_msg.data+9]
  478.         xor     eax, eax
  479.         xor     ebx, ebx
  480.         mov     ecx, 3
  481.   .statusloop:
  482.         lodsb
  483.         sub     al, '0'
  484.         jb      .invalid_header
  485.         cmp     al, 9
  486.         ja      .invalid_header
  487.         lea     ebx, [ebx + 4*ebx]
  488.         shl     ebx, 1
  489.         add     ebx, eax
  490.         dec     ecx
  491.         jnz     .statusloop
  492.         mov     [ebp + http_msg.status], ebx
  493.         DEBUGF  1, "Status: %u\n", ebx
  494.  
  495. ; Now, convert all header names to lowercase.
  496. ; This way, it will be much easier to find certain header fields, later on.
  497.  
  498.         lea     esi, [ebp + http_msg.data]
  499.         mov     ecx, [ebp + http_msg.header_length]
  500.   .need_newline:
  501.         inc     esi
  502.         dec     ecx
  503.         jz      .convert_done
  504.         cmp     byte[esi], 10
  505.         jne     .need_newline
  506. ; Ok, we have a newline, a line beginning with space or tabs has no header fields.
  507.  
  508.         inc     esi
  509.         dec     ecx
  510.         jz      .convert_done
  511.         cmp     byte[esi], ' '
  512.         je      .need_newline
  513.         cmp     byte[esi], 9    ; horizontal tab
  514.         je      .need_newline
  515.         jmp     .convert_loop
  516.   .next_char:
  517.         inc     esi
  518.         dec     ecx
  519.         jz      .convert_done
  520.   .convert_loop:
  521.         cmp     byte[esi], ':'
  522.         je      .need_newline
  523.         cmp     byte[esi], 'A'
  524.         jb      .next_char
  525.         cmp     byte[esi], 'Z'
  526.         ja      .next_char
  527.         or      byte[esi], 0x20 ; convert to lowercase
  528.         jmp     .next_char
  529.   .convert_done:
  530.         mov     byte[esi-1], 0
  531.         lea     esi, [ebp + http_msg.data]
  532.         DEBUGF  1, "Header names converted to lowercase:\n%s\n", esi
  533.  
  534. ; Check for content-length header field.
  535.         stdcall find_header_field, ebp, str_cl
  536.         test    eax, eax
  537.         jz      .no_content
  538.         or      [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
  539.  
  540.         xor     edx, edx
  541.   .cl_loop:
  542.         movzx   ebx, byte[eax]
  543.         inc     eax
  544.         cmp     bl, 10
  545.         je      .cl_ok
  546.         cmp     bl, 13
  547.         je      .cl_ok
  548.         cmp     bl, ' '
  549.         je      .cl_ok
  550.         sub     bl, '0'
  551.         jb      .invalid_header
  552.         cmp     bl, 9
  553.         ja      .invalid_header
  554.         lea     edx, [edx + edx*4]      ; edx = edx*10
  555.         shl     edx, 1                  ;
  556.         add     edx, ebx
  557.         jmp     .cl_loop
  558.  
  559.   .cl_ok:
  560.         mov     [ebp + http_msg.content_length], edx
  561.         DEBUGF  1, "Content-length: %u\n", edx
  562.  
  563. ; Resize buffer according to content-length.
  564.         add     edx, [ebp + http_msg.header_length]
  565.         add     edx, http_msg.data
  566.  
  567.         mov     ecx, edx
  568.         sub     ecx, [ebp + http_msg.write_ptr]
  569.         mov     [ebp + http_msg.buffer_length], ecx
  570.  
  571.         invoke  mem.realloc, ebp, edx
  572.         or      eax, eax
  573.         jz      .no_ram_pop
  574.  
  575.         pop     eax
  576.         sub     eax, [ebp + http_msg.header_length]
  577.         jmp     .header_parsed  ; hooray!
  578.  
  579.   .no_content:
  580.         DEBUGF  1, "Content-length not found.\n"
  581.  
  582. ; We didnt find 'content-length', maybe server is using chunked transfer encoding?
  583. ; Try to find 'transfer-encoding' header.
  584.         stdcall find_header_field, ebp, str_te
  585.         test    eax, eax
  586.         jz      .invalid_header
  587.  
  588.         mov     ebx, dword[eax]
  589.         or      ebx, 0x20202020
  590.         cmp     ebx, 'chun'
  591.         jne     .invalid_header
  592.         mov     ebx, dword[eax+4]
  593.         or      ebx, 0x00202020
  594.         and     ebx, 0x00ffffff
  595.         cmp     ebx, 'ked'
  596.         jne     .invalid_header
  597.  
  598.         or      [ebp + http_msg.flags], FLAG_CHUNKED
  599.         DEBUGF  1, "Transfer type is: chunked\n"
  600.  
  601.         pop     eax
  602.  
  603. ; Set chunk pointer where first chunk should begin.
  604.         lea     eax, [ebp + http_msg.data]
  605.         add     eax, [ebp + http_msg.header_length]
  606.         mov     [ebp + http_msg.chunk_ptr], eax
  607.  
  608.   .chunk_loop:
  609.         mov     ecx, [ebp + http_msg.write_ptr]
  610.         sub     ecx, [ebp + http_msg.chunk_ptr]
  611.         jb      .need_more_data_chunked         ; TODO: use this ecx !!!
  612.  
  613. ; Chunkline starts here, convert the ASCII hex number into ebx
  614.         mov     esi, [ebp + http_msg.chunk_ptr]
  615.         xor     ebx, ebx
  616.   .chunk_hexloop:
  617.         lodsb
  618.         sub     al, '0'
  619.         jb      .chunk_
  620.         cmp     al, 9
  621.         jbe     .chunk_hex
  622.         sub     al, 'A' - '0' - 10
  623.         jb      .chunk_
  624.         cmp     al, 15
  625.         jbe     .chunk_hex
  626.         sub     al, 'a' - 'A'
  627.         cmp     al, 15
  628.         ja      .chunk_
  629.   .chunk_hex:
  630.         shl     ebx, 4
  631.         add     bl, al
  632.         jmp     .chunk_hexloop
  633.   .chunk_:
  634.         DEBUGF  1, "got chunk of %u bytes\n", ebx
  635. ;;        cmp     esi, [ebp + http_msg.chunk_ptr]
  636. ;;        je
  637. ; If chunk size is 0, all chunks have been received.
  638.         test    ebx, ebx
  639.         jz      .got_all_data_chunked           ; last chunk, hooray! FIXME: what if it wasnt a valid hex number???
  640.  
  641. ; Chunkline ends with a CR, LF or simply LF
  642.   .end_of_chunkline?:
  643.         cmp     al, 10
  644.         je      .end_of_chunkline
  645.         lodsb
  646.         cmp     edi, [ebp + http_msg.write_ptr]
  647.         jb      .end_of_chunkline?
  648.         jmp     .need_more_data
  649.  
  650.   .end_of_chunkline:
  651. ; Update chunk ptr, and remember old one
  652.         mov     edi, [ebp + http_msg.chunk_ptr]
  653.         add     [ebp + http_msg.chunk_ptr], ebx
  654. ; Realloc buffer, make it 'chunksize' bigger.
  655.         mov     eax, [ebp + http_msg.buffer_length]
  656.         add     eax, ebx
  657.         invoke  mem.realloc, ebp, eax
  658.         or      eax, eax
  659.         jz      .no_ram
  660.         add     [ebp + http_msg.buffer_length], ebx
  661.  
  662. ; Update write ptr
  663.         mov     eax, esi
  664.         sub     eax, edi
  665.         sub     [ebp + http_msg.write_ptr], eax
  666.  
  667. ; Now move all received data to the left (remove chunk header).
  668. ; Update content_length accordingly.
  669.         mov     ecx, [ebp + http_msg.write_ptr]
  670.         sub     ecx, esi
  671.         add     [ebp + http_msg.content_received], ecx
  672.         rep     movsb
  673.         jmp     .chunk_loop
  674.  
  675. ; Check if we got all the data.
  676.   .header_parsed:
  677.         add     [ebp + http_msg.content_received], eax
  678.         mov     eax, [ebp + http_msg.content_length]
  679.         cmp     eax, [ebp + http_msg.content_received]
  680.         jae     .got_all_data
  681.         jmp     .need_more_data
  682.   .need_more_data_pop:
  683.         pop     eax
  684.   .need_more_data:
  685.         popa
  686.         xor     eax, eax
  687.         dec     eax
  688.         ret
  689.  
  690.   .need_more_data_chunked:
  691.         add     [ebp + http_msg.content_received], eax
  692.         popa
  693.         xor     eax, eax
  694.         dec     eax
  695.         ret
  696.  
  697.   .got_all_data_chunked:
  698.         mov     eax, [ebp + http_msg.chunk_ptr]
  699.         sub     eax, [ebp + http_msg.header_length]
  700.         sub     eax, http_msg.data
  701.         sub     eax, ebp
  702.         mov     [ebp + http_msg.content_length], eax
  703.         mov     [ebp + http_msg.content_received], eax
  704.   .got_all_data:
  705.         DEBUGF  1, "We got all the data! (%u bytes)\n", [ebp + http_msg.content_length]
  706.         or      [ebp + http_msg.flags], FLAG_GOT_ALL_DATA
  707.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  708.         mcall   close, [ebp + http_msg.socket]
  709.         popa
  710.         xor     eax, eax
  711.         ret
  712.  
  713.   .check_socket:
  714.         cmp     ebx, EWOULDBLOCK
  715.         je      .need_more_data
  716.         DEBUGF  1, "ERROR: socket error %u\n", ebx
  717.  
  718.         or      [ebp + http_msg.flags], FLAG_SOCKET_ERROR
  719.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  720.         mcall   close, [ebp + http_msg.socket]
  721.   .connection_closed:
  722.         popa
  723.         xor     eax, eax
  724.         ret
  725.  
  726.   .invalid_header:
  727.         pop     eax
  728.         DEBUGF  1, "ERROR: invalid header\n"
  729.         or      [ebp + http_msg.flags], FLAG_INVALID_HEADER
  730.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  731.         mcall   close, [ebp + http_msg.socket]
  732.         popa
  733.         xor     eax, eax
  734.         ret
  735.  
  736.   .no_ram_pop:
  737.         pop     eax
  738.   .no_ram:
  739.         DEBUGF  1, "ERROR: out of RAM\n"
  740.         or      [ebp + http_msg.flags], FLAG_NO_RAM
  741.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  742.         mcall   close, [ebp + http_msg.socket]
  743.         popa
  744.         xor     eax, eax
  745.         ret
  746.  
  747. endp
  748.  
  749.  
  750.  
  751.  
  752. ;;================================================================================================;;
  753. proc HTTP_free identifier ;///////////////////////////////////////////////////////////////////////;;
  754. ;;------------------------------------------------------------------------------------------------;;
  755. ;? Free the http_msg structure                                                                    ;;
  756. ;;------------------------------------------------------------------------------------------------;;
  757. ;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
  758. ;;------------------------------------------------------------------------------------------------;;
  759. ;< none                                                                                           ;;
  760. ;;================================================================================================;;
  761.  
  762.         pusha
  763.         mov     ebp, [identifier]
  764.  
  765.         test    [ebp + http_msg.flags], FLAG_CONNECTED
  766.         jz      .not_connected
  767.  
  768.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  769.         mcall   close, [ebp + http_msg.socket]
  770.  
  771.   .not_connected:
  772.         invoke  mem.free, ebp
  773.  
  774.         popa
  775.         ret
  776.  
  777. endp
  778.  
  779.  
  780.  
  781. ;;================================================================================================;;
  782. proc HTTP_stop identifier ;///////////////////////////////////////////////////////////////////////;;
  783. ;;------------------------------------------------------------------------------------------------;;
  784. ;? Stops the open connection                                                                      ;;
  785. ;;------------------------------------------------------------------------------------------------;;
  786. ;> identifier   = pointer to buffer containing http_msg struct.                                   ;;
  787. ;;------------------------------------------------------------------------------------------------;;
  788. ;< none                                                                                           ;;
  789. ;;================================================================================================;;
  790.  
  791.         pusha
  792.         mov     ebp, [identifier]
  793.  
  794.         and     [ebp + http_msg.flags], not FLAG_CONNECTED
  795.         mcall   close, [ebp + http_msg.socket]
  796.  
  797.         popa
  798.         ret
  799.  
  800. endp
  801.  
  802.  
  803.  
  804. ;;================================================================================================;;
  805. proc find_header_field identifier, headername ;///////////////////////////////////////////////////;;
  806. ;;------------------------------------------------------------------------------------------------;;
  807. ;? Find a header field in the received HTTP header                                                ;;
  808. ;;------------------------------------------------------------------------------------------------;;
  809. ;> identifier   = ptr to http_msg struct                                                          ;;
  810. ;> headername   = ptr to ASCIIZ string containg field you want to find (must be in lowercase)     ;;
  811. ;;------------------------------------------------------------------------------------------------;;
  812. ;< eax = 0 (error) / ptr to content of the HTTP header field                                      ;;
  813. ;;================================================================================================;;
  814.         push    ebx ecx edx esi edi
  815.  
  816.         DEBUGF  1, "Find header field: %s\n", [headername]
  817.  
  818.         mov     ebx, [identifier]
  819.         test    [ebx + http_msg.flags], FLAG_GOT_HEADER
  820.         jz      .fail
  821.  
  822.         lea     edx, [ebx + http_msg.data]
  823.         mov     ecx, edx
  824.         add     ecx, [ebx + http_msg.header_length]
  825.  
  826.   .restart:
  827.         mov     esi, [headername]
  828.         mov     edi, edx
  829.   .loop:
  830.         cmp     edi, ecx
  831.         jae     .fail
  832.         lodsb
  833.         scasb
  834.         je      .loop
  835.         test    al, al
  836.         jz      .done?
  837.   .next:
  838.         inc     edx
  839.         jmp     .restart
  840.  
  841.   .not_done:
  842.         inc     edi
  843.   .done?:
  844.         cmp     byte[edi-1], ':'
  845.         je      .almost_done
  846.         cmp     byte[edi-1], ' '
  847.         je      .not_done
  848.         cmp     byte[edi-1], 9  ; tab
  849.         je      .not_done
  850.  
  851.         jmp     .next
  852.  
  853.   .almost_done:                 ; FIXME: buffer overflow?
  854.         dec     edi
  855.         DEBUGF  1, "Found header field\n"
  856.   .spaceloop:
  857.         inc     edi
  858.         cmp     byte[edi], ' '
  859.         je      .spaceloop
  860.         cmp     byte[edi], 9    ; tab
  861.         je      .spaceloop
  862.  
  863.         mov     eax, edi
  864.         pop     edi esi edx ecx ebx
  865.         ret
  866.  
  867.   .fail:
  868.         pop     edi esi edx ecx ebx
  869.         xor     eax, eax
  870.         ret
  871.  
  872. endp
  873.  
  874.  
  875.  
  876.  
  877.  
  878. ;;================================================================================================;;
  879. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  880. ;;================================================================================================;;
  881. ;! Internal procedures section                                                                    ;;
  882. ;;================================================================================================;;
  883. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  884. ;;================================================================================================;;
  885.  
  886.  
  887.  
  888.  
  889. ;;================================================================================================;;
  890. proc open_connection hostname, port ;/////////////////////////////////////////////////////////////;;
  891. ;;------------------------------------------------------------------------------------------------;;
  892. ;? Connects to a HTTP server                                                                      ;;
  893. ;;------------------------------------------------------------------------------------------------;;
  894. ;> hostname     = ptr to ASCIIZ hostname                                                          ;;
  895. ;> port         = port (x86 byte order)                                                           ;;
  896. ;;------------------------------------------------------------------------------------------------;;
  897. ;< eax = 0 (error) / socketnum                                                                    ;;
  898. ;;================================================================================================;;
  899.  
  900. locals
  901.         sockaddr        dd ?
  902.         socketnum       dd ?
  903. endl
  904.  
  905. ; Resolve the hostname
  906.         DEBUGF  1, "Resolving hostname\n"
  907.         push    esp     ; reserve stack place
  908.         push    esp     ; fourth parameter
  909.         push    0       ; third parameter
  910.         push    0       ; second parameter
  911.         push    [hostname]
  912.         call    [getaddrinfo]
  913.         pop     esi
  914.         test    eax, eax
  915.         jnz     .error1
  916.  
  917. ; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
  918.         mov     esi, [esi + addrinfo.ai_addr]
  919.         mov     [sockaddr], esi
  920.         mov     eax, [esi + sockaddr_in.sin_addr]
  921.         test    eax, eax
  922.         jz      .error2
  923.  
  924.         DEBUGF  1, "Server ip=%u.%u.%u.%u\n", \
  925.         [esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
  926.         [esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
  927.  
  928.         mov     [esi + sockaddr_in.sin_family], AF_INET4
  929.         mov     eax, [port]
  930.         xchg    al, ah
  931.         mov     [esi + sockaddr_in.sin_port], ax
  932.  
  933. ; Connect to the server.
  934.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  935.         test    eax, eax
  936.         jz      .error2
  937.         mov     [socketnum], eax
  938.         DEBUGF  1, "Socket: 0x%x\n", eax
  939.  
  940.         mcall   connect, [socketnum], [sockaddr], 18
  941.         test    eax, eax
  942.         jnz     .error2
  943.         DEBUGF  1, "Socket is now connected.\n"
  944.  
  945. ; free allocated memory
  946.         push    [sockaddr]
  947.         call    [freeaddrinfo]
  948.  
  949.         mov     eax, [socketnum]
  950.         ret
  951.  
  952.   .error2:
  953.  
  954. ; free allocated memory
  955.         push    [sockaddr]
  956.         call    [freeaddrinfo]
  957.  
  958.   .error1:
  959.         xor     eax, eax
  960.         ret
  961.  
  962. endp
  963.  
  964.  
  965. ;;================================================================================================;;
  966. proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
  967. ;;------------------------------------------------------------------------------------------------;;
  968. ;? Split a given URL into hostname and pageaddr                                                   ;;
  969. ;;------------------------------------------------------------------------------------------------;;
  970. ;> URL = ptr to ASCIIZ URL                                                                        ;;
  971. ;;------------------------------------------------------------------------------------------------;;
  972. ;< eax = 0 (error) / ptr to ASCIIZ hostname                                                       ;;
  973. ;< ebx = ptr to ASCIIZ pageaddr                                                                   ;;
  974. ;;================================================================================================;;
  975.  
  976. locals
  977.         urlsize         dd ?
  978.         hostname        dd ?
  979.         pageaddr        dd ?
  980. endl
  981.  
  982.         DEBUGF  1, "parsing URL: %s\n", [URL]
  983.  
  984. ; remove any leading protocol text
  985.         mov     esi, [URL]
  986.         mov     ecx, URLMAXLEN
  987.         mov     ax, '//'
  988.   .loop1:
  989.         cmp     byte[esi], 0            ; end of URL?
  990.         je      .url_ok                 ; yep, so not found
  991.         cmp     [esi], ax
  992.         je      .skip_proto
  993.         inc     esi
  994.         dec     ecx
  995.         jnz     .loop1
  996.  
  997.         DEBUGF  1, "Invalid URL\n"
  998.         xor     eax, eax
  999.         ret
  1000.  
  1001.   .skip_proto:
  1002.         inc     esi                     ; skip the two '/'
  1003.         inc     esi
  1004.         mov     [URL], esi              ; update pointer so it skips protocol
  1005.         jmp     .loop1                  ; we still need to find the length of the URL
  1006.  
  1007.   .url_ok:
  1008.         sub     esi, [URL]              ; calculate total length of URL
  1009.         mov     [urlsize], esi
  1010.  
  1011.  
  1012. ; now look for page delimiter - it's a '/' character
  1013.         mov     ecx, esi                ; URL length
  1014.         mov     edi, [URL]
  1015.         mov     al, '/'
  1016.         repne   scasb
  1017.         jne     @f
  1018.         dec     edi                     ; return one char, '/' must be part of the pageaddr
  1019.         inc     ecx                     ;
  1020.   @@:
  1021.         push    ecx edi                 ; remember the pointer and length of pageaddr
  1022.  
  1023.         mov     ecx, edi
  1024.         sub     ecx, [URL]
  1025.         inc     ecx                     ; we will add a 0 byte at the end
  1026.         invoke  mem.alloc, ecx
  1027.         or      eax, eax
  1028.         jz      .no_mem
  1029.  
  1030.         mov     [hostname], eax         ; copy hostname to buffer
  1031.         mov     edi, eax
  1032.         mov     esi, [URL]
  1033.         dec     ecx
  1034.         rep     movsb
  1035.         xor     al, al
  1036.         stosb
  1037.  
  1038.         mov     [pageaddr], str_slash   ; assume there is no pageaddr
  1039.         pop     esi ecx
  1040.         test    ecx, ecx
  1041.         jz      .no_page
  1042.         inc     ecx                     ; we will add a 0 byte at the end
  1043.         invoke  mem.alloc, ecx
  1044.         or      eax, eax
  1045.         jz      .no_mem
  1046.  
  1047.         mov     [pageaddr], eax         ; copy pageaddr to buffer
  1048.         mov     edi, eax
  1049.         dec     ecx
  1050.         rep     movsb
  1051.         xor     al, al
  1052.         stosb
  1053.   .no_page:
  1054.  
  1055.         mov     eax, [hostname]
  1056.         mov     ebx, [pageaddr]
  1057.  
  1058.         DEBUGF  1, "hostname: %s\n", eax
  1059.         DEBUGF  1, "pageaddr: %s\n", ebx
  1060.  
  1061.         ret
  1062.  
  1063.   .no_mem:
  1064.         xor     eax, eax
  1065.         ret
  1066.  
  1067. endp
  1068.  
  1069.  
  1070. ;;================================================================================================;;
  1071. proc ascii_dec ;//////////////////////////////////////////////////////////////////////////////////;;
  1072. ;;------------------------------------------------------------------------------------------------;;
  1073. ;? Convert eax to ASCII decimal number                                                            ;;
  1074. ;;------------------------------------------------------------------------------------------------;;
  1075. ;> eax = number                                                                                   ;;
  1076. ;> edi = ptr where to write ASCII decimal number                                                  ;;
  1077. ;;------------------------------------------------------------------------------------------------;;
  1078. ;< /                                                                                              ;;
  1079. ;;================================================================================================;;
  1080.  
  1081.         push    -'0'
  1082.         mov     ecx, 10
  1083.   .loop:
  1084.         xor     edx, edx
  1085.         div     ecx
  1086.         add     dl, '0'
  1087.         push    edx
  1088.         test    eax, eax
  1089.         jnz     .loop
  1090.  
  1091.   .loop2:
  1092.         pop     eax
  1093.         add     al, '0'
  1094.         jz      .done
  1095.         stosb
  1096.         jmp     .loop2
  1097.   .done:
  1098.  
  1099.         ret
  1100.  
  1101. endp
  1102.  
  1103.  
  1104. ;;================================================================================================;;
  1105. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1106. ;;================================================================================================;;
  1107. ;! Imported functions section                                                                     ;;
  1108. ;;================================================================================================;;
  1109. ;;////////////////////////////////////////////////////////////////////////////////////////////////;;
  1110. ;;================================================================================================;;
  1111.  
  1112.  
  1113. align 16
  1114. @IMPORT:
  1115.  
  1116. library \
  1117.         libini, 'libini.obj', \
  1118.         network, 'network.obj'
  1119.  
  1120. import  libini, \
  1121.         ini.get_str, 'ini_get_str', \
  1122.         ini.get_int, 'ini_get_int'
  1123.  
  1124. import  network,\
  1125.         getaddrinfo, 'getaddrinfo',\
  1126.         freeaddrinfo,  'freeaddrinfo',\
  1127.         inet_ntoa, 'inet_ntoa'
  1128.  
  1129. ;;===========================================================================;;
  1130. ;;///////////////////////////////////////////////////////////////////////////;;
  1131. ;;===========================================================================;;
  1132. ;! Exported functions section                                                ;;
  1133. ;;===========================================================================;;
  1134. ;;///////////////////////////////////////////////////////////////////////////;;
  1135. ;;===========================================================================;;
  1136.  
  1137.  
  1138. align 4
  1139. @EXPORT:
  1140. export  \
  1141.         lib_init                , 'lib_init'            , \
  1142.         0x00010001              , 'version'             , \
  1143.         HTTP_get                , 'get'                 , \
  1144.         HTTP_head               , 'head'                , \
  1145.         HTTP_post               , 'post'                , \
  1146.         find_header_field       , 'find_header_field'   , \
  1147.         HTTP_process            , 'process'             , \
  1148.         HTTP_free               , 'free'                , \
  1149.         HTTP_stop               , 'stop'
  1150.  
  1151. ;        HTTP_put                , 'put'                 , \
  1152. ;        HTTP_delete             , 'delete'              , \
  1153. ;        HTTP_trace              , 'trace'               , \
  1154. ;        HTTP_connect            , 'connect'             , \
  1155.  
  1156.  
  1157.  
  1158. section '.data' data readable writable align 16
  1159.  
  1160. inifile         db '/sys/settings/network.ini', 0
  1161.  
  1162. sec_proxy:
  1163. key_proxy       db 'proxy', 0
  1164. key_proxyport   db 'port', 0
  1165. key_user        db 'user', 0
  1166. key_password    db 'password', 0
  1167.  
  1168. str_http11      db ' HTTP/1.1', 13, 10, 'Host: '
  1169.   .length       = $ - str_http11
  1170. str_post_cl     db 13, 10, 'Content-Length: '
  1171.   .length       = $ - str_post_cl
  1172. str_post_ct     db 13, 10, 'Content-Type: '
  1173.   .length       = $ - str_post_ct
  1174. str_close       db 13, 10, 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
  1175.   .length       = $ - str_close
  1176. str_proxy_auth  db 13, 10, 'Proxy-Authorization: Basic '
  1177.   .length       = $ - str_proxy_auth
  1178.  
  1179. base64_table    db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  1180.                 db '0123456789+/'
  1181.  
  1182. str_cl          db 'content-length', 0
  1183. str_slash       db '/', 0
  1184. str_te          db 'transfer-encoding', 0
  1185. str_get         db 'GET ', 0
  1186. str_head        db 'HEAD ', 0
  1187. str_post        db 'POST ', 0
  1188.  
  1189. include_debug_strings
  1190.  
  1191. ; uninitialized data
  1192. mem.alloc       dd ?
  1193. mem.free        dd ?
  1194. mem.realloc     dd ?
  1195. dll.load        dd ?
  1196.  
  1197. proxyAddr       rb 256
  1198. proxyUser       rb 256
  1199. proxyPassword   rb 256
  1200. proxyPort       dd ?