182 строки
3.4 KiB
Go
182 строки
3.4 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// TODO: Something about redialing.
|
|
|
|
package google
|
|
|
|
import (
|
|
// "flag"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"net/rpc"
|
|
"os"
|
|
"os/exec"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
func Dir() string {
|
|
dir := os.Getenv("HOME") + "/.goog"
|
|
st, err := os.Stat(dir)
|
|
if err != nil {
|
|
if err := os.Mkdir(dir, 0700); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
st, err = os.Stat(dir)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
if !st.IsDir() {
|
|
log.Fatalf("%s exists but is not a directory", dir)
|
|
}
|
|
if st.Mode()&0077 != 0 {
|
|
log.Fatalf("%s exists but allows group or other permissions: %#o", dir, st.Mode()&0777)
|
|
}
|
|
return dir
|
|
}
|
|
|
|
func Dial() (*Client, error) {
|
|
socket := Dir() + "/socket"
|
|
c, err := net.Dial("unix", socket)
|
|
if err == nil {
|
|
return &Client{rpc.NewClient(c)}, nil
|
|
}
|
|
log.Print("starting server")
|
|
os.Remove(socket)
|
|
runServer()
|
|
for i := 0; i < 50; i++ {
|
|
c, err = net.Dial("unix", socket)
|
|
if err == nil {
|
|
return &Client{rpc.NewClient(c)}, nil
|
|
}
|
|
time.Sleep(200e6)
|
|
if i == 0 {
|
|
log.Print("waiting for server...")
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
type Client struct {
|
|
client *rpc.Client
|
|
}
|
|
|
|
type Empty struct{}
|
|
|
|
func (g *Client) Ping() error {
|
|
return g.client.Call("goog.Ping", &Empty{}, &Empty{})
|
|
}
|
|
|
|
func (g *Client) Accounts() ([]string, error) {
|
|
var out []string
|
|
if err := g.client.Call("goog.Accounts", &Empty{}, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func runServer() {
|
|
cmd := exec.Command("googleserver", "serve")
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
if err := cmd.Start(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type Config struct {
|
|
Account []*Account
|
|
}
|
|
|
|
type Account struct {
|
|
Email string
|
|
Password string
|
|
Nick string
|
|
}
|
|
|
|
func (cfg *Config) AccountByEmail(email string) *Account {
|
|
for _, a := range cfg.Account {
|
|
if a.Email == email {
|
|
return a
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var Cfg Config
|
|
|
|
func ReadConfig() {
|
|
file := Dir() + "/config"
|
|
st, err := os.Stat(file)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if st.Mode()&0077 != 0 {
|
|
log.Fatalf("%s exists but allows group or other permissions: %#o", file, st.Mode()&0777)
|
|
}
|
|
data, err := ioutil.ReadFile(file)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
Cfg = Config{}
|
|
if err := json.Unmarshal(data, &Cfg); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func WriteConfig() {
|
|
file := Dir() + "/config"
|
|
st, err := os.Stat(file)
|
|
if err != nil {
|
|
if err := ioutil.WriteFile(file, nil, 0600); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
st, err = os.Stat(file)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
if st.Mode()&0077 != 0 {
|
|
log.Fatalf("%s exists but allows group or other permissions: %#o", file, st.Mode()&0777)
|
|
}
|
|
data, err := json.MarshalIndent(&Cfg, "", "\t")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := ioutil.WriteFile(file, data, 0600); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
st, err = os.Stat(file)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if st.Mode()&0077 != 0 {
|
|
log.Fatalf("%s allows group or other permissions after writing: %#o", file, st.Mode()&0777)
|
|
}
|
|
}
|
|
|
|
func Acct(name string) Account {
|
|
ReadConfig()
|
|
if name == "" {
|
|
if len(Cfg.Account) == 0 {
|
|
fmt.Fprintf(os.Stderr, "no accounts configured\n")
|
|
os.Exit(2)
|
|
}
|
|
return *Cfg.Account[0]
|
|
}
|
|
|
|
for _, a := range Cfg.Account {
|
|
if a.Email == name || a.Nick == name {
|
|
return *a
|
|
}
|
|
}
|
|
fmt.Fprintf(os.Stderr, "cannot find account %#q", name)
|
|
os.Exit(2)
|
|
panic("not reached")
|
|
}
|