1
0
forked from jshiffer/go-xmpp

Compare commits

..

128 Commits

Author SHA1 Message Date
370c500a5e
xmpp: Add reply and stanza ID serialization/deserialization 2024-11-04 04:46:30 +01:00
Wim
7154bfeb76
Update module 2024-05-24 01:01:55 +02:00
ValdikSS
243a438354
Implement XMPP message ID and message correction functionality 2024-05-24 00:56:19 +02:00
Martin Dosch
e9123cc4b3 Make use of FAST configurable. 2024-05-11 15:05:41 +02:00
Martin Dosch
4be597a84a Add commit that Client.Mechanism is the SCRAM mechanism used. 2024-05-11 14:55:32 +02:00
Martin Dosch
464fbe04ef Update go modules. 2024-05-06 21:16:36 +02:00
Martin Dosch
e223dcf94b Only set from if connection is encrypted. 2024-04-23 21:24:34 +02:00
Martin Dosch
321c2b14a5 FAST: Add option to invalidate current fast token. 2024-04-13 12:08:36 +02:00
Martin Dosch
9161feef4d FAST: Check that connection is encrypted. 2024-04-12 17:27:33 +02:00
Martin Dosch
fc3ed9a0b8 Update go modules. 2024-04-12 12:56:37 +02:00
Martin Dosch
d9df620fa4 Revert "FAST success: New token OR reduced expiry not AND."
This reverts commit f067814851.
2024-04-12 11:51:56 +02:00
Martin Dosch
f067814851 FAST success: New token OR reduced expiry not AND. 2024-04-12 10:53:15 +02:00
Martin Dosch
961b7e435e FAST: Check for changed token after sasl2Success. 2024-04-12 10:51:09 +02:00
Martin Dosch
12a04e0950 Set shutdown earlier to further reduce probability of races. 2024-04-11 09:50:28 +02:00
Martin
2f331ed19c
Merge pull request from mdosch/fast
Basic FAST support.
2024-04-10 15:14:39 +02:00
Martin Dosch
b0f55a8f7f Basic FAST support. 2024-04-10 15:13:20 +02:00
Martin
7486b7a363
Add support for SASL2 and BIND2 ()
* Add basic support for SASL2 (XEP-0388) and Bind2 (XEP-0386).
2024-04-09 10:53:38 +02:00
Martin Dosch
da2377ecb0 Recv: Return error on stream error. 2024-04-07 11:25:16 +02:00
Martin Dosch
44095406a2 Update go modules. 2024-04-07 00:59:08 +02:00
Martin Dosch
d7aee6b636 Harmonize newlines. 2024-04-05 11:57:09 +02:00
Martin Dosch
0324b31f56 Update go modules. 2024-04-04 11:05:14 +02:00
Martin Dosch
ca4e49201e Do not try to read from the xml stream if it's going to be closed. 2024-04-02 16:32:30 +02:00
Martin Dosch
6e5d6e449e Remove checking for xml.Endelement in nextStart().
This seems to have negative side effects on parsing in next()
2024-04-02 15:48:34 +02:00
Martin Dosch
0ae62a33a2 Further reduce possible data races. 2024-04-02 13:39:45 +02:00
Martin Dosch
ce687243c1 NextEnd: Use Token() instead of RawToken()
I assumed RawToken would be better suited here, but there were errors
parsing messages that could be fixed by reverting to Token().
2024-03-31 15:50:04 +02:00
Martin Dosch
78d07e9eee XEP-0478: Further improve error message. 2024-03-28 18:17:45 +01:00
Martin Dosch
416bb6e7b7 XEP-0478: Be more verbose in error if max stanza size is exceeded. 2024-03-28 18:07:23 +01:00
Martin Dosch
aef1257ed1 Fix timeout when server doesn't reply with closing stream element. 2024-03-28 17:22:02 +01:00
Martin Dosch
da17a46e6f Also read stream limits after authentication
The [business rules](https://xmpp.org/extensions/xep-0478.html#rules)
mention the following:

> It is acceptable for the limits on a stream to change whenever new stream features are announced - such as before and after authentication of the connecting entity.

The first detection of the stream limits is not deleted as there is also
ANONYMOUS authentication.
2024-03-28 15:57:56 +01:00
Martin Dosch
eedd7259cb Generate new ID for session (fixes )
Co-authored-by: https://github.com/ikq
2024-03-28 15:53:09 +01:00
Martin Dosch
07196efcf3 Add support for XEP-0478: Stream Limits Advertisement. 2024-03-28 15:49:08 +01:00
Martin Dosch
bbd90cc04b Copy token in nextStart function.
See
bc81053dbc (commitcomment-140301890)
and bc81053dbc (commitcomment-140303962)
2024-03-27 21:12:42 +01:00
Martin Dosch
0c7ee22452 Revert "Don't copy token."
This reverts commit bc81053dbc.
2024-03-27 21:09:43 +01:00
Martin Dosch
862c21f845 Make XEP-0474 configurable (default off).
As it is still experimental we should not enable it per default.
2024-03-26 21:36:15 +01:00
Martin Dosch
bc81053dbc Don't copy token.
This should be unnecessary and is probably a
leftover of previous experiments to fix some races
when closing the connection.
2024-03-26 19:26:19 +01:00
Martin
94ab540b80
Merge pull request from vcabbage/move-mutex
move nextMutex to Client to prevent blocking separate Clients
2024-03-26 19:18:05 +01:00
Kale Blankenship
f6a9836fdf move nextMutex to Client to prevent blocking separate Clients
Avoids a global mutex which could end up unexpectedly blocking a
separate client. For example, if there were a client with few messages
and a client with many messages, the client with few could hold the lock
waiting for a token blocking the client with many from receiving.
2024-03-26 11:02:05 -07:00
Martin Dosch
8ab32d885f Fix race condition for nextStart and nextEnd. 2024-03-16 19:04:09 +01:00
Martin Dosch
73f06c9f3d Catch stream error after bind request. 2024-03-10 13:44:33 +01:00
Martin Dosch
9c5e758356 Use RawToken() instead of Token() for finding nextEnd.
This should improve stability as RawToken() does not
verify that start and end elements match.
2024-03-10 11:30:39 +01:00
Martin Dosch
ea4874e8c9 SCRAM: Check for SASL failure after sending auth message. 2024-03-09 19:51:47 +01:00
Martin Dosch
dab6865bd2 Update go modules. 2024-03-06 21:01:03 +01:00
Martin Dosch
c051d69509 Improve closing the stream. 2024-03-03 12:10:45 +01:00
Martin Dosch
aed021cf3e Wait for the server closing the stream before closing the connection. 2024-03-02 14:17:47 +01:00
Martin Dosch
2c4708e724
[gofumpt]: Remove empty line. 2024-02-27 22:26:58 +01:00
Martin Dosch
746409f074
Update dependencies. 2024-02-26 23:04:17 +01:00
Martin Dosch
9684a8ff69 Close stream before closing connection. 2024-02-25 12:25:57 +01:00
Martin Dosch
b7ea9f4be1 Update dependencies. 2024-02-23 13:55:02 +01:00
Martin Dosch
e2bc7bf6d7 Remove unused type clientPubsub. 2024-02-22 18:59:22 +01:00
Martin Dosch
0bcc057225 Remove unused types. 2024-02-22 17:58:48 +01:00
Martin Dosch
49054ca9e9 Remove unused function saslDigestResponse. 2024-02-22 17:55:38 +01:00
Martin
b369b7df10
Merge pull request from mdosch/update-docs
Update import path in examples.
2024-02-01 13:58:52 +08:00
Martin Dosch
cc481e54e7 Update import path in examples. 2024-02-01 13:58:04 +08:00
Martin
88855eac82
Merge pull request from mdosch/gofumpt
[gofumpt] Improve formatting.
2024-02-01 13:56:27 +08:00
Martin Dosch
0cc0a72c15 [gofumpt] Improve formatting. 2024-02-01 13:54:58 +08:00
Martin
d6e9a15f29
Merge pull request from mdosch/scram-improvement
Improve RFC5802 compatibility.
2024-02-01 13:52:47 +08:00
Martin Dosch
6ffd595a06 Improve RFC5802 compatibility.
According to RFC 5802 5.1 only the "m" attribute should
cause an authentication error.
2024-02-01 13:35:55 +08:00
Martin
62928b3483
Merge pull request from mdosch/fix-134
Filter invalid UTF8 from message body.
2024-01-18 20:04:09 +01:00
Martin Dosch
d67787ca0f Filter invalid UTF8 from message body.
Closes 
2024-01-18 19:46:18 +01:00
Martin Dosch
f8a24505f4 Update dependencies. 2024-01-14 00:15:36 +01:00
Martin
685570cbd8
Merge pull request from mdosch/socks
Add support for SOCKS5 proxies.
2024-01-13 14:58:10 +01:00
Martin Dosch
7bfa331758 Add support for SOCKS5 proxies. 2024-01-13 14:05:35 +01:00
Martin
3f0cbac307
Merge pull request from mdosch/server-end-point
Tls-server-end-point improvements.
2024-01-12 12:12:52 +01:00
Martin Dosch
7ccad52e63 (Indirectly) check that TLS was not renegotioated when using "tls-server-end-point". 2024-01-12 12:10:06 +01:00
Martin Dosch
705f68d1a5 Simplify tls-server-end-point channel binding code. 2024-01-12 11:56:32 +01:00
Martin Dosch
b49bdce100 Merge branch 'master' into server-end-point 2024-01-12 11:54:03 +01:00
Martin
f4c732fdc7
SCRAM: Add support for tls-server-end-point channel binding. () 2024-01-11 13:33:32 +01:00
Martin Dosch
d3d16d5db9 SCRAM: Add support for tls-server-end-point channel binding. 2024-01-11 13:29:59 +01:00
Eleksir
34d683d25a
Extend SendPresence() stub func to allow send useful statuses () 2024-01-10 23:24:28 +01:00
Martin
dffa92c129
Remove DIGEST-MD5 ()
As mentioned in https://github.com/xmppo/go-xmpp/issues/166#issuecomment-1884898526
DIGEST-MD5 is obsolete for a long time now.
2024-01-10 22:41:08 +01:00
Martin
8531e2e36a
improve no more auth err msg ()
Improve error message when no viable authentication method is available
2024-01-10 16:17:02 +01:00
Martin
e7d5b17113
Readability improvements. ()
* Improve readability of switch statement for auth mechanism choice

We have enough space in the width here, so it is not
necessary to span the cases over two lines.
2024-01-10 16:04:40 +01:00
Martin
c1b9689e75
Change import path to xmppo/go-xmpp ()
* Change import path to xmppo/go-xmpp
2024-01-10 15:52:41 +01:00
Martin
424970d23c
Fix manual choice of auth mechanism. () 2024-01-10 15:49:08 +01:00
Martin
5fdcf18a81
Simplify authentication choice code. ()
* Simplify authentication choice code.

Should be a lot easier to read and understand now.
2024-01-10 14:53:36 +01:00
Martin
794ed98f9f
Provide access to xml:lang information. () 2024-01-10 13:57:27 +01:00
Martin
2f9bd427e8
Merge pull request from mdosch/master
Update go.mod
2024-01-10 13:18:48 +01:00
Martin Dosch
70c2fe6900 Update dependencies. 2024-01-10 13:17:25 +01:00
Martin
39f5b80375
Authentication improvements. ()
* Add XEP-0474 support.
* Add missing error handling.
2024-01-09 10:24:56 +01:00
Martin Dosch
2449f4192b Remove debugging stuff.
Remove previously overlooked println.
2024-01-08 19:48:13 +01:00
Martin Dosch
3462085098 Add missing error handling. 2024-01-08 19:32:08 +01:00
Martin Dosch
6c9243326e Add XEP-0474 support. 2024-01-08 19:30:17 +01:00
Martin
31c7eb6919
Merge pull request from mdosch/rework-newlines
Harmonize newlines
2023-11-11 15:10:53 +01:00
Martin Dosch
9dcf67c0ad Merge branch 'master' into rework-newlines 2023-11-11 14:37:59 +01:00
Martin
4c385a334c
Add SCRAM PLUS variants. () 2023-11-11 21:08:17 +09:00
Yasuhiro Matsumoto
24e0f536cb
add go.mod 2023-11-11 21:04:41 +09:00
Martin
a6b124c9b2
Fix typo. () 2023-09-24 23:18:19 +09:00
Martin Dosch
6138e9dbe5 Harmonize newlines
Now there should be no more newlines in between any stanza and a
newline after every stanza.
This should not affect functionality but is looking better if
stanzas are printed for debugging.
2023-08-14 10:28:33 +02:00
Martin
98ff0d4df7
Rework printing of sent stanzas when debug is enabled ()
* Rework printing of sent stanzas when debug is enabled

This got reworked to also work with multiple connections
as pointed out by @vcabbage in
https://github.com/mattn/go-xmpp/pull/141#issuecomment-1557334066

* Remove StanzaWriter.
2023-07-28 23:42:12 +09:00
Martin
bef3e549f7
add scram auth ()
* Fix syntax errors.

* gofmt

* Add SCRAM-SHA-1, SCRAM-SHA-256 and SCRAM-SHA-512 auth
2023-05-21 16:26:59 +09:00
Martin
9129a110df
fix syntax errors ()
* Fix syntax errors.

* gofmt
2023-03-03 00:20:58 +09:00
PapaTutuWawa
d72a0f3154
Implement Disco queries against other entities ()
* Improve support for XEP-0030

This commit allows the user to query information about the server
or a node belonging to the server as per XEP-0030.

* Fix broken PubSub functionality
2023-03-02 13:23:29 +09:00
Martin
9fc0b1236c
Print sent stanzas in debug mode. ()
* Print sent stanzas in debug mode.

* Remove unnecessary newline.
2023-03-02 13:22:44 +09:00
Martin
05cd75074a
success msg ()
* Remove unnecessary newline.

* Make success content available.

Closes .
2023-03-02 13:20:49 +09:00
vakalmikov
369824c83a
Update xmpp_subscription.go ()
https://www.rfc-editor.org/rfc/rfc6121.html#section-3.3.1
2023-03-02 10:32:52 +09:00
Martin
2eb234970c
Remove unnecessary newline. () 2022-07-13 07:17:24 +09:00
Martin
3b26f73300
[codespell] Fix typo. () 2022-07-11 02:58:21 +09:00
milampi
1411b9cc8b
Add xml attribute support for XMLElement ()
* Save attributes of the xml element

* Update unittest to check xml attributes
2022-05-13 17:24:06 +09:00
Martin
99ddfc1aa4
Return all pubsub IQs. ()
* Return all pubsub IQs.

This makes other pubsub requests accessible via
client.Recv().

* Fix formatting (gofmt).
2022-04-10 14:46:12 +09:00
Martin
e773596ea0
Provide error replies for IQs. ()
This should fix .
2022-03-19 22:58:56 +09:00
Polynomdivision
912ba61489
Prevent crash in avatar code ()
* Prevent crash on empty urn:xmpp:avatar:* nodes

* Fix issue with errors

* Add a test for empty avatar pubsub items
2021-10-30 00:14:15 +09:00
Josh Martin
3871461df9
Update xmpp_information_query.go ()
Fix a typo in the code comments.
2021-07-23 11:55:38 +09:00
tytan652
db1339b3a5
Fix host with anonymous connection () 2021-07-22 23:17:14 +09:00
marzzzello
b40e129499
use ServerName to verify tls hostname () 2021-01-21 17:27:23 +09:00
Steven Santos Erenst
42ee290fc5
Add the ability to customize the connection timeout ()
Fixes 
2021-01-21 17:26:29 +09:00
Steven Santos Erenst
da2b7586cd
Avoid creating copies of locks ()
tls.Config contains fields of type sync.Once and sync.RWMutex. My understanding
is that if the copy happens to occur while the lock is in a locked state, the
lock will remain locked indefinitely and cause a deadlock. Instead use
tls.Config.Clone() to create a shallow copy.

Also the lock copy made `go vet` upset:

$ go vet ./...
./xmpp.go:242:17: assignment copies lock value to newconfig: crypto/tls.Config contains sync.Once contains sync.Mutex
./xmpp.go:530:9: assignment copies lock value to *tc: crypto/tls.Config contains sync.Once contains sync.Mutex
2021-01-21 17:25:57 +09:00
Alexander
37fa6ef92f
Implement XEP-0084 (User Avatar) ()
* Implement XEP-0084 (User Avatar)

* Fix style with gofmt
2021-01-21 17:24:39 +09:00
Alexander
899ef71e80
Implement a bit of XEP-0060 (PubSub) ()
This squashed series of commits implements basic
PubSub functionality like requesting data or
subscribing to a PubSub node.
2020-03-09 18:10:41 +09:00
Qais Patankar
3e4868bd3e
Implement OOB in Send() and add SendOOB() function for messages without body ()
Co-authored-by: Qais Patankar <qaisjp@gmail.com>

Co-authored-by: ValdikSS <iam@valdikss.org.ru>
2020-03-09 18:10:06 +09:00
harald-mueller
a86b6abcb3 Proxy handling / additional send method ()
* respect enviroment var no_proxy

* add method to send IQ messages without <query> element

* check also for uppercase NO_PROXY  env

* Uppercase NO_PROXY takes precedence over no_proxy as in HTTP_PROXY

* add comments

* revert copyright to the original one
2020-01-29 00:58:07 +09:00
mattn
ac4c216a42
Merge pull request from kjx98/master
implement DNS SRV lookup for NewClient method
2020-01-29 00:56:32 +09:00
mattn
6093f50721
Merge pull request from eaglerayp/feature/noTLS
Fix client no StartTLS & server no required
2019-01-24 18:32:44 +09:00
rayshih
1f614e5b8d Fix client no StartTLS & server no required 2019-01-24 15:48:01 +08:00
Jesse Kuang
ef6a1a617c keep IQ struct unchange 2019-01-15 10:53:08 +08:00
Jesse Kuang
65fd08aee2 mv xmpp_get_info to other repo 2019-01-12 14:46:18 +08:00
Jesse Kuang
a79a0e59ef remove GNUmakefile .gitignore
mv new example.go to other repo
2019-01-11 23:22:15 +08:00
Jesse Kuang
5709ddefa8 move IQ stuff to xmpp_get_info and example 2019-01-11 11:20:54 +08:00
Jesse Kuang
51b558cd2c add urn:xmpp:time; now response jabber GetInfo 2019-01-10 23:36:50 +08:00
Jesse Kuang
66c008d798 add iq:version, iq:last 2019-01-10 22:53:01 +08:00
Jesse Kuang
224305b3ef test with local prosody without conferenc 2019-01-10 20:43:44 +08:00
Jesse Kuang
1e7b50b41c add conference support 2019-01-10 14:46:50 +08:00
Jesse Kuang
c18873b880 fix query roster
process subscription="remove" roster
improve roster process
2019-01-10 10:12:39 +08:00
Jesse Kuang
2c5079ea28 fix param of tlsconn.VerifyHostname 2019-01-09 15:35:32 +08:00
Jesse Kuang
113d9c0420 implement DNS SRV lookup 2019-01-09 13:52:43 +08:00
Yasuhiro Matsumoto
e543ad3fcd
go fmt 2018-05-05 20:33:05 +09:00
Richard Phillips
4fdbee9ac5
Add 'id' to outgoing message using cnonce 2018-05-05 20:32:25 +09:00
mattn
8a5843171f
Merge pull request from frankbraun/debug
introduce DebugWriter
2018-04-23 11:14:11 +09:00
Frank Braun
04ea54f191 introduce DebugWriter
This allows to use a different writer than os.Stderr to write debugging
output to.
2018-04-22 21:35:30 +00:00
14 changed files with 1827 additions and 316 deletions

View File

@ -3,4 +3,4 @@ go-xmpp
go xmpp library (original was written by russ cox )
[Documentation](https://godoc.org/github.com/mattn/go-xmpp)
[Documentation](https://godoc.org/github.com/xmppo/go-xmpp)

View File

@ -2,11 +2,12 @@ package main
import (
"crypto/tls"
"github.com/mattn/go-gtk/gtk"
"github.com/mattn/go-xmpp"
"log"
"os"
"strings"
"github.com/matterbridge/go-xmpp"
"github.com/mattn/go-gtk/gtk"
)
func main() {

View File

@ -5,20 +5,23 @@ import (
"crypto/tls"
"flag"
"fmt"
"github.com/mattn/go-xmpp"
"log"
"os"
"strings"
"github.com/matterbridge/go-xmpp"
)
var server = flag.String("server", "talk.google.com:443", "server")
var username = flag.String("username", "", "username")
var password = flag.String("password", "", "password")
var status = flag.String("status", "xa", "status")
var statusMessage = flag.String("status-msg", "I for one welcome our new codebot overlords.", "status message")
var notls = flag.Bool("notls", false, "No TLS")
var debug = flag.Bool("debug", false, "debug output")
var session = flag.Bool("session", false, "use server session")
var (
server = flag.String("server", "talk.google.com:443", "server")
username = flag.String("username", "", "username")
password = flag.String("password", "", "password")
status = flag.String("status", "xa", "status")
statusMessage = flag.String("status-msg", "I for one welcome our new codebot overlords.", "status message")
notls = flag.Bool("notls", false, "No TLS")
debug = flag.Bool("debug", false, "debug output")
session = flag.Bool("session", false, "use server session")
)
func serverName(host string) string {
return strings.Split(host, ":")[0]
@ -48,7 +51,8 @@ func main() {
var talk *xmpp.Client
var err error
options := xmpp.Options{Host: *server,
options := xmpp.Options{
Host: *server,
User: *username,
Password: *password,
NoTLS: *notls,
@ -59,7 +63,6 @@ func main() {
}
talk, err = options.NewClient()
if err != nil {
log.Fatal(err)
}

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module github.com/matterbridge/go-xmpp
go 1.21.5
require (
golang.org/x/crypto v0.23.0
golang.org/x/net v0.25.0
)

4
go.sum Normal file
View File

@ -0,0 +1,4 @@
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=

1415
xmpp.go

File diff suppressed because it is too large Load Diff

125
xmpp_avatar.go Normal file
View File

@ -0,0 +1,125 @@
package xmpp
import (
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"encoding/xml"
"errors"
"strconv"
)
const (
XMPPNS_AVATAR_PEP_DATA = "urn:xmpp:avatar:data"
XMPPNS_AVATAR_PEP_METADATA = "urn:xmpp:avatar:metadata"
)
type clientAvatarData struct {
XMLName xml.Name `xml:"data"`
Data []byte `xml:",innerxml"`
}
type clientAvatarInfo struct {
XMLName xml.Name `xml:"info"`
Bytes string `xml:"bytes,attr"`
Width string `xml:"width,attr"`
Height string `xml:"height,attr"`
ID string `xml:"id,attr"`
Type string `xml:"type,attr"`
URL string `xml:"url,attr"`
}
type clientAvatarMetadata struct {
XMLName xml.Name `xml:"metadata"`
XMLNS string `xml:"xmlns,attr"`
Info clientAvatarInfo `xml:"info"`
}
type AvatarData struct {
Data []byte
From string
}
type AvatarMetadata struct {
From string
Bytes int
Width int
Height int
ID string
Type string
URL string
}
func handleAvatarData(itemsBody []byte, from, id string) (AvatarData, error) {
var data clientAvatarData
err := xml.Unmarshal(itemsBody, &data)
if err != nil {
return AvatarData{}, err
}
// Base64-decode the avatar data to check its SHA1 hash
dataRaw, err := base64.StdEncoding.DecodeString(
string(data.Data))
if err != nil {
return AvatarData{}, err
}
hash := sha1.Sum(dataRaw)
hashStr := hex.EncodeToString(hash[:])
if hashStr != id {
return AvatarData{}, errors.New("SHA1 hashes do not match")
}
return AvatarData{
Data: dataRaw,
From: from,
}, nil
}
func handleAvatarMetadata(body []byte, from string) (AvatarMetadata, error) {
var meta clientAvatarMetadata
err := xml.Unmarshal(body, &meta)
if err != nil {
return AvatarMetadata{}, err
}
return AvatarMetadata{
From: from,
Bytes: atoiw(meta.Info.Bytes),
Width: atoiw(meta.Info.Width),
Height: atoiw(meta.Info.Height),
ID: meta.Info.ID,
Type: meta.Info.Type,
URL: meta.Info.URL,
}, nil
}
// A wrapper for atoi which just returns -1 if an error occurs
func atoiw(str string) int {
i, err := strconv.Atoi(str)
if err != nil {
return -1
}
return i
}
func (c *Client) AvatarSubscribeMetadata(jid string) {
c.PubsubSubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
}
func (c *Client) AvatarUnsubscribeMetadata(jid string) {
c.PubsubUnsubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
}
func (c *Client) AvatarRequestData(jid string) {
c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_DATA, jid)
}
func (c *Client) AvatarRequestDataByID(jid, id string) {
c.PubsubRequestItem(XMPPNS_AVATAR_PEP_DATA, jid, id)
}
func (c *Client) AvatarRequestMetadata(jid string) {
c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_METADATA, jid)
}

99
xmpp_disco.go Normal file
View File

@ -0,0 +1,99 @@
package xmpp
import (
"encoding/xml"
)
const (
XMPPNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items"
XMPPNS_DISCO_INFO = "http://jabber.org/protocol/disco#info"
)
type clientDiscoFeature struct {
XMLName xml.Name `xml:"feature"`
Var string `xml:"var,attr"`
}
type clientDiscoIdentity struct {
XMLName xml.Name `xml:"identity"`
Category string `xml:"category,attr"`
Type string `xml:"type,attr"`
Name string `xml:"name,attr"`
}
type clientDiscoQuery struct {
XMLName xml.Name `xml:"query"`
Features []clientDiscoFeature `xml:"feature"`
Identities []clientDiscoIdentity `xml:"identity"`
}
type clientDiscoItem struct {
XMLName xml.Name `xml:"item"`
Jid string `xml:"jid,attr"`
Node string `xml:"node,attr"`
Name string `xml:"name,attr"`
}
type clientDiscoItemsQuery struct {
XMLName xml.Name `xml:"query"`
Items []clientDiscoItem `xml:"item"`
}
type DiscoIdentity struct {
Category string
Type string
Name string
}
type DiscoItem struct {
Jid string
Name string
Node string
}
type DiscoResult struct {
Features []string
Identities []DiscoIdentity
}
type DiscoItems struct {
Jid string
Items []DiscoItem
}
func clientFeaturesToReturn(features []clientDiscoFeature) []string {
var ret []string
for _, feature := range features {
ret = append(ret, feature.Var)
}
return ret
}
func clientIdentitiesToReturn(identities []clientDiscoIdentity) []DiscoIdentity {
var ret []DiscoIdentity
for _, id := range identities {
ret = append(ret, DiscoIdentity{
Category: id.Category,
Type: id.Type,
Name: id.Name,
})
}
return ret
}
func clientDiscoItemsToReturn(items []clientDiscoItem) []DiscoItem {
var ret []DiscoItem
for _, item := range items {
ret = append(ret, DiscoItem{
Jid: item.Jid,
Name: item.Name,
Node: item.Node,
})
}
return ret
}

View File

@ -5,27 +5,45 @@ import (
"strconv"
)
const IQTypeGet = "get"
const IQTypeSet = "set"
const IQTypeResult = "result"
const (
IQTypeGet = "get"
IQTypeSet = "set"
IQTypeResult = "result"
)
func (c *Client) Discovery() (string, error) {
const namespace = "http://jabber.org/protocol/disco#items"
// use getCookie for a pseudo random id.
// use UUIDv4 for a pseudo random id.
reqID := strconv.FormatUint(uint64(getCookie()), 10)
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, namespace, "")
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, XMPPNS_DISCO_ITEMS, "")
}
// Discover information about a node
func (c *Client) DiscoverNodeInfo(node string) (string, error) {
query := fmt.Sprintf("<query xmlns='%s' node='%s'/>", XMPPNS_DISCO_INFO, node)
return c.RawInformation(c.jid, c.domain, "info3", IQTypeGet, query)
}
// Discover items that the server exposes
func (c *Client) DiscoverServerItems() (string, error) {
return c.DiscoverEntityItems(c.domain)
}
// Discover items that an entity exposes
func (c *Client) DiscoverEntityItems(jid string) (string, error) {
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_ITEMS)
return c.RawInformation(c.jid, jid, "info1", IQTypeGet, query)
}
// RawInformationQuery sends an information query request to the server.
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>"
_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>\n"
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
return id, err
}
// rawInformation send a IQ request with the the payload body to the server
// rawInformation send a IQ request with the payload body to the server
func (c *Client) RawInformation(from, to, id, iqType, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>"
_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>\n"
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
return id, err
}

View File

@ -8,9 +8,9 @@
package xmpp
import (
"errors"
"fmt"
"time"
"errors"
)
const (
@ -25,7 +25,7 @@ const (
// Send sends room topic wrapped inside an XMPP message stanza body.
func (c *Client) SendTopic(chat Chat) (n int, err error) {
return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>\n",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}
@ -33,10 +33,10 @@ func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) {
if nick == "" {
nick = c.jid
}
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxchars='0'/></x>\n"+
"</presence>",
"<history maxchars='0'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC)
}
@ -47,34 +47,34 @@ func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_da
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s' />\n" +
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s' />"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC)
case CharHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history maxchars='%d'/></x>\n"+
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxchars='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case StanzaHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxstanzas='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SecondsHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history seconds='%d'/></x>\n"+
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history seconds='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history since='%s'/></x>\n" +
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history since='%s'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339))
}
}
@ -88,40 +88,40 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>" +
"</x>\n" +
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"</x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
case CharHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history maxchars='%d'/></x>\n"+
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history maxchars='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case StanzaHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history maxstanzas='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SecondsHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history seconds='%d'/></x>\n"+
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history seconds='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history since='%s'/></x>\n" +
"</presence>",
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history since='%s'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
}
}
@ -130,6 +130,6 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ
// xep-0045 7.14
func (c *Client) LeaveMUC(jid string) (n int, err error) {
return fmt.Fprintf(c.conn, "<presence from='%s' to='%s' type='unavailable' />",
return fmt.Fprintf(c.stanzaWriter, "<presence from='%s' to='%s' type='unavailable' />\n",
c.jid, xmlEscape(jid))
}

View File

@ -11,23 +11,23 @@ func (c *Client) PingC2S(jid, server string) error {
if server == "" {
server = c.domain
}
_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='c2s1' type='get'>"+
"<ping xmlns='urn:xmpp:ping'/>"+
"</iq>\n",
xmlEscape(jid), xmlEscape(server))
return err
}
func (c *Client) PingS2S(fromServer, toServer string) error {
_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='s2s1' type='get'>"+
"<ping xmlns='urn:xmpp:ping'/>"+
"</iq>\n",
xmlEscape(fromServer), xmlEscape(toServer))
return err
}
func (c *Client) SendResultPing(id, toServer string) error {
_, err := fmt.Fprintf(c.conn, "<iq type='result' to='%s' id='%s'/>",
_, err := fmt.Fprintf(c.stanzaWriter, "<iq type='result' to='%s' id='%s'/>\n",
xmlEscape(toServer), xmlEscape(id))
return err
}

128
xmpp_pubsub.go Normal file
View File

@ -0,0 +1,128 @@
package xmpp
import (
"encoding/xml"
"fmt"
)
const (
XMPPNS_PUBSUB = "http://jabber.org/protocol/pubsub"
XMPPNS_PUBSUB_EVENT = "http://jabber.org/protocol/pubsub#event"
)
type clientPubsubItem struct {
XMLName xml.Name `xml:"item"`
ID string `xml:"id,attr"`
Body []byte `xml:",innerxml"`
}
type clientPubsubItems struct {
XMLName xml.Name `xml:"items"`
Node string `xml:"node,attr"`
Items []clientPubsubItem `xml:"item"`
}
type clientPubsubEvent struct {
XMLName xml.Name `xml:"event"`
XMLNS string `xml:"xmlns,attr"`
Items clientPubsubItems `xml:"items"`
}
type clientPubsubError struct {
XMLName xml.Name
}
type clientPubsubSubscription struct {
XMLName xml.Name `xml:"subscription"`
Node string `xml:"node,attr"`
JID string `xml:"jid,attr"`
SubID string `xml:"subid,attr"`
}
type PubsubEvent struct {
Node string
Items []PubsubItem
}
type PubsubSubscription struct {
SubID string
JID string
Node string
Errors []string
}
type PubsubUnsubscription PubsubSubscription
type PubsubItem struct {
ID string
InnerXML []byte
}
type PubsubItems struct {
Node string
Items []PubsubItem
}
// Converts []clientPubsubItem to []PubsubItem
func pubsubItemsToReturn(items []clientPubsubItem) []PubsubItem {
var tmp []PubsubItem
for _, i := range items {
tmp = append(tmp, PubsubItem{
ID: i.ID,
InnerXML: i.Body,
})
}
return tmp
}
func pubsubClientToReturn(event clientPubsubEvent) PubsubEvent {
return PubsubEvent{
Node: event.Items.Node,
Items: pubsubItemsToReturn(event.Items.Items),
}
}
func pubsubStanza(body string) string {
return fmt.Sprintf("<pubsub xmlns='%s'>%s</pubsub>",
XMPPNS_PUBSUB, body)
}
func pubsubSubscriptionStanza(node, jid string) string {
body := fmt.Sprintf("<subscribe node='%s' jid='%s'/>",
xmlEscape(node),
xmlEscape(jid))
return pubsubStanza(body)
}
func pubsubUnsubscriptionStanza(node, jid string) string {
body := fmt.Sprintf("<unsubscribe node='%s' jid='%s'/>",
xmlEscape(node),
xmlEscape(jid))
return pubsubStanza(body)
}
func (c *Client) PubsubSubscribeNode(node, jid string) {
c.RawInformation(c.jid,
jid,
"sub1",
"set",
pubsubSubscriptionStanza(node, c.jid))
}
func (c *Client) PubsubUnsubscribeNode(node, jid string) {
c.RawInformation(c.jid,
jid,
"unsub1",
"set",
pubsubUnsubscriptionStanza(node, c.jid))
}
func (c *Client) PubsubRequestLastItems(node, jid string) {
body := fmt.Sprintf("<items node='%s'/>", node)
c.RawInformation(c.jid, jid, "items1", "get", pubsubStanza(body))
}
func (c *Client) PubsubRequestItem(node, jid, id string) {
body := fmt.Sprintf("<items node='%s'><item id='%s'/></items>", node, id)
c.RawInformation(c.jid, jid, "items3", "get", pubsubStanza(body))
}

View File

@ -5,16 +5,21 @@ import (
)
func (c *Client) ApproveSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>",
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribed'/>\n",
xmlEscape(jid))
}
func (c *Client) RevokeSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>",
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='unsubscribed'/>\n",
xmlEscape(jid))
}
func (c *Client) RetrieveSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribe'/>\n",
xmlEscape(jid))
}
func (c *Client) RequestSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>",
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribe'/>\n",
xmlEscape(jid))
}

