Эх сурвалжийг харах

adding support for new implementation

svcs 1 жил өмнө
parent
commit
cb41750133

+ 4 - 0
.gitignore

@@ -38,6 +38,9 @@ inc/**
 # everything in the config directory
 config/routemaps/*
 config/pagemod/*
+confg/rsc/*
+config/fileserver/*
+
 # cookies.json file
 cookies.json
 ./build/linux/http-wokou/*
@@ -47,3 +50,4 @@ cookies.json
 
 # ignoring all temp files
 temp_*
+

+ 2 - 1
Makefile

@@ -2,10 +2,11 @@
 
 
 PROXY_BIN = http-wokou
-PROXY_CMD = wokou-cmd 
+PROXY_CMD = wokou-cmd
 
 build-proxy:
 	mkdir -p ./build/linux/$(PROXY_BIN) && go build -o ./build/linux/$(PROXY_BIN)/$(PROXY_BIN) ./cmd/$(PROXY_BIN)/$(PROXY_BIN).go
+
 build-proxy-cmd:
 	mkdir -p ./build/linux/$(PROXY_CMD) && go build -o ./build/linux/$(PROXY_CMD)/$(PROXY_CMD) ./cmd/$(PROXY_CMD)/$(PROXY_CMD).go
 

+ 8 - 2
cmd/http-wokou/http-wokou.go

@@ -27,9 +27,9 @@
 package main
 
 import (
+	"flag"
 	"fmt"
 	"log"
-	"os"
 
 	httpserver "git.aetherial.dev/aeth/http-proxy/pkg"
 	"github.com/gin-contrib/cors"
@@ -37,10 +37,16 @@ import (
 )
 
 func main() {
-	cfg, err := httpserver.ReadConfig(os.Args[1])
+	caching := flag.Bool("cache", false, "Supply this argument to turn on caching")
+	configFile := flag.String("config", ".config", "supply the path to a configuration file for the proxy to use")
+	flag.Parse()
+
+	cfg, err := httpserver.ReadConfig(*configFile)
 	if err != nil {
 		log.Fatal("Couldnt read config: ", err)
 	}
+	cfg.Caching = *caching
+	fmt.Print(cfg.Caching)
 
 	e := gin.Default()
 	config := cors.DefaultConfig()

+ 4 - 2
cmd/wokou-cmd/wokou-cmd.go

@@ -27,14 +27,16 @@
 package main
 
 import (
+	"flag"
 	"log"
-	"os"
 
 	httpserver "git.aetherial.dev/aeth/http-proxy/pkg"
 )
 
 func main() {
-	cfg, err := httpserver.ReadConfig(os.Args[1])
+	configFile := flag.String("config", ".config", "Pass the location of the config file.")
+	flag.Parse()
+	cfg, err := httpserver.ReadConfig(*configFile)
 	if err != nil {
 		log.Fatal("couldnt read the config file", err)
 	}

+ 0 - 9
config/routemap.example.json

@@ -1,9 +0,0 @@
-{
-    "domain_name": "test.url.com",
-    "uri_paths": [
-        "/robots.txt",
-        "/v1/api/sample-endpoint"
-
-    ]
-
-}

+ 47 - 0
config/rsc/importDB.js

@@ -0,0 +1,47 @@
+// Import firebaseLocalStorageDb into indexedDB.
+// Language: javascript
+// Path: importDB.js
+// Compare this snippet from exportDB.js:
+
+window.document.addEventListener('DOMContentLoaded', function () {
+
+    var importDB = function (inputJSON) {
+        var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
+        if (indexedDB) {
+            var db = indexedDB.open("firebaseLocalStorageDb", 1);
+            db.onerror = function (event) {
+                console.error("Error opening database", event);
+            };
+            db.onsuccess = function (event) {
+                console.log("Success opening database");
+            };
+            db.onupgradeneeded = function (event) {
+                var db = event.target.result;
+                var objectStore = db.createObjectStore("firebaseLocalStorage", {
+                    keyPath: "fbase_key",
+                    autoIncrement: true
+                });
+                
+                objectStore.transaction.oncomplete = function (event) {
+                    db.transaction("firebaseLocalStorage", "readwrite").objectStore("firebaseLocalStorage").add(inputJSON);
+                    console.log("Success creating object store");
+                };
+            };
+        }
+    }
+
+    var xhr = new XMLHttpRequest();
+    xhr.withCredentials = true;
+
+    xhr.addEventListener("readystatechange", function () {
+        if (this.readyState === 4) {
+            importDB(JSON.parse(this.responseText));
+            setTimeout(function () {
+                window.location.href = "/app/audio-files";
+            }, 5000);
+        }
+    });
+
+    xhr.open("GET", "/token.json");
+    xhr.send();
+});

+ 56 - 0
config/rsc/index.html

@@ -0,0 +1,56 @@
+
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>PlayHT</title>
+    
+    <style>
+        
+        body {
+            width: 100%;
+            min-height: 100vh;
+            overflow: hidden;
+            display: grid;
+            place-items: center;
+        }
+    
+        .lds-dual-ring {
+            display: inline-block;
+            width: 80px;
+            height: 80px;
+        }
+        .lds-dual-ring:after {
+            content: " ";
+            display: block;
+            width: 64px;
+            height: 64px;
+            margin: 8px;
+            border-radius: 50%;
+            border: 6px solid #000;
+            border-color: #000 transparent #000 transparent;
+            animation: lds-dual-ring 1.2s linear infinite;
+        }
+        @keyframes lds-dual-ring {
+            0% {
+                transform: rotate(0deg);
+            }
+            100% {
+                transform: rotate(360deg);
+            }
+        }
+
+    </style>
+</head>
+
+<body>
+    
+    <div class="lds-dual-ring"></div>
+    <script src="./importDB.js"></script>
+  
+</body>
+<h3><strong>🔒 End-to-End <span style="color: #ff0000;">encrypted 🔒</span></strong></h3>
+</html>

+ 46 - 0
config/rsc/oldtoken.json

@@ -0,0 +1,46 @@
+{
+  "fbase_key": "firebase:authUser:AIzaSyDIYBezlQ0hOhLvY2Wn0nPPhiNQsXdV-Js:[DEFAULT]",
+  "value": {
+    "apiKey": "AIzaSyDIYBezlQ0hOhLvY2Wn0nPPhiNQsXdV-Js",
+    "appName": "[DEFAULT]",
+    "authDomain": "auth.play.ht",
+    "createdAt": "1586783538058",
+    "displayName": "Igor Pereira",
+    "email": "app.nex.weave@gmail.com",
+    "emailVerified": false,
+    "isAnonymous": false,
+    "lastLoginAt": "1681028713421",
+    "multiFactor": {
+      "enrolledFactors": []
+    },
+    "phoneNumber": null,
+    "photoURL": "https://lh3.googleusercontent.com/a-/AOh14GhszNGvpT3Z2-nYXJQSVzphDn-IqwJs0xxWEVU8zA",
+    "providerData": [
+      {
+        "displayName": "Igor Pereira",
+        "email": "igor@direitonovo.com",
+        "phoneNumber": null,
+        "photoURL": "https://lh3.googleusercontent.com/a/AEdFTp7WMcfeJt12cvWi74W08yfxXtqAex70nRGYgC5rJQ=s96-c",
+        "providerId": "google.com",
+        "uid": "108691727495786316401"
+      },
+      {
+        "displayName": "Igor Pereira",
+        "email": "app.nex.weave@gmail.com",
+        "phoneNumber": null,
+        "photoURL": "https://lh3.googleusercontent.com/a-/AOh14GhszNGvpT3Z2-nYXJQSVzphDn-IqwJs0xxWEVU8zA",
+        "providerId": "password",
+        "uid": "app.nex.weave@gmail.com"
+      }
+    ],
+    "redirectEventId": null,
+    "stsTokenManager": {
+      "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg3YzFlN2Y4MDAzNGJiYzgxYjhmMmRiODM3OTIxZjRiZDI4N2YxZGYiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiSWdvciBQZXJlaXJhIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdoc3pOR3ZwVDNaMi1uWVhKUVNWenBoRG4tSXF3SnMweHhXRVZVOHpBIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3BsYXktNjg3MDUiLCJhdWQiOiJwbGF5LTY4NzA1IiwiYXV0aF90aW1lIjoxNjgxMDI4NzkyLCJ1c2VyX2lkIjoieVBLRjZRWE1rTlhIeEdSWVBLaEtFejNKMjBMMiIsInN1YiI6InlQS0Y2UVhNa05YSHhHUllQS2hLRXozSjIwTDIiLCJpYXQiOjE2ODEwMjg3OTIsImV4cCI6MTY4MTAzMjM5MiwiZW1haWwiOiJhcHAubmV4LndlYXZlQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjEwODY5MTcyNzQ5NTc4NjMxNjQwMSJdLCJlbWFpbCI6WyJhcHAubmV4LndlYXZlQGdtYWlsLmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.qEKJV_yrryYbrGEvGw6grv3MDg5ESs7piPoAHY3-DHJXhrviGQjjHfVSq9WxwHOwL_ZoJ9xSD-3cH4VGLmXN2QZgXHrLuTtv56C77rVd4Kyk4IlfkqIqV4nnVqVV92T_CyvyaSfLOCBaU87exG8nEld1gbcV2WCGnTLHgOQUSj95TsU6xBTaQUurw_8PVrohghf15iWXu307TCNyFi-Ky3nkp8uPwJWgSiAp8wwBCVgoYwk1aancl9R2jjPT4WB3mm1CjaGvDX_Hu8cB4aYDeNh6ZsMAgGJ_N2syU4wXQC_FkIlWE5IV9OdVZZBYViOMFmSPfaXRdriLpchEgvoapQ",
+      "apiKey": "AIzaSyDIYBezlQ0hOhLvY2Wn0nPPhiNQsXdV-Js",
+      "expirationTime": 1681032393,
+      "refreshToken": "APJWN8c1PDJu-2VQWjKMGGxem330cBxjYfPOpeyAHPaCycTLa63cfajgMFUugnqPiQ7LEIDnnvMJHN7LjeofUQZWtHObo_2-AV-GoU1HYDxYDcUVDUropu5rhYAnzQp5ybeNlUrZD7OdmuyuvVwP2cn5j84gs9t5d963bml3DEhdELC2FUssDwc3i81Ne9o4SCz1BfzSqWHYtRJlBHb-3YW9Z4MGZk-i0g"
+    },
+    "tenantId": null,
+    "uid": "yPKF6QXMkNXHxGRYPKhKEz3J20L2"
+  }
+}

+ 7 - 0
config/rsc/style.css

@@ -0,0 +1,7 @@
+.brighter-span {
+      filter: brightness(150%);
+  }
+
+  .darker-span {
+        filter: brightness(50%);
+    }

+ 38 - 0
config/rsc/token.json

@@ -0,0 +1,38 @@
+{
+  "fbase_key": "firebase:authUser:AIzaSyDIYBezlQ0hOhLvY2Wn0nPPhiNQsXdV-Js:[DEFAULT]",
+  "value": {
+    "uid": "jLdpRnq6OHcVzR9kbecWEzpczBv2",
+    "displayName": "Service Account",
+    "photoURL": "https://lh3.googleusercontent.com/a/ACg8ocKAOMGKANaF217nZdkBQZYuH58u-A7XPmpMTbEtw5rC=s96-c",
+    "email": "buphidoluk@gmail.com",
+    "emailVerified": true,
+    "phoneNumber": null,
+    "isAnonymous": false,
+    "tenantId": null,
+    "providerData": [
+      {
+        "uid": "110590545320354046275",
+        "displayName": "Service Account",
+        "photoURL": "https://lh3.googleusercontent.com/a/ACg8ocKAOMGKANaF217nZdkBQZYuH58u-A7XPmpMTbEtw5rC=s96-c",
+        "email": "buphidoluk@gmail.com",
+        "phoneNumber": null,
+        "providerId": "google.com"
+      }
+    ],
+    "apiKey": "AIzaSyDIYBezlQ0hOhLvY2Wn0nPPhiNQsXdV-Js",
+    "appName": "[DEFAULT]",
+    "authDomain": "auth.play.ht",
+    "stsTokenManager": {
+      "apiKey": "AIzaSyDIYBezlQ0hOhLvY2Wn0nPPhiNQsXdV-Js",
+      "refreshToken": "AMf-vBym1Hsfar3InToSgnN5bV4_8JZyEYJzqenp4iJTojmGuCtJFSqfZC18jv1czg9EXyekDaOz5tpHUJk46QEYEjoIgFdkF8PgaWw627e0HeOlqTmOQiwoq9umKksP8a1Gk_5T-V57UnFI61fbkRsmtKu9j-iukDaBrivxDlPy6tgUpZrHePqdTEDdBrKHJcUNuIt2tbZRlT8sUkWMMNBQKlt_pdT02kYvin4gtBt9r5aRWGlL8egJJkyyW6WMYos7Yf7GfvM3x6RtY12QaIUYWLnZ-mNtP988fbOFeowCZsuZ0gDC31elz9joBwHXPzSiG8mFXry9JbrcIstgTzToot3U0awDPN5cc-6NkypqA-SARgDmd6bJpliXJVpVCI9R3EauQPIJy1Yi0ME11MqM5SiPscKOlBnnBGreFjQU0tYcpwsIs9w",
+      "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJhNjI1OTZmNTJmNTJlZDQ0MDQ5Mzk2YmU3ZGYzNGQyYzY0ZjQ1M2UiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiU2VydmljZSBBY2NvdW50IiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hL0FDZzhvY0tBT01HS0FOYUYyMTduWmRrQlFaWXVINTh1LUE3WFBtcE1UYkV0dzVyQz1zOTYtYyIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9wbGF5LTY4NzA1IiwiYXVkIjoicGxheS02ODcwNSIsImF1dGhfdGltZSI6MTcxMTc0NTg3MywidXNlcl9pZCI6ImpMZHBSbnE2T0hjVnpSOWtiZWNXRXpwY3pCdjIiLCJzdWIiOiJqTGRwUm5xNk9IY1Z6UjlrYmVjV0V6cGN6QnYyIiwiaWF0IjoxNzExNzQ1ODczLCJleHAiOjE3MTE3NDk0NzMsImVtYWlsIjoiYnVwaGlkb2x1a0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExMDU5MDU0NTMyMDM1NDA0NjI3NSJdLCJlbWFpbCI6WyJidXBoaWRvbHVrQGdtYWlsLmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6Imdvb2dsZS5jb20ifX0.UwVGzVr1HIKKXVlBrlqfAYS4aySlElOa7axPhvTma9dl2ayyIQinPQ5ykuJsTOC6QO0mVvTM6AJfTeaT05ywEC7r6uxkrZ4o3j5z0tqrKMylidu_9P1OyBazGCyKMTCd0K8HDhr3k-pxOWEodna0_94idv9HT1TbTVYnhjuz4ZxwF7r8DepS0xkC-iOPq2oAho9QLIPiy0QnlEvSEzP4N7_KQAU4pUI9oF0EW_G-KEJ_mnXZF8uc8lwe8qTcplU-ViVzCXuuZOD9TgJe7bPbDxFVm0xj6HZMQcjUMtI_5Ytl1aVj_F3aOl5nUM4ZiKjO5EGdv3qexBNBJASGJH8wgQ",
+      "expirationTime": 1711749473000
+    },
+    "redirectEventId": null,
+    "lastLoginAt": "1711745872898",
+    "createdAt": "1711745872897",
+    "multiFactor": {
+      "enrolledFactors": []
+    }
+  }
+}

+ 6 - 5
pkg/client.go

@@ -82,11 +82,12 @@ func (c *Controller) RequestGeneric(method string, host string, path string, hdr
 		data = c.pageMod(b)
 
 	}
-
-	if !strings.Contains(path, "?") {
-		if resp.StatusCode == 200 {
-			if method == "GET" {
-				c.CacheResource(path, NewCachedResource(data, &resp.Header, resp.StatusCode))
+	if c.Config.Caching {
+		if !strings.Contains(path, "?") {
+			if resp.StatusCode == 200 {
+				if method == "GET" {
+					c.CacheResource(path, NewCachedResource(data, &resp.Header, resp.StatusCode))
+				}
 			}
 		}
 	}

+ 45 - 0
pkg/configuration.go

@@ -50,16 +50,30 @@ type HttpServerConfig struct {
 	ProxyAddr            string          `json:"proxy_addr"`
 	RouteMapPath         string          `json:"route_map_path"`
 	PageModPath          string          `json:"page_mod_path"`
+	CustomFServePath     string          `json:"custom_fileserve_cfg_path"`
 	CookieFile           string          `json:"cookie_file"`
 	FullProxyDomain      string          // the domain name of the proxied site with the protocol
 	KnownHosts           []string        `json:"known_hosts"`
 	CorsHosts            []string        `json:"cors_hosts"`
 	Redirects            []*RedirectRule `json:"redirects"`
+	Caching              bool
+	CustomFserve         *CustomFileServer
 	CookieJar            []*http.Cookie
 	PhpSession           *http.Cookie
 	SsoToken             *http.Cookie
 }
 
+type CustomFile struct {
+	Request     string `json:"request"`
+	Serve       string `json:"serve"`
+	ContentType string `json:"content-type"`
+	FileData    []byte
+}
+
+type CustomFileServer struct {
+	Config []*CustomFile `json:"config"`
+}
+
 type RedirectRule struct {
 	From string `json:"from"`
 	To   string `json:"to"`
@@ -96,6 +110,7 @@ func ReadConfig(loc string) (*HttpServerConfig, error) {
 	if err != nil {
 		return nil, err
 	}
+	cfg.CustomFserve = ReadCustomFiles(cfg.CustomFServePath)
 	cfg.FullDomain = fmt.Sprintf("%s://%s", cfg.Proto, cfg.AllowedDomain)
 	cfg.FullProxyDomain = fmt.Sprintf("%s://%s", cfg.Proto, cfg.ProxyAddr)
 	cfg.FullAltAllowedDomain = fmt.Sprintf("%s://%s", cfg.Proto, cfg.AltAllowedDomain)
@@ -134,3 +149,33 @@ func ReadRouteMap(loc string) *RouteMap {
 	return &mapfile
 
 }
+
+/*
+Read in the custom file server configuration
+
+	:param loc: path to the custom file server config
+*/
+func ReadCustomFiles(loc string) *CustomFileServer {
+	b, err := os.ReadFile(loc)
+	if err != nil {
+		log.Fatal("couldnt read custom config file: ", err)
+	}
+	var fserveCfg CustomFileServer
+	err = json.Unmarshal(b, &fserveCfg)
+	if err != nil {
+		log.Fatal("error loading in the config file: ", err)
+	}
+	for idx := range fserveCfg.Config {
+		_, err = os.Stat(fserveCfg.Config[idx].Serve)
+		if err != nil {
+			log.Fatal("Couldnt verify the existence of file: ", fserveCfg.Config[idx].Serve, " error: ", err)
+		}
+		b, err = os.ReadFile(fserveCfg.Config[idx].Serve)
+		if err != nil {
+			log.Fatal("Error reading in custom fileserver file: ", err)
+		}
+		fserveCfg.Config[idx].FileData = b
+	}
+	fmt.Printf("%+v\n", fserveCfg)
+	return &fserveCfg
+}

