Moving to glide
Этот коммит содержится в:
575
vendor/github.com/mattermost/rsc/google/acme/Chat/main.go
сгенерированный
поставляемый
Обычный файл
575
vendor/github.com/mattermost/rsc/google/acme/Chat/main.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,575 @@
|
||||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- Del of main window should move to other window.
|
||||
- Editing main window should update status on \n or something like that.
|
||||
- Make use of full names from roster
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/goplan9/plan9/acme"
|
||||
"github.com/mattermost/rsc/google"
|
||||
"github.com/mattermost/rsc/xmpp"
|
||||
)
|
||||
|
||||
var acmeDebug = flag.Bool("acmedebug", false, "print acme debugging")
|
||||
|
||||
type Window struct {
|
||||
*acme.Win // acme window
|
||||
*acme.Event // most recent event received
|
||||
err error // error reading event
|
||||
typ string // kind of window "main", "chat"
|
||||
name string // acme window title
|
||||
remote string // for typ=="chat", remote address
|
||||
dirty bool // window is dirty
|
||||
blinky bool // window's dirty box is blinking
|
||||
|
||||
lastTime time.Time
|
||||
}
|
||||
|
||||
type Msg struct {
|
||||
w *Window // window where message belongs
|
||||
*xmpp.Chat // recently received chat
|
||||
err error // error reading chat message
|
||||
}
|
||||
|
||||
var (
|
||||
client *xmpp.Client // current xmpp client (can reconnect)
|
||||
acct google.Account // google acct info
|
||||
statusCache = make(map[string][]*xmpp.Presence)
|
||||
active = make(map[string]*Window) // active windows
|
||||
acmeChan = make(chan *Window) // acme events
|
||||
msgChan = make(chan *Msg) // chat events
|
||||
mainWin *Window
|
||||
status = xmpp.Available
|
||||
statusMsg = ""
|
||||
lastActivity time.Time
|
||||
)
|
||||
|
||||
const (
|
||||
awayTime = 10 * time.Minute
|
||||
extendedAwayTime = 30 * time.Minute
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: Chat [-a acct] name...\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var acctName = flag.String("a", "", "account to use")
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
acct = google.Acct(*acctName)
|
||||
|
||||
aw, err := acme.New()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
aw.Name("Chat/" + acct.Nick + "/")
|
||||
|
||||
client, err = xmpp.NewClient("talk.google.com:443", acct.Email, acct.Password)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
w := &Window{Win: aw, typ: "main", name: "Chat/" + acct.Nick + "/"}
|
||||
data, err := ioutil.ReadFile(google.Dir() + "/chat." + acct.Nick)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err == nil {
|
||||
w.Write("body", data)
|
||||
}
|
||||
mainWin = w
|
||||
active[w.name] = w
|
||||
go w.readAcme()
|
||||
client.Roster()
|
||||
setStatus(status)
|
||||
go w.readChat()
|
||||
lastActivity = time.Now()
|
||||
|
||||
tick := time.Tick(0.5e9)
|
||||
Loop:
|
||||
for len(active) > 0 {
|
||||
select {
|
||||
case w := <-acmeChan:
|
||||
if w == nil {
|
||||
// Sync with reader.
|
||||
continue
|
||||
}
|
||||
if w.err != nil {
|
||||
if active[w.name] == nil {
|
||||
continue
|
||||
}
|
||||
log.Fatal(w.err)
|
||||
}
|
||||
if *acmeDebug {
|
||||
fmt.Fprintf(os.Stderr, "%s %c%c %d,%d %q\n", w.name, w.C1, w.C2, w.Q0, w.Q1, w.Text)
|
||||
}
|
||||
if w.C1 == 'M' || w.C1 == 'K' {
|
||||
lastActivity = time.Now()
|
||||
if status != xmpp.Available {
|
||||
setStatus(xmpp.Available)
|
||||
}
|
||||
}
|
||||
if (w.C2 == 'x' || w.C2 == 'X') && string(w.Text) == "Del" {
|
||||
// TODO: Hangup connection for w.typ == "acct"?
|
||||
delete(active, w.name)
|
||||
w.Del(true)
|
||||
continue Loop
|
||||
}
|
||||
|
||||
switch w.typ {
|
||||
case "main":
|
||||
switch w.C2 {
|
||||
case 'L': // Button 3 in body: load chat window for contact.
|
||||
w.expand()
|
||||
fallthrough
|
||||
case 'l': // Button 3 in tag
|
||||
arg := string(w.Text)
|
||||
showContact(arg)
|
||||
continue Loop
|
||||
}
|
||||
case "chat":
|
||||
if w.C1 == 'F' && w.C2 == 'I' {
|
||||
continue Loop
|
||||
}
|
||||
if w.C1 != 'M' && w.C1 != 'K' {
|
||||
break
|
||||
}
|
||||
if w.blinky {
|
||||
w.blinky = false
|
||||
w.Fprintf("ctl", "dirty\n")
|
||||
}
|
||||
switch w.C2 {
|
||||
case 'X', 'x':
|
||||
if string(w.Text) == "Ack" {
|
||||
w.Fprintf("ctl", "clean\n")
|
||||
}
|
||||
case 'I':
|
||||
w.sendMsg()
|
||||
continue Loop
|
||||
}
|
||||
}
|
||||
w.WriteEvent(w.Event)
|
||||
|
||||
case msg := <-msgChan:
|
||||
w := msg.w
|
||||
if msg.err != nil {
|
||||
w.Fprintf("body", "ERROR: %s\n", msg.err)
|
||||
continue Loop
|
||||
}
|
||||
you := msg.Remote
|
||||
if i := strings.Index(you, "/"); i >= 0 {
|
||||
you = you[:i]
|
||||
}
|
||||
switch msg.Type {
|
||||
case "chat":
|
||||
w := showContact(you)
|
||||
text := strings.TrimSpace(msg.Text)
|
||||
if text == "" {
|
||||
// Probably a composing notification.
|
||||
continue
|
||||
}
|
||||
w.message("> %s\n", text)
|
||||
w.blinky = true
|
||||
w.dirty = true
|
||||
|
||||
case "presence":
|
||||
pr := msg.Presence
|
||||
pr, new := savePresence(pr, you)
|
||||
if !new {
|
||||
continue
|
||||
}
|
||||
w := lookContact(you)
|
||||
if w != nil {
|
||||
w.status(pr)
|
||||
}
|
||||
mainStatus(pr, you)
|
||||
}
|
||||
|
||||
case t := <-tick:
|
||||
switch status {
|
||||
case xmpp.Available:
|
||||
if t.Sub(lastActivity) > awayTime {
|
||||
setStatus(xmpp.Away)
|
||||
}
|
||||
case xmpp.Away:
|
||||
if t.Sub(lastActivity) > extendedAwayTime {
|
||||
setStatus(xmpp.ExtendedAway)
|
||||
}
|
||||
}
|
||||
for _, w := range active {
|
||||
if w.blinky {
|
||||
w.dirty = !w.dirty
|
||||
if w.dirty {
|
||||
w.Fprintf("ctl", "dirty\n")
|
||||
} else {
|
||||
w.Fprintf("ctl", "clean\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setStatus(st xmpp.Status) {
|
||||
status = st
|
||||
client.Status(status, statusMsg)
|
||||
mainWin.statusTag(status, statusMsg)
|
||||
}
|
||||
|
||||
func savePresence(pr *xmpp.Presence, you string) (pr1 *xmpp.Presence, new bool) {
|
||||
old := cachedPresence(you)
|
||||
|
||||
pr.StatusMsg = strings.TrimSpace(pr.StatusMsg)
|
||||
c := statusCache[you]
|
||||
for i, p := range c {
|
||||
if p.Remote == pr.Remote {
|
||||
c[i] = pr
|
||||
c[0], c[i] = c[i], c[0]
|
||||
goto Best
|
||||
}
|
||||
}
|
||||
c = append(c, pr)
|
||||
c[0], c[len(c)-1] = c[len(c)-1], c[0]
|
||||
statusCache[you] = c
|
||||
|
||||
Best:
|
||||
best := cachedPresence(you)
|
||||
return best, old == nil || old.Status != best.Status || old.StatusMsg != best.StatusMsg
|
||||
}
|
||||
|
||||
func cachedPresence(you string) *xmpp.Presence {
|
||||
c := statusCache[you]
|
||||
if len(c) == 0 {
|
||||
return nil
|
||||
}
|
||||
best := c[0]
|
||||
for _, p := range c {
|
||||
if p.Status > best.Status {
|
||||
best = p
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
func short(st xmpp.Status) string {
|
||||
switch st {
|
||||
case xmpp.Unavailable:
|
||||
return "?"
|
||||
case xmpp.ExtendedAway:
|
||||
return "x"
|
||||
case xmpp.Away:
|
||||
return "-"
|
||||
case xmpp.Available:
|
||||
return "+"
|
||||
case xmpp.DoNotDisturb:
|
||||
return "!"
|
||||
}
|
||||
return st.String()
|
||||
}
|
||||
|
||||
func long(st xmpp.Status) string {
|
||||
switch st {
|
||||
case xmpp.Unavailable:
|
||||
return "unavailable"
|
||||
case xmpp.ExtendedAway:
|
||||
return "offline"
|
||||
case xmpp.Away:
|
||||
return "away"
|
||||
case xmpp.Available:
|
||||
return "available"
|
||||
case xmpp.DoNotDisturb:
|
||||
return "busy"
|
||||
}
|
||||
return st.String()
|
||||
}
|
||||
|
||||
func (w *Window) time() string {
|
||||
/*
|
||||
Auto-date chat windows:
|
||||
|
||||
Show date and time on first message.
|
||||
Show time if minute is different from last message.
|
||||
Show date if day is different from last message.
|
||||
|
||||
Oct 10 12:01 > hi
|
||||
12:03 hello there
|
||||
12:05 > what's up?
|
||||
|
||||
12:10 [Away]
|
||||
*/
|
||||
now := time.Now()
|
||||
m1, d1, y1 := w.lastTime.Date()
|
||||
m2, d2, y2 := now.Date()
|
||||
w.lastTime = now
|
||||
|
||||
if m1 != m2 || d1 != d2 || y1 != y2 {
|
||||
return now.Format("Jan 2 15:04 ")
|
||||
}
|
||||
return now.Format("15:04 ")
|
||||
}
|
||||
|
||||
func (w *Window) status(pr *xmpp.Presence) {
|
||||
msg := ""
|
||||
if pr.StatusMsg != "" {
|
||||
msg = ": " + pr.StatusMsg
|
||||
}
|
||||
w.message("[%s%s]\n", long(pr.Status), msg)
|
||||
|
||||
w.statusTag(pr.Status, pr.StatusMsg)
|
||||
}
|
||||
|
||||
func (w *Window) statusTag(status xmpp.Status, statusMsg string) {
|
||||
data, err := w.ReadAll("tag")
|
||||
if err != nil {
|
||||
log.Printf("read tag: %v", err)
|
||||
return
|
||||
}
|
||||
//log.Printf("tag1: %s\n", data)
|
||||
i := bytes.IndexByte(data, '|')
|
||||
if i >= 0 {
|
||||
data = data[i+1:]
|
||||
} else {
|
||||
data = nil
|
||||
}
|
||||
//log.Printf("tag2: %s\n", data)
|
||||
j := bytes.IndexByte(data, '|')
|
||||
if j >= 0 {
|
||||
data = data[j+1:]
|
||||
}
|
||||
//log.Printf("tag3: %s\n", data)
|
||||
|
||||
msg := ""
|
||||
if statusMsg != "" {
|
||||
msg = " " + statusMsg
|
||||
}
|
||||
w.Ctl("cleartag\n")
|
||||
w.Write("tag", []byte(" "+short(status)+msg+" |"+string(data)))
|
||||
}
|
||||
|
||||
func mainStatus(pr *xmpp.Presence, you string) {
|
||||
w := mainWin
|
||||
if err := w.Addr("#0/^(.[ \t]+)?" + regexp.QuoteMeta(you) + "([ \t]*|$)/"); err != nil {
|
||||
return
|
||||
}
|
||||
q0, q1, err := w.ReadAddr()
|
||||
if err != nil {
|
||||
log.Printf("ReadAddr: %s\n", err)
|
||||
return
|
||||
}
|
||||
if err := w.Addr("#%d/"+regexp.QuoteMeta(you)+"/", q0); err != nil {
|
||||
log.Printf("Addr2: %s\n", err)
|
||||
}
|
||||
q2, q3, err := w.ReadAddr()
|
||||
if err != nil {
|
||||
log.Printf("ReadAddr2: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
space := " "
|
||||
if q1 > q3 || pr.StatusMsg == "" { // already have or don't need space
|
||||
space = ""
|
||||
}
|
||||
if err := w.Addr("#%d/.*/", q1); err != nil {
|
||||
log.Printf("Addr3: %s\n", err)
|
||||
}
|
||||
w.Fprintf("data", "%s%s", space, pr.StatusMsg)
|
||||
|
||||
space = ""
|
||||
if q0 == q2 {
|
||||
w.Addr("#%d,#%d", q0, q0)
|
||||
space = " "
|
||||
} else {
|
||||
w.Addr("#%d,#%d", q0, q0+1)
|
||||
}
|
||||
w.Fprintf("data", "%s%s", short(pr.Status), space)
|
||||
}
|
||||
|
||||
func (w *Window) expand() {
|
||||
// Use selection if any.
|
||||
w.Fprintf("ctl", "addr=dot\n")
|
||||
q0, q1, err := w.ReadAddr()
|
||||
if err == nil && q0 <= w.Q0 && w.Q0 <= q1 {
|
||||
goto Read
|
||||
}
|
||||
if err = w.Addr("#%d-/[a-zA-Z0-9_@.\\-]*/,#%d+/[a-zA-Z0-9_@.\\-]*/", w.Q0, w.Q1); err != nil {
|
||||
log.Printf("expand: %v", err)
|
||||
return
|
||||
}
|
||||
q0, q1, err = w.ReadAddr()
|
||||
if err != nil {
|
||||
log.Printf("expand: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Read:
|
||||
data, err := w.ReadAll("xdata")
|
||||
if err != nil {
|
||||
log.Printf("read: %v", err)
|
||||
return
|
||||
}
|
||||
w.Text = data
|
||||
w.Q0 = q0
|
||||
w.Q1 = q1
|
||||
return
|
||||
}
|
||||
|
||||
// Invariant: in chat windows, the acme addr corresponds to the
|
||||
// empty string just before the input being typed. Text before addr
|
||||
// is the chat history (usually ending in a blank line).
|
||||
|
||||
func (w *Window) message(format string, args ...interface{}) {
|
||||
if *acmeDebug {
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
log.Printf("message; addr=%d,%d", q0, q1)
|
||||
}
|
||||
if err := w.Addr(".-/\\n?\\n?/"); err != nil && *acmeDebug {
|
||||
log.Printf("set addr: %s", err)
|
||||
}
|
||||
q0, _, _ := w.ReadAddr()
|
||||
nl := ""
|
||||
if q0 > 0 {
|
||||
nl = "\n"
|
||||
}
|
||||
if *acmeDebug {
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
log.Printf("inserting; addr=%d,%d", q0, q1)
|
||||
}
|
||||
w.Fprintf("data", nl+w.time()+format+"\n", args...)
|
||||
if *acmeDebug {
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
log.Printf("wrote; addr=%d,%d", q0, q1)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Window) sendMsg() {
|
||||
if *acmeDebug {
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
log.Printf("sendMsg; addr=%d,%d", q0, q1)
|
||||
}
|
||||
if err := w.Addr(`.,./(.|\n)*\n/`); err != nil {
|
||||
if *acmeDebug {
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
log.Printf("no text (%s); addr=%d,%d", err, q0, q1)
|
||||
}
|
||||
return
|
||||
}
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
if *acmeDebug {
|
||||
log.Printf("found msg; addr=%d,%d", q0, q1)
|
||||
}
|
||||
line, _ := w.ReadAll("xdata")
|
||||
trim := string(bytes.TrimSpace(line))
|
||||
if len(trim) > 0 {
|
||||
err := client.Send(xmpp.Chat{Remote: w.remote, Type: "chat", Text: trim})
|
||||
|
||||
// Select blank line before input (if any) and input.
|
||||
w.Addr("#%d-/\\n?\\n?/,#%d", q0, q1)
|
||||
if *acmeDebug {
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
log.Printf("selected text; addr=%d,%d", q0, q1)
|
||||
}
|
||||
q0, _, _ := w.ReadAddr()
|
||||
|
||||
// Overwrite with \nmsg\n\n.
|
||||
// Leaves addr after final \n, which is where we want it.
|
||||
nl := ""
|
||||
if q0 > 0 {
|
||||
nl = "\n"
|
||||
}
|
||||
errstr := ""
|
||||
if err != nil {
|
||||
errstr = fmt.Sprintf("\n%s", errstr)
|
||||
}
|
||||
w.Fprintf("data", "%s%s%s%s\n\n", nl, w.time(), trim, errstr)
|
||||
if *acmeDebug {
|
||||
q0, q1, _ := w.ReadAddr()
|
||||
log.Printf("wrote; addr=%d,%d", q0, q1)
|
||||
}
|
||||
w.Fprintf("ctl", "clean\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Window) readAcme() {
|
||||
for {
|
||||
e, err := w.ReadEvent()
|
||||
if err != nil {
|
||||
w.err = err
|
||||
acmeChan <- w
|
||||
break
|
||||
}
|
||||
//fmt.Printf("%c%c %d,%d %d,%d %#x %#q %#q %#q\n", e.C1, e.C2, e.Q0, e.Q1, e.OrigQ0, e.OrigQ1, e.Flag, e.Text, e.Arg, e.Loc)
|
||||
w.Event = e
|
||||
acmeChan <- w
|
||||
acmeChan <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Window) readChat() {
|
||||
for {
|
||||
msg, err := client.Recv()
|
||||
if err != nil {
|
||||
msgChan <- &Msg{w: w, err: err}
|
||||
break
|
||||
}
|
||||
//fmt.Printf("%s\n", *msg)
|
||||
msgChan <- &Msg{w: w, Chat: &msg}
|
||||
}
|
||||
}
|
||||
|
||||
func lookContact(you string) *Window {
|
||||
return active["Chat/"+acct.Nick+"/"+you]
|
||||
}
|
||||
|
||||
func showContact(you string) *Window {
|
||||
w := lookContact(you)
|
||||
if w != nil {
|
||||
w.Ctl("show\n")
|
||||
return w
|
||||
}
|
||||
|
||||
ww, err := acme.New()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
name := "Chat/" + acct.Nick + "/" + you
|
||||
ww.Name(name)
|
||||
w = &Window{Win: ww, typ: "chat", name: name, remote: you}
|
||||
w.Fprintf("body", "\n")
|
||||
w.Addr("#1")
|
||||
w.OpenEvent()
|
||||
w.Fprintf("ctl", "cleartag\n")
|
||||
w.Fprintf("tag", " Ack")
|
||||
if p := cachedPresence(you); p != nil {
|
||||
w.status(p)
|
||||
}
|
||||
active[name] = w
|
||||
go w.readAcme()
|
||||
return w
|
||||
}
|
||||
|
||||
func randid() string {
|
||||
return fmt.Sprint(time.Now())
|
||||
}
|
||||
39
vendor/github.com/mattermost/rsc/google/chat.go
сгенерированный
поставляемый
Обычный файл
39
vendor/github.com/mattermost/rsc/google/chat.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
package google
|
||||
|
||||
import "github.com/mattermost/rsc/xmpp"
|
||||
|
||||
type ChatID struct {
|
||||
ID string
|
||||
Email string
|
||||
Status xmpp.Status
|
||||
StatusMsg string
|
||||
}
|
||||
|
||||
type ChatSend struct {
|
||||
ID *ChatID
|
||||
Msg xmpp.Chat
|
||||
}
|
||||
|
||||
func (g *Client) ChatRecv(cid *ChatID) (*xmpp.Chat, error) {
|
||||
var msg xmpp.Chat
|
||||
if err := g.client.Call("goog.ChatRecv", cid, &msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
func (g *Client) ChatStatus(cid *ChatID) error {
|
||||
return g.client.Call("goog.ChatRecv", cid, &Empty{})
|
||||
}
|
||||
|
||||
func (g *Client) ChatSend(cid *ChatID, msg *xmpp.Chat) error {
|
||||
return g.client.Call("goog.ChatSend", &ChatSend{cid, *msg}, &Empty{})
|
||||
}
|
||||
|
||||
func (g *Client) ChatRoster(cid *ChatID) error {
|
||||
return g.client.Call("goog.ChatRoster", cid, &Empty{})
|
||||
}
|
||||
1241
vendor/github.com/mattermost/rsc/google/gmail/gmail.go
сгенерированный
поставляемый
Обычный файл
1241
vendor/github.com/mattermost/rsc/google/gmail/gmail.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
370
vendor/github.com/mattermost/rsc/google/gmailsend/send.go
сгенерированный
поставляемый
Обычный файл
370
vendor/github.com/mattermost/rsc/google/gmailsend/send.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,370 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/rsc/google"
|
||||
)
|
||||
|
||||
func enc(s string) string {
|
||||
// TODO =? .. ?=
|
||||
return s
|
||||
}
|
||||
|
||||
type Addr struct {
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
func (a Addr) enc() string {
|
||||
if a.Name == "" {
|
||||
return "<" + a.Email + ">"
|
||||
}
|
||||
if a.Email == "" {
|
||||
return enc(a.Name) + ":;"
|
||||
}
|
||||
return enc(a.Name) + " <" + a.Email + ">"
|
||||
}
|
||||
|
||||
type Addrs []Addr
|
||||
|
||||
func (a *Addrs) String() string {
|
||||
return "[addrlist]"
|
||||
}
|
||||
|
||||
func (a Addrs) has(s string) bool {
|
||||
for _, aa := range a {
|
||||
if aa.Email == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Addrs) Set(s string) bool {
|
||||
s = strings.TrimSpace(s)
|
||||
if strings.HasSuffix(s, ">") {
|
||||
j := strings.LastIndex(s, "<")
|
||||
if j >= 0 {
|
||||
*a = append(*a, Addr{strings.TrimSpace(s[:j]), s[j+1 : len(s)-1]})
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(s, " ") {
|
||||
fmt.Fprintf(os.Stderr, "invalid address: %s", s)
|
||||
os.Exit(2)
|
||||
}
|
||||
*a = append(*a, Addr{"", s})
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *Addrs) parseLine(s string) {
|
||||
for _, f := range strings.Split(s, ",") {
|
||||
f = strings.TrimSpace(f)
|
||||
if f != "" {
|
||||
a.Set(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a Addrs) fixDomain() {
|
||||
i := strings.Index(acct.Email, "@")
|
||||
if i < 0 {
|
||||
return
|
||||
}
|
||||
dom := acct.Email[i:]
|
||||
for i := range a {
|
||||
if a[i].Email != "" && !strings.Contains(a[i].Email, "@") {
|
||||
a[i].Email += dom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var from, to, cc, bcc, replyTo Addrs
|
||||
var inReplyTo, subject string
|
||||
var appendFile = flag.String("append", "", "file to append to end of body")
|
||||
|
||||
var acct google.Account
|
||||
var acctName = flag.String("a", "", "account to use")
|
||||
var inputHeader = flag.Bool("i", false, "read additional header lines from stdin")
|
||||
|
||||
func holdmode() {
|
||||
if os.Getenv("TERM") == "9term" {
|
||||
// forgive me
|
||||
os.Stdout.WriteString("\x1B];*9term-hold+\x07")
|
||||
}
|
||||
}
|
||||
|
||||
func match(line, prefix string, arg *string) bool {
|
||||
if len(line) < len(prefix) || !strings.EqualFold(line[:len(prefix)], prefix) {
|
||||
return false
|
||||
}
|
||||
*arg = strings.TrimSpace(line[len(prefix):])
|
||||
return true
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.StringVar(&inReplyTo, "in-reply-to", "", "In-Reply-To")
|
||||
flag.StringVar(&subject, "s", "", "Subject")
|
||||
flag.Var(&from, "from", "From (can repeat)")
|
||||
flag.Var(&to, "to", "To (can repeat)")
|
||||
flag.Var(&cc, "cc", "CC (can repeat)")
|
||||
flag.Var(&bcc, "bcc", "BCC (can repeat)")
|
||||
flag.Var(&replyTo, "replyTo", "Reply-To (can repeat)")
|
||||
|
||||
flag.Parse()
|
||||
if flag.NArg() != 0 && !*inputHeader {
|
||||
flag.Usage()
|
||||
}
|
||||
|
||||
var body bytes.Buffer
|
||||
input := bufio.NewReader(os.Stdin)
|
||||
if *inputHeader {
|
||||
holdmode()
|
||||
Loop:
|
||||
for {
|
||||
s, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break Loop
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "reading stdin: %s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
var arg string
|
||||
switch {
|
||||
default:
|
||||
if ok, _ := regexp.MatchString(`^\S+:`, s); ok {
|
||||
fmt.Fprintf(os.Stderr, "unknown header line: %s", s)
|
||||
os.Exit(2)
|
||||
}
|
||||
body.WriteString(s)
|
||||
break Loop
|
||||
case match(s, "from:", &arg):
|
||||
from.parseLine(arg)
|
||||
case match(s, "to:", &arg):
|
||||
to.parseLine(arg)
|
||||
case match(s, "cc:", &arg):
|
||||
cc.parseLine(arg)
|
||||
case match(s, "bcc:", &arg):
|
||||
bcc.parseLine(arg)
|
||||
case match(s, "reply-to:", &arg):
|
||||
replyTo.parseLine(arg)
|
||||
case match(s, "subject:", &arg):
|
||||
subject = arg
|
||||
case match(s, "in-reply-to:", &arg):
|
||||
inReplyTo = arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
acct = google.Acct(*acctName)
|
||||
from.fixDomain()
|
||||
to.fixDomain()
|
||||
cc.fixDomain()
|
||||
bcc.fixDomain()
|
||||
replyTo.fixDomain()
|
||||
|
||||
smtpTo := append(append(to, cc...), bcc...)
|
||||
|
||||
if len(from) == 0 {
|
||||
// TODO: Much better
|
||||
name := ""
|
||||
email := acct.Email
|
||||
if email == "rsc@swtch.com" || email == "rsc@google.com" {
|
||||
name = "Russ Cox"
|
||||
}
|
||||
if email == "rsc@google.com" && (smtpTo.has("go@googlecode.com") || smtpTo.has("golang-dev@googlegroups.com") || smtpTo.has("golang-nuts@googlegroups.com")) {
|
||||
from = append(from, Addr{name, "rsc@golang.org"})
|
||||
} else {
|
||||
from = append(from, Addr{name, email})
|
||||
}
|
||||
}
|
||||
|
||||
if len(from) > 1 {
|
||||
fmt.Fprintf(os.Stderr, "missing -from\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if len(to)+len(cc)+len(bcc) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "missing destinations\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if !*inputHeader {
|
||||
holdmode()
|
||||
}
|
||||
_, err := io.Copy(&body, input)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "reading stdin: %s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if *appendFile != "" {
|
||||
f, err := os.Open(*appendFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "append: %s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
_, err = io.Copy(&body, f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "append: %s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
var msg bytes.Buffer
|
||||
fmt.Fprintf(&msg, "MIME-Version: 1.0\n")
|
||||
if len(from) > 0 {
|
||||
fmt.Fprintf(&msg, "From: ")
|
||||
for i, a := range from {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(&msg, ", ")
|
||||
}
|
||||
fmt.Fprintf(&msg, "%s", a.enc())
|
||||
}
|
||||
fmt.Fprintf(&msg, "\n")
|
||||
}
|
||||
if len(to) > 0 {
|
||||
fmt.Fprintf(&msg, "To: ")
|
||||
for i, a := range to {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(&msg, ", ")
|
||||
}
|
||||
fmt.Fprintf(&msg, "%s", a.enc())
|
||||
}
|
||||
fmt.Fprintf(&msg, "\n")
|
||||
}
|
||||
if len(cc) > 0 {
|
||||
fmt.Fprintf(&msg, "CC: ")
|
||||
for i, a := range cc {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(&msg, ", ")
|
||||
}
|
||||
fmt.Fprintf(&msg, "%s", a.enc())
|
||||
}
|
||||
fmt.Fprintf(&msg, "\n")
|
||||
}
|
||||
if len(replyTo) > 0 {
|
||||
fmt.Fprintf(&msg, "Reply-To: ")
|
||||
for i, a := range replyTo {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(&msg, ", ")
|
||||
}
|
||||
fmt.Fprintf(&msg, "%s", a.enc())
|
||||
}
|
||||
fmt.Fprintf(&msg, "\n")
|
||||
}
|
||||
if inReplyTo != "" {
|
||||
fmt.Fprintf(&msg, "In-Reply-To: %s\n", inReplyTo)
|
||||
}
|
||||
if subject != "" {
|
||||
fmt.Fprintf(&msg, "Subject: %s\n", enc(subject))
|
||||
}
|
||||
fmt.Fprintf(&msg, "Date: xxx\n")
|
||||
fmt.Fprintf(&msg, "Content-Type: text/plain; charset=\"utf-8\"\n")
|
||||
fmt.Fprintf(&msg, "Content-Transfer-Encoding: base64\n")
|
||||
fmt.Fprintf(&msg, "\n")
|
||||
enc64 := base64.StdEncoding.EncodeToString(body.Bytes())
|
||||
for len(enc64) > 72 {
|
||||
fmt.Fprintf(&msg, "%s\n", enc64[:72])
|
||||
enc64 = enc64[72:]
|
||||
}
|
||||
fmt.Fprintf(&msg, "%s\n\n", enc64)
|
||||
|
||||
auth := smtp.PlainAuth(
|
||||
"",
|
||||
acct.Email,
|
||||
acct.Password,
|
||||
"smtp.gmail.com",
|
||||
)
|
||||
var smtpToEmail []string
|
||||
for _, a := range smtpTo {
|
||||
if a.Email != "" {
|
||||
smtpToEmail = append(smtpToEmail, a.Email)
|
||||
}
|
||||
}
|
||||
|
||||
if err := sendMail("smtp.gmail.com:587", auth, from[0].Email, smtpToEmail, msg.Bytes()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "sending mail: %s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MIME-Version: 1.0
|
||||
Subject: commit/plan9port: rsc: 9term: hold mode back door
|
||||
From: Bitbucket <commits-noreply@bitbucket.org>
|
||||
To: plan9port-dev@googlegroups.com
|
||||
Date: Tue, 11 Oct 2011 13:34:30 -0000
|
||||
Message-ID: <20111011133430.31146.55070@bitbucket13.managed.contegix.com>
|
||||
Reply-To: commits-noreply@bitbucket.org
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
1 new changeset in plan9port:
|
||||
|
||||
http://bitbucket.org/rsc/plan9port/changeset/8735d7708a1b/
|
||||
changeset: 8735d7708a1b
|
||||
user: rsc
|
||||
date: 2011-10-11 15:34:25
|
||||
summary: 9term: hold mode back door
|
||||
|
||||
R=3Drsc
|
||||
http://codereview.appspot.com/5248056
|
||||
affected #: 2 files (-1 bytes)
|
||||
|
||||
Repository URL: https://bitbucket.org/rsc/plan9port/
|
||||
|
||||
--
|
||||
|
||||
This is a commit notification from bitbucket.org. You are receiving
|
||||
this because you have the service enabled, addressing the recipient of
|
||||
this email.
|
||||
|
||||
*/
|
||||
|
||||
func sendMail(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
|
||||
c, err := smtp.Dial(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.StartTLS(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.Auth(a); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.Mail(from); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, addr := range to {
|
||||
if err = c.Rcpt(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Quit()
|
||||
}
|
||||
80
vendor/github.com/mattermost/rsc/google/googleserver/chat.go
сгенерированный
поставляемый
Обычный файл
80
vendor/github.com/mattermost/rsc/google/googleserver/chat.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,80 @@
|
||||
// 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: Add ChatHangup.
|
||||
// TODO: Auto-hangup chats that are gone.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/rsc/google"
|
||||
"github.com/mattermost/rsc/xmpp"
|
||||
)
|
||||
|
||||
type chatClient struct {
|
||||
email string
|
||||
id string
|
||||
xmpp *xmpp.Client
|
||||
}
|
||||
|
||||
var chatClients = map[string]*chatClient{}
|
||||
|
||||
func (*Server) chatClient(cid *google.ChatID) (*chatClient, error) {
|
||||
id := cid.ID
|
||||
cc := chatClients[cid.ID]
|
||||
if cc == nil {
|
||||
a := google.Cfg.AccountByEmail(cid.Email)
|
||||
if a == nil {
|
||||
return nil, fmt.Errorf("unknown account %s", cid.Email)
|
||||
}
|
||||
// New client.
|
||||
cli, err := xmpp.NewClient("talk.google.com:443", a.Email, a.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc = &chatClient{email: a.Email, id: id, xmpp: cli}
|
||||
cc.xmpp.Status(cid.Status, cid.StatusMsg)
|
||||
chatClients[id] = cc
|
||||
}
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
func (srv *Server) ChatRecv(cid *google.ChatID, msg *xmpp.Chat) error {
|
||||
cc, err := srv.chatClient(cid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chat, err := cc.xmpp.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*msg = chat
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ChatStatus(cid *google.ChatID, _ *Empty) error {
|
||||
cc, err := srv.chatClient(cid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cc.xmpp.Status(cid.Status, cid.StatusMsg)
|
||||
}
|
||||
|
||||
func (srv *Server) ChatSend(arg *google.ChatSend, _ *Empty) error {
|
||||
cc, err := srv.chatClient(arg.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cc.xmpp.Send(arg.Msg)
|
||||
}
|
||||
|
||||
func (srv *Server) ChatRoster(cid *google.ChatID, _ *Empty) error {
|
||||
cc, err := srv.chatClient(cid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cc.xmpp.Roster()
|
||||
}
|
||||
139
vendor/github.com/mattermost/rsc/google/googleserver/main.go
сгенерированный
поставляемый
Обычный файл
139
vendor/github.com/mattermost/rsc/google/googleserver/main.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,139 @@
|
||||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
// "flag"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/mattermost/rsc/google"
|
||||
"github.com/mattermost/rsc/xmpp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
google.ReadConfig()
|
||||
switch os.Args[1] {
|
||||
case "add":
|
||||
google.Cfg.Account = append(google.Cfg.Account, &google.Account{Email: os.Args[2], Password: os.Args[3]})
|
||||
google.WriteConfig()
|
||||
case "serve":
|
||||
serve()
|
||||
case "accounts":
|
||||
c, err := google.Dial()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
out, err := c.Accounts()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, email := range out {
|
||||
fmt.Printf("%s\n", email)
|
||||
}
|
||||
case "ping":
|
||||
c, err := google.Dial()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := c.Ping(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "chat":
|
||||
c, err := google.Dial()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cid := &google.ChatID{ID: "1", Email: os.Args[2], Status: xmpp.Available, StatusMsg: ""}
|
||||
go chatRecv(c, cid)
|
||||
c.ChatRoster(cid)
|
||||
b := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
line, err := b.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
line = line[:len(line)-1]
|
||||
i := strings.Index(line, ": ")
|
||||
if i < 0 {
|
||||
log.Printf("<who>: <msg>, please")
|
||||
continue
|
||||
}
|
||||
who, msg := line[:i], line[i+2:]
|
||||
if err := c.ChatSend(cid, &xmpp.Chat{Remote: who, Type: "chat", Text: msg}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func chatRecv(c *google.Client, cid *google.ChatID) {
|
||||
for {
|
||||
msg, err := c.ChatRecv(cid)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
switch msg.Type {
|
||||
case "roster":
|
||||
for _, contact := range msg.Roster {
|
||||
fmt.Printf("%v\n", contact)
|
||||
}
|
||||
case "presence":
|
||||
fmt.Printf("%v\n", msg.Presence)
|
||||
case "chat":
|
||||
fmt.Printf("%s: %s\n", msg.Remote, msg.Text)
|
||||
default:
|
||||
fmt.Printf("<%s>\n", msg.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listen() net.Listener {
|
||||
socket := google.Dir() + "/socket"
|
||||
os.Remove(socket)
|
||||
l, err := net.Listen("unix", socket)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func serve() {
|
||||
f, err := os.OpenFile(google.Dir()+"/log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.SetOutput(f)
|
||||
syscall.Dup2(f.Fd(), 2)
|
||||
os.Stdout = f
|
||||
os.Stderr = f
|
||||
l := listen()
|
||||
rpc.RegisterName("goog", &Server{})
|
||||
rpc.Accept(l)
|
||||
log.Fatal("rpc.Accept finished: server exiting")
|
||||
}
|
||||
|
||||
type Server struct{}
|
||||
|
||||
type Empty google.Empty
|
||||
|
||||
func (*Server) Ping(*Empty, *Empty) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*Server) Accounts(_ *Empty, out *[]string) error {
|
||||
var email []string
|
||||
for _, a := range google.Cfg.Account {
|
||||
email = append(email, a.Email)
|
||||
}
|
||||
*out = email
|
||||
return nil
|
||||
}
|
||||
181
vendor/github.com/mattermost/rsc/google/main.go
сгенерированный
поставляемый
Обычный файл
181
vendor/github.com/mattermost/rsc/google/main.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,181 @@
|
||||
// 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")
|
||||
}
|
||||
Ссылка в новой задаче
Block a user