Fetch latest changes

This commit is contained in:
Thomas
2015-05-17 00:53:09 +02:00
3 changed files with 158 additions and 85 deletions

View File

@@ -32,8 +32,12 @@ func main() {
} }
flag.Parse() flag.Parse()
if *username == "" || *password == "" { if *username == "" || *password == "" {
if *debug && *username == "" && *password == "" {
fmt.Fprintf(os.Stderr, "no username or password were given; attempting ANONYMOUS auth\n")
} else if *username != "" || *password != "" {
flag.Usage() flag.Usage()
} }
}
if !*notls { if !*notls {
xmpp.DefaultConfig = tls.Config{ xmpp.DefaultConfig = tls.Config{

65
xmpp.go
View File

@@ -273,11 +273,14 @@ func (c *Client) init(o *Options) error {
c.p = xml.NewDecoder(c.conn) c.p = xml.NewDecoder(c.conn)
} }
var domain string
a := strings.SplitN(o.User, "@", 2) a := strings.SplitN(o.User, "@", 2)
if len(o.User) > 0 {
if len(a) != 2 { if len(a) != 2 {
return errors.New("xmpp: invalid username (want user@domain): " + o.User) return errors.New("xmpp: invalid username (want user@domain): " + o.User)
} }
domain := a[1] domain = a[1]
} // Otherwise, we'll be attempting ANONYMOUS
// Declare intent to be a jabber client and gather stream features. // Declare intent to be a jabber client and gather stream features.
f, err := c.startStream(o, domain) f, err := c.startStream(o, domain)
@@ -290,6 +293,19 @@ func (c *Client) init(o *Options) error {
return err return err
} }
if o.User == "" && o.Password == "" {
foundAnonymous := false
for _, m := range f.Mechanisms.Mechanism {
if m == "ANONYMOUS" {
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
foundAnonymous = true
break
}
}
if !foundAnonymous {
return fmt.Errorf("ANONYMOUS authentication is not an option and username and password were not specified")
}
} else {
// Even digest forms of authentication are unsafe if we do not know that the host // Even digest forms of authentication are unsafe if we do not know that the host
// we are talking to is the actual server, and not a man in the middle playing // we are talking to is the actual server, and not a man in the middle playing
// proxy. // proxy.
@@ -371,7 +387,7 @@ func (c *Client) init(o *Options) error {
if mechanism == "" { if mechanism == "" {
return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism) return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
} }
}
// Next message should be either success or failure. // Next message should be either success or failure.
name, val, err := next(c.p) name, val, err := next(c.p)
if err != nil { if err != nil {
@@ -514,10 +530,19 @@ type Chat struct {
Remote string Remote string
Type string Type string
Text string Text string
Roster Roster
Other []string Other []string
Stamp time.Time Stamp time.Time
} }
type Roster []Contact
type Contact struct {
Remote string
Name string
Group []string
}
// Presence is an XMPP presence notification. // Presence is an XMPP presence notification.
type Presence struct { type Presence struct {
From string From string
@@ -541,13 +566,19 @@ func (c *Client) Recv() (stanza interface{}, err error) {
v.Delay.Stamp, v.Delay.Stamp,
) )
chat := Chat{ chat := Chat{
v.From, Remote: v.From,
v.Type, Type: v.Type,
v.Body, Text: v.Body,
v.Other, Other: v.Other,
stamp, Stamp: stamp,
} }
return chat, nil return chat, nil
case *clientQuery:
var r Roster
for _, item := range v.Item {
r = append(r, Contact{item.Jid, item.Name, item.Group})
}
return Chat{Type: "roster", Roster: r}, nil
case *clientPresence: case *clientPresence:
return Presence{v.From, v.To, v.Type, v.Show}, nil return Presence{v.From, v.To, v.Type, v.Show}, nil
} }
@@ -574,6 +605,12 @@ func (c *Client) SendHtml(chat Chat) (n int, err error) {
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text) xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text)
} }
// Roster asks for the chat roster.
func (c *Client) Roster() error {
fmt.Fprintf(c.conn, "<iq from='%s' type='get' id='roster1'><query xmlns='jabber:iq:roster'/></iq>\n", xmlEscape(c.jid))
return nil
}
// RFC 3920 C.1 Streams name space // RFC 3920 C.1 Streams name space
type streamFeatures struct { type streamFeatures struct {
XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"` XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
@@ -700,11 +737,23 @@ type clientError struct {
Text string Text string
} }
type clientQuery struct {
Item []rosterItem
}
type rosterItem struct {
XMLName xml.Name `xml:"jabber:iq:roster item"`
Jid string `xml:",attr"`
Name string `xml:",attr"`
Subscription string `xml:",attr"`
Group []string
}
// Scan XML token stream to find next StartElement. // Scan XML token stream to find next StartElement.
func nextStart(p *xml.Decoder) (xml.StartElement, error) { func nextStart(p *xml.Decoder) (xml.StartElement, error) {
for { for {
t, err := p.Token() t, err := p.Token()
if err != nil && err != io.EOF { if err != nil && err != io.EOF || t == nil {
return xml.StartElement{}, err return xml.StartElement{}, err
} }
switch t := t.(type) { switch t := t.(type) {

20
xmpp_subscription.go Normal file
View File

@@ -0,0 +1,20 @@
package xmpp
import (
"fmt"
)
func (c* Client) ApproveSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>",
xmlEscape(jid))
}
func (c* Client) RevokeSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>",
xmlEscape(jid))
}
func (c* Client) RequestSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>",
xmlEscape(jid))
}