;Name:        parsejsonobject.asm
;Build:       nasm -felf64 parsejsonobject.asm -o parsejsonobject.o
;             ld -s -melf_x86_64 -o parsejsonobject parsejsonobject.o
;Description: reply data in the form of a JSON object.  After building this application,you need
;             to upload this to your webservers cgi root folder.
bits 64

[list -]
     %include "unistd.inc"
[list +]

section .bss

    oldbrkaddr:     resq 1
    charbuffer:     resb 1
    content:        resq 1   ; memory to store content len in
section .rodata

    requestmethod:  db   "REQUEST_METHOD=POST"
    .len:           equ  $-requestmethod
    contentlength:  db   "CONTENT_LENGTH="
    .len:           equ  $-contentlength
    top:            db   'Content-type: text/html',0x0A,0x0A,'{"'
    .len:           equ  $-top
    middle:         db   '":"'
    .len:           equ  $-middle    
    separator:      db   '","'
    .len:           equ  $-separator
    bottom:         db   '"}'
    .len:           equ  $-bottom

section .text

global _start
    ;first check if the form was posted
    ;adjust stack to environment parameters
    pop	    rax
    pop     rax
    pop     rax
    mov     rbp,rsp			;save begin of list in rbp
    ;let's loop through the webserver variables,searching for REQUEST_METHOD=POST
    pop     rsi
    or      rsi,rsi			;done yet?
    jz      .exit			;we didn't find the REQUEST_METHOD=POST string   
    ;RDI contains a pointer to CONTENT_LENGTH=POST the variable we are searching for
    ;look for the required variable name amongst them
    mov     rcx,requestmethod.len
    mov     rdi,requestmethod
    rep     cmpsb			;compare RCX bytes
    jne     .getrequestmethod		;no match get the next variable,if any
    ;we got a match,now read the CONTENT_LENGTH
    ;restore top of (list-1)
    mov     rsp,rbp
    cld					;just in case
    pop     rsi
    or      rsi,rsi			;done yet
    jz      .exit			;this shouldn't occur
    ;RDI contains a pointer to CONTENT_LENGTH= the variable we are searching for
    mov     rcx,contentlength.len
    mov     rdi,contentlength
    rep     cmpsb			;compare RCX bytes
    jne     .getcontentlength		;no match get the next variable,if any
    ;we got the CONTENT_LENGTH=,RSI points to the first character of the ASCII digit of the length
    ;initialise rcx
    xor     rcx,rcx
    xor     rax,rax
    lodsb				;get digit
    and     al,al			;if 0 then no digits
    je      .endofparamstring
    xor     rdx,rdx
    mov     rbx,10
    imul    rcx,rbx
    and     al,0x0F
    add     rcx,rax			;previous digit x 10 + current digit    
    jmp     .nextparamstringchar
    ;RCX contains the content_length in hexadecimal
    ;reserve space on,the heap to store the parameters from STDIN
    mov     qword[content],rcx
    syscall brk,0
    mov     qword[oldbrkaddr],rax	;save the address to de-allocate memory
    ;reserve memory for the parameters
    add     rax,qword[content]          ;add contentlength to the program break
    syscall brk,rax
    cmp     rax,0
    je      .exit			;if RAX = 0 then no memory is available,now we exit
    ;read the params in our created buffer
    syscall read,stdin,qword[oldbrkaddr],qword[content]
    ;start the output of the data as a JSON object
    syscall write,stdout,top,top.len
    ;parse the data to a JSON object
    ;loop through the data until '='
    mov     rsi,qword[oldbrkaddr]
    and     al,al
    jz      .endloop
    cmp     al,"="
    je      .printmiddle
    cmp     al,"&"
    je      .printseparator
    cmp     al,"+"
    jne     .printchar
    ;+ will be replaced by space
    mov     al," "
    push    rsi			;rsi on stack    
    ;print the character
    mov     byte[charbuffer],al
    syscall   write,stdout,charbuffer,1
    pop     rsi
    jmp     .repeat
    push    rsi
    syscall write,stdout,middle,middle.len
    pop     rsi
    jmp     .repeat
    push    rsi
    syscall write,stdout,separator,separator.len
    pop     rsi
    jmp     .repeat
    syscall write,stdout,bottom,bottom.len
    ;release the reserved memory
    ;free the allocated memory
    syscall brk,qword[oldbrkaddr]
    ;and exit the program
    syscall exit,0