View File

@ -85,12 +85,14 @@ func TestStanzaError(t *testing.T) {
"\n\t\t\n\t\t\n\t",
},
OtherElem: []XMLElement{
XMLElement{
{
XMLName: xml.Name{Space: "google:mobile:data", Local: "gcm"},
Attr: []xml.Attr{{Name: xml.Name{Space: "", Local: "xmlns"}, Value: "google:mobile:data"}},
InnerXML: "\n\t\t{\"random\": \"&lt;text&gt;\"}\n\t",
},
XMLElement{
{
XMLName: xml.Name{Space: "jabber:client", Local: "error"},
Attr: []xml.Attr{{Name: xml.Name{Space: "", Local: "code"}, Value: "400"}, {Name: xml.Name{Space: "", Local: "type"}, Value: "modify"}},
InnerXML: `
<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
@ -114,3 +116,28 @@ func TestEOFError(t *testing.T) {
t.Errorf("Recv() did not return io.EOF on end of input stream")
}
}
var emptyPubSub = strings.TrimSpace(`
<iq xmlns="jabber:client" type='result' from='juliet@capulet.lit' id='items3'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<items node='urn:xmpp:avatar:data'></items>
</pubsub>
</iq>
`)
func TestEmptyPubsub(t *testing.T) {
var c Client
c.conn = tConnect(emptyPubSub)
c.p = xml.NewDecoder(c.conn)
m, err := c.Recv()
switch m.(type) {
case AvatarData:
if err == nil {
t.Errorf("Expected an error to be returned")
}
default:
t.Errorf("Recv() = %v", m)
t.Errorf("Expected a return value of AvatarData")
}
}