Subversion Repositories Kolibri OS

Rev

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