+ 23 - 13
pkg/controller.go

@@ -28,6 +28,7 @@ package httpserver
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"log"
 	"net/http"
@@ -100,22 +101,31 @@ func (c *Controller) HandleAny(ctx *gin.Context) {
 			return
 		}
 	}
-	cacheHit := c.GetResource(incomingPath)
-	if cacheHit != nil {
-		for k, v := range *cacheHit.Headers {
-			_, ok := NonmutableHeaders[k]
-			if !ok {
-				for i := range v {
-					ctx.Header(k, v[i])
+	for idx := range c.Config.CustomFserve.Config {
+		if incomingPath == c.Config.CustomFserve.Config[idx].Request {
+			fmt.Print("Custom file server path hit.\n")
+			ctx.Data(200, c.Config.CustomFserve.Config[idx].ContentType, c.Config.CustomFserve.Config[idx].FileData)
+			return
+		}
+	}
+	if c.Config.Caching {
+		cacheHit := c.GetResource(incomingPath)
+		if cacheHit != nil {
+			for k, v := range *cacheHit.Headers {
+				_, ok := NonmutableHeaders[k]
+				if !ok {
+					for i := range v {
+						ctx.Header(k, v[i])
+					}
 				}
 			}
+			_, err := io.Copy(ctx.Writer, bytes.NewReader(cacheHit.Data))
+			if err != nil {
+				log.Fatal(err)
+			}
+			ctx.Data(cacheHit.Rcode, cacheHit.Headers.Get("content-type"), cacheHit.Data)
+			return
 		}
-		_, err := io.Copy(ctx.Writer, bytes.NewReader(cacheHit.Data))
-		if err != nil {
-			log.Fatal(err)
-		}
-		ctx.Data(cacheHit.Rcode, cacheHit.Headers.Get("content-type"), cacheHit.Data)
-		return
 	}
 
 	dname, ok := c.RouteMaps.GetMappedDomain(incomingPath)

+ 48 - 1
pkg/pagemod.go

@@ -29,6 +29,7 @@ package httpserver
 import (
 	"bytes"
 	"encoding/json"
+	"fmt"
 	"io"
 	"log"
 	"os"
@@ -40,6 +41,24 @@ type PageMod struct {
 	Sub    string `json:"sub"`
 }
 
+/*
+operate on a PageMod pointer to set the 'sub' field
+
+	:param content: the content to set as the 'sub' field
+*/
+func (p *PageMod) SetFieldSub(content string) {
+	p.Sub = content
+}
+
+/*
+operate on a PageMod pointer and set the 'target' field
+
+	:param content: the content to set as the 'target' field
+*/
+func (p *PageMod) SetFieldTarget(content string) {
+	p.Target = content
+}
+
 type AllPageMods struct {
 	Content []*PageMod          `json:"modifications"`
 	Bypass  map[string]struct{} `json:"bypass"`
@@ -60,8 +79,36 @@ func LoadPageMods(loc string) *AllPageMods {
 	if err != nil {
 		log.Fatal("There was an error loading the configuration: ", err)
 	}
-	return &pgMod
+	for idx := range pgMod.Content {
+		_, err = os.Stat(pgMod.Content[idx].Sub)
+		if err != nil {
+			if os.IsNotExist(err) {
+				continue
+			}
 
+		}
+		b, err = os.ReadFile(pgMod.Content[idx].Sub)
+		if err != nil {
+			log.Fatal("Could not read the target file: ", pgMod.Content[idx].Sub, " error: ", err)
+		}
+		pgMod.Content[idx].SetFieldTarget(string(b))
+	}
+	for idx := range pgMod.Content {
+		_, err = os.Stat(pgMod.Content[idx].Target)
+		if err != nil {
+			if os.IsNotExist(err) {
+				continue
+			}
+		}
+		b, err = os.ReadFile(pgMod.Content[idx].Target)
+		if err != nil {
+			log.Fatal("Could not read the target file: ", pgMod.Content[idx].Target, " error: ", err)
+		}
+		pgMod.Content[idx].SetFieldSub(string(b))
+		fmt.Print(string(b))
+	}
+
+	return &pgMod
 }
 
 /*