agi.http.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. package agi
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "net/url"
  11. "os"
  12. "path/filepath"
  13. "github.com/robertkrimen/otto"
  14. "imuslab.com/arozos/mod/agi/static"
  15. "imuslab.com/arozos/mod/info/logger"
  16. )
  17. /*
  18. AJGI HTTP Request Library
  19. This is a library for allowing AGI script to make HTTP Request from the VM
  20. Returning either the head or the body of the request
  21. Author: tobychui
  22. */
  23. func (g *Gateway) HTTPLibRegister() {
  24. err := g.RegisterLib("http", g.injectHTTPFunctions)
  25. if err != nil {
  26. logger.PrintAndLog("Agi", fmt.Sprint(err), nil)
  27. os.Exit(1)
  28. }
  29. }
  30. func (g *Gateway) injectHTTPFunctions(payload *static.AgiLibInjectionPayload) {
  31. vm := payload.VM
  32. u := payload.User
  33. //scriptFsh := payload.ScriptFsh
  34. //scriptPath := payload.ScriptPath
  35. w := payload.Writer
  36. //r := payload.Request
  37. vm.Set("_http_get", func(call otto.FunctionCall) otto.Value {
  38. //Get URL from function variable
  39. url, err := call.Argument(0).ToString()
  40. if err != nil {
  41. return otto.NullValue()
  42. }
  43. //Get respond of the url
  44. res, err := http.Get(url)
  45. if err != nil {
  46. return otto.NullValue()
  47. }
  48. bodyContent, err := io.ReadAll(res.Body)
  49. if err != nil {
  50. return otto.NullValue()
  51. }
  52. returnValue, err := vm.ToValue(string(bodyContent))
  53. if err != nil {
  54. return otto.NullValue()
  55. }
  56. return returnValue
  57. })
  58. vm.Set("_http_post", func(call otto.FunctionCall) otto.Value {
  59. //Get URL from function paramter
  60. url, err := call.Argument(0).ToString()
  61. if err != nil {
  62. return otto.NullValue()
  63. }
  64. //Get JSON content from 2nd paramter
  65. sendWithPayload := true
  66. jsonContent, err := call.Argument(1).ToString()
  67. if err != nil {
  68. //Disable the payload send
  69. sendWithPayload = false
  70. }
  71. //Create the request
  72. var req *http.Request
  73. if sendWithPayload {
  74. req, _ = http.NewRequest("POST", url, bytes.NewBuffer([]byte(jsonContent)))
  75. } else {
  76. req, _ = http.NewRequest("POST", url, bytes.NewBuffer([]byte("")))
  77. }
  78. req.Header.Set("Content-Type", "application/json")
  79. req.Header.Set("User-Agent", "arozos-http-client/1.1")
  80. //Send the request
  81. client := &http.Client{}
  82. resp, err := client.Do(req)
  83. if err != nil {
  84. logger.PrintAndLog("Agi", fmt.Sprint(err), nil)
  85. return otto.NullValue()
  86. }
  87. defer resp.Body.Close()
  88. bodyContent, err := io.ReadAll(resp.Body)
  89. if err != nil {
  90. return otto.NullValue()
  91. }
  92. returnValue, _ := vm.ToValue(string(bodyContent))
  93. return returnValue
  94. })
  95. vm.Set("_http_head", func(call otto.FunctionCall) otto.Value {
  96. //Get URL from function paramter
  97. url, err := call.Argument(0).ToString()
  98. if err != nil {
  99. return otto.NullValue()
  100. }
  101. //Request the url
  102. resp, err := http.Get(url)
  103. if err != nil {
  104. return otto.NullValue()
  105. }
  106. headerKey, err := call.Argument(1).ToString()
  107. if err != nil || headerKey == "undefined" {
  108. //No headkey set. Return the whole header as JSON
  109. js, _ := json.Marshal(resp.Header)
  110. returnValue, _ := vm.ToValue(string(js))
  111. return returnValue
  112. } else {
  113. //headerkey is set. Return if exists
  114. possibleValue := resp.Header.Get(headerKey)
  115. js, _ := json.Marshal(possibleValue)
  116. returnValue, _ := vm.ToValue(string(js))
  117. return returnValue
  118. }
  119. })
  120. //Get target status code for response
  121. vm.Set("_http_code", func(call otto.FunctionCall) otto.Value {
  122. //Get URL from function paramter
  123. url, err := call.Argument(0).ToString()
  124. if err != nil {
  125. return otto.FalseValue()
  126. }
  127. req, err := http.NewRequest("GET", url, nil)
  128. if err != nil {
  129. g.RaiseError(err)
  130. return otto.FalseValue()
  131. }
  132. payload := ""
  133. client := new(http.Client)
  134. client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
  135. //Redirection. Return the target location as well
  136. dest, _ := req.Response.Location()
  137. payload = dest.String()
  138. return errors.New("Redirect")
  139. }
  140. response, err := client.Do(req)
  141. if err != nil {
  142. return otto.FalseValue()
  143. }
  144. defer client.CloseIdleConnections()
  145. vm.Run(`var _location = "` + payload + `";`)
  146. value, _ := otto.ToValue(response.StatusCode)
  147. return value
  148. })
  149. vm.Set("_http_download", func(call otto.FunctionCall) otto.Value {
  150. //Get URL from function paramter
  151. downloadURL, err := call.Argument(0).ToString()
  152. if err != nil {
  153. return otto.FalseValue()
  154. }
  155. decodedURL, _ := url.QueryUnescape(downloadURL)
  156. //Get download desintation from paramter
  157. vpath, err := call.Argument(1).ToString()
  158. if err != nil {
  159. return otto.FalseValue()
  160. }
  161. //Optional: filename paramter
  162. filename, err := call.Argument(2).ToString()
  163. if err != nil || filename == "undefined" {
  164. //Extract the filename from the url instead
  165. filename = filepath.Base(decodedURL)
  166. }
  167. //Check user acess permission
  168. if !u.CanWrite(vpath) {
  169. g.RaiseError(errors.New("Permission Denied"))
  170. return otto.FalseValue()
  171. }
  172. //Convert the vpath to realpath. Check if it exists
  173. fsh, rpath, err := static.VirtualPathToRealPath(vpath, u)
  174. if err != nil {
  175. return otto.FalseValue()
  176. }
  177. if !fsh.FileSystemAbstraction.FileExists(rpath) || !fsh.FileSystemAbstraction.IsDir(rpath) {
  178. g.RaiseError(errors.New(vpath + " is a file not a directory."))
  179. return otto.FalseValue()
  180. }
  181. downloadDest := filepath.Join(rpath, filename)
  182. //Ok. Download the file
  183. resp, err := http.Get(decodedURL)
  184. if err != nil {
  185. return otto.FalseValue()
  186. }
  187. defer resp.Body.Close()
  188. // Create the file
  189. err = fsh.FileSystemAbstraction.WriteStream(downloadDest, resp.Body, 0775)
  190. if err != nil {
  191. return otto.FalseValue()
  192. }
  193. return otto.TrueValue()
  194. })
  195. vm.Set("_http_getb64", func(call otto.FunctionCall) otto.Value {
  196. //Get URL from function variable and return bytes as base64
  197. url, err := call.Argument(0).ToString()
  198. if err != nil {
  199. return otto.NullValue()
  200. }
  201. //Get respond of the url
  202. res, err := http.Get(url)
  203. if err != nil {
  204. return otto.NullValue()
  205. }
  206. bodyContent, err := io.ReadAll(res.Body)
  207. if err != nil {
  208. return otto.NullValue()
  209. }
  210. sEnc := base64.StdEncoding.EncodeToString(bodyContent)
  211. r, err := otto.ToValue(string(sEnc))
  212. if err != nil {
  213. logger.PrintAndLog("Agi", err.Error(), nil)
  214. return otto.NullValue()
  215. }
  216. return r
  217. })
  218. vm.Set("_http_redirect", func(call otto.FunctionCall) otto.Value {
  219. //Redirect the current request to another url
  220. targetUrl, err := call.Argument(0).ToString()
  221. if err != nil {
  222. return otto.NullValue()
  223. }
  224. statusCode, err := call.Argument(1).ToInteger()
  225. if err != nil {
  226. //Default: Temporary redirect
  227. statusCode = 307
  228. }
  229. w.Header().Set("Location", targetUrl)
  230. w.WriteHeader(int(statusCode))
  231. return otto.TrueValue()
  232. })
  233. //Wrap all the native code function into an imagelib class
  234. vm.Run(`
  235. var http = {};
  236. http.get = _http_get;
  237. http.post = _http_post;
  238. http.head = _http_head;
  239. http.download = _http_download;
  240. http.getb64 = _http_getb64;
  241. http.getCode = _http_code;
  242. http.redirect = function(t, c){
  243. if (typeof(c) == "undefined"){
  244. c = 307;
  245. }
  246. _http_redirect(t,c);
  247. };
  248. `)
  249. }