1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package xmpp implements the XMPP IM protocol, as specified in RFC 6120 and 6// 6121. 7package xmpp 8 9import ( 10 "bytes" 11 "encoding/xml" 12 "errors" 13 "fmt" 14 "io" 15 16 "github.com/coyim/coyim/xmpp/data" 17 "github.com/coyim/coyim/xmpp/interfaces" 18) 19 20// Various errors signalled by the registration component 21var ( 22 ErrUsernameConflict = errors.New("xmpp: the username is not available for registration") 23 ErrMissingRequiredRegistrationInfo = errors.New("xmpp: missing required registration information") 24 ErrRegistrationFailed = errors.New("xmpp: account creation failed") 25 ErrWrongCaptcha = errors.New("xmpp: the captcha entered is wrong") 26 ErrResourceConstraint = errors.New("xmpp: already reached the configured number of allowable resources") 27) 28 29// XEP-0077 30func (d *dialer) negotiateInBandRegistration(c interfaces.Conn) (bool, error) { 31 if c.Features().InBandRegistration == nil { 32 return false, nil 33 } 34 35 user := d.getJIDLocalpart() 36 return c.RegisterAccount(user, d.password) 37} 38 39func (c *conn) RegisterAccount(user, password string) (bool, error) { 40 if c.config.CreateCallback == nil { 41 return false, nil 42 } 43 44 err := c.createAccount(user, password) 45 if err != nil { 46 return false, err 47 } 48 49 return true, c.closeImmediately() 50} 51 52func (c *conn) createAccount(user, password string) error { 53 io.WriteString(c.config.GetLog(), "Attempting to create account\n") 54 fmt.Fprintf(c.out, "<iq type='get' id='create_1'><query xmlns='jabber:iq:register'/></iq>") 55 var iq data.ClientIQ 56 if err := c.in.DecodeElement(&iq, nil); err != nil { 57 return errors.New("unmarshal <iq>: " + err.Error()) 58 } 59 60 if iq.Type != "result" { 61 return ErrRegistrationFailed 62 } 63 64 var register data.RegisterQuery 65 66 if err := xml.NewDecoder(bytes.NewBuffer(iq.Query)).Decode(®ister); err != nil { 67 return err 68 } 69 70 if len(register.Form.Type) > 0 { 71 reply, err := processForm(®ister.Form, register.Datas, c.config.CreateCallback) 72 fmt.Fprintf(c.rawOut, "<iq type='set' id='create_2'><query xmlns='jabber:iq:register'>") 73 if err = xml.NewEncoder(c.rawOut).Encode(reply); err != nil { 74 return err 75 } 76 77 fmt.Fprintf(c.rawOut, "</query></iq>") 78 } else if register.Username != nil && register.Password != nil { 79 //TODO: make sure this only happens via SSL 80 //TODO: should generate form asking for username and password, 81 //and call processForm for consistency 82 83 // Try the old-style registration. 84 fmt.Fprintf(c.rawOut, "<iq type='set' id='create_2'><query xmlns='jabber:iq:register'><username>%s</username><password>%s</password></query></iq>", user, password) 85 } 86 87 iq2 := &data.ClientIQ{} 88 if err := c.in.DecodeElement(iq2, nil); err != nil { 89 return errors.New("unmarshal <iq>: " + err.Error()) 90 } 91 92 if iq2.Type == "error" { 93 switch iq2.Error.Condition.XMLName.Local { 94 case "conflict": 95 // <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> 96 return ErrUsernameConflict 97 case "not-acceptable": 98 // <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> 99 return ErrMissingRequiredRegistrationInfo 100 // TODO: this case shouldn't happen 101 case "bad-request": 102 //<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> 103 return ErrRegistrationFailed 104 case "not-allowed": 105 //<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> 106 return ErrWrongCaptcha 107 case "resource-constraint": 108 //<resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> 109 return ErrResourceConstraint 110 default: 111 return ErrRegistrationFailed 112 } 113 } 114 115 return nil 116} 117 118// CancelRegistration cancels the account registration with the server 119func (c *conn) CancelRegistration() (reply <-chan data.Stanza, cookie data.Cookie, err error) { 120 // https://xmpp.org/extensions/xep-0077.html#usecases-cancel 121 registrationCancel := rawXML(` 122 <query xmlns='jabber:iq:register'> 123 <remove/> 124 </query> 125 `) 126 127 return c.SendIQ("", "set", registrationCancel) 128} 129