CGI - How to process HTML forms!

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
Julcar
Posts: 141
Joined: Oct 19, 2010 18:52
Contact:

CGI - How to process HTML forms!

Post by Julcar »

Hi all, I want to share with the community this code for processing HTML forms, some very useful with CGI:

Code: Select all

Sub PrintHeaders()
  Print !"Content-type:text/html;Charset=us-ascii\n"
End Sub

Function SplitVar(query as string, arg as string) as string
  Dim as string value, result
  If InStr(query, arg) <> 0 Then
    value = Mid(query, InStr(query, arg), len(query))
    If Instr(value, "&") <> 0 Then
      result = Mid(value, len(arg) + 2, Instr(value, "&") - len(arg) - 2)
    Else
      result = Mid(value, len(arg) + 2, len(value))
    End If
  Else
    result = ""
  End If
  SplitVar = result
End Function

Function ReadStdin() as string
  Dim numChars as integer
  Dim postQuery as string
  numChars = val(environ("CONTENT_LENGTH"))
  open cons for input as #1
  postQuery = space(numChars)
  get #1,,postQuery
  close #1
  ReadStdin = postQuery
End Function

Function QueryString(arg as string) as string
  If environ("REQUEST_METHOD") = "GET" Then
    QueryString = SplitVar(environ("QUERY_STRING"), arg)
  Else
    QueryString = ""
  End If
End Function

Function Post(query as string, arg as string) as string
  If environ("REQUEST_METHOD") = "POST" Then
    Post = SplitVar(query, arg)
  Else
    Post = ""
  End If
End Function

'Now we can print the page content

PrintHeaders()

Print "<html>"
Print "<head>"
Print "<body>"
Print "<form id=""personal-data"" method=""post"" action=""test.exe"">"
Print "<div style=""text-align:center;"">"
Print "<h2>Fill this form with your data</h2>"
Print "First Name: <input type=""text"" name=""firstname"" /><br />"
Print "Last Name: <input type=""text"" name=""lastname"" /><br />"
Print "City: <input type=""text"" name=""city"" /><br />"
Print "Country: <input type=""text"" name=""country"" /><br />"
Print "<input type=""submit"" id=""send"" value=""Send"" />"
Print "</div></form>"
Print "<div style=""text-align:center;"">"
If environ("REQUEST_METHOD") = "POST" Then
Dim as String strPost = ReadStdin()
Print "<em>Your data was successfully receipted!</em><br>"
Print "<p>Your name is: <strong>"& Post(strPost, "firstname") &" "& Post(strPost, "lastname") &"</strong></p>"
Print "<p>And you lives on: <strong>"& Post(strPost, "city") &" - "& Post(strPost, "country") &"</strong> </p>"
Else
Print "<em>No data sent yet!</em>"
End If
Print "<div>"
Print "</body>"
Print "</head>"
Print "</html>"
Just compile as TEST.EXE and run as CGI on a webserver like TinyWeb: http://www.ritlabs.com/en/products/tinyweb/

You must only create a CGI-BIN folder into your root and there place the EXE compiled, now go to your browser and prompt http://localhost/cgi-bin/test.exe and you will see the form ready to work.

Please comment about my code!

Regards
teccs
Posts: 6
Joined: Dec 24, 2006 20:27
Location: Cheyenne, WY
Contact:

Re: CGI - How to process HTML forms!

Post by teccs »

I am a newbie. Can this be made to work in Linux on ubuntu with apache?
Thanks you in advance.
JohanD
Posts: 11
Joined: Dec 23, 2014 7:06

Re: CGI - How to process HTML forms!

Post by JohanD »

Yes, easily.. I just worked it out myself.
Compile it, and place it (standard ubuntu) in /usr/lub/cgi-bin
Create a form, with post action, and start the compiled freebasic with it.
Works well.

Note that you need to alter above to decode the url-encoded recieved text, otherwise % and + signs are added.
Not at my system now, I will post the altered version with auto translation later this day.
JohanD
Posts: 11
Joined: Dec 23, 2014 7:06

Re: CGI - How to process HTML forms!

Post by JohanD »

My changes:

Code: Select all

