Handling url schemes with golang? - windows

I would love to be able to set my executable to handle custom Url Scheme (aaa:) so that I can handle deep linking with my program.

You need to modify Windows registry, in order to tell os which protocol to open with which executable.
There are multiple ways you can go about it, depending if you want to add your url scheme handler for all users or for current user.
Easiest way to handle it is to make Windows pass custom url as argument to default executable.
That way both chrome and edge will ask user to open your installed app.
Here is a minimal Golang implementation to set your executable as default url scheme handler:
var k registry.Key
var err error
prefix := "SOFTWARE\\Classes\\"
urlScheme := "aaa"
basePath := prefix + urlScheme
permission := uint32(registry.QUERY_VALUE | registry.SET_VALUE)
baseKey := registry.CURRENT_USER
programLocation := "\"C:\\Windows\\notepad.exe\""
// create key
registry.CreateKey(baseKey, basePath, permission)
// set description
k.SetStringValue("", "Notepad app")
k.SetStringValue("URL Protocol", "")
// set icon
registry.CreateKey(registry.CURRENT_USER, "lumiere\\DefaultIcon", registry.ALL_ACCESS)
k.SetStringValue("", softwareToOpen+",1")
// create tree
registry.CreateKey(baseKey, basePath+"\\shell", permission)
registry.CreateKey(baseKey, basePath+"\\shell\\open", permission)
registry.CreateKey(baseKey, basePath+"\\shell\\open\\command", permission)
// set open command
k.SetStringValue("", softwareToOpen+" \"%1\"")
Getting custom URL after having your app set as default is quite straightforward
You can do this:
func main() {
url := os.Args[1:]
log.Printf("my custom aaa url is: %s", url)
}

Related

Creating MS Word documents with Go OLE binding

