/* DESCRIPTION request.go provides unexported functionality for creating and sending requests required to configure settings of the GeoVision camera. AUTHORS Saxon A. Nelson-Milton LICENSE Copyright (C) 2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License in gpl.txt. If not, see http://www.gnu.org/licenses. */ package config import ( "bytes" "fmt" "io/ioutil" "net/http" "net/url" "regexp" "strconv" ) // Relevant sub directories. const ( loginSubDir = "/ssi.cgi/login.htm" // Used to get log-in page. loggedInSubDir = "/LoginPC.cgi" // Used to submit log-in. settingsSubDir = "/VideoSetting.cgi" // Used to submit settings. ) // TODO: make this configurable. const ( user = "admin" pass = "admin" ) // getLogin gets the log-in page and extracts the randomly generated cc values // from which (as well as username and password) two hashes are generated. // The generated hex is encoded into a url encoded form and returned as a string. func getLogin(c *http.Client, id, host string) (string, error) { req, err := http.NewRequest("GET", "http://"+host+loginSubDir, nil) if err != nil { return "", fmt.Errorf("can't create GET request for log-in page: %w", err) } req.Header.Set("Connection", "keep-alive") req.Header.Set("Cache-Control", "max-age=0") req.Header.Set("Upgrade-Insecure-Requests", "1") req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36") req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3") req.Header.Set("Accept-Encoding", "gzip, deflate") req.Header.Set("Accept-Language", "en-GB,en-US;q=0.9,en;q=0.8") req.Header.Set("Cookie", "CLIENT_ID="+id) resp, err := c.Do(req) if err != nil { return "", fmt.Errorf("could not do GET request for log-in page: %w", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("could not read response of GET request for log-in page: %w", err) } // Find the CC values in the source of the response. // These are used in calculation of the md5 hashes for the form submitted at // log-in. var cc [2]string for i := range cc { regStr := "cc" + strconv.Itoa(i+1) + "=\".{4}\"" exp := regexp.MustCompile(regStr).FindString(string(body)) cc[i] = exp[5 : len(exp)-1] } f := url.Values{} f.Set("grp", "-1") f.Set("username", "") f.Set("password", "") f.Set("Apply", "Apply") f.Set("umd5", md5Hex(cc[0]+user+cc[1])) f.Set("pmd5", md5Hex(cc[1]+pass+cc[0])) f.Set("browser", "1") f.Set("is_check_OCX_OK", "0") return f.Encode(), nil } // login will submit the form b generated by genLogin. func login(c *http.Client, id, host, b string) error { req, err := http.NewRequest("POST", "http://"+host+loggedInSubDir, bytes.NewBuffer([]byte(b))) if err != nil { return fmt.Errorf("could not create log-in request: %w", err) } req.Header.Set("Connection", "keep-alive") req.Header.Set("Content-Length", "142") req.Header.Set("Cache-Control", "max-age=0") req.Header.Set("Origin", "http://"+host) req.Header.Set("Upgrade-Insecure-Requests", "1") req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36") req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3") req.Header.Set("Referer", "http://"+host+"/ssi.cgi/Login.htm") req.Header.Set("Accept-Encoding", "gzip, deflate") req.Header.Set("Accept-Language", "en-GB,en-US;q=0.9,en;q=0.8") req.Header.Set("Cookie", "CLIENT_ID="+id+"; CLIENT_ID="+id) _, err = c.Do(req) if err != nil { return fmt.Errorf("could not do log-in request: %w", err) } return nil } // submitSettings will populate a url encoded form using s and submit the // settings to the server. func submitSettings(c *http.Client, id, host string, s settings) error { fBytes := []byte(populateForm(s).Encode()) req, err := http.NewRequest("POST", "http://"+host+settingsSubDir, bytes.NewReader(fBytes)) if err != nil { return fmt.Errorf("could not create settings submit request: %w", err) } req.Header.Set("Connection", "keep-alive") req.Header.Set("Content-Length", strconv.Itoa(len(fBytes))) req.Header.Set("Cache-Control", "max-age=0") req.Header.Set("Origin", "http://"+host) req.Header.Set("Upgrade-Insecure-Requests", "1") req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36") req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3") req.Header.Set("Referer", "http://"+host+"/ssi.cgi/VideoSettingSub.htm?cam="+strconv.Itoa(int(s.ch))) req.Header.Set("Accept-Encoding", "gzip, deflate") req.Header.Set("Accept-Language", "en-GB,en-US;q=0.9,en;q=0.8") req.Header.Set("Cookie", "CLIENT_ID="+id) // NB: not capturing error, as we always get one here for some reason. // TODO: figure out why. Does not affect submission. c.Do(req) return nil }