Function URLDecode(arg as string) as string
      Dim as string char, query, value, rlt
      Dim i as integer
      char = "&H"
      For i = 0 To len(arg)
        query = Mid(arg, i, 1)
        If query = "%" Then
          value = chr(valint(char & Mid(arg, i + 1, 2)))
          rlt = rlt & value
          i = i + 2
        ElseIf query = "+" Then
          value = " "
          rlt = rlt & value
        Else
          rlt = rlt & query
        End If
      Next
      URLDecode = rlt
End Function

Code: Select all

Function SplitVar(query as string, arg as string) as string
  Dim as string value, rlt
  If InStr(query, arg) <> 0 Then
    value = Mid(query, InStr(query, arg), len(query))
    If Instr(value, "&") <> 0 Then
      rlt = Mid(value, len(arg) + 2, Instr(value, "&") - len(arg) - 2)
    Else
      rlt = Mid(value, len(arg) + 2, len(value))
    End If
  Else
    rlt = ""
  End If
  SplitVar = URLDecode(rlt)   <================================================= !!
End Function

Function ReadStdin() as string
  Dim numChars as integer
  Dim postQuery as string
  numChars = val(environ("CONTENT_LENGTH"))
  open cons for input as #1
  postQuery = space(numChars)
  get #1,,postQuery
  close #1
  ReadStdin = postQuery
End Function

Function QueryString(arg as string) as string
  If environ("REQUEST_METHOD") = "GET" Then
    QueryString = SplitVar(environ("QUERY_STRING"), arg)
  Else
    QueryString = ""
  End If
End Function

Function Post(query as string, arg as string) as string
  If environ("REQUEST_METHOD") = "POST" Then
    Post = SplitVar(query, arg)
  Else
    Post = ""
  End If
End Function
Julcar
Posts: 141
Joined: Oct 19, 2010 18:52
Contact:

Re: CGI - How to process HTML forms!

Post by Julcar »

Here are the code translated to langqb

Code: Select all

FUNCTION ReadStdin$
  IF ENVIRON$("REQUEST_METHOD") = "POST" THEN
    numChars = VAL(ENVIRON$("CONTENT_LENGTH"))
    postQuery$ = INPUT$(numChars)
    ReadStdin$ = postQuery$
  ELSE
    ReadStdin$ = ""
  END IF
END FUNCTION

FUNCTION SplitVar$ (strQuery$, arg$, delimiter$)
  result$ = ""
  IF INSTR(strQuery$, arg$) <> 0 THEN
    strVal$ = MID$(strQuery$, INSTR(strQuery$, arg$), LEN(strQuery$))
    IF INSTR(strVal$, delimiter$) <> 0 THEN
      result$ = MID$(strVal$, LEN(arg$) + 2, INSTR(strVal$, delimiter$) - LEN(arg$) - 2)
    ELSE
      result$ = MID$(strVal$, LEN(arg$) + 2, LEN(strVal$))
    END IF
  END IF
  SplitVar$ = result$
END FUNCTION

FUNCTION UnEscape$ (strQuery$)
  result$ = ""
  FOR i = 1 TO LEN(strQuery$)
    char$ = MID$(strQuery$, i, 1)
    SELECT CASE char$
      CASE "%"
        value$ = CHR$(VAL("&H" + MID$(strQuery$, i + 1, 2)))
        result$ = result$ + value$
        i = i + 2
      CASE "+"
        value$ = " "
        result$ = result$ + value$
      CASE ELSE
        result$ = result$ + char$
    END SELECT
  NEXT
  UnEscape$ = result$
END FUNCTION

FUNCTION Replace$ (strQuery$, lookFor$, replaceWith$)
  result$ = strQuery$
  DO
    foundPos = INSTR(result$, lookFor$)
    IF foundPos <> 0 THEN
      startPos = foundPos - 1
      nextPos = foundPos + LEN(lookFor$)
      strLeft$ = LEFT$(result$, startPos)
      strRight$ = MID$(result$, nextPos, LEN(result$))
      result$ = strLeft$ + replaceWith$ + strRight$
    ELSE
     EXIT DO
    END IF
  LOOP
  Replace$ = result$
