forked from KolibriOS/kolibrios
787 lines
26 KiB
NASM
787 lines
26 KiB
NASM
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;; ;;
|
||
|
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
|
||
|
;; Distributed under terms of the GNU General Public License ;;
|
||
|
;; ;;
|
||
|
;; HTTP library for KolibriOS ;;
|
||
|
;; ;;
|
||
|
;; Written by hidnplayr@kolibrios.org ;;
|
||
|
;; ;;
|
||
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
||
|
;; Version 2, June 1991 ;;
|
||
|
;; ;;
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
|
||
|
; references:
|
||
|
; "HTTP made really easy", http://www.jmarshall.com/easy/http/
|
||
|
; "Hypertext Transfer Protocol -- HTTP/1.1", http://tools.ietf.org/html/rfc2616
|
||
|
|
||
|
|
||
|
URLMAXLEN = 65535
|
||
|
BUFFERSIZE = 4096
|
||
|
|
||
|
__DEBUG__ = 1
|
||
|
__DEBUG_LEVEL__ = 1
|
||
|
|
||
|
|
||
|
format MS COFF
|
||
|
|
||
|
public @EXPORT as 'EXPORTS'
|
||
|
|
||
|
include '../../../struct.inc'
|
||
|
include '../../../proc32.inc'
|
||
|
include '../../../macros.inc'
|
||
|
purge section,mov,add,sub
|
||
|
include '../../../debug-fdo.inc'
|
||
|
|
||
|
include '../../../network.inc'
|
||
|
include 'http.inc'
|
||
|
|
||
|
virtual at 0
|
||
|
http_msg http_msg
|
||
|
end virtual
|
||
|
|
||
|
macro copy_till_zero {
|
||
|
@@:
|
||
|
lodsb
|
||
|
test al, al
|
||
|
jz @f
|
||
|
stosb
|
||
|
jmp @r
|
||
|
@@:
|
||
|
}
|
||
|
|
||
|
section '.flat' code readable align 16
|
||
|
|
||
|
;;===========================================================================;;
|
||
|
lib_init: ;//////////////////////////////////////////////////////////////////;;
|
||
|
;;---------------------------------------------------------------------------;;
|
||
|
;? Library entry point (called after library load) ;;
|
||
|
;;---------------------------------------------------------------------------;;
|
||
|
;> eax = pointer to memory allocation routine ;;
|
||
|
;> ebx = pointer to memory freeing routine ;;
|
||
|
;> ecx = pointer to memory reallocation routine ;;
|
||
|
;> edx = pointer to library loading routine ;;
|
||
|
;;---------------------------------------------------------------------------;;
|
||
|
;< eax = 1 (fail) / 0 (ok) (library initialization result) ;;
|
||
|
;;===========================================================================;;
|
||
|
mov [mem.alloc], eax
|
||
|
mov [mem.free], ebx
|
||
|
mov [mem.realloc], ecx
|
||
|
mov [dll.load], edx
|
||
|
|
||
|
invoke dll.load, @IMPORT
|
||
|
or eax, eax
|
||
|
jz .ok
|
||
|
|
||
|
; load proxy settings
|
||
|
invoke ini.get_str, inifile, sec_proxy, key_proxy, proxyAddr, 256, proxyAddr
|
||
|
invoke ini.get_int, inifile, sec_proxy, key_proxyport, 80
|
||
|
mov [proxyPort], eax
|
||
|
invoke ini.get_str, inifile, sec_proxy, key_user, proxyUser, 256, proxyUser
|
||
|
invoke ini.get_str, inifile, sec_proxy, key_password, proxyPassword, 256, proxyPassword
|
||
|
|
||
|
xor eax, eax
|
||
|
inc eax
|
||
|
ret
|
||
|
|
||
|
.ok:
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
;;================================================================================================;;
|
||
|
proc HTTP_get URL ;///////////////////////////////////////////////////////////////////////////////;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;? ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;> _ ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;< eax = 0 (error) / buffer ptr ;;
|
||
|
;;================================================================================================;;
|
||
|
locals
|
||
|
hostname dd ?
|
||
|
pageaddr dd ?
|
||
|
sockaddr dd ?
|
||
|
socketnum dd ?
|
||
|
buffer dd ?
|
||
|
endl
|
||
|
|
||
|
; split the URL into hostname and pageaddr
|
||
|
stdcall parse_url, [URL]
|
||
|
test eax, eax
|
||
|
jz .error
|
||
|
mov [hostname], eax
|
||
|
mov [pageaddr], ebx
|
||
|
|
||
|
; Do we need to use a proxy?
|
||
|
cmp [proxyAddr], 0
|
||
|
jne .proxy_done
|
||
|
|
||
|
; TODO
|
||
|
.proxy_done:
|
||
|
|
||
|
; Resolve the hostname
|
||
|
DEBUGF 1, "Resolving hostname\n"
|
||
|
push esp ; reserve stack place
|
||
|
push esp ; fourth parameter
|
||
|
push 0 ; third parameter
|
||
|
push 0 ; second parameter
|
||
|
push [hostname]
|
||
|
call [getaddrinfo]
|
||
|
pop esi
|
||
|
test eax, eax
|
||
|
jnz .error
|
||
|
|
||
|
; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
|
||
|
mov esi, [esi + addrinfo.ai_addr]
|
||
|
mov [sockaddr], esi
|
||
|
mov eax, [esi + sockaddr_in.sin_addr]
|
||
|
test eax, eax
|
||
|
jz .error
|
||
|
|
||
|
DEBUGF 1, "Server ip=%u.%u.%u.%u\n", \
|
||
|
[esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
|
||
|
[esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
|
||
|
|
||
|
mov [esi + sockaddr_in.sin_family], AF_INET4
|
||
|
mov [esi + sockaddr_in.sin_port], 80 shl 8 ;;; FIXME
|
||
|
|
||
|
; Connect to the server.
|
||
|
mcall socket, AF_INET4, SOCK_STREAM, 0
|
||
|
test eax, eax
|
||
|
jz .error
|
||
|
mov [socketnum], eax
|
||
|
DEBUGF 1, "Socket: 0x%x\n", eax
|
||
|
|
||
|
mcall connect, [socketnum], [sockaddr], 18
|
||
|
test eax, eax
|
||
|
jnz .error
|
||
|
DEBUGF 1, "Socket is now connected.\n"
|
||
|
|
||
|
; TODO: free address buffer(s)
|
||
|
|
||
|
; Create the HTTP request.
|
||
|
invoke mem.alloc, BUFFERSIZE
|
||
|
test eax, eax
|
||
|
jz .error
|
||
|
mov [buffer], eax
|
||
|
DEBUGF 1, "Buffer has been allocated.\n"
|
||
|
|
||
|
mov dword[eax], 'GET '
|
||
|
lea edi, [eax + 4]
|
||
|
mov esi, [pageaddr] ; TODO: for proxy use http:// and then full URL
|
||
|
copy_till_zero
|
||
|
|
||
|
mov esi, str_http11
|
||
|
mov ecx, str_http11.length
|
||
|
rep movsb
|
||
|
|
||
|
mov esi, [hostname]
|
||
|
copy_till_zero
|
||
|
|
||
|
mov esi, str_close
|
||
|
mov ecx, str_close.length
|
||
|
rep movsb
|
||
|
|
||
|
mov byte[edi], 0
|
||
|
DEBUGF 1, "Request:\n%s", [buffer]
|
||
|
|
||
|
; now send the request
|
||
|
mov esi, edi
|
||
|
sub esi, [buffer] ; length
|
||
|
xor edi, edi ; flags
|
||
|
|
||
|
mcall send, [socketnum], [buffer]
|
||
|
test eax, eax
|
||
|
jz .error
|
||
|
DEBUGF 1, "Request has been sent to server.\n"
|
||
|
|
||
|
; Now that we have sent the request, re-purpose buffer as receive buffer
|
||
|
mov eax, [buffer]
|
||
|
push [socketnum]
|
||
|
popd [eax + http_msg.socket]
|
||
|
lea esi, [eax + http_msg.data]
|
||
|
mov [eax + http_msg.flags], 0
|
||
|
mov [eax + http_msg.write_ptr], esi
|
||
|
mov [eax + http_msg.buffer_length], BUFFERSIZE - http_msg.data
|
||
|
mov [eax + http_msg.chunk_ptr], 0
|
||
|
|
||
|
mov [eax + http_msg.status], 0
|
||
|
mov [eax + http_msg.header_length], 0
|
||
|
mov [eax + http_msg.content_length], 0
|
||
|
|
||
|
ret ; return buffer ptr
|
||
|
|
||
|
.error:
|
||
|
DEBUGF 1, "Error!\n"
|
||
|
xor eax, eax ; return 0 = error
|
||
|
ret
|
||
|
|
||
|
endp
|
||
|
|
||
|
|
||
|
|
||
|
;;================================================================================================;;
|
||
|
proc HTTP_process identifier ;////////////////////////////////////////////////////////////////////;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;? ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;> _ ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;< eax = -1 (not finished) / 0 finished ;;
|
||
|
;;================================================================================================;;
|
||
|
pusha
|
||
|
mov ebp, [identifier]
|
||
|
mcall recv, [ebp + http_msg.socket], [ebp + http_msg.write_ptr], \
|
||
|
[ebp + http_msg.buffer_length], MSG_DONTWAIT
|
||
|
cmp eax, 0xffffffff
|
||
|
je .check_socket
|
||
|
DEBUGF 1, "Received %u bytes\n", eax
|
||
|
|
||
|
mov edi, [ebp + http_msg.write_ptr]
|
||
|
add [ebp + http_msg.write_ptr], eax
|
||
|
sub [ebp + http_msg.buffer_length], eax
|
||
|
jz .got_all_data
|
||
|
test [ebp + http_msg.flags], FLAG_GOT_HEADER
|
||
|
jnz .header_parsed
|
||
|
|
||
|
sub eax, 4
|
||
|
jl .no_header
|
||
|
.scan:
|
||
|
; scan for end of header (empty line)
|
||
|
cmp dword[edi], 0x0a0d0a0d ; end of header
|
||
|
je .end_of_header
|
||
|
cmp word[edi+2], 0x0a0a
|
||
|
je .end_of_header
|
||
|
inc edi
|
||
|
dec eax
|
||
|
jnz .scan
|
||
|
|
||
|
.no_header:
|
||
|
popa
|
||
|
xor eax, eax
|
||
|
dec eax
|
||
|
ret
|
||
|
|
||
|
.end_of_header:
|
||
|
add edi, 4 - http_msg.data
|
||
|
sub edi, ebp
|
||
|
mov [ebp + http_msg.header_length], edi
|
||
|
or [ebp + http_msg.flags], FLAG_GOT_HEADER
|
||
|
DEBUGF 1, "Header length: %u\n", edi
|
||
|
|
||
|
; Ok, we have found header:
|
||
|
cmp dword[ebp + http_msg.data], 'HTTP'
|
||
|
jne .invalid_header
|
||
|
cmp dword[ebp + http_msg.data+4], '/1.0'
|
||
|
je .http_1.0
|
||
|
cmp dword[ebp + http_msg.data+4], '/1.1'
|
||
|
jne .invalid_header
|
||
|
or [ebp + http_msg.flags], FLAG_HTTP11
|
||
|
.http_1.0:
|
||
|
cmp byte[ebp + http_msg.data+8], ' '
|
||
|
jne .invalid_header
|
||
|
DEBUGF 1, "Header seems valid.\n"
|
||
|
|
||
|
lea esi, [ebp + http_msg.data+9]
|
||
|
xor eax, eax
|
||
|
xor ebx, ebx
|
||
|
mov ecx, 3
|
||
|
.statusloop:
|
||
|
lodsb
|
||
|
sub al, '0'
|
||
|
jb .invalid_header
|
||
|
cmp al, 9
|
||
|
ja .invalid_header
|
||
|
lea ebx, [ebx + 4*ebx]
|
||
|
shl ebx, 1
|
||
|
add ebx, eax
|
||
|
dec ecx
|
||
|
jnz .statusloop
|
||
|
mov [ebp + http_msg.status], ebx
|
||
|
DEBUGF 1, "Status: %u\n", ebx
|
||
|
|
||
|
; Now, convert all header names to lowercase.
|
||
|
; This way, it will be much easier to find certain header fields, later on.
|
||
|
|
||
|
lea esi, [ebp + http_msg.data]
|
||
|
mov ecx, [ebp + http_msg.header_length]
|
||
|
.need_newline:
|
||
|
inc esi
|
||
|
dec ecx
|
||
|
jz .convert_done
|
||
|
cmp byte[esi], 10
|
||
|
jne .need_newline
|
||
|
; Ok, we have a newline, a line beginning with space or tabs has no header fields.
|
||
|
|
||
|
inc esi
|
||
|
dec ecx
|
||
|
jz .convert_done
|
||
|
cmp byte[esi], ' '
|
||
|
je .need_newline
|
||
|
cmp byte[esi], 9 ; horizontal tab
|
||
|
je .need_newline
|
||
|
jmp .convert_loop
|
||
|
.next_char:
|
||
|
inc esi
|
||
|
dec ecx
|
||
|
jz .convert_done
|
||
|
.convert_loop:
|
||
|
cmp byte[esi], ':'
|
||
|
je .need_newline
|
||
|
cmp byte[esi], 'A'
|
||
|
jb .next_char
|
||
|
cmp byte[esi], 'Z'
|
||
|
ja .next_char
|
||
|
or byte[esi], 0x20 ; convert to lowercase
|
||
|
jmp .next_char
|
||
|
.convert_done:
|
||
|
mov byte[esi-1], 0
|
||
|
lea esi, [ebp + http_msg.data]
|
||
|
DEBUGF 1, "Header names converted to lowercase:\n%s\n", esi
|
||
|
|
||
|
; Check for content-length header field.
|
||
|
stdcall find_header_field, ebp, str_cl
|
||
|
test eax, eax
|
||
|
jz .no_content
|
||
|
or [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
|
||
|
|
||
|
xor edx, edx
|
||
|
.cl_loop:
|
||
|
movzx ebx, byte[eax]
|
||
|
inc eax
|
||
|
cmp bl, 10
|
||
|
je .cl_ok
|
||
|
cmp bl, 13
|
||
|
je .cl_ok
|
||
|
cmp bl, ' '
|
||
|
je .cl_ok
|
||
|
sub bl, '0'
|
||
|
jb .invalid_header
|
||
|
cmp bl, 9
|
||
|
ja .invalid_header
|
||
|
lea edx, [edx + edx*4] ; edx = edx*10
|
||
|
shl edx, 1 ;
|
||
|
add edx, ebx
|
||
|
jmp .cl_loop
|
||
|
|
||
|
.cl_ok:
|
||
|
mov [ebp + http_msg.content_length], edx
|
||
|
DEBUGF 1, "Content-length: %u\n", edx
|
||
|
|
||
|
; Resize buffer according to content-length.
|
||
|
mov eax, [ebp + http_msg.header_length]
|
||
|
add eax, [ebp + http_msg.content_length]
|
||
|
add eax, http_msg.data
|
||
|
|
||
|
mov ecx, eax
|
||
|
sub ecx, [ebp + http_msg.write_ptr]
|
||
|
mov [ebp + http_msg.buffer_length], ecx
|
||
|
|
||
|
invoke mem.realloc, ebp, eax
|
||
|
or eax, eax
|
||
|
jz .no_ram
|
||
|
jmp .header_parsed ; hooray!
|
||
|
|
||
|
.no_content:
|
||
|
DEBUGF 1, "Content-length not found.\n"
|
||
|
|
||
|
; We didnt find 'content-length', maybe server is using chunked transfer encoding?
|
||
|
; Try to find 'transfer-encoding' header.
|
||
|
stdcall find_header_field, ebp, str_te
|
||
|
test eax, eax
|
||
|
jz .invalid_header
|
||
|
|
||
|
mov ebx, dword[eax]
|
||
|
or eax, 0x20202020
|
||
|
cmp ebx, 'chun'
|
||
|
jne .invalid_header
|
||
|
mov ebx, dword[eax+4]
|
||
|
or eax, 0x00202020
|
||
|
and eax, 0x00ffffff
|
||
|
cmp ebx, 'ked'
|
||
|
jne .invalid_header
|
||
|
|
||
|
or [ebp + http_msg.flags], FLAG_CHUNKED
|
||
|
|
||
|
DEBUGF 1, "Transfer type is: chunked\n"
|
||
|
|
||
|
; Set chunk pointer where first chunk should begin.
|
||
|
mov eax, [ebp + http_msg.header_length]
|
||
|
add eax, http_msg.data
|
||
|
mov [ebp + http_msg.chunk_ptr], eax
|
||
|
|
||
|
.header_parsed:
|
||
|
; If data is chunked, combine chunks into contiguous data if so.
|
||
|
test [ebp + http_msg.flags], FLAG_CHUNKED
|
||
|
jz .not_chunked
|
||
|
|
||
|
.chunkloop:
|
||
|
mov ecx, [ebp + http_msg.write_ptr]
|
||
|
sub ecx, [ebp + http_msg.chunk_ptr]
|
||
|
jb .not_finished
|
||
|
|
||
|
mov esi, [ebp + http_msg.chunk_ptr]
|
||
|
xor ebx, ebx
|
||
|
.chunk_hexloop:
|
||
|
lodsb
|
||
|
sub al, '0'
|
||
|
jb .chunk_
|
||
|
cmp al, 9
|
||
|
jbe .chunk_hex
|
||
|
sub al, 'A' - '0'
|
||
|
jb .chunk_
|
||
|
cmp al, 5
|
||
|
jbe .chunk_hex
|
||
|
sub al, 'a' - 'A'
|
||
|
cmp al, 5
|
||
|
ja .chunk_
|
||
|
.chunk_hex:
|
||
|
shl ebx, 4
|
||
|
add bl, al
|
||
|
jmp .chunk_hexloop
|
||
|
.chunk_:
|
||
|
DEBUGF 1, "got chunk of %u bytes\n", ebx
|
||
|
; If chunk size is 0, all chunks have been received.
|
||
|
test ebx, ebx
|
||
|
jz .got_all_data ; last chunk, hooray! FIXME: what if it wasnt a valid hex number???
|
||
|
add [ebp + http_msg.chunk_ptr], ebx
|
||
|
|
||
|
; Chunkline ends with a CR, LF or simply LF
|
||
|
.end_of_chunkline?: ; FIXME: buffer overflow possible!
|
||
|
cmp al, 10
|
||
|
je .end_of_chunkline
|
||
|
lodsb
|
||
|
jmp .end_of_chunkline?
|
||
|
|
||
|
.end_of_chunkline:
|
||
|
; Now move all received data to the left (remove chunk header).
|
||
|
; Meanwhile, update write_ptr and content_length accordingly.
|
||
|
mov edi, [ebp + http_msg.chunk_ptr]
|
||
|
mov ecx, [ebp + http_msg.write_ptr]
|
||
|
sub ecx, esi
|
||
|
mov eax, esi
|
||
|
sub eax, edi
|
||
|
sub [ebp + http_msg.write_ptr], eax
|
||
|
add [ebp + http_msg.content_length], ecx
|
||
|
rep movsb
|
||
|
jmp .chunkloop
|
||
|
|
||
|
.not_chunked:
|
||
|
; Check if we got all the data.
|
||
|
mov eax, [ebp + http_msg.header_length]
|
||
|
add eax, [ebp + http_msg.content_length]
|
||
|
cmp eax, [ebp + http_msg.buffer_length]
|
||
|
je .got_all_data
|
||
|
|
||
|
.not_finished:
|
||
|
; DEBUGF 1, "Needs more processing...\n"
|
||
|
popa
|
||
|
xor eax, eax
|
||
|
dec eax
|
||
|
ret
|
||
|
|
||
|
.got_all_data:
|
||
|
DEBUGF 1, "We got all the data!\n"
|
||
|
or [ebp + http_msg.flags], FLAG_GOT_DATA
|
||
|
mcall close, [ebp + http_msg.socket]
|
||
|
popa
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
.check_socket:
|
||
|
cmp ebx, EWOULDBLOCK
|
||
|
je .not_finished
|
||
|
DEBUGF 1, "ERROR: socket error %u\n", ebx
|
||
|
|
||
|
or [ebp + http_msg.flags], FLAG_SOCKET_ERROR
|
||
|
popa
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
.invalid_header:
|
||
|
DEBUGF 1, "ERROR: invalid header\n"
|
||
|
or [ebp + http_msg.flags], FLAG_INVALID_HEADER
|
||
|
popa
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
.no_ram:
|
||
|
DEBUGF 1, "ERROR: out of RAM\n"
|
||
|
or [ebp + http_msg.flags], FLAG_NO_RAM
|
||
|
popa
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
endp
|
||
|
|
||
|
|
||
|
|
||
|
;;================================================================================================;;
|
||
|
proc find_header_field identifier, headername ;///////////////////////////////////////////////////;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;? ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;> _ ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;< eax = -1 (error) / 0 ;;
|
||
|
;;================================================================================================;;
|
||
|
push ebx ecx edx esi edi
|
||
|
|
||
|
DEBUGF 1, "Find header field: %s\n", [headername]
|
||
|
|
||
|
mov ebx, [identifier]
|
||
|
lea edx, [ebx + http_msg.data]
|
||
|
mov ecx, edx
|
||
|
add ecx, [ebx + http_msg.header_length]
|
||
|
|
||
|
.restart:
|
||
|
mov esi, [headername]
|
||
|
mov edi, edx
|
||
|
.loop:
|
||
|
cmp edi, ecx
|
||
|
jae .fail
|
||
|
lodsb
|
||
|
scasb
|
||
|
je .loop
|
||
|
test al, al
|
||
|
jz .done?
|
||
|
.next:
|
||
|
inc edx
|
||
|
jmp .restart
|
||
|
|
||
|
.not_done:
|
||
|
inc edi
|
||
|
.done?:
|
||
|
cmp byte[edi-1], ':'
|
||
|
je .almost_done
|
||
|
cmp byte[edi-1], ' '
|
||
|
je .not_done
|
||
|
cmp byte[edi-1], 9 ; tab
|
||
|
je .not_done
|
||
|
|
||
|
jmp .next
|
||
|
|
||
|
.almost_done: ; FIXME: buffer overflow?
|
||
|
dec edi
|
||
|
DEBUGF 1, "Found header field\n"
|
||
|
.spaceloop:
|
||
|
inc edi
|
||
|
cmp byte[edi], ' '
|
||
|
je .spaceloop
|
||
|
cmp byte[edi], 9 ; tab
|
||
|
je .spaceloop
|
||
|
|
||
|
mov eax, edi
|
||
|
pop edi esi edx ecx ebx
|
||
|
ret
|
||
|
|
||
|
.fail:
|
||
|
pop edi esi edx ecx ebx
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
endp
|
||
|
|
||
|
|
||
|
; internal procedures start here:
|
||
|
|
||
|
|
||
|
;;================================================================================================;;
|
||
|
proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;? ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;> _ ;;
|
||
|
;;------------------------------------------------------------------------------------------------;;
|
||
|
;< eax = -1 (error) / 0 ;;
|
||
|
;;================================================================================================;;
|
||
|
|
||
|
locals
|
||
|
urlsize dd ?
|
||
|
hostname dd ?
|
||
|
pageaddr dd ?
|
||
|
endl
|
||
|
|
||
|
DEBUGF 1, "URL: %s\n", [URL]
|
||
|
|
||
|
; remove any leading protocol text
|
||
|
mov esi, [URL]
|
||
|
mov ecx, URLMAXLEN
|
||
|
mov ax, '//'
|
||
|
.loop1:
|
||
|
cmp byte[esi], 0 ; end of URL?
|
||
|
je .url_ok ; yep, so not found
|
||
|
cmp [esi], ax
|
||
|
je .skip_proto
|
||
|
inc esi
|
||
|
dec ecx
|
||
|
jnz .loop1
|
||
|
|
||
|
; URL invalid !
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
.skip_proto:
|
||
|
inc esi ; skip the two '/'
|
||
|
inc esi
|
||
|
mov [URL], esi ; update pointer so it skips protocol
|
||
|
jmp .loop1 ; we still need to find the length of the URL
|
||
|
|
||
|
.url_ok:
|
||
|
sub esi, [URL] ; calculate total length of URL
|
||
|
mov [urlsize], esi
|
||
|
|
||
|
;;; FIXME: urls with no pageaddr are not parsed correctly!!
|
||
|
|
||
|
; now look for page delimiter - it's a '/' character
|
||
|
mov ecx, esi ; URL length
|
||
|
mov edi, [URL]
|
||
|
mov al, '/'
|
||
|
repne scasb
|
||
|
dec edi ; return one char, '/' must be part of the pageaddr
|
||
|
inc ecx ;
|
||
|
push ecx edi ; remember the pointer and length of pageaddr
|
||
|
|
||
|
mov ecx, edi
|
||
|
sub ecx, [URL]
|
||
|
inc ecx ; we will add a 0 byte at the end
|
||
|
invoke mem.alloc, ecx
|
||
|
or eax, eax
|
||
|
jz .no_mem
|
||
|
|
||
|
mov [hostname], eax ; copy hostname to buffer
|
||
|
mov edi, eax
|
||
|
mov esi, [URL]
|
||
|
dec ecx
|
||
|
rep movsb
|
||
|
xor al, al
|
||
|
stosb
|
||
|
|
||
|
mov [pageaddr], null_str ; assume there is no pageaddr
|
||
|
pop esi ecx
|
||
|
test ecx, ecx
|
||
|
jz .no_page
|
||
|
inc ecx ; we will add a 0 byte at the end
|
||
|
invoke mem.alloc, ecx
|
||
|
or eax, eax
|
||
|
jz .no_mem
|
||
|
|
||
|
mov [pageaddr], eax ; copy pageaddr to buffer
|
||
|
mov edi, eax
|
||
|
dec ecx
|
||
|
rep movsb
|
||
|
xor al, al
|
||
|
stosb
|
||
|
.no_page:
|
||
|
|
||
|
mov eax, [hostname]
|
||
|
mov ebx, [pageaddr]
|
||
|
|
||
|
DEBUGF 1, "hostname: %s\n", eax
|
||
|
DEBUGF 1, "pageaddr: %s\n", ebx
|
||
|
|
||
|
ret
|
||
|
|
||
|
.no_mem:
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
endp
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
;;================================================================================================;;
|
||
|
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
|
||
|
;;================================================================================================;;
|
||
|
;! Imported functions section ;;
|
||
|
;;================================================================================================;;
|
||
|
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
|
||
|
;;================================================================================================;;
|
||
|
|
||
|
|
||
|
align 16
|
||
|
@IMPORT:
|
||
|
|
||
|
library \
|
||
|
libini, 'libini.obj', \
|
||
|
network, 'network.obj'
|
||
|
|
||
|
import libini, \
|
||
|
ini.get_str, 'ini_get_str', \
|
||
|
ini.get_int, 'ini_get_int'
|
||
|
|
||
|
import network,\
|
||
|
getaddrinfo, 'getaddrinfo',\
|
||
|
freeaddrinfo, 'freeaddrinfo',\
|
||
|
inet_ntoa, 'inet_ntoa'
|
||
|
|
||
|
;;===========================================================================;;
|
||
|
;;///////////////////////////////////////////////////////////////////////////;;
|
||
|
;;===========================================================================;;
|
||
|
;! Exported functions section ;;
|
||
|
;;===========================================================================;;
|
||
|
;;///////////////////////////////////////////////////////////////////////////;;
|
||
|
;;===========================================================================;;
|
||
|
|
||
|
|
||
|
align 4
|
||
|
@EXPORT:
|
||
|
export \
|
||
|
lib_init , 'lib_init' , \
|
||
|
0x00010001 , 'version' , \
|
||
|
HTTP_get , 'get' , \
|
||
|
find_header_field , 'find_header_field' , \
|
||
|
HTTP_process , 'process'
|
||
|
|
||
|
; HTTP_head , 'head' , \
|
||
|
; HTTP_post , 'post' , \
|
||
|
; HTTP_put , 'put' , \
|
||
|
; HTTP_delete , 'delete' , \
|
||
|
; HTTP_trace , 'trace' , \
|
||
|
; HTTP_connect , 'connect' , \
|
||
|
|
||
|
|
||
|
|
||
|
section '.data' data readable writable align 16
|
||
|
|
||
|
inifile db '/sys/settings/network.ini', 0
|
||
|
|
||
|
sec_proxy:
|
||
|
key_proxy db 'proxy', 0
|
||
|
key_proxyport db 'port', 0
|
||
|
key_user db 'user', 0
|
||
|
key_password db 'password', 0
|
||
|
|
||
|
str_http11 db ' HTTP/1.1', 13, 10, 'Host: '
|
||
|
.length = $ - str_http11
|
||
|
str_close db 13, 10, 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
|
||
|
.length = $ - str_close
|
||
|
str_proxy_auth db 13, 10, 'Proxy-Authorization: Basic '
|
||
|
.length = $ - str_proxy_auth
|
||
|
|
||
|
base64_table db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||
|
db '0123456789+/'
|
||
|
|
||
|
str_cl db 'content-length', 0
|
||
|
null_str db 0
|
||
|
str_te db 'transfer-encoding', 0
|
||
|
|
||
|
include_debug_strings
|
||
|
|
||
|
; uninitialized data
|
||
|
mem.alloc dd ?
|
||
|
mem.free dd ?
|
||
|
mem.realloc dd ?
|
||
|
dll.load dd ?
|
||
|
|
||
|
proxyAddr rb 256
|
||
|
proxyUser rb 256
|
||
|
proxyPassword rb 256
|
||
|
proxyPort dd ?
|