123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- (add-to-load-path (dirname (current-filename)))
- (use-modules
- (web server)
- (web request)
- (web response)
- (web uri)
- (decode)
- (oop goops)
- (submit)
- (sxml simple)
- (srfi srfi-9) ;;records
- ;; (srfi srfi-19)
- )
- ;; I might be able to use curl to email
- ;; https://stackoverflow.com/questions/14722556/using-curl-to-send-email
- ;; https://blog.edmdesigner.com/send-email-from-linux-command-line/
- ;; guile also has some curl bindings...
- ;; joshua@dobby ~$ curl --url 'smtps://smtp.dismail.de:465' -F subject='test email' --ssl-reqd --mail-from 'jbranso@dismail.de' --mail-rcpt 'jbranso@dismail.de' --user 'jbranso@dimail.de:sticky4RunWhy;' --insecure
- ;; curl: (67) Login denied
- ;; joshua@dobby ~$
- ;; I'll need to do server side validation of email
- ;;https://www.regular-expressions.info/email.html
- ;;http://synthcode.com/scheme/irregex
- ;;before I implement a captcha here are 10 things to check for
- ;;
- ;; 1) validate everything server side. If input has any HTML, do not accept it.
- ;; 2) Check for links. Any input should not have links.
- ;; 3) check for the right number of POST and GET fields. If there are extra, it's probably a hacking attempt.
- ;; 4) Check the HTTP header
- ;; spam bots do not normally set a user agent (HTTP_USER_AGENT) or a referring page (HTTP_REFERER). You should certainly ensure the referrer is the page where your form is located.
- ;; 5) Use a honeypot field. Have an input field that should be left blank! Set it to display none. A spammer will try
- ;; to fill it out.
- ;; 6) 90% of computer users use javascript. Use js to checksum the data. The server can then verify that checksum.
- ;; 7) Show a verification page. Bots have a tough time verifying data. Show the user data once more, and have them verify. and re-submit.
- ;; 8) Time the user response! Put in the form the time that it was generated. Use the user IP address as the encryption key. If the user took 5-10 minutes to complete, then it is probably a human. Otherwise it is probably a bot.
- ;; 9) Log everything. This should help me spot hacking attempts.
- ;; 10) You could put a captcha in, if a user fails one of the above. You do not always have to show the captcha!
- (define navbar
- '(nav (@ (class "navbar navbar-expand-lg navbar-light bg-light"))
- (a (@ (class "navbar-brand")) "My IFT")
- (button (@ (class "navbar-toggler")
- (type "button")
- (data-toggle "collapse")
- (data-target "#navbarSupportedContent")
- (aria-controls "navbarSupportedContent")
- (aria-expanded "false")
- (aria-label "Toggle navigation"))
- (span (@ (class "navbar-toggler-icon"))))
- (div (@ (class "collapse navbar-collapse")
- (id "navbarSupportedcontent"))
- (ul (@ (class ("navbar-nav mr-auto")))
- ;; (li (@ (class "nav-item"))
- ;; (a (@ (class "nav-link")
- ;; (href "About"))
- ;; "About"))
- ;; (li (@ (class "nav-item active"))
- ;; (a (@ (class "nav-link")
- ;; (href "apply"))
- ;; "Apply"))
- ))))
- (define (templatize title body)
- `(html (head (title ,title)
- (head
- (link
- (@ (rel "stylesheet")
- (href "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css")
- ;;(href "localhost:8081/css/bootstrap.min.css")
- )
- ))
- (body ,navbar ,@body))))
- (define (insert-option-values values)
- (if (null? values) '()
- (let ([value (car values)])
- (cons `(options (@ (value ,value))
- ,value)
- (insert-option-values (cdr values))))))
- (define* (my-select id options
- #:key
- (required #t))
- `(select (@ (id ,id)
- (name ,id)
- ,(if (eq? required #t)
- '(required)
- '(not-required))
- (class "custom-select"))
- (option (@ (value "")
- (selected ""))
- "Choose...")
- ,(let loop ([options options])
- (if (null? options)
- '()
- (cons `(option (@ (value ,(car options)))
- ,(car options))
- (loop (cdr options)))))))
- ;; (define-syntax m-basic-form-group
- ;; (syntax-rules ()
- ;; [(m-basic-form-group the-label id)
- ;; '(div (@ (class "form-row"))
- ;; (div (@ (class "col-md-8"))
- ;; (label the-label)
- ;; (input (@ (class "form-control")))
- ;; ))]
- ;; [(m-basic-form-group the-label id ...)
- ;; (begin
- ;; '(div (@ (class "col-md-3"))
- ;; (label the-label)
- ;; (input (@ (class "form-control"))))
- ;; (m-basic-form-group the-label id ...))]))
- (define* (my-input input-type label id
- #:optional options
- #:key
- (placeholder "")
- (required #t))
- (let ([input-type input-type])
- (cond [(string= input-type "select")
- (my-select id options #:required required)]
- [(string= input-type "textarea")
- `(textarea (@ (id ,id)
- (name ,id)
- (type ,input-type)
- (class "form-control")
- ,(if (eq? required #t)
- '(required)
- '(not-required))
- ;; the "" is necessary to make sxml put a
- ;; closing </textarea>
- (placeholder ,placeholder)) "")]
- [else (let ([input "input"])
- (when (string= input-type "textarea")
- (set! input "textarea"))
- `(input (@ (id ,id)
- (name ,id)
- (type ,input-type)
- (class "form-control")
- ,(if (eq? required #t)
- '(required)
- '(not-required))
- (placeholder ,placeholder))))])))
- ;; I can make this better! (basic-form-group #:options '("red"
- ;;"green")) is obviously a select. I could infer that! Also
- ;;(basic-form-group #:input-type "select") would have the default
- ;;options "No" and "Yes"
- (define* (basic-form-group label id
- #:key
- (input-type "text")
- (placeholder "")
- (required #t)
- options
- (width 8))
- `(div (@ (class ,(string-append "form-group col-md-"
- (number->string width))))
- (label ,label)
- ,(my-input input-type label id options #:required required)
- ;;(input (@ (class "form-control") (placeholder ,placeholder)))
- ))
- ;; not working
- ;; (define-syntax define-record-type*
- ;; (syntax-rules ()
- ;; ((define-record-type* type
- ;; constructor
- ;; constructor?
- ;; (fieldname var1) ...)
- ;; (define-record-type type
- ;; (constructor fieldname ...)
- ;; constructor?
- ;; (fieldname var1) ...))))
- ;; this works!!!!
- (define-syntax my-define-record-type
- (syntax-rules ()
- ((my-define-record-type type
- constructor
- constructor?
- (fieldname var1) ...)
- (define-record-type type
- (constructor fieldname ...)
- constructor?
- (fieldname var1) ... ))))
- (my-define-record-type <bs-form-group>
- make-dog
- dog?
- (age dog-age))
- (define-record-type <bs-form-group>
- (make-bs-form-group type placeholder)
- bs-form-group?
- ;; horizontal or vertical
- (type bs-form-group)
- (placeholder bs-form-group-placeholder))
- (define-syntax bs-horizontal-form-group
- (syntax-rules (placeholder)
- ((bs-horizontal-form-group ((var1 var2)
- (placeholder var3)) ...)
- (horizontal-form-group var1 var2 #:placeholder var3))))
- (define* (horizontal-form-group label id
- #:key
- (form-class "row")
- placeholder
- (input-type "text")
- (required #t)
- options
- )
- `(div (@ (class "form-group row"))
- (label (@ (class "col-md-2")
- (for ,id))
- ,label)
- (div (@ (class "col-md-6"))
- ,(my-input input-type label id options #:required required)
- (div (@ (class "valid-feedback"))
- "Looks good!"))))
- (define* (respond #:optional body #:key
- (status 200)
- (title "My IFT")
- (doctype "<!DOCTYPE html>\n")
- (content-type-params '((charset . "utf-8")))
- (content-type 'text/html)
- (extra-headers '())
- (sxml (and body (templatize title body))))
- (values (build-response
- #:code status
- #:headers `((content-type
- . (,content-type ,@content-type-params))
- ,@extra-headers))
- (lambda (port)
- (if sxml
- (begin
- (if doctype (display doctype port))
- (sxml->xml sxml port))))))
- (define (request-path-components request)
- (split-and-decode-uri-path (uri-path (request-uri request))))
- ;; Paste this in your REPL
- (define (not-found request)
- (values (build-response #:code 404)
- (string-append "Resource not found: "
- (uri->string (request-uri request)))))
- (define (test-form)
- (respond
- `((div (@ (class "container"))
- (div (@ (class "row")))
- (div (@ (class "col-md-12"))
- (form (@ (method "post")
- (action "submit1")
- (id "test-form"))
- ,(horizontal-form-group "Your first name" "first-name"
- #:placeholder "Jason")
- ,(horizontal-form-group "Your last name" "last-name"
- #:placeholder "Smith")
- ,(horizontal-form-group "Your email" "email"
- #:placeholder "youremail@gmail.com"
- #:input-type "email")
- (input (@ (name "hidden") (hidden)))
- (div (@ (class "row"))
- (div (@ (class "col-md-2"))
- (input (@ (class "btn btn-primary")
- (type "submit")
- )
- "Submit")))
- ))))))
- (define (main-page)
- (respond
- `((div (@ (class "container"))
- (div (@ (class "row"))
- (div (@ (class "col-md-12"))
- (h1 "Apply for a loan")
- (form (@ (method "post")
- (action "submit"))
- ,(horizontal-form-group "Your first name" "first-name"
- #:placeholder "James")
- ,(horizontal-form-group "Your last name" "last-name"
- #:placeholder "Jones")
- ,(horizontal-form-group "Your number" "number"
- ;;TODO
- ;; I want to format this as a number via
- ;;<input type="tel" name="phone" pattern="[0-9]{3}-[0-9]{2}-[0-9]{3}">
- ;; as seen here: https://www.w3schools.com/html/html_form_input_types.asp
- ;;#:input-type "tel"
- #:placeholder "765 293 4930" )
- ,(horizontal-form-group "Your email" "email"
- #:placeholder "youremail@gmail.com"
- #:input-type "email")
- ,(horizontal-form-group "Address" "address1"
- #:placeholder "123 Main Street")
- ,(horizontal-form-group "Address Line 2" "address1"
- #:placeholder "123 Main Street")
- (div (@ (class "form-row"))
- ,(basic-form-group "City" "city" #:width 4)
- ,(basic-form-group "State" "state" #:width 2)
- ,(basic-form-group "Zip" "zip" #:width 2)
- )
- ,(horizontal-form-group "Are you a U.S. Citizen?" "state"
- #:input-type "select"
- #:options '("No" "Yes"))
- ,(basic-form-group "Co-Borrower's (If applicable)" "co-borrowers"
- #:required #f)
- ,(basic-form-group "Borrower's Fico Score? (Please list all borrowers scores)"
- ;#:placeholder "700, 750, 800"
- "fico")
- ,(basic-form-group "Company name the borrower is closing in? (must close in a corporate entity)"
- "company")
- ;;This is my honeypot input. If a user puts data in this, then they are probably not a user.
- (input (@ (name "hidden") (hidden)))
- ,(basic-form-group "Who makes up the entity and what are their percentages of ownership?"
- "percentages" #:input-type "textarea")
- (div (@ (class "form-row"))
- ,(basic-form-group "Do you own any other investment properties?"
- "own"
- #:width 6)
- ,(basic-form-group "If yes, how many?"
- "own-number"
- #:width 2
- #:required #f))
- ,(basic-form-group
- "Does the borrower have any Tax liens, judgments, past bankruptcies, past chapter filings, past foreclosures, recent or pending lawsuits against them?"
- "past-problems"
- #:input-type "select"
- #:options '("No" "Yes"))
- ,(basic-form-group "If YES, please explain and list date(s)"
- "past-problems-reasons"
- #:input-type "textarea" #:required #f)
- ,(basic-form-group "Do you rent or own?" "rent-or-own"
- #:input-type "select" #:options '("Own" "Rent"))
- ,(basic-form-group "Have you ever had any late rent payments/mortgage?"
- "late-payments"
- #:input-type "select" #:options '("No" "Yes"))
- ,(basic-form-group "If yes when?" "late-payments-reasons" #:required #f)
- ,(basic-form-group "Are you already working with another broker or lender?"
- "other-lender"
- #:input-type "select" #:options '("No" "Yes"))
- ,(basic-form-group "If yes, who?" "other-lender-name" #:required #f)
- ,(basic-form-group "Are you currently working with an 11 Capital Finance IAP?"
- "11-capital-lender"
- #:input-type "select" #:options '("Yes" "No"))
- ,(basic-form-group "If yes, who?" "11-capital-lender-name" #:required #f)
- ,(basic-form-group "Are you or any member of the borrowing entity related by blood or marriage?"
- "related-borrowers"
- #:input-type "select" #:options '("No" "Yes"))
- ,(basic-form-group "What rates and terms are you expecting?"
- "rates-and-terms"
- #:input-type "textarea")
- ;; this is the second page
- ,(horizontal-form-group "Property Address" "address1"
- #:placeholder "123 Main Street")
- ,(horizontal-form-group "Property Address Line 2" "address1"
- #:placeholder "123 Main Street")
- ,(basic-form-group
- "Exact property type? eg: SFR, 2unit, 7unit: (If commercial property please be very specific."
- "exact-property-type")
- ,(basic-form-group "What is the property square footage?: (If applicable)"
- "footage"
- #:required #f)
- ,(basic-form-group "Loan type" "loan-type"
- #:input-type "select"
- #:options '("Permanent Finance"
- "Bridge Loan"
- "Rehab Loan"
- "Ground up Construction"))
- ;; this is the third page
- ,(basic-form-group "Purchase price" "purchase-price")
- ,(basic-form-group "Current fair market value of the property" "fair-market-value")
- ,(basic-form-group "Are you already in a purchase and sales contract" "purchase-or-sales-contract")
- ,(basic-form-group (string-append "How much money do you have to contribute towards "
- "the transaction? (Most commercial purchases require 30% down."
- "Borrower also needs to cover closing costs.")
- "money")
- ,(basic-form-group "Total cash on hand?" "cash-on-hand")
- ,(basic-form-group "What is the loan amount requested? In USD?" "loan-money")
- ,(basic-form-group "When does the borrower need to close?" "closing date")
- ,(basic-form-group "Is the property owner occupied or a pure investment property?"
- "occupied-pure-investment-property"
- #:input-type "select" #:options '("Owner Occupied" "Pure Investment"))
- ,(basic-form-group "What is the monthly rental income on the property?"
- "monthly-rent")
- ,(basic-form-group "What is the occupancy percentage of the property? (%)"
- "occupancy-percentage" #:placeholder "100%")
- ,(basic-form-group "What are the monthly taxes on the property? In USD?"
- "monthly-taxes")
- ,(basic-form-group "What is the insurance on the property?"
- "property-insurance")
- ,(basic-form-group "If the property type is a condo, is this a warrantable or non-warrantable condo?"
- "warrantable-or-not"
- #:required #f
- #:input-type "select" #:options '("Warrantable" "Non-Warrantable"))
- ,(basic-form-group "If yes, what are the dues?" "dues"
- #:required #f)
- ,(basic-form-group "If yes, how are the dues paid? eg: monthly, quartly, yearly"
- "how-paid" #:required #f)
- ,(basic-form-group "What is specific about your deal?" "specific" #:input-type "textarea")
- (div (@ (class "row"))
- (div (@ (class "col-md-2"))
- (input (@ (class "btn btn-primary")
- (type "submit")))))
- )
- ))))))
- (define (run-page request body)
- ;;(display (request-path-components request))
- (let ([current-page (request-path-components request)])
- (cond [(equal? current-page '())
- (main-page)
- ;;(respond '((h1 "Are you ready to kick start your loan?")))
- ]
- [(equal? current-page '("hacker"))
- (respond '((h1 "Hello Hacker!")))]
- [(equal? current-page '("submit"))
- (respond (submit-response body))
- ;;(respond '((h1 "Thank you for submitting your request! We will reach out to you soon!")))
- ;; (if (eq? (verify-request (request)) #t)
- ;; (main-page))
- ]
- [(equal? current-page '("test-form"))
- (test-form)]
- [(equal? current-page '("submit1"))
- (if (verify-body body)
- (respond '((h1 "Your entered correct data")))
- (respond '((h1 "You did not enter correct data."))))]
- [(equal? current-page '("css" "bootstrap.min.css"))
- (values `((content-type . (text/css))
- (cache-control . (public))
- ;;(parse-header 'date ,(current-date))
- )
- (let ([port (open-file "css/bootstrap.min.css" "r")])
- (define css-file (get-string-all port))
- (close-port port)
- css-file))]
- [(equal? current-page '("apply")
- (main-page)
- )]
- [(equal? current-page '("hello"))
- (values `((content-type . (text/plain))
- )
- "Hello hacker!")]
- [else
- (respond `((h1 "Page not found.")
- (h1 ,(let loop ([current-page current-page])
- (if (null? current-page) ""
- (string-append (car current-page) "/"
- (loop (cdr current-page))))))))])))
- (run-server run-page 'http '(#:port 8081) ; '(#:host="localhost")
- )
|