END FUNCTION

COMMON SHARED StdinQuery$
StdinQuery$ = ReadStdin$

FUNCTION Post$ (arg$)
  Post$ = UnEscape$(SplitVar$(StdinQuery$, arg$, "&"))
END FUNCTION

FUNCTION QueryString$ (arg$)
  QueryString$ = UnEscape$(SplitVar$(ENVIRON$("QUERY_STRING"), arg$, "&"))
END FUNCTION
jdmcbride
Posts: 28
Joined: Aug 06, 2016 16:13

Re: CGI - How to process HTML forms!

Post by jdmcbride »

Two years later... Thank you.
JohanD
Posts: 11
Joined: Dec 23, 2014 7:06

Re: CGI - How to process HTML forms!

Post by JohanD »

Lol.. 2 years later I want to pick off where I left..
How to do cookie handling ?
Julcar
Posts: 141
Joined: Oct 19, 2010 18:52
Contact:

Re: CGI - How to process HTML forms!

Post by Julcar »

JohanD wrote:Lol.. 2 years later I want to pick off where I left..
How to do cookie handling ?
Is coded in QB lang, but should be easy to convert to FB lang

As SetCookie modifies HTTP headers, you must call the function BEFORE any output has been sent to client's web browser

Code: Select all

FUNCTION SetCookie$(cookieName$, cookieValue$, domain$, path$, expiresDate$, maxAgeTime, secure, httpOnly)
  DIM strOutput$
  'Add standard parameters
  strOutput$ = "Set-Cookie:" + CHR$(32) + cookieName$ + CHR$(61) + cookieValue$
  IF domain$ <> "" THEN
    strOutput$ = strOutput$ + CHR$(59) + CHR$(32) + "Domain=" + domain$
  END IF
  IF path$ <> "" THEN
    strOutput$ = strOutput$ + CHR$(59) + CHR$(32) + "Path=" + path$
  END IF
  IF expiresDate$ <> "" THEN
    'FIXME: validate standard date format Wdy, DD Mon YYYY HH:MM:SS GMT
    strOutput$ = strOutput$ + CHR$(59) + CHR$(32) + "Expires=" + expiresDate$
  END IF
  IF maxAgeTime > 0 THEN
    strOutput$ = strOutput$ + CHR$(59) + CHR$(32) + "Max-Age=" + LTRIM$(STR$(maxAgeTime))
  END IF
  IF secure = 1 THEN
    strOutput$ = strOutput$ + CHR$(59) + CHR$(32) + "Secure"
  END IF
  IF httpOnly = 1 THEN
    strOutput$ = strOutput$ + CHR$(59) + CHR$(32) + "HttpOnly"
  END IF
  'Add the line feed
  strOutput$ = strOutput$ + CHR$(13) + CHR$(10)
  SetCookie$ = strOutput$
  strOutput$ = ""
END FUNCTION

FUNCTION GetCookie$(cookieName$)
  DIM RawCookieStr$
  RawCookieStr$ = ENVIRON$("HTTP_COOKIE")
  IF cookieName$ <> "" THEN
    GetCookie$ = SplitVar$(RawCookieStr$, cookieName$, CHR$(59))
  ELSE
    GetCookie$ = RawCookieStr$
  END IF
  RawCookieStr$ = ""
END FUNCTION
JohanD
Posts: 11
Joined: Dec 23, 2014 7:06

Re: CGI - How to process HTML forms!

Post by JohanD »

Thanks sir, I need to take a deep look into this
bcohio2001
Posts: 556
Joined: Mar 10, 2007 15:44
Location: Ohio, USA
Contact:

Re: CGI - How to process HTML forms!

Post by bcohio2001 »

Here is something I use in my own creation of a web browser, still WIP.

Cookie.bi

Code: Select all

Type CookData
	As String CookName
	As String CookValue
	As Double Expires
	As String Path
	As String Host
End Type

Type Cook
	As Integer CookieCount
	As CookData CookieArray(Any)
	As Long LocalTimeBias 'minute difference between user and UTC (GMT)
	'
	Declare Constructor() 'load cookie file
	Declare Destructor() 'saves final cookie file
	Declare Function SendCookie(Host As String, Path As String) As String
	Declare Sub RecCookie(Header As String, Host As String, Path As String)
	Declare Function IsCurrent(Index As Integer) As Integer
