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(&register); err != nil {
67		return err
68	}
69
70	if len(register.Form.Type) > 0 {
71		reply, err := processForm(&register.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