I've been playing around and learning how to make Word docs programmatically. I know it can easily be done using pywin32. This simple snippet retrieves the default Visual Basic "code" inside the new Word doc.
import win32com.client
word = win32com.client.Dispatch("Word.Application")
word.Visible = True
document = word.Documents.Add()
document.VBProject.Name = "TEST"
wordModule = document.VBProject.VBComponents("ThisDocument") # WORKS
input()
You can then add VB code to wordModule.
I wanted to do the same using Golang. There is a OLE binding for Go, the code is on Github -> https://github.com/go-ole/go-ole
It's a bit less user friendly but I managed to make it work, except that I'm not able to retrieve the default VBComponents.
The default code resides in "ThisDocument" and can be retrieved with the simple python code document.VBProject.VBComponents("ThisDocument") except that, it doesn't work in Go...
You can see in the code below that I tried to get "ThisDocument" using multiple ways, without success. Each time, the error message is panic: Unknown name.
// +build windows
package main
import (
"fmt"
ole "github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)
func main() {
defer ole.CoUninitialize()
ole.CoInitialize(0)
unknown, _ := oleutil.CreateObject("Word.Application")
word, _ := unknown.QueryInterface(ole.IID_IDispatch)
oleutil.PutProperty(word, "Visible", true)
documents := oleutil.MustGetProperty(word, "Documents").ToIDispatch()
document := oleutil.MustCallMethod(documents, "Add").ToIDispatch()
vbproject := oleutil.MustGetProperty(document, "VBProject").ToIDispatch()
oleutil.PutProperty(vbproject, "Name", "TEST")
// oleutil.MustCallMethod(vbproject, "VBComponents", "ThisDocument").ToIDispatch() --> panic: Unknown name.
// oleutil.MustGetProperty(vbproject, "VBComponents", "ThisDocument").ToIDispatch() --> panic: Unknown name.
// vbcomponents := oleutil.MustGetProperty(vbproject, "VBComponents").ToIDispatch()
// oleutil.MustGetProperty(vbcomponents, "ThisDocument").ToIDispatch() --> panic: Unknown name.
var input string
fmt.Scanln(&input)
oleutil.PutProperty(document, "Saved", true)
oleutil.CallMethod(documents, "Close", false)
oleutil.CallMethod(word, "Quit")
word.Release()
}
Any ideas on why it doesn't work?
Thanks a lot.
Turns out "github.com/go-ole/go-ole" has a bug when using ForEach. VBComponets is a Collection, so you have to iterate as stated by Microsoft doc
Use the VBComponents collection to access, add, or remove components in a project. A component can be a form, module, or class. The VBComponents collection is a standard collection that can be used in a For...Each block.
This line -> https://github.com/go-ole/go-ole/blob/master/oleutil/oleutil.go#L106
should be replace by
newEnum, err := disp.CallMethod("_NewEnum")
Now it works as intended.

Golang upload image to GCS with specific path

I had stuck with Google Cloud Storage and os.create()
This is my example code
func upload(w http.ResponseWriter, r *http.Request) {
// some process request msg, decode base64 to image byte
// create image file in current directory with os.create()
//
path := os.create("shop_logo.jpeg")
bucket := client.Bucket("myBucket")
write := bucket.Object(path).NewWriter(ctx)
}
Create file with directory
func upload(w http.ResponseWriter, r *http.Request) {
// some process request msg, decode base64 to image byte
// create image file in current directory with os.create()
//
path := os.create("home/../project/repo/shop_logo.jpeg") //absolute path
bucket := client.Bucket("myBucket")
write := bucket.Object(path).NewWriter(ctx)
}
Acutally everything it work, like mybucket/shop_logo.jpeg
But I want to organize the bucket path such as mybucket/shop_v1/shop_logo.jpeg
But the I used the os.create() like os.create("shop_v1/shop_logo.jpeg)
It's can't work, look like this function can't create the folder.
but when I used the Absolute Path It's work. like os.create("/home/project/shop_v1/shop_logo.jpeg)
The problem is bucket.Object("path/to/image_file") is require the path of file.
So If I used the Absolute Path to upload it.
My bucket will be myBucket/home/project/shop_v1/shop_logo.jpeg .
But the thing that I expect it is mybucket/shop_v1/shop_logo.jpeg
Anyone have idea?
Oh, I just found the os.create() can create directory also.
It my fault like this
inputPath := "/shop_v1/shop_logo.jpeg"
os.create(inputPath)
The application will error, can't find blah blah.
I just have to remove the first slash of inputPath variable
inputPath := "shop_v1/shop_logo.jpeg"
os.create(inputPath)
Now it work!.

Relative paths in revel framework

How can import a file realtive to the revel basefolder in revel framework. Currently I do the following to get hold of some config values.
file, err := ioutil.ReadFile("conf/config.conf")
...
This results in my server only working if i stand in the app directory when starting revel with
revel run myapp
Is there a way to access the base folder?
There are exported global variables in the revel package, you can use any of these:
var (
// App details
AppName string // e.g. "sample"
BasePath string // e.g. "/Users/revel/gocode/src/corp/sample"
AppPath string // e.g. "/Users/revel/gocode/src/corp/sample/app"
ViewsPath string // e.g. "/Users/revel/gocode/src/corp/sample/app/views"
ImportPath string // e.g. "corp/sample"
SourcePath string // e.g. "/Users/revel/gocode/src"
// Revel installation details
RevelPath string // e.g. "/Users/revel/gocode/src/revel"
// Where to look for templates and configuration.
// Ordered by priority. (Earlier paths take precedence over later paths.)
CodePaths []string
ConfPaths []string
TemplatePaths []string
)
If it is empty for you, that is most likely because you started your app from the base folder.
Note that these paths are set by the Init(mode, importPath, srcPath string) function. It's documentation states:
srcPath - the path to the source directory, containing Revel and the app.
If not specified (""), then a functioning Go installation is required.
Also check out: how to reference a relative file from code and tests
I use this method:
In conf/app.conf add a line with the configuration path of this way:
projectname.path = "/foldersnames/"
and in the controller Write a method like this:
func info(field string) string {
config, err := revel.LoadConfig("app.conf")
if err != nil || config == nil {
log.Fatalln("Failed to load configuration file", err)
}
return config.StringDefault(field, "empty")
}
You can build a helper with this code and take configurations variables from all applications.
You must call of this way:
info("projectname.path")

Organizing Environment Variables Golang

In Node.js I use the nconf module to house environment variables like S3 keys, GCM keys, etc for each of my projects.
I haven't been able to find a similar solution in Go.
What are the generally accepted tools to help manage environment variables for each Go project?
Thanks in advance.
I would strongly recommend using github.com/namsral/flag instead.
It's like the built in flag except you can also supply the parameters via environment variables.
For example, suppose you have this code:
package main
import "fmt"
import "github.com/namsral/flag"
func main() {
var port = 3000
flag.IntVar(&port, "port", port, "Port number")
flag.Parse()
fmt.Println("You seem to prefer", port)
}
Then you can supply the values with either a command line option or an environment variable:
:~/dev/GO$ go run dummy.go
You seem to prefer 3000
:~/dev/GO$ go run dummy.go -port=1234
You seem to prefer 1234
:~/dev/GO$ PORT=4321 go run dummy.go
You seem to prefer 4321
:~/dev/GO$ PORT=4321 go run dummy.go -port=5555
You seem to prefer 5555
This might matter when it's hard to supply command line args. For example, if you use gin to automatically restart a server you have no way to supply command line arguments since gin is just calling go run on the main code without any arguments passed along.
I did some reading on this a while back when I was getting started with Go. According to this link, http://peter.bourgon.org/go-in-production/, they recommend using CLI flags (parameters) instead of environment vars - they even convert environment vars to flags to their CLI apps.
It took some getting used to; but, I really do see the advantages of going pure CLI flags between development, staging and production environments - having specific scripts for each environment.
For example, here's a little web app I wrote recently:
// global flags
var isdebug bool
var port int
var cert string
var key string
var dbdsn string
var dbmaxidle int
var dbmaxopen int
var imguri string
// init is the entry point for the entire web application.
func init() {
log.Println("Starting wwwgo ...")
// setup the flags
//flag.StringVar(&host, "host", "", "Specify a host to redirect to. Use this to redirect all traffic to a single url.")
flag.IntVar(&port, "port", 8080, "Specify the port to listen to.")
flag.BoolVar(&isdebug, "isdebug", false, "Set to true to run the app in debug mode. In debug, it may panic on some errors.")
flag.StringVar(&cert, "cert", "", "Enables listening on 443 with -cert and -key files specified. This must be a full path to the certificate .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.")
flag.StringVar(&key, "key", "", "Enables listening on 443 with -cert and -key files specified. This must be a full path to the key .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.")
flag.StringVar(&dbdsn, "dbdsn", "root:root#tcp(localhost:3306)/dev_db?timeout=5s&tls=false&autocommit=true", "Specifies the MySql DSN connection.")
flag.IntVar(&dbmaxidle, "dbmaxidle", 0, "Sets the database/sql MaxIdleConns.")
flag.IntVar(&dbmaxopen, "dbmaxopen", 500, "Sets the database/sql MaxOpenConns.")
flag.StringVar(&imguri, "imguri", "/cdn/uploads/", "Set this to the full base uri of all images, for example on a remote CDN server or local relative virtual directory.")
flag.Parse()
// log our flags
if isdebug != false {
log.Println("DEBUG mode enabled")
}
if cert != "" && key != "" {
log.Println("Attempting SSL binding with supplied cert and key.")
}
if dbdsn != "" {
log.Printf("Using default dbdsn: %s", dbdsn)
}
...
}
This really becomes nice in staging/production environments.
A quick ./wwwgo -h for "what the heck was that parameter?" gives you full documentation:
admin#dev01:~/code/frontend/src/wwwgo [master]$ ./wwwgo -h
Usage of ./wwwgo:
-cert="": Enables listening on 443 with -cert and -key files specified. This must be a full path to the certificate .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.
-dbdsn="root:root#tcp(localhost:3306)/dev_db?timeout=5s&tls=false&autocommit=true": Specifies the MySql DSN connection.
-dbmaxidle=0: Sets the database/sql MaxIdleConns.
-dbmaxopen=500: Sets the database/sql MaxOpenConns.
-imguri="/cdn/uploads/": Set this to the full base uri of all images, for example on a remote CDN server or local relative virtual directory.
-isdebug=false: Set to true to run the app in debug mode. In debug, it may panic on some errors.
-key="": Enables listening on 443 with -cert and -key files specified. This must be a full path to the key .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.
-port=8080: Specify the port to listen to.
Very nice to have many options at CLI, and no documentation required - it's built into the flags package.
You can clearly see the defaults immediately.
With this type of documentation, I tend to setup all the defaults for common "development environments" that the team uses. We all have root/root access to our local databases. We all are using port 8080 for this particular web app during development, etc. That way, you just have to run:
go build
./wwwgo
And the app runs with all defaults - defaults that are documented. In production, just override the defaults. The built-in flag parsers will panic the application if any parameters are in the wrong format, which is also very nice.
We have used this for a large scale microservice application. This doesn't involve use of any third party libraries, just plain go lang using reflection. It is very simple to use and follows DRY principle. Adding new environment variables and configuring defaults is quite easy. You can also set loading from a config file as a fallback by changing just 2 lines of code. Checkout github page for more info.
package config
import (
"fmt"
"os"
"reflect"
)
/* Tag names to load configuration from environment variable */
const (
ENV = "env"
DEFAULT = "default"
)
type Configuration struct {
Port string `env:"port" default:"3009"`
MongoURL string `env:"MongoUrl" default:"mongodb://localhost:27017/test"`
UserService string `env:"UserService" default:"http://localhost:3005"`
AuthService string `env:"AuthService" default:"http://localhost:3050"`
Debug string `env:"Debug" default:"true"`
}
/* Non-exported instance to avoid accidental overwrite */
var serviceConfig Configuration
func setConfig() {
// ValueOf returns a Value representing the run-time data
v := reflect.ValueOf(serviceConfig)
for i := 0; i < v.NumField(); i++ {
// Get the field tag value
tag := v.Type().Field(i).Tag.Get(ENV)
defaultTag := v.Type().Field(i).Tag.Get(DEFAULT)
// Skip if tag is not defined or ignored
if tag == "" || tag == "-" {
continue
}
a := reflect.Indirect(reflect.ValueOf(serviceConfig))
EnvVar, Info := loadFromEnv(tag, defaultTag)
if Info != "" {
fmt.Println("Missing environment configuration for '" + a.Type().Field(i).Name + "', Loading default setting!")
}
/* Set the value in the environment variable to the respective struct field */
reflect.ValueOf(&serviceConfig).Elem().Field(i).SetString(EnvVar)
}
}
func loadFromEnv(tag string, defaultTag string) (string, string) {
/* Check if the tag is defined in the environment or else replace with default value */
envVar := os.Getenv(tag)
if envVar == "" {
envVar = defaultTag
/* '1' is used to indicate that default value is being loaded */
return envVar, "1"
}
return envVar, ""
}
/*GetConfiguration :Exported function to return a copy of the configuration instance */
func GetConfiguration() Configuration {
return serviceConfig
}
func init() {
setConfig()
fmt.Printf("Service configuration : %+v\n ", serviceConfig)
}
Well I prefer go-arg for setting environment variables. It is easy to use and has nice features.
For example:
package configs
import (
"fmt"
"github.com/alexflint/go-arg"
)
type config struct {
DBHost string `arg:"env:DBHost, -D, --dbhost" help:"Host of the database" placeholder:"DBHost"`
}
var Config config
func main(){
arg.MustParse(&Config)
fmt.Println(Config.DBHost)
}
With this library either you can take the variable from your env or you can pass through args.
export DBHost=127.0.0.1
or
go run ./main.go --dbhost=127.0.0.1
In nodejs, nconf is a great tool. It gives more than storing secret key.
In Golang, as far as i know there is two great package to use .env file easily , godotenv and viper , I prefer godotenv beacuse it's much easier .
Pros: Developers won't see your production secrets. You can use different secrets in dev, test, and production, without having to modify the code.
Cons: Malicious code can read your secrets. The bulk of your application's code is probably open-source libraries. Bad code may creep in without you knowing it.
Example for using godotenv
First run this command inside your terminal,
go get github.com/joho/godotenv
In your .env file
S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
In your main.go file
package main
import (
"github.com/joho/godotenv"
"log"
"os"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
s3Bucket := os.Getenv("S3_BUCKET")
secretKey := os.Getenv("SECRET_KEY")
// now do something with s3 or whatever
}

How to mark a message as read , \Seen on IMAP ( Go )

I'm trying to mark a message/list of messages as "\SEEN" (permanently) using IMAP . However unlike fetch, search and friends there seem to be no Flag function on the imap package. Should I send raw commands with the UIDs ?
You have to select the mailbox as writable
youImapConnection.Select(mailboxName, false) // true would be for readonly
And then simply do the following
seq, _ := imap.NewSeqSet("")
err := seq.AddNum(612) // 612 is your UID
_, err = imap.Wait(youImapConnection.UIDStore(seq, "+FLAGS", imap.NewFlagSet(`\Seen`))) // Here you tell to add the flag \Seen
Finally you will have to expunge:
_, err := imap.Wait(youImapConnection.Close(true)) // Here you tell to apply changes, necessary if you mark an Email as deleted
And you should be good :-)
And do not hesitate to browse the doc/source code, it's easy to understand and you'll find all you need.
IMAP uses the STORE command to set flags on messages, e.g.:
foo UID STORE 135211 +FLAGS (\Seen)
So I'd guess that you should use the Store or UIDStore functions to set flags.

Resources