End Type
Cookie.bas

Code: Select all

Constructor Cook()
'load in cookies
'ReDim CookieArray(0)
If FileExists(ExePath + "/Cookie.txt") Then
	Dim As Integer ff = FreeFile
	Open ExePath + "/Cookie.txt" For Input As #ff
	While Eof(ff) = 0
		ReDim Preserve CookieArray(CookieCount)
		Input #ff, CookieArray(CookieCount).Host
		Input #ff, CookieArray(CookieCount).Path
		Input #ff, CookieArray(CookieCount).CookName
		Input #ff, CookieArray(CookieCount).CookValue
		Input #ff, CookieArray(CookieCount).Expires
		'perm or non expired?
		If IsCurrent(CookieCount) = 1 Then
			'keep
			CookieCount += 1
		EndIf
	Wend
	Close #ff
EndIf
End Constructor

Destructor Cook()
'save non expired cookies
If FileExists(ExePath + "/Cookie.txt") Then Kill(ExePath + "/Cookie.txt")
If CookieCount Then
	Dim As Integer x, Valid, ff
	'any "Valid"
	For x = 0 To CookieCount - 1
		If IsCurrent(x) = 1 And CookieArray(x).CookValue <> "" Then Valid += 1
	Next
	If Valid Then
		ff = FreeFile
		Open ExePath + "/Cookie.txt" For Output As #ff
		For x = 0 To CookieCount - 1
			If IsCurrent(x) = 1 And CookieArray(x).CookValue <> "" Then
				Print #ff, CookieArray(x).Host
				Print #ff, CookieArray(x).Path
				Print #ff, CookieArray(x).CookName
				Print #ff, CookieArray(x).CookValue
				Print #ff, CookieArray(x).Expires
			EndIf
		Next
	EndIf
EndIf
End Destructor

Function Cook.SendCookie(Host As String, Path As String) As String
	If CookieCount = 0 Then Return ""
	Dim As String R = "" 'return value
	Dim As String CookieList = "" 'listing of cookies to add
	Dim As Integer x, S
	Dim As String PathOnly
	'Cookie: <name>=<value> [;<name>=<value>]...
	While x < CookieCount
		If IsCurrent(x) Then
			If CookieArray(x).Host = Host Then
				'exact match, check path
				If Left(Path, Len(CookieArray(x).Path)) = CookieArray(x).Path Then
					'Path match
					S = 1
				Else
					S = 0
				EndIf
			Else
				'check for partal match (global host)
				If Right(Host, Len(CookieArray(x).Host)) = CookieArray(x).Host Then
					S = 1 'flag to add
				Else
					S = 0 'do not add
				EndIf
			EndIf
			If S > 0 And Len(CookieArray(x).CookValue) > 0 Then
				'add
				If Len(CookieList) Then CookieList += ";"
				CookieList += CookieArray(x).CookName + "=" + CookieArray(x).CookValue
			EndIf
		EndIf
		x += 1
	Wend
	If Len(CookieList) Then R = "Cookie: " + CookieList 'add this to header
	Return R
End Function

