diff --git a/_example/example.go b/_example/example.go
index 136af5a..1639714 100644
--- a/_example/example.go
+++ b/_example/example.go
@@ -40,7 +40,7 @@ func main() {
}
if !*notls {
- xmpp.DefaultConfig = tls.Config{
+ xmpp.DefaultConfig = &tls.Config{
ServerName: serverName(*server),
InsecureSkipVerify: false,
}
diff --git a/xmpp.go b/xmpp.go
index 051c11a..4dd48a6 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -1096,6 +1096,29 @@ func (c *Client) Recv() (stanza interface{}, err error) {
return handleAvatarMetadata(p.Items[0].Body,
v
}*/
+ case "searchAccount1":
+ if v.Query.XMLName.Space == nsSearch {
+ var accountQuery clientSearchAccountQuery
+ err := xml.Unmarshal(v.InnerXML, &accountQuery)
+ if err != nil {
+ return SearchAccountResult{}, err
+ }
+
+ return SearchAccountResult{
+ Jid: v.From,
+ Accounts: clientSearchAccountItemToReturn(accountQuery.Items),
+ }, nil
+ }
+ case "requestLastActivity1":
+ if v.Query.XMLName.Space == nsLastActivity {
+ var lastActivity lastActivity
+ err := xml.Unmarshal(v.InnerXML, &lastActivity)
+ if err != nil {
+ return LastActivityResult{}, err
+ }
+
+ return handleLastActivityResult(v.From, lastActivity)
+ }
default:
res, err := xml.Marshal(v.Query)
if err != nil {
diff --git a/xmpp_account.go b/xmpp_account.go
new file mode 100644
index 0000000..2a78483
--- /dev/null
+++ b/xmpp_account.go
@@ -0,0 +1,150 @@
+package xmpp
+
+import (
+ "encoding/xml"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+const (
+ nsRegister = "jabber:iq:register"
+ nsSearch = "jabber:iq:search"
+ nsLastActivity = "jabber:iq:last"
+)
+
+type clientSearchAccountItem struct {
+ Jid string `xml:"jid,attr"`
+ First string `xml:"first"`
+ Last string `xml:"last"`
+ Nick string `xml:"nick"`
+ Email string `xml:"email"`
+}
+
+type clientSearchAccountQuery struct {
+ XMLName xml.Name `xml:"query"`
+ Xmlns string `xml:"xmlns,attr"`
+ Items []clientSearchAccountItem `xml:"item"`
+}
+
+// SearchAccountResultItem represent search account item result
+type SearchAccountResultItem struct {
+ Jid string
+ FirstName string
+ LastName string
+ NickName string
+ Email string
+}
+
+// SearchAccountResult represent search account item result
+type SearchAccountResult struct {
+ Jid string
+ Accounts []SearchAccountResultItem
+}
+
+type lastActivity struct {
+ XMLName xml.Name `xml:"query"`
+ Text string `xml:",chardata"`
+ Xmlns string `xml:"xmlns,attr"`
+ Seconds string `xml:"seconds,attr"`
+}
+
+// LastActivityResult represent last activity response
+// when current entity session is authorized to view the user's presence information
+type LastActivityResult struct {
+ From string
+ Text string
+ LastActiveSeconds int
+}
+
+func clientSearchAccountItemToReturn(accounts []clientSearchAccountItem) []SearchAccountResultItem {
+ var ret []SearchAccountResultItem
+ for _, account := range accounts {
+ ret = append(ret, SearchAccountResultItem{
+ Jid: account.Jid,
+ FirstName: account.First,
+ LastName: account.Last,
+ NickName: account.Nick,
+ Email: account.Email,
+ })
+ }
+
+ return ret
+}
+
+func handleLastActivityResult(from string, lastActivity lastActivity) (LastActivityResult, error) {
+ lastActiveSeconds, err := strconv.Atoi(lastActivity.Seconds)
+ if err != nil {
+ return LastActivityResult{}, err
+ }
+
+ return LastActivityResult{
+ From: from,
+ Text: lastActivity.Text,
+ LastActiveSeconds: lastActiveSeconds,
+ }, nil
+}
+
+func buildAccountAttr(username string, password string, attributes map[string]string) string {
+ var attrBuilder strings.Builder
+ attrBuilder.WriteString(fmt.Sprintf("%s", xmlEscape(username)))
+ attrBuilder.WriteString(fmt.Sprintf("%s", xmlEscape(password)))
+
+ if attributes != nil {
+ for k, v := range attributes {
+ attrBuilder.WriteString(fmt.Sprintf("<%s>%s%s>", k, v, k))
+ }
+ }
+
+ return attrBuilder.String()
+}
+
+// CreateAccount Creates a new account using the specified username, password and extra attributes
+func (c *Client) CreateAccount(username string, password string, attributes map[string]string) error {
+ attrBuilder := buildAccountAttr(username, password, attributes)
+ const xmlIQ = "%s"
+ _, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, nsRegister, attrBuilder)
+ return err
+}
+
+// ChangePassword enables a user to change his or her password with a server or service.
+// With the user's username parameter which will change the password and new password
+func (c *Client) ChangePassword(username string, newPassword string) error {
+ attrBuilder := buildAccountAttr(username, newPassword, nil)
+ const xmlIQ = "%s"
+ _, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(c.domain), nsRegister, attrBuilder)
+ return err
+}
+
+// RemoveAccount cancel or delete the current session registration
+// with a host by sending a element in an IQ set.
+func (c *Client) RemoveAccount() error {
+ from := c.jid
+ const xmlIQ = ""
+ _, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), nsRegister)
+ return err
+}
+
+// SearchAccount search information repositories on the Jabber network.
+// searchServiceName is the Search Service Properties Name from your server.
+func (c *Client) SearchAccount(searchServiceName, fieldName, fieldValue string) error {
+ from := c.jid
+ searchService := fmt.Sprintf("%s.%s", searchServiceName, c.domain)
+ searchQuery := fmt.Sprintf("<%s>%s%s>", fieldName, xmlEscape(fieldValue), fieldName)
+ const xmlIQ = "%s\n"
+ _, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), searchService, nsSearch, searchQuery)
+ return err
+}
+
+// RequestLastActivity request last activity information regarding another entity
+func (c *Client) RequestLastActivity(to string) error {
+ targetEntity := strings.SplitN(to, "@", 2)
+ if len(targetEntity) < 2 {
+ to = fmt.Sprintf("%s@%s", to, c.domain)
+ }
+
+ from := c.jid
+ const xmlIQ = ""
+ _, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), nsLastActivity)
+ return err
+}