Sub Cook.RecCookie(Header As String, Host As String, Path As String)
	Dim As Integer CookieStart = InStr(Header, "Set-Cookie:")
	Dim As Integer S, F
	Dim As CookData TempCookie
	Dim As String CookiePart
	'
	While CookieStart
		S = InStr(CookieStart, Header, Chr(13)) 'end of set-cookie
		CookiePart = Mid(Header, CookieStart, S - CookieStart)
		'Cookie name
		S = InStr(CookiePart, " ") + 1
		F = InStr(S, CookiePart, "=")
		TempCookie.CookName = Mid(CookiePart, S, F - S)
		'Cookie value
		S = F + 1
		F = InStr(S, CookiePart, ";")
		TempCookie.CookValue = Mid(CookiePart, S, F - S)
		'expires
		S = InStr(CookiePart, "expires=")
		If S Then
			'has expire
			S += 8
			F = InStr(S, CookiePart, ";")
			TempCookie.Expires = HttpDate(Mid(CookiePart, S, F - S)) 'GMT
		Else
			TempCookie.Expires = 0 'permanent or session
		EndIf
		'host
		S = InStr(CookiePart, "domain=")
		If S Then
			'has domain
			S += 7
			F = InStr(S, CookiePart, ";")
			TempCookie.Host = Mid(CookiePart, S, F - S)
		Else
			'SEE COMMENTS AT END OF FILE
			'session cookie
			TempCookie.Host = Host
			TempCookie.Expires = -1 'mark as so
		EndIf
		'path spec for host
		S = InStr(CookiePart, "path=")
		If S Then
			'has path
			S += 5
			F = InStr(S, CookiePart, ";")
			TempCookie.Path = Mid(CookiePart, S, F - S)
		Else
			'SEE COMMENTS AT END OF FILE
			S = InStrRev(Path, "/")
			If S Then
				TempCookie.Path = Left(Path, S)
			Else
				TempCookie.Path = "/"
			EndIf
		EndIf
		'search cookie array for one already set
		If CookieCount Then
			For S = 0 To CookieCount - 1
				If TempCookie.CookName = CookieArray(S).CookName And TempCookie.Host = CookieArray(S).Host And TempCookie.Path = CookieArray(S).Path Then
					CookieArray(S).CookValue = TempCookie.CookValue
					CookieArray(S).Expires = TempCookie.Expires
					Exit For
				EndIf
			Next
		Else
			S = 0
		EndIf
		If S = CookieCount Then
			'add
			ReDim Preserve CookieArray(CookieCount)
			CookieArray(S).Host = TempCookie.Host
			CookieArray(S).Path = TempCookie.Path
			CookieArray(S).CookName = TempCookie.CookName
			CookieArray(S).CookValue = TempCookie.CookValue
			CookieArray(S).Expires = TempCookie.Expires
			CookieCount += 1
		EndIf
		CookieStart = InStr(CookieStart + 5, Header, "Set-Cookie:")
	Wend
End Sub

Function Cook.IsCurrent(Index As Integer) As Integer
	If CookieArray(Index).Expires < 0 Then Return 2 'Session cookie
	If CookieArray(Index).Expires = 0 Then Return 1 'permenant cookie
	If CookieArray(Index).Expires > Now + TimeSerial(0, LocalTimeBias, 0) Then Return 1 'not expired
	Return 0 'EXPIRED
End Function

/'
Specifying the domain name, using the pattern domain=domain_name, is optional for persistent cookies and is used to indicate the end of the domain for which the cookie is valid.
Session cookies that specify a domain are rejected.
If the specified domain name ending matches the request, the cookie tries to match the path to determine if the cookie should be sent.
For example, if the domain name ending is .microsoft.com, requests to home.microsoft.com and support.microsoft.com would be checked to see if the specified pattern matches the request.
The domain name must have at least two or three periods in it to prevent cookies from being set for widely used domain name endings, such as .com, .edu, and co.jp.
Allowable domain names would be similar to .microsoft.com, .someschool.edu, and .someserver.co.jp. Only hosts within the specified domain can set a cookie for a domain.

Setting the path, using the pattern path=some_path, is optional and can be used to specify a subset of the URLs for which the cookie is valid.
If a path is specified, the cookie is considered valid for any requests that match that path.
For example, if the specified path is /example, requests with the paths /examplecode and /example/code.htm would match.
If no path is specified, the path is assumed to be the path of the resource associated with the Set-Cookie header.
'/
angros47
Posts: 2324
Joined: Jun 21, 2005 19:04

Re: CGI - How to process HTML forms!

Post by angros47 »

Under Linux, a tiny server can be created by

python3 -m http.server --cgi

I just tried with the example, it worked perfectly
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: CGI - How to process HTML forms!

Post by TJF »

A CGI service gets started for each request and stopped afterwards. It's a slow solution in case of large services / high request trafic.

In contrast an FCGI service gets started only once and then serves all further requests. Full sure, for complex tasks it's the better solution.
Post Reply