Upgrade to ejabberd-1.1.3
Lars Strojny

Lars Strojny commited on 2007-03-18 16:29:14
Zeige 12 geänderte Dateien mit 1682 Einfügungen und 6 Löschungen.

... ...
@@ -51,9 +51,11 @@ DEPEND=">=net-im/jabber-base-0.0
51 51
 	
52 52
 RDEPEND=">=dev-python/twisted-1.3.0
53 53
 	>=dev-python/twisted-words-0.1.0
54
+	<dev-python/twisted-words-0.4
54 55
 	>=dev-python/twisted-xish-0.1.0
55
-	>=dev-python/twisted-web-0.5.0
56
+	=dev-python/twisted-web-0.5*
56 57
 	>=dev-python/nevow-0.4.1
58
+	<dev-python/nevow-0.8
57 59
 	>=dev-python/imaging-1.1"
58 60
 
59 61
 PYTRANSPORT_CONFIG=config_example.xml
... ...
@@ -42,20 +42,49 @@ AUX ejabberd-1.1.2.initd 1260 RMD160 0565503966512228f21079213a8587d7ada7a823 SH
42 42
 MD5 608e482a70b1cd9a1a78d721edc8d42d files/ejabberd-1.1.2.initd 1260
43 43
 RMD160 0565503966512228f21079213a8587d7ada7a823 files/ejabberd-1.1.2.initd 1260
44 44
 SHA256 cd06a6efdefda36993eb82fb6617a9a7e65228941f4deac42d583e96b1d6dfc9 files/ejabberd-1.1.2.initd 1260
45
+AUX ejabberd-1.1.3-http_binding.patch 29551 RMD160 6c1c9bc7516a5d4a13220be5d7508348aca1128a SHA1 d65a1fdf14c74154b25778e495568aeec6dcad77 SHA256 c55992c2b6e9ca36d9687237b990c27c47511bbe025d746d2380f122fecbda32
46
+MD5 8e25a0686567a645914aa51440e7d521 files/ejabberd-1.1.3-http_binding.patch 29551
47
+RMD160 6c1c9bc7516a5d4a13220be5d7508348aca1128a files/ejabberd-1.1.3-http_binding.patch 29551
48
+SHA256 c55992c2b6e9ca36d9687237b990c27c47511bbe025d746d2380f122fecbda32 files/ejabberd-1.1.3-http_binding.patch 29551
49
+AUX ejabberd-1.1.3-mod_irc-utf-8.patch 369 RMD160 11b3a06b4f8af77c6c368aeacfe8943eace50da5 SHA1 123a95c4535102aaa076435c464025e40f9fb889 SHA256 a3884a97f9e4239df643ac327c48baf78f77c9cffbd5d67f39f018a811dcdc9f
50
+MD5 3ca38149e85443cac4dfc58ca8bf1c08 files/ejabberd-1.1.3-mod_irc-utf-8.patch 369
51
+RMD160 11b3a06b4f8af77c6c368aeacfe8943eace50da5 files/ejabberd-1.1.3-mod_irc-utf-8.patch 369
52
+SHA256 a3884a97f9e4239df643ac327c48baf78f77c9cffbd5d67f39f018a811dcdc9f files/ejabberd-1.1.3-mod_irc-utf-8.patch 369
53
+AUX ejabberd-1.1.3-mysql-connect-utf8.patch 622 RMD160 36bf34ba4a584ed19a4f93f709df0cfeb7d0ad7b SHA1 3fd01b6d6782bea7feeb3f784f393c04a7b7022c SHA256 747fbd494d6ebb75d5b73d407be10b0dba10f59a4a46561509c788d72a30ff08
54
+MD5 2b686065c6f72505ae252cdb5c7bebe8 files/ejabberd-1.1.3-mysql-connect-utf8.patch 622
55
+RMD160 36bf34ba4a584ed19a4f93f709df0cfeb7d0ad7b files/ejabberd-1.1.3-mysql-connect-utf8.patch 622
56
+SHA256 747fbd494d6ebb75d5b73d407be10b0dba10f59a4a46561509c788d72a30ff08 files/ejabberd-1.1.3-mysql-connect-utf8.patch 622
57
+AUX ejabberd-1.1.3-proxy65.patch 13293 RMD160 cdd06d4ca07773349ad4d97d854cff8b5547c8e2 SHA1 a6d9000a8b751efb1045830544a9e9a89e8122da SHA256 ade6a713cdb57c74ee29063104e4cf44476bb7fb5e63b3ea9f1353c20e9813aa
58
+MD5 6284b9ed2a52690a62fbdca052c5a571 files/ejabberd-1.1.3-proxy65.patch 13293
59
+RMD160 cdd06d4ca07773349ad4d97d854cff8b5547c8e2 files/ejabberd-1.1.3-proxy65.patch 13293
60
+SHA256 ade6a713cdb57c74ee29063104e4cf44476bb7fb5e63b3ea9f1353c20e9813aa files/ejabberd-1.1.3-proxy65.patch 13293
61
+AUX ejabberd-1.1.3.confd 671 RMD160 e727a01c6e0418468bd785447b85d349a536f3ea SHA1 e40c4e24ed1976a781b9557d8ba186fb0fd5cda2 SHA256 fb8f66daaa4bf9063867b80ac6e677be7f6739d3c245bf389467df2ea687887d
62
+MD5 03cdfdbfb294de6eccffda248d349040 files/ejabberd-1.1.3.confd 671
63
+RMD160 e727a01c6e0418468bd785447b85d349a536f3ea files/ejabberd-1.1.3.confd 671
64
+SHA256 fb8f66daaa4bf9063867b80ac6e677be7f6739d3c245bf389467df2ea687887d files/ejabberd-1.1.3.confd 671
65
+AUX ejabberd-1.1.3.initd 1260 RMD160 0565503966512228f21079213a8587d7ada7a823 SHA1 09d2ca4ab1a371fe2a987f649c6761742531c658 SHA256 cd06a6efdefda36993eb82fb6617a9a7e65228941f4deac42d583e96b1d6dfc9
66
+MD5 608e482a70b1cd9a1a78d721edc8d42d files/ejabberd-1.1.3.initd 1260
67
+RMD160 0565503966512228f21079213a8587d7ada7a823 files/ejabberd-1.1.3.initd 1260
68
+SHA256 cd06a6efdefda36993eb82fb6617a9a7e65228941f4deac42d583e96b1d6dfc9 files/ejabberd-1.1.3.initd 1260
45 69
 AUX inetrc 36 RMD160 4b79020864689ede547969610fde18fe490f5810 SHA1 1e0bae0f7251e2ae3b62ba9d3e5cc86bb5dd271e SHA256 0f383befc4c46134d88ce14d3bd06c404ef6575391f4ac0b5e8c28ba383b28fc
46 70
 MD5 e088cd52d4316efddc54195dc939cd24 files/inetrc 36
47 71
 RMD160 4b79020864689ede547969610fde18fe490f5810 files/inetrc 36
48 72
 SHA256 0f383befc4c46134d88ce14d3bd06c404ef6575391f4ac0b5e8c28ba383b28fc files/inetrc 36
49 73
 DIST ejabberd-1.1.1.tar.gz 803278 RMD160 b9c0b7ab3fe1f1b2dce52e1460bba04b313ea534 SHA1 4f23d787afe75c7c866decdff6f539195449776e SHA256 52a97275537073066bd352f5718954f6994b272d1efa51187e17edf0c9b11082
50 74
 DIST ejabberd-1.1.2.tar.gz 836240 RMD160 e763752e6c5fb46c51b71e265ab2ceda6d043a0d SHA1 9e94bdbc10fee5b781405daf43a0b4abc4dee6c1 SHA256 029129a6bcb5d15dbccc5aa756f61c52692eb6882ec7aad0193aa940b6a20bb6
75
+DIST ejabberd-1.1.3.tar.gz 826057 RMD160 99b4c73ae29ef9814f5c572f8e3b3a82d960f5ee SHA1 66b00ca52fa4f4f4e097b0e897c3b5a2e4526603 SHA256 3b8ac67673fa6c08bc25382d3e99171ebc71d4759899eb5a730e65117256e703
51 76
 EBUILD ejabberd-1.1.1.ebuild 5044 RMD160 3c0213f2cd39fdb54505cc0b394b70477b10fbb4 SHA1 491cbd3d3ab8dde27a5280fdbf2783131da4fa7b SHA256 9346b48122eb7812e6beba93e223d0c1d027719877ef7307415edac56b1a1d06
52 77
 MD5 2d16ab0045996aa62fdc6867e8c62edb ejabberd-1.1.1.ebuild 5044
53 78
 RMD160 3c0213f2cd39fdb54505cc0b394b70477b10fbb4 ejabberd-1.1.1.ebuild 5044
54 79
 SHA256 9346b48122eb7812e6beba93e223d0c1d027719877ef7307415edac56b1a1d06 ejabberd-1.1.1.ebuild 5044
55
-EBUILD ejabberd-1.1.2-r5.ebuild 5070 RMD160 81251f4089f5489a80aae24df07fc5d3229b7121 SHA1 d2d30f92242eab59119b93d2d64951bf796bfcb1 SHA256 103e56d7c532126d7dc746473b81ee75dac4ae9e29fa3210639d3b49553d6a5a
56
-MD5 5e30031f8c3d768b9c6e23416ea1f2ab ejabberd-1.1.2-r5.ebuild 5070
57
-RMD160 81251f4089f5489a80aae24df07fc5d3229b7121 ejabberd-1.1.2-r5.ebuild 5070
58
-SHA256 103e56d7c532126d7dc746473b81ee75dac4ae9e29fa3210639d3b49553d6a5a ejabberd-1.1.2-r5.ebuild 5070
80
+EBUILD ejabberd-1.1.2-r5.ebuild 5072 RMD160 d1ad865d489b7856ea9b252ac0fc9444fa812cd7 SHA1 695f4b8715050907072e347ea846b59fe673c181 SHA256 ada01971d7d7b5362ad8b9a839a990eb1b2d8013f7c66fdbba502486b02cbbfb
81
+MD5 27ae991ce72e44d8838c5e7c9f3a5331 ejabberd-1.1.2-r5.ebuild 5072
82
+RMD160 d1ad865d489b7856ea9b252ac0fc9444fa812cd7 ejabberd-1.1.2-r5.ebuild 5072
83
+SHA256 ada01971d7d7b5362ad8b9a839a990eb1b2d8013f7c66fdbba502486b02cbbfb ejabberd-1.1.2-r5.ebuild 5072
84
+EBUILD ejabberd-1.1.3-r1.ebuild 5072 RMD160 d1ad865d489b7856ea9b252ac0fc9444fa812cd7 SHA1 695f4b8715050907072e347ea846b59fe673c181 SHA256 ada01971d7d7b5362ad8b9a839a990eb1b2d8013f7c66fdbba502486b02cbbfb
85
+MD5 27ae991ce72e44d8838c5e7c9f3a5331 ejabberd-1.1.3-r1.ebuild 5072
86
+RMD160 d1ad865d489b7856ea9b252ac0fc9444fa812cd7 ejabberd-1.1.3-r1.ebuild 5072
87
+SHA256 ada01971d7d7b5362ad8b9a839a990eb1b2d8013f7c66fdbba502486b02cbbfb ejabberd-1.1.3-r1.ebuild 5072
59 88
 MISC ChangeLog 1674 RMD160 a7cde836a8d747863d148edef3a9d1b7cf781af2 SHA1 529722882cfb1e1135eda10c99d0aa8e753704a3 SHA256 c37aa58a182a91857395f0c53c3299d19cfe4960d5240673c7606f18199d3681
60 89
 MD5 678fcb919ef8ac4a080934c500537f8d ChangeLog 1674
61 90
 RMD160 a7cde836a8d747863d148edef3a9d1b7cf781af2 ChangeLog 1674
... ...
@@ -70,3 +99,6 @@ SHA256 d3659c3dbc43cf807fb7309da84ba62393e9257a7a16a3305621ee60f97378a6 files/di
70 99
 MD5 a38033cd51c7bb84bc09310952063222 files/digest-ejabberd-1.1.2-r5 244
71 100
 RMD160 d8117ebc19953eb40137b4ebca37d7577ff9e5b6 files/digest-ejabberd-1.1.2-r5 244
72 101
 SHA256 e1ff34ed81425e1b009e60d29d7f6b9bdeff7cdba4407372cfdb8e40ca00efcb files/digest-ejabberd-1.1.2-r5 244
102
+MD5 9b247a2a345b5d2c9cdf1a1678f4edf9 files/digest-ejabberd-1.1.3-r1 244
103
+RMD160 59a1985b119be6cf343af1ea13929c233e59f0b4 files/digest-ejabberd-1.1.3-r1 244
104
+SHA256 5dad3cfcf7b609862a65b6d09c18ed318563573f866e6c19010c7b9ee7836998 files/digest-ejabberd-1.1.3-r1 244
... ...
@@ -9,7 +9,7 @@ JABBER_RUN="/var/run/jabber"
9 9
 JABBER_SPOOL="/var/spool/jabber"
10 10
 JABBER_LOG="/var/log/jabber"
11 11
 
12
-E_MYSQL_V="5"
12
+E_MYSQL_V="0.1"
13 13
 E_MYSQL_N="mysql"
14 14
 E_MYSQL=${E_MYSQL_N}-${E_MYSQL_V}
15 15
 
... ...
@@ -0,0 +1,197 @@
1
+# Copyright 2005-2006 BreakMyGentoo.net
2
+# Distributed under the terms of the GNU General Public License v2
3
+# $Header: $
4
+
5
+inherit eutils multilib ssl-cert versionator
6
+
7
+JABBER_ETC="/etc/jabber"
8
+JABBER_RUN="/var/run/jabber"
9
+JABBER_SPOOL="/var/spool/jabber"
10
+JABBER_LOG="/var/log/jabber"
11
+
12
+E_MYSQL_V="0.1"
13
+E_MYSQL_N="mysql"
14
+E_MYSQL=${E_MYSQL_N}-${E_MYSQL_V}
15
+
16
+E_PGSQL_V="0.0.1"
17
+E_PGSQL_N="pgsql-cvs"
18
+E_PGSQL=${E_PGSQL_N}-${E_PGSQL_V}
19
+
20
+DESCRIPTION="The Erlang Jabber Daemon"
21
+HOMEPAGE="http://ejabberd.jabber.ru/"
22
+SRC_URI="http://process-one.net/en/projects/ejabberd/download/${PV}/${P}.tar.gz"
23
+LICENSE="GPL-2"
24
+SLOT="0"
25
+KEYWORDS="~x86 amd64"
26
+IUSE="${IUSE} irc ldap muc odbc postgres pubsub web mysql httpbind proxy"
27
+
28
+DEPEND="${RDEPEND}
29
+		>=net-im/jabber-base-0.00
30
+		>=dev-libs/expat-1.95
31
+		>=dev-lang/erlang-10.2.0
32
+		odbc? ( dev-db/unixODBC )
33
+		ldap? ( =net-nds/openldap-2* )
34
+		postgres? ( =dev-erl/${E_PGSQL} )
35
+		mysql? ( =dev-erl/${E_MYSQL} )"
36
+
37
+RDEPEND="postgres? ( dev-db/postgresql )
38
+		mysql? ( dev-db/mysql )"
39
+
40
+PROVIDE="virtual/jabber-server"
41
+S=${WORKDIR}/${P}/src
42
+
43
+src_unpack() {
44
+	unpack ${A}
45
+	MYSQL_P=$(best_version dev-db/mysql)
46
+	MYSQL_PV=${MYSQL_P/dev-db\/mysql-/}
47
+
48
+	#
49
+	# If we have to work with MySQL 4.1 or greater, ejabberd's native
50
+	# MySQL-driver has to be patched to query "SET NAMES 'utf8'" on connecting
51
+	# the database.
52
+	#
53
+	if use mysql &&                                                          \
54
+	((                                                                       \
55
+		[ $(get_major_version ${MYSQL_PV}) -eq 4 ] &&                        \
56
+		[ $(get_major_version $(get_after_major_version ${MYSQL_PV})) -ge 1] \
57
+	)                                                                        \
58
+	||                                                                       \
59
+		[ $(get_major_version ${MYSQL_PV}) -ge 5 ]                           \
60
+	); then
61
+		epatch ${FILESDIR}/${P}-mysql-connect-utf8.patch || die
62
+	fi
63
+
64
+	cd ${S}
65
+	if use httpbind; then
66
+		epatch ${FILESDIR}/${P}-http_binding.patch || die
67
+	fi
68
+
69
+	if use proxy; then
70
+		epatch ${FILESDIR}/${P}-proxy65.patch || die 
71
+	fi
72
+
73
+	epatch ${FILESDIR}/${P}-mod_irc-utf-8.patch || die
74
+}
75
+
76
+
77
+src_compile() {
78
+	local myconf
79
+
80
+	if ! use mysql && ! use postgres && ! use odbc; then
81
+		myconf="--disable-odbc"
82
+	else
83
+		myconf="--enable-odbc"
84
+	fi
85
+	
86
+	#
87
+	# configure ejabberd
88
+	#
89
+	econf ${myconf}                          \
90
+		--enable-roster-gateway-workaround   \
91
+		$(use_enable irc mod_irc)            \
92
+		$(use_enable ldap eldap)             \
93
+		$(use_enable muc mod_muc)            \
94
+		$(use_enable pubsub mod_pubsub)      \
95
+		$(use_enable ssl tls)                \
96
+		$(use_enable web web)                \
97
+		|| die "econf failed"
98
+	
99
+	#
100
+	# Build ejabberd core
101
+	#
102
+	emake || die "compiling ejabberd core failed"
103
+}
104
+
105
+src_install() {
106
+	#
107
+	# Install ejabberd
108
+	#
109
+	make                                                   \
110
+		DESTDIR=${D}                                       \
111
+		EJABBERDDIR=${D}/usr/$(get_libdir)/erlang/lib/${P} \
112
+		ETCDIR=${D}${JABBER_ETC}                           \
113
+		LOGDIR=${D}${JABBER_LOG}                           \
114
+	    install \
115
+	    || die "install failed"
116
+
117
+
118
+	insinto /usr/share/doc/${PF}
119
+	use postgres && doins odbc/pg.sql
120
+	use mysql && doins odbc/mysql.sql
121
+	dodoc doc/release_notes_${PV}.txt
122
+	dohtml doc/*.{html,png}
123
+
124
+
125
+	use postgres && {
126
+		pa="-pa /usr/$(get_libdir)/erlang/lib/${E_PGSQL}/ebin"
127
+	}
128
+
129
+	use mysql && {
130
+		pa=${pa}" -pa /usr/$(get_libdir)/erlang/lib/${E_MYSQL}/ebin"
131
+	}
132
+
133
+	#
134
+	# Create /usr/bin/ejabberd
135
+	#
136
+	cat <<EOF > ${T}/ejabberd
137
+#!/bin/bash
138
+
139
+erl -pa /usr/$(get_libdir)/erlang/lib/${P}/ebin \\
140
+	${pa} \\
141
+	-sname ejabberd \\
142
+	-s ejabberd \\
143
+	-ejabberd config \"${JABBER_ETC}/ejabberd.cfg\" \\
144
+	log_path \"${JABBER_LOG}/ejabberd.log\" \\
145
+	-kernel inetrc \"${JABBER_ETC}/inetrc\" \\
146
+	-sasl sasl_error_logger \{file,\"${JABBER_LOG}/sasl.log\"\} \\
147
+	-mnesia dir \"${JABBER_SPOOL}\" \\
148
+	\$@
149
+EOF
150
+
151
+	#
152
+	# Create /usr/bin/ejabberdctl
153
+	#
154
+	cat <<EOF > ${T}/ejabberdctl
155
+#!/bin/sh
156
+
157
+exec env HOME=${JABBER_RUN} \\
158
+	erl -pa /usr/$(get_libdir)/erlang/lib/${P}/ebin \\
159
+		${pa} \\
160
+		-noinput \\
161
+		-sname ejabberdctl \\
162
+		-s ejabberd_ctl \\
163
+		-extra \$@
164
+EOF
165
+	
166
+	dobin ${T}/ejabberdctl
167
+	dobin ${T}/ejabberd
168
+
169
+	newinitd ${FILESDIR}/${P}.initd ${PN}
170
+	newconfd ${FILESDIR}/${P}.confd ${PN}
171
+
172
+	insinto ${JABBER_ETC}
173
+	if use ssl; then
174
+		docert ssl
175
+		rm -f ${D}${JABBER_ETC}/ssl.{crt,csr,key}
176
+		fowners jabber:jabber ${JABBER_ETC}/ssl.pem
177
+	fi
178
+	doins ${FILESDIR}/inetrc
179
+}
180
+
181
+pkg_postinst() {
182
+	if [ ! -e ${JABBER_ETC}/ejabberd.cfg ]
183
+	then
184
+		einfo "Configuration file has been installed in ${JABBER_ETC}/ejabberd.cfg."
185
+		einfo "Edit it according to your needs. For configuration instructions,"
186
+		einfo "please see /usr/share/doc/${PF}/html/guide.html"
187
+	fi
188
+	if use ssl ; then
189
+		einfo "A script to generate a ssl key has been installed in"
190
+		einfo "${JABBER_ETC}/self-cert.sh . Use it and change the config file to"
191
+		einfo "point to the full path"
192
+	fi
193
+	if ! use web ; then
194
+		einfo "The web USE flag is off, this will disable the web admin interface,"
195
+		einfo "if this was not the intention then add web to your USE flags."
196
+	fi
197
+}
... ...
@@ -0,0 +1,3 @@
1
+MD5 bdb39965a147506fc194d5a28117172a ejabberd-1.1.3.tar.gz 826057
2
+RMD160 99b4c73ae29ef9814f5c572f8e3b3a82d960f5ee ejabberd-1.1.3.tar.gz 826057
3
+SHA256 3b8ac67673fa6c08bc25382d3e99171ebc71d4759899eb5a730e65117256e703 ejabberd-1.1.3.tar.gz 826057
... ...
@@ -0,0 +1,3 @@
1
+MD5 5b947e19e18a6b554bf31d1c95176eb6 ejabberd-1.1.2.tar.gz 836240
2
+RMD160 e763752e6c5fb46c51b71e265ab2ceda6d043a0d ejabberd-1.1.2.tar.gz 836240
3
+SHA256 029129a6bcb5d15dbccc5aa756f61c52692eb6882ec7aad0193aa940b6a20bb6 ejabberd-1.1.2.tar.gz 836240
... ...
@@ -0,0 +1,934 @@
1
+Index: src/ejabberd.cfg.example
2
+===================================================================
3
+--- src/ejabberd.cfg.example	(Revision 565)
4
++++ src/ejabberd.cfg.example	(Arbeitskopie)
5
+@@ -124,7 +124,7 @@
6
+   {5269, ejabberd_s2s_in,  [{shaper, s2s_shaper},
7
+ 			    {max_stanza_size, 131072}
8
+ 			   ]},
9
+-  {5280, ejabberd_http,    [http_poll, web_admin]},
10
++  {5280, ejabberd_http,    [http_poll, http_bind, web_admin]},
11
+   {8888, ejabberd_service, [{access, all},
12
+ 			    {hosts, ["icq.localhost", "sms.localhost"],
13
+ 			     [{password, "secret"}]}]}
14
+Index: src/web/Makefile.in
15
+===================================================================
16
+--- src/web/Makefile.in	(Revision 565)
17
++++ src/web/Makefile.in	(Arbeitskopie)
18
+@@ -15,7 +15,8 @@
19
+ 	$(OUTDIR)/ejabberd_http.beam \
20
+ 	$(OUTDIR)/ejabberd_web.beam  \
21
+ 	$(OUTDIR)/ejabberd_web_admin.beam  \
22
+-	$(OUTDIR)/ejabberd_http_poll.beam
23
++	$(OUTDIR)/ejabberd_http_poll.beam \
24
++	$(OUTDIR)/ejabberd_http_bind.beam
25
+ 
26
+ all:    $(OBJS)
27
+ 
28
+Index: src/web/ejabberd_http_bind.erl
29
+===================================================================
30
+--- src/web/ejabberd_http_bind.erl	(Revision 0)
31
++++ src/web/ejabberd_http_bind.erl	(Revision 0)
32
+@@ -0,0 +1,731 @@
33
++%%%----------------------------------------------------------------------
34
++%%% File    : ejabberd_http_bind.erl
35
++%%% Author  : Stefan Strigler <steve@zeank.in-berlin.de>
36
++%%% Purpose : HTTP Binding support (JEP-0124)
37
++%%% Created : 21 Sep 2005 by Stefan Strigler <steve@zeank.in-berlin.de>
38
++%%% Id      : $Id: ejabberd_http_bind.erl,v 1.2 2006/01/16 10:47:50 zeank Exp $
39
++%%%----------------------------------------------------------------------
40
++
41
++-module(ejabberd_http_bind).
42
++-author('steve@zeank.in-berlin.de').
43
++-vsn('Revision: 1.3').
44
++
45
++-behaviour(gen_fsm).
46
++
47
++%% External exports
48
++-export([start_link/2,
49
++	 init/1,
50
++	 handle_event/3,
51
++	 handle_sync_event/4,
52
++	 code_change/4,
53
++	 handle_info/3,
54
++	 terminate/3,
55
++	 send/2,
56
++	 setopts/2,
57
++	 controlling_process/2,
58
++	 close/1,
59
++	 process_request/1]).
60
++
61
++%%-define(ejabberd_debug, true).
62
++
63
++-include("ejabberd.hrl").
64
++-include("jlib.hrl").
65
++-include("ejabberd_http.hrl").
66
++
67
++-record(http_bind, {id, pid, hold, wait}).
68
++
69
++%% http binding request
70
++-record(hbr, {rid,
71
++	      key,
72
++	      in,
73
++	      out}).
74
++
75
++-record(state, {id,
76
++		rid = error,
77
++		key,
78
++		output = "",
79
++		input = "",
80
++		waiting_input = false,
81
++		last_receiver,
82
++		last_poll,
83
++		ctime = 0,
84
++		timer,
85
++		req_list = [] % list of requests
86
++	       }).
87
++
88
++
89
++%-define(DBGFSM, true).
90
++
91
++-ifdef(DBGFSM).
92
++-define(FSMOPTS, [{debug, [trace]}]).
93
++-else.
94
++-define(FSMOPTS, []).
95
++-endif.
96
++
97
++-define(MAX_REQUESTS, 2).  % number of simultaneous requests
98
++-define(MIN_POLLING, "2"). % don't poll faster than that or we will shoot you
99
++-define(MAX_WAIT, 60).     % max num of secs to keep a request on hold
100
++-define(MAX_INACTIVITY, 30000). % msecs to wait before terminating idle sessions
101
++-define(CT, {"Content-Type", "text/xml; charset=utf-8"}).
102
++-define(BAD_REQUEST, ?CT).
103
++
104
++
105
++%%%----------------------------------------------------------------------
106
++%%% API
107
++%%%----------------------------------------------------------------------
108
++start(ID, Key) ->
109
++    mnesia:create_table(http_bind,
110
++        		[{ram_copies, [node()]},
111
++        		 {attributes, record_info(fields, http_bind)}]),
112
++    supervisor:start_child(ejabberd_http_bind_sup, [ID, Key]).
113
++
114
++start_link(ID, Key) ->
115
++    gen_fsm:start_link(?MODULE, [ID, Key], ?FSMOPTS).
116
++
117
++send({http_bind, FsmRef}, Packet) ->
118
++    gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}).
119
++
120
++setopts({http_bind, FsmRef}, Opts) ->
121
++    case lists:member({active, once}, Opts) of
122
++	true ->
123
++	    gen_fsm:sync_send_all_state_event(FsmRef, activate);
124
++	_ ->
125
++	    ok
126
++    end.
127
++
128
++controlling_process(_Socket, _Pid) ->
129
++    ok.
130
++
131
++close({http_bind, FsmRef}) ->
132
++    catch gen_fsm:sync_send_all_state_event(FsmRef, close).
133
++
134
++
135
++process_request(#request{path = [],
136
++			 data = Data}) ->
137
++    case catch parse_request(Data) of
138
++	{ok, ID1, RID, Key, NewKey, Attrs, Packet} ->
139
++	    XmppDomain = xml:get_attr_s("to",Attrs),
140
++	    if 
141
++		(ID1 == "") and (XmppDomain == "") ->
142
++		    {200, [?CT], "<body type='terminate' "
143
++		     "condition='improper-addressing' "
144
++		     "xmlns='http://jabber.org/protocol/httpbind'/>"};
145
++		true ->
146
++	    ID = if
147
++		     (ID1 == "") ->
148
++			 %% create new session
149
++			 NewID = sha:sha(term_to_binary({now(), make_ref()})),
150
++			 {ok, Pid} = start(NewID, Key),
151
++			 Wait = case
152
++				    string:to_integer(xml:get_attr_s("wait",Attrs))
153
++				    of
154
++				    {error, _} ->
155
++					?MAX_WAIT;
156
++				    {CWait, _} ->
157
++					if 
158
++					    (CWait > ?MAX_WAIT) ->
159
++						?MAX_WAIT;
160
++					    true ->
161
++						CWait
162
++					end
163
++				end,
164
++			 Hold = case
165
++				    string:to_integer(
166
++				      xml:get_attr_s("hold",Attrs))
167
++				    of
168
++				    {error, _} ->
169
++					(?MAX_REQUESTS - 1);
170
++				    {CHold, _} ->
171
++					if 
172
++					    (CHold > (?MAX_REQUESTS - 1)) ->
173
++						(?MAX_REQUESTS - 1);
174
++					    true ->
175
++						CHold
176
++					end
177
++				end,
178
++			 mnesia:transaction(
179
++			   fun() ->
180
++				   mnesia:write(#http_bind{id = NewID,
181
++							   pid = Pid,
182
++							   wait = Wait,
183
++							   hold = Hold})
184
++			   end),
185
++			 InPacket = if
186
++					(XmppDomain /= "") ->
187
++					    ["<stream:stream to='",
188
++					     XmppDomain, 
189
++					     "' xmlns='jabber:client' "
190
++					     "xmlns:stream='http://etherx.jabber.org/streams'>"];
191
++					true ->
192
++					    ""
193
++				    end,
194
++			 NewID;
195
++		     true ->
196
++			 %% old session
197
++			 Type = xml:get_attr_s("type",Attrs),
198
++			 Wait = ?MAX_WAIT,
199
++			 Hold = (?MAX_REQUESTS - 1),
200
++			 if 
201
++			     (Type == "terminate") ->
202
++				 %% terminate session
203
++				 InPacket = Packet ++ "</stream:stream>";
204
++			     true ->
205
++				 InPacket = Packet
206
++			 end,
207
++			 ID1
208
++		 end,
209
++%%		    ?DEBUG("~n InPacket: ~s ~n", [InPacket]),
210
++	    case http_put(ID, RID, Key, NewKey, Hold, InPacket) of
211
++		{error, not_exists} ->
212
++		    ?DEBUG("no session associated with sid: ~p", [ID]),
213
++		    {404, [?BAD_REQUEST], ""};
214
++		{error, bad_key} ->
215
++		    ?DEBUG("bad key: ~s", Key),
216
++		    case mnesia:dirty_read({http_bind, ID}) of
217
++			[] ->
218
++			    {404, [?BAD_REQUEST], ""};
219
++			[#http_bind{pid = FsmRef}] ->
220
++			    gen_fsm:sync_send_all_state_event(FsmRef,stop),
221
++			    {404, [?BAD_REQUEST], ""}		    
222
++		    end;
223
++		{error, polling_too_frequently} ->
224
++		    ?DEBUG("polling too frequently: ~p", [ID]),
225
++		    case mnesia:dirty_read({http_bind, ID}) of
226
++			[] -> %% unlikely! (?)
227
++			    {404, [?BAD_REQUEST], ""};
228
++			[#http_bind{pid = FsmRef}] ->
229
++			    gen_fsm:sync_send_all_state_event(FsmRef,stop),
230
++			    {403, [?BAD_REQUEST], ""}		    
231
++		    end;
232
++		{repeat, OutPacket} ->
233
++		    ?DEBUG("http_put said 'repeat!' ...~nOutPacket: ~p", 
234
++			   [OutPacket]),
235
++		    send_outpacket(ID, OutPacket);
236
++		ok ->
237
++		    receive_loop(ID,ID1,RID,Wait,Hold,Attrs)
238
++	    end
239
++	    end;
240
++	_ ->
241
++	    {400, [?BAD_REQUEST], ""}
242
++    end;
243
++process_request(_Request) ->
244
++    {400, [], {xmlelement, "h1", [],
245
++	       [{xmlcdata, "400 Bad Request"}]}}.
246
++
247
++receive_loop(ID,ID1,RID,Wait,Hold,Attrs) ->
248
++    receive
249
++	after 100 -> ok
250
++	end,
251
++    prepare_response(ID,ID1,RID,Wait,Hold,Attrs).
252
++
253
++prepare_response(ID,ID1,RID,Wait,Hold,Attrs) ->
254
++    case http_get(ID,RID) of
255
++	{error, not_exists} ->
256
++            ?DEBUG("no session associated with sid: ~s", ID),
257
++	    {404, [?BAD_REQUEST], ""};
258
++	{ok, keep_on_hold} ->
259
++	    receive_loop(ID,ID1,RID,Wait,Hold,Attrs);
260
++	{ok, cancel} ->
261
++	    %% actually it would be better if we could completely
262
++	    %% cancel this request, but then we would have to hack
263
++	    %% ejabberd_http and I'm too lazy now
264
++	    {404, [?BAD_REQUEST], ""}; 
265
++	{ok, OutPacket} ->
266
++            ?DEBUG("OutPacket: ~s", [OutPacket]),
267
++	    if
268
++		ID == ID1 ->
269
++		    send_outpacket(ID, OutPacket);
270
++		true ->
271
++		    To = xml:get_attr_s("to",Attrs),
272
++		    case xml_stream:parse_element(
273
++			   OutPacket++"</stream:stream>") of
274
++			El when element(1, El) == xmlelement ->
275
++			    {xmlelement, _, OutAttrs, _} = El,
276
++			    AuthID = xml:get_attr_s("id", OutAttrs),
277
++			    StreamError = false;
278
++			{error, _} ->
279
++			    AuthID = "",
280
++			    StreamError = true
281
++		    end,
282
++		    if
283
++			To == "" ->
284
++			    {200, [?CT], "<body type='terminate' "
285
++			     "condition='improper-addressing' "
286
++			     "xmlns='http://jabber.org/protocol/httpbind'/>"};
287
++			StreamError == true ->
288
++			    {200, [?CT], "<body type='terminate' "
289
++			     "condition='host-unknown' "
290
++			     "xmlns='http://jabber.org/protocol/httpbind'/>"};
291
++			true ->
292
++			    {200, [?CT],
293
++			     xml:element_to_string(
294
++			       {xmlelement,"body",
295
++				[{"xmlns",
296
++				  "http://jabber.org/protocol/httpbind"},
297
++				 {"sid",ID},
298
++				 {"wait", integer_to_list(Wait)},
299
++				 {"requests", integer_to_list(Hold+1)},
300
++				 {"inactivity", 
301
++				  integer_to_list(trunc(?MAX_INACTIVITY/1000))},
302
++				 {"polling", ?MIN_POLLING},
303
++				 {"authid", AuthID}
304
++				],[]})}
305
++		    end
306
++	    end
307
++    end.
308
++    
309
++send_outpacket(ID, OutPacket) ->
310
++    case OutPacket of
311
++	"" ->
312
++	    {200, [?CT], "<body xmlns='http://jabber.org/protocol/httpbind'/>"};
313
++	"</stream:stream>" ->
314
++	    case mnesia:dirty_read({http_bind, ID}) of
315
++		[#http_bind{pid = FsmRef}] ->
316
++		    gen_fsm:sync_send_all_state_event(FsmRef,stop)
317
++	    end,
318
++	    {200, [?CT], "<body xmlns='http://jabber.org/protocol/httpbind'/>"};
319
++	_ ->
320
++	    case xml_stream:parse_element("<body>" 
321
++					  ++ OutPacket
322
++					  ++ "</body>") 
323
++		of
324
++		El when element(1, El) == xmlelement ->
325
++		    {xmlelement, _, _, OEls} = El,
326
++		    TypedEls = [xml:replace_tag_attr("xmlns",
327
++						     "jabber:client",OEl) ||
328
++				   OEl <- OEls],
329
++		    ?DEBUG(" --- outgoing data --- ~n~s~n --- END --- ~n",
330
++			   [xml:element_to_string(
331
++			      {xmlelement,"body",
332
++			       [{"xmlns",
333
++				 "http://jabber.org/protocol/httpbind"}],
334
++			       TypedEls})]
335
++			  ),
336
++		    {200, [?CT],
337
++		     xml:element_to_string(
338
++		       {xmlelement,"body",
339
++			[{"xmlns",
340
++			  "http://jabber.org/protocol/httpbind"}],
341
++			TypedEls})};
342
++		{error, _E} ->
343
++                    ?DEBUG("parse error: ~p", [_E]),
344
++                    StreamErrCond = case xml_stream:parse_element(
345
++					   "<stream:stream>"++OutPacket) of
346
++                                        El when element(1, El) == xmlelement ->
347
++                                            {xmlelement, _Tag, _Attr, Els} = El,
348
++                                            [{xmlelement, SE, _, Cond} | _] = Els,
349
++                                            if 
350
++                                                SE == "stream:error" ->
351
++                                                    Cond;
352
++                                                true ->
353
++                                                    null
354
++                                            end;
355
++                                        {error, _E} ->
356
++                                            null
357
++                                    end,
358
++		    case mnesia:dirty_read({http_bind, ID}) of
359
++			[#http_bind{pid = FsmRef}] ->
360
++			    gen_fsm:sync_send_all_state_event(FsmRef,stop);
361
++                        _ ->
362
++                            err %% hu?
363
++		    end,
364
++                    case StreamErrCond of
365
++                        null ->
366
++                            {200, [?CT],
367
++                             "<body type='terminate' "
368
++			     "condition='internal-server-error' "
369
++			     "xmlns='http://jabber.org/protocol/httpbind'/>"};
370
++                        _ ->
371
++                            {200, [?CT],
372
++                             "<body type='terminate' "
373
++                             "condition='remote-stream-error' "
374
++                             "xmlns='http://jabber.org/protocol/httpbind'>" ++
375
++                             elements_to_string(StreamErrCond) ++
376
++                             "</body>"}
377
++                    end
378
++	    end
379
++    end.
380
++
381
++%%%----------------------------------------------------------------------
382
++%%% Callback functions from gen_fsm
383
++%%%----------------------------------------------------------------------
384
++
385
++%%----------------------------------------------------------------------
386
++%% Func: init/1
387
++%% Returns: {ok, StateName, StateData}          |
388
++%%          {ok, StateName, StateData, Timeout} |
389
++%%          ignore                              |
390
++%%          {stop, StopReason}                   
391
++%%----------------------------------------------------------------------
392
++init([ID, Key]) ->
393
++    ?INFO_MSG("started: ~p", [{ID, Key}]),
394
++    Opts = [], % TODO
395
++    {ok, C2SPid} = ejabberd_c2s:start({?MODULE, {http_bind, self()}}, Opts),
396
++    ejabberd_c2s:become_controller(C2SPid),
397
++    Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []),
398
++    {ok, loop, #state{id = ID,
399
++		      key = Key,
400
++		      timer = Timer}}.
401
++
402
++%%----------------------------------------------------------------------
403
++%% Func: StateName/2
404
++%% Returns: {next_state, NextStateName, NextStateData}          |
405
++%%          {next_state, NextStateName, NextStateData, Timeout} |
406
++%%          {stop, Reason, NewStateData}                         
407
++%%----------------------------------------------------------------------
408
++
409
++
410
++%%----------------------------------------------------------------------
411
++%% Func: StateName/3
412
++%% Returns: {next_state, NextStateName, NextStateData}            |
413
++%%          {next_state, NextStateName, NextStateData, Timeout}   |
414
++%%          {reply, Reply, NextStateName, NextStateData}          |
415
++%%          {reply, Reply, NextStateName, NextStateData, Timeout} |
416
++%%          {stop, Reason, NewStateData}                          |
417
++%%          {stop, Reason, Reply, NewStateData}                    
418
++%%----------------------------------------------------------------------
419
++%state_name(Event, From, StateData) ->
420
++%    Reply = ok,
421
++%    {reply, Reply, state_name, StateData}.
422
++
423
++%%----------------------------------------------------------------------
424
++%% Func: handle_event/3
425
++%% Returns: {next_state, NextStateName, NextStateData}          |
426
++%%          {next_state, NextStateName, NextStateData, Timeout} |
427
++%%          {stop, Reason, NewStateData}                         
428
++%%----------------------------------------------------------------------
429
++handle_event(_Event, StateName, StateData) ->
430
++    {next_state, StateName, StateData}.
431
++
432
++%%----------------------------------------------------------------------
433
++%% Func: handle_sync_event/4
434
++%% Returns: {next_state, NextStateName, NextStateData}            |
435
++%%          {next_state, NextStateName, NextStateData, Timeout}   |
436
++%%          {reply, Reply, NextStateName, NextStateData}          |
437
++%%          {reply, Reply, NextStateName, NextStateData, Timeout} |
438
++%%          {stop, Reason, NewStateData}                          |
439
++%%          {stop, Reason, Reply, NewStateData}                    
440
++%%----------------------------------------------------------------------
441
++handle_sync_event({send, Packet}, _From, StateName, StateData) ->
442
++    Output = [StateData#state.output | Packet],
443
++    Reply = ok,
444
++    {reply, Reply, StateName, StateData#state{output = Output}};
445
++
446
++handle_sync_event(activate, From, StateName, StateData) ->
447
++    case StateData#state.input of
448
++	"" ->
449
++	    {reply, ok, StateName, StateData#state{
450
++				     waiting_input = From}};
451
++	Input ->
452
++	    From ! {tcp, {http_bind, self()}, list_to_binary(Input)},
453
++	    {reply, ok, StateName, StateData#state{
454
++				     input = "",
455
++				     waiting_input = false,
456
++				     last_receiver = From}}
457
++    end;
458
++
459
++handle_sync_event(stop, _From, _StateName, StateData) ->
460
++    Reply = ok,
461
++    {stop, normal, Reply, StateData};
462
++
463
++handle_sync_event({http_put, RID, Key, NewKey, Hold, Packet},
464
++		  _From, StateName, StateData) ->
465
++    %% check if RID valid
466
++    RidAllow = case RID of
467
++		   error -> 
468
++		       false;
469
++		   _ ->
470
++		       case StateData#state.rid of
471
++			   error -> 
472
++			       %% first request - nothing saved so far
473
++			       true;
474
++			   OldRID ->
475
++			       ?DEBUG("state.rid/cur rid: ~p/~p", 
476
++				      [OldRID, RID]),
477
++			       if 
478
++				   (OldRID < RID) and 
479
++				   (RID =< (OldRID + Hold + 1)) ->
480
++				       true;
481
++				   (RID =< OldRID) and 
482
++				   (RID > OldRID - Hold - 1) ->
483
++				       repeat;
484
++				   true ->
485
++				       false
486
++			       end
487
++		       end
488
++	       end,
489
++    %% check if key valid
490
++    KeyAllow = case RidAllow of
491
++		   repeat -> 
492
++		       true;
493
++		   false ->
494
++		       false;
495
++		   true ->
496
++		       case StateData#state.key of
497
++			   "" ->
498
++			       true;
499
++			   OldKey ->
500
++			       NextKey = httpd_util:to_lower(
501
++					   hex(binary_to_list(
502
++						 crypto:sha(Key)))),
503
++			       ?DEBUG("Key/OldKey/NextKey: ~s/~s/~s", 
504
++				      [Key, OldKey, NextKey]),
505
++			       if
506
++				   OldKey == NextKey ->
507
++				       true;
508
++				   true ->
509
++				       ?DEBUG("wrong key: ~s",[Key]),
510
++				       false
511
++			       end
512
++		       end
513
++	       end,
514
++    {_,TSec,TMSec} = now(),
515
++    TNow = TSec*1000*1000 + TMSec,
516
++    LastPoll = if 
517
++		   Packet == "" ->
518
++		       TNow;
519
++		   true ->
520
++		       0
521
++	       end,
522
++    {MinPoll, _} = string:to_integer(?MIN_POLLING),
523
++    if
524
++	(Packet == "") and 
525
++	(TNow - StateData#state.last_poll < MinPoll*1000*1000) ->
526
++	    Reply = {error, polling_too_frequently},
527
++	    {reply, Reply, StateName, StateData};
528
++	KeyAllow ->
529
++	    case RidAllow of
530
++		false ->
531
++		    Reply = {error, not_exists},
532
++		    {reply, Reply, StateName, StateData};
533
++		repeat ->
534
++		    ?DEBUG("REPEATING ~p", [RID]),
535
++		    [Out | _XS] = [El#hbr.out || 
536
++				      El <- StateData#state.req_list, 
537
++				      El#hbr.rid == RID],
538
++		    case Out of 
539
++			[[] | OutPacket] ->
540
++			    Reply = {repeat, OutPacket};
541
++			_ ->
542
++			    Reply = {repeat, Out}
543
++		    end,
544
++		    {reply, Reply, StateName, 
545
++		     StateData#state{input = "cancel", last_poll = LastPoll}};
546
++		true ->
547
++		    SaveKey = if 
548
++				  NewKey == "" ->
549
++				      Key;
550
++				  true ->
551
++				      NewKey
552
++			      end,
553
++		    ?DEBUG(" -- SaveKey: ~s~n", [SaveKey]),
554
++
555
++		    %% save request
556
++		    ReqList = [#hbr{rid=RID,
557
++				    key=StateData#state.key,
558
++				    in=StateData#state.input,
559
++				    out=StateData#state.output
560
++				   } | 
561
++			       [El || El <- StateData#state.req_list, 
562
++				      El#hbr.rid < RID, 
563
++				      El#hbr.rid > (RID - 1 - Hold)]
564
++			      ],
565
++%%		    ?DEBUG("reqlist: ~p", [ReqList]),
566
++		    case StateData#state.waiting_input of
567
++			false ->
568
++			    cancel_timer(StateData#state.timer),
569
++			    Timer = erlang:start_timer(
570
++				      ?MAX_INACTIVITY, self(), []),
571
++			    Input = Packet ++ [StateData#state.input],
572
++			    Reply = ok,
573
++			    {reply, Reply, StateName, 
574
++			     StateData#state{input = Input,
575
++					     rid = RID,
576
++					     key = SaveKey,
577
++					     ctime = TNow,
578
++					     timer = Timer,
579
++					     last_poll = LastPoll,
580
++					     req_list = ReqList
581
++					    }};
582
++			{Receiver, _Tag} ->
583
++			    Receiver ! {tcp, {http_bind, self()},
584
++					list_to_binary(Packet)},
585
++			    cancel_timer(StateData#state.timer),
586
++			    Timer = erlang:start_timer(
587
++				      ?MAX_INACTIVITY, self(), []),
588
++			    Reply = ok,
589
++			    {reply, Reply, StateName,
590
++			     StateData#state{waiting_input = false,
591
++					     last_receiver = Receiver,
592
++					     input = "",
593
++					     rid = RID,
594
++					     key = SaveKey,
595
++					     ctime = TNow,
596
++					     timer = Timer,
597
++					     last_poll = LastPoll,
598
++					     req_list = ReqList
599
++					    }}
600
++		    end
601
++	    end;
602
++	true ->
603
++	    Reply = {error, bad_key},
604
++	    {reply, Reply, StateName, StateData}
605
++    end;
606
++
607
++handle_sync_event({http_get, RID, Wait, Hold}, _From, StateName, StateData) ->
608
++    {_,TSec,TMSec} = now(),
609
++    TNow = TSec*1000*1000 + TMSec,
610
++    cancel_timer(StateData#state.timer),
611
++    Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []),
612
++    if 
613
++	(Hold > 0) and 
614
++	(StateData#state.output == "") and 
615
++	((TNow - StateData#state.ctime) < (Wait*1000*1000)) and 
616
++	(StateData#state.rid == RID) and 
617
++	(StateData#state.input /= "cancel") ->
618
++	    Output = StateData#state.output,
619
++	    ReqList = StateData#state.req_list,
620
++	    Reply = {ok, keep_on_hold};
621
++	(StateData#state.input == "cancel") ->
622
++	    Output = StateData#state.output,
623
++	    ReqList = StateData#state.req_list,
624
++	    Reply = {ok, cancel};
625
++	true ->
626
++	    case StateData#state.output of
627
++		[[]| OutPacket] ->
628
++		    Reply = {ok, OutPacket};
629
++		_ ->
630
++		    Reply = {ok, StateData#state.output}
631
++	    end,
632
++	    %% save request
633
++	    ReqList = [#hbr{rid=RID,
634
++			    key=StateData#state.key,
635
++			    in=StateData#state.input,
636
++			    out=StateData#state.output
637
++			   } | 
638
++		       [El || El <- StateData#state.req_list, 
639
++			      El#hbr.rid /= RID ] 
640
++		      ],
641
++	    Output = ""
642
++    end,
643
++    {reply, Reply, StateName, StateData#state{
644
++				input = "",
645
++				output = Output, 
646
++				timer = Timer,
647
++				req_list = ReqList}};
648
++
649
++handle_sync_event(_Event, _From, StateName, StateData) ->
650
++    Reply = ok,
651
++    {reply, Reply, StateName, StateData}.
652
++
653
++code_change(_OldVsn, StateName, StateData, _Extra) ->
654
++    {ok, StateName, StateData}.
655
++
656
++%%----------------------------------------------------------------------
657
++%% Func: handle_info/3
658
++%% Returns: {next_state, NextStateName, NextStateData}          |
659
++%%          {next_state, NextStateName, NextStateData, Timeout} |
660
++%%          {stop, Reason, NewStateData}                         
661
++%%----------------------------------------------------------------------
662
++handle_info({timeout, Timer, _}, _StateName,
663
++	    #state{timer = Timer} = StateData) ->
664
++    ?DEBUG("ding dong", []),
665
++    {stop, normal, StateData};
666
++
667
++handle_info(_, StateName, StateData) ->
668
++    {next_state, StateName, StateData}.
669
++
670
++%%----------------------------------------------------------------------
671
++%% Func: terminate/3
672
++%% Purpose: Shutdown the fsm
673
++%% Returns: any
674
++%%----------------------------------------------------------------------
675
++terminate(_Reason, _StateName, StateData) ->
676
++    ?DEBUG("terminate: deleting session ~s", [StateData#state.id]),
677
++    mnesia:transaction(
678
++      fun() ->
679
++	      mnesia:delete({http_bind, StateData#state.id})
680
++      end),
681
++    case StateData#state.waiting_input of
682
++	false ->
683
++	    case StateData#state.last_receiver of
684
++		undefined -> ok;
685
++		Receiver -> Receiver ! {tcp_closed, {http_bind, self()}}
686
++	    end;
687
++	{Receiver, _Tag} -> Receiver ! {tcp_closed, {http_bind, self()}}
688
++    end,
689
++    ok.
690
++
691
++%%%----------------------------------------------------------------------
692
++%%% Internal functions
693
++%%%----------------------------------------------------------------------
694
++
695
++
696
++http_put(ID, RID, Key, NewKey, Hold, Packet) ->
697
++    case mnesia:dirty_read({http_bind, ID}) of
698
++	[] ->
699
++	    {error, not_exists};
700
++	[#http_bind{pid = FsmRef}] ->
701
++	    gen_fsm:sync_send_all_state_event(
702
++	      FsmRef, {http_put, RID, Key, NewKey, Hold, Packet})
703
++    end.
704
++
705
++http_get(ID,RID) ->
706
++    case mnesia:dirty_read({http_bind, ID}) of
707
++	[] ->
708
++	    {error, not_exists};
709
++	[#http_bind{pid = FsmRef, wait = Wait, hold = Hold}] ->
710
++	    gen_fsm:sync_send_all_state_event(FsmRef, 
711
++					      {http_get, RID, Wait, Hold})
712
++    end.
713
++
714
++
715
++parse_request(Data) ->
716
++    ?DEBUG("--- incoming data --- ~n~s~n --- END --- ",
717
++	   [Data]),
718
++    case xml_stream:parse_element(Data) of
719
++	El when element(1, El) == xmlelement ->
720
++	    {xmlelement, Name, Attrs, Els} = El,
721
++	    ID = xml:get_attr_s("sid",Attrs),
722
++	    {RID,_X} = string:to_integer(xml:get_attr_s("rid",Attrs)),
723
++	    Key = xml:get_attr_s("key",Attrs),
724
++	    NewKey = xml:get_attr_s("newkey",Attrs),
725
++	    Xmlns = xml:get_attr_s("xmlns",Attrs),
726
++	    Packet = [xml:element_to_string(
727
++			xml:remove_tag_attr("xmlns",E)) || 
728
++			 E <- Els],
729
++	    if 
730
++		Name /= "body" -> 
731
++		    {error, bad_request};
732
++		Xmlns /= "http://jabber.org/protocol/httpbind" ->
733
++		    {error, bad_request};
734
++		true ->
735
++		    {ok, ID, RID, Key, NewKey, Attrs, Packet}
736
++	    end;
737
++	{error, _Reason} ->
738
++	    {error, bad_request}
739
++    end.
740
++
741
++cancel_timer(Timer) ->
742
++    erlang:cancel_timer(Timer),
743
++    receive
744
++	{timeout, Timer, _} ->
745
++	    ok
746
++    after 0 ->
747
++	    ok
748
++    end.
749
++
750
++hex(Bin) when binary(Bin) -> hex(binary_to_list(Bin));
751
++hex([]) -> "";
752
++hex([H|T]) -> 
753
++	[A,B] = if 
754
++		H == 0 -> "00";
755
++		H < 16 -> [$0,element(H,{$1,$2,$3,$4,$5,$6,$7,$8,$9,$a,$b,$c,$d,$e,$f})];
756
++		true   -> erlang:integer_to_list(H,16)
757
++	end,
758
++	[A,B|hex(T)].
759
++
760
++elements_to_string([]) ->
761
++    [];
762
++elements_to_string([El | Els]) ->
763
++    xml:element_to_string(El) ++ elements_to_string(Els).
764
+Index: src/web/ejabberd_web.erl
765
+===================================================================
766
+--- src/web/ejabberd_web.erl	(Revision 565)
767
++++ src/web/ejabberd_web.erl	(Arbeitskopie)
768
+@@ -49,7 +49,7 @@
769
+ 		      {"value", Value}])).
770
+ 
771
+ 
772
+-process_get({_, true},
773
++process_get({_, _, true},
774
+ 	    #request{auth = Auth,
775
+ 		     path = ["admin", "server", SHost | RPath],
776
+ 		     q = Query,
777
+@@ -94,7 +94,7 @@
778
+ 	    {404, [], make_xhtml([?XC("h1", "Not found")])}
779
+     end;
780
+ 
781
+-process_get({_, true},
782
++process_get({_, _, true},
783
+ 	    #request{auth = Auth,
784
+ 		     path = ["admin" | RPath],
785
+ 		     q = Query,
786
+@@ -133,12 +133,19 @@
787
+ 				       [{xmlcdata, "401 Unauthorized"}]}])}
788
+     end;
789
+ 
790
+-process_get({true, _},
791
++process_get({true, _, _},
792
+ 	    #request{path = ["http-poll" | RPath],
793
+ 		     q = _Query,
794
+ 		     lang = _Lang} = Request) ->
795
+     ejabberd_http_poll:process_request(Request#request{path = RPath});
796
+ 
797
++process_get({_, true, _},
798
++	    #request{us = _US,
799
++		     path = ["http-bind" | RPath],
800
++		     q = _Query,
801
++		     lang = _Lang} = Request) ->
802
++    ejabberd_http_bind:process_request(Request#request{path = RPath});
803
++
804
+ process_get(_, _Request) ->
805
+     {404, [], make_xhtml([?XC("h1", "Not found")])}.
806
+ 
807
+Index: src/web/ejabberd_http.erl
808
+===================================================================
809
+--- src/web/ejabberd_http.erl	(Revision 565)
810
++++ src/web/ejabberd_http.erl	(Arbeitskopie)
811
+@@ -30,6 +30,7 @@
812
+ 		request_keepalive,
813
+ 		request_content_length,
814
+ 		request_lang = "en",
815
++		use_http_bind = false,
816
+ 		use_http_poll = false,
817
+ 		use_web_admin = false,
818
+ 		end_of_request = false,
819
+@@ -70,15 +71,17 @@
820
+ 	_ ->
821
+ 	    ok
822
+     end,
823
++    UseHTTPBind = lists:member(http_bind, Opts),
824
+     UseHTTPPoll = lists:member(http_poll, Opts),
825
+     UseWebAdmin = lists:member(web_admin, Opts),
826
+-    ?DEBUG("S: ~p~n", [{UseHTTPPoll, UseWebAdmin}]),
827
++    ?DEBUG("S: ~p~n", [{UseHTTPPoll, UseHTTPBind, UseWebAdmin}]),
828
+     ?INFO_MSG("started: ~p", [{SockMod1, Socket1}]),
829
+     {ok, proc_lib:spawn_link(ejabberd_http,
830
+ 			     receive_headers,
831
+ 			     [#state{sockmod = SockMod1,
832
+ 				     socket = Socket1,
833
+ 				     use_http_poll = UseHTTPPoll,
834
++				     use_http_bind = UseHTTPBind,
835
+ 				     use_web_admin = UseWebAdmin}])}.
836
+ 
837
+ 
838
+@@ -185,6 +188,7 @@
839
+ 		    #state{sockmod = SockMod,
840
+ 			   socket = Socket,
841
+ 			   use_http_poll = State#state.use_http_poll,
842
++			   use_http_bind = State#state.use_http_bind,
843
+ 			   use_web_admin = State#state.use_web_admin};
844
+ 		_ ->
845
+ 		    #state{end_of_request = true}
846
+@@ -200,6 +204,7 @@
847
+ 		       request_auth = Auth,
848
+ 		       request_lang = Lang,
849
+ 		       use_http_poll = UseHTTPPoll,
850
++		       use_http_bind = UseHTTPBind,
851
+ 		       use_web_admin = UseWebAdmin} = State) ->
852
+     case (catch url_decode_q_split(Path)) of
853
+ 	{'EXIT', _} ->
854
+@@ -217,7 +222,7 @@
855
+ 			       q = LQuery,
856
+ 			       auth = Auth,
857
+ 			       lang = Lang},
858
+-	    case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
859
++	    case ejabberd_web:process_get({UseHTTPPoll, UseHTTPBind, UseWebAdmin},
860
+ 					  Request) of
861
+ 		El when element(1, El) == xmlelement ->
862
+ 		    make_xhtml_output(State, 200, [], El);
863
+@@ -240,6 +245,7 @@
864
+ 		       sockmod = SockMod,
865
+ 		       socket = Socket,
866
+ 		       use_http_poll = UseHTTPPoll,
867
++		       use_http_bind = UseHTTPBind,
868
+ 		       use_web_admin = UseWebAdmin} = State)
869
+   when is_integer(Len) ->
870
+     case SockMod of
871
+@@ -267,7 +273,7 @@
872
+ 			       auth = Auth,
873
+ 			       data = Data,
874
+ 			       lang = Lang},
875
+-	    case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
876
++	    case ejabberd_web:process_get({UseHTTPPoll, UseHTTPBind, UseWebAdmin},
877
+ 					  Request) of
878
+ 		El when element(1, El) == xmlelement ->
879
+ 		    make_xhtml_output(State, 200, [], El);
880
+Index: src/xml.erl
881
+===================================================================
882
+--- src/xml.erl	(Revision 565)
883
++++ src/xml.erl	(Arbeitskopie)
884
+@@ -18,6 +18,7 @@
885
+ 	 get_tag_attr/2, get_tag_attr_s/2,
886
+ 	 get_subtag/2,
887
+ 	 get_path_s/2,
888
++	 remove_tag_attr/2,
889
+ 	 replace_tag_attr/3]).
890
+ 
891
+ %element_to_string(El) ->
892
+@@ -224,6 +225,14 @@
893
+ get_path_s(El, [cdata]) ->
894
+     get_tag_cdata(El).
895
+ 
896
++remove_tag_attr(Attr, El) ->
897
++    case El of
898
++        {xmlelement, Name, Attrs, Els} ->
899
++            Attrs1 = lists:keydelete(Attr, 1, Attrs),
900
++            {xmlelement, Name, Attrs1, Els};
901
++        _ ->
902
++            El
903
++    end.
904
+ 
905
+ replace_tag_attr(Attr, Value, {xmlelement, Name, Attrs, Els}) ->
906
+     Attrs1 = lists:keydelete(Attr, 1, Attrs),
907
+ 
908
+Index: src/ejabberd_sup.erl
909
+===================================================================
910
+--- src/ejabberd_sup.erl	(Revision 565)
911
++++ src/ejabberd_sup.erl	(Arbeitskopie)
912
+@@ -123,6 +123,14 @@
913
+ 	 infinity,
914
+ 	 supervisor,
915
+ 	 [ejabberd_tmp_sup]},
916
++    HTTPBindSupervisor =
917
++	{ejabberd_http_bind_sup,
918
++	 {ejabberd_tmp_sup, start_link,
919
++	  [ejabberd_http_bind_sup, ejabberd_http_bind]},
920
++	 permanent,
921
++	 infinity,
922
++	 supervisor,
923
++	 [ejabberd_tmp_sup]},
924
+     IQSupervisor =
925
+ 	{ejabberd_iq_sup,
926
+ 	 {ejabberd_tmp_sup, start_link,
927
+@@ -145,6 +153,7 @@
928
+ 	   ServiceSupervisor,
929
+ 	   HTTPSupervisor,
930
+ 	   HTTPPollSupervisor,
931
++	   HTTPBindSupervisor,
932
+ 	   IQSupervisor,
933
+ 	   Listener]}}.
934
+ 
... ...
@@ -0,0 +1,11 @@
1
+--- mod_irc/mod_irc.erl.orig	2006-11-19 14:40:02.463429250 +0100
2
++++ mod_irc/mod_irc.erl	2006-11-19 14:40:25.888893250 +0100
3
+@@ -27,7 +27,7 @@
4
+ -include("ejabberd.hrl").
5
+ -include("jlib.hrl").
6
+ 
7
+--define(DEFAULT_IRC_ENCODING, "koi8-r").
8
++-define(DEFAULT_IRC_ENCODING, "utf-8").
9
+ 
10
+ -record(irc_connection, {jid_server_host, pid}).
11
+ -record(irc_custom, {us_host, data}).
... ...
@@ -0,0 +1,11 @@
1
+diff -Naur ejabberd-1.1.1.orig/src/odbc/ejabberd_odbc.erl ejabberd-1.1.1/src/odbc/ejabberd_odbc.erl
2
+--- ejabberd-1.1.1.orig/src/odbc/ejabberd_odbc.erl	2006-05-07 19:05:12.549917750 +0400
3
++++ ejabberd-1.1.1/src/odbc/ejabberd_odbc.erl	2006-05-07 19:06:11.337591750 +0400
4
+@@ -281,6 +281,7 @@
5
+     case mysql_conn:start(Server, ?MYSQL_PORT, Username, Password, DB, NoLogFun) of
6
+ 	{ok, Ref} ->
7
+ 	    erlang:monitor(process, Ref),
8
++	    catch mysql_conn:fetch(Ref, "SET NAMES 'utf8';", self()),
9
+ 	    {ok, #state{db_ref = Ref, db_type = mysql}};
10
+ 	{error, Reason} ->
11
+ 	    ?ERROR_MSG("MySQL connection failed: ~p~n", [Reason]),
... ...
@@ -0,0 +1,406 @@
1
+* modified files
2
+
3
+
4
+--- jlib.hrl
5
++++ jlib.hrl
6
+@@ -34,6 +34,7 @@
7
+ -define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
8
+ -define(NS_PUBSUB_NMI,   "http://jabber.org/protocol/pubsub#node-meta-info").
9
+ -define(NS_COMMANDS,     "http://jabber.org/protocol/commands").
10
++-define(NS_BYTESTREAMS,  "http://jabber.org/protocol/bytestreams").
11
+ 
12
+ -define(NS_EJABBERD_CONFIG, "ejabberd:config").
13
+ 
14
+
15
+
16
+
17
+* added files
18
+
19
+--- /dev/null
20
++++ mod_proxy65.erl
21
+@@ -0,0 +1,189 @@
22
++%%%----------------------------------------------------------------------
23
++%%% File    : mod_proxy65.erl
24
++%%% Author  : Magnus Henoch <henoch@dtek.chalmers.se>
25
++%%% Purpose : Handle Jabber communications for JEP-0065 proxy
26
++%%% Created : 27 Dec 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
27
++%%% Id      : $Id: ejabberd_c2s.erl 440 2005-11-22 18:00:56Z alexey $
28
++%%%----------------------------------------------------------------------
29
++
30
++-module(mod_proxy65).
31
++-author('henoch@dtek.chalmers.se').
32
++-vsn('$Revision$ ').
33
++
34
++-behaviour(gen_mod).
35
++
36
++-export([start/2,
37
++	 init/1,
38
++	 stop/1]).
39
++
40
++-include("ejabberd.hrl").
41
++-include("jlib.hrl").
42
++
43
++-record(proxy65_connection, {cookie, firstpid = none, secondpid = none}).
44
++
45
++-record(proxy65_options, {host, access, streamhosts}).
46
++
47
++-define(PROCNAME, ejabberd_mod_proxy65).
48
++
49
++start(Host, Opts) ->
50
++    mnesia:create_table(proxy65_connection,
51
++			[{ram_copies, [node()]},
52
++			 {attributes, record_info(fields, proxy65_connection)}]),
53
++    MyHost = gen_mod:get_opt(host, Opts, "proxy." ++ Host),
54
++    Access = gen_mod:get_opt(access, Opts, all),
55
++    Streamhosts = gen_mod:get_opt(streamhosts, Opts, [{Host, 7777}]),
56
++    
57
++    register(gen_mod:get_module_proc(Host, ?PROCNAME),
58
++	     spawn(?MODULE, init, [#proxy65_options{host = MyHost, access = Access,
59
++						    streamhosts = Streamhosts}])).
60
++
61
++
62
++stop(Host) ->
63
++    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
64
++    Proc ! stop,
65
++    {wait, Proc}.
66
++
67
++init(#proxy65_options{host = Host} = Opts) ->
68
++    ejabberd_router:register_route(Host),
69
++    loop(Opts).
70
++
71
++loop(#proxy65_options{host = Host} = Opts) ->
72
++    receive
73
++	{route, From, To, Packet} ->
74
++	    case catch do_route(Opts, From, To, Packet) of
75
++		{'EXIT', Reason} ->
76
++		    ?ERROR_MSG("~p", [Reason]);
77
++		_ ->
78
++		    ok
79
++	    end,
80
++	    loop(Opts);
81
++	stop ->
82
++	    ejabberd_router:unregister_route(Host),
83
++	    ok;
84
++	_ ->
85
++	    loop(Opts)
86
++    end.
87
++
88
++do_route(#proxy65_options{host = Host, access = Access} = Opts,
89
++	 From, To, Packet) ->
90
++    case acl:match_rule(Host, Access, From) of
91
++	allow ->
92
++	    do_route1(Opts, From, To, Packet);
93
++	_ ->
94
++	    {xmlelement, _Name, Attrs, _Els} = Packet,
95
++	    Lang = xml:get_attr_s("xml:lang", Attrs),
96
++	    ErrText = "Access denied by service policy",
97
++	    Err = jlib:make_error_reply(Packet,
98
++					?ERRT_FORBIDDEN(Lang, ErrText)),
99
++	    ejabberd_router:route(To, From, Err)
100
++    end.
101
++
102
++do_route1(#proxy65_options{host = Host, streamhosts = Streamhosts}, From, To, Packet) ->
103
++    {xmlelement, Name, _Attrs, _Els} = Packet,
104
++    case Name of
105
++	"iq" ->
106
++	    case jlib:iq_query_info(Packet) of
107
++		#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
108
++		    lang = Lang, sub_el = SubEl} = IQ ->
109
++		    Node = xml:get_tag_attr_s("node", SubEl),
110
++		    if Node == [] ->
111
++			    Res = IQ#iq{type = result,
112
++					sub_el = [{xmlelement, "query",
113
++						   [{"xmlns", XMLNS}],
114
++						   [{xmlelement, "identity",
115
++						     [{"category", "proxy"},
116
++						      {"type", "bytestreams"},
117
++						      {"name", translate:translate(Lang, "SOCKS5 bytestreams proxy")}],
118
++						     []},
119
++						    {xmlelement, "feature",
120
++						     [{"var", ?NS_BYTESTREAMS}], []}]}]};
121
++		       true ->
122
++			    Res = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND)
123
++		    end;
124
++		#iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS} = IQ ->
125
++		    Res = IQ#iq{type = result,
126
++				sub_el = [{xmlelement, "query",
127
++					   [{"xmlns", XMLNS}], []}]};
128
++		#iq{type = get, xmlns = ?NS_VERSION} = IQ ->
129
++		    OSType = case os:type() of
130
++				 {Osfamily, Osname} ->
131
++				     atom_to_list(Osfamily) ++ "/" ++
132
++					 atom_to_list(Osname);
133
++				 Osfamily ->
134
++				     atom_to_list(Osfamily)
135
++			     end,
136
++		    OSVersion = case os:version() of
137
++				    {Major, Minor, Release} ->
138
++					lists:flatten(
139
++					  io_lib:format("~w.~w.~w",
140
++							[Major, Minor, Release]));
141
++				    VersionString ->
142
++					VersionString
143
++				end,
144
++		    OS = OSType ++ " " ++ OSVersion,
145
++		    Res = IQ#iq{type = result,
146
++				sub_el = [{xmlelement, "query",
147
++					   [{"xmlns", ?NS_VERSION}],
148
++					   [{xmlelement, "name", [],
149
++					     [{xmlcdata, "ejabberd mod_proxy65 (unofficial)"}]},
150
++					    {xmlelement, "version", [],
151
++					     [{xmlcdata, "0.1"}]},
152
++					    {xmlelement, "os", [],
153
++					     [{xmlcdata, OS}]}
154
++					   ]}]};
155
++		#iq{type = get, xmlns = ?NS_BYTESTREAMS = XMLNS} = IQ ->
156
++		    Res = IQ#iq{type = result,
157
++				sub_el = [{xmlelement, "query",
158
++					   [{"xmlns", XMLNS}],
159
++					   return_streamhosts(Host, Streamhosts)}]};
160
++		#iq{type = set, xmlns = ?NS_BYTESTREAMS} = IQ ->
161
++		    Res = activate(Packet, From, To, IQ);
162
++		_ ->
163
++		    Res = jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED)
164
++	    end,
165
++	    case Res of
166
++		#iq{} ->
167
++		    ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
168
++		_ ->
169
++		    ejabberd_router:route(To, From, Res)
170
++	    end;
171
++	_ ->
172
++	    ejabberd_router:route(To, From, jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED))
173
++    end.
174
++
175
++return_streamhosts(_JID, []) ->
176
++    [];
177
++return_streamhosts(JID, [{Host, Port} | Streamhosts]) ->
178
++    %% This is not tail-recursive, but it doesn't matter.
179
++    [{xmlelement, "streamhost",
180
++      [{"jid", JID},
181
++       {"host", Host},
182
++       {"port", integer_to_list(Port)}],
183
++      []} | return_streamhosts(JID, Streamhosts)].
184
++
185
++activate(Packet, From, _To, #iq{sub_el = SubEl} = IQ) ->
186
++    case SubEl of
187
++	{xmlelement, "query", Attrs, _SubEls} ->
188
++	    Sid = xml:get_attr_s("sid", Attrs),
189
++	    ActivateTag = xml:get_subtag(SubEl, "activate"),
190
++	    if ActivateTag /= false ->
191
++		    TargetJID = jlib:string_to_jid(xml:get_tag_cdata(ActivateTag));
192
++	       true ->
193
++		    TargetJID = false
194
++	    end,
195
++	    
196
++	    if Sid /= [], TargetJID /= false, TargetJID /= error ->
197
++		    case proxy65_listener:activate(From, TargetJID, Sid) of
198
++			ok ->
199
++			    ?INFO_MSG("Activated connection between ~s and ~s",
200
++				      [jlib:jid_to_string(From), TargetJID]),
201
++			    IQ#iq{type = result, sub_el = []};
202
++			_ ->
203
++			    jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR)
204
++		    end;
205
++	       true ->
206
++		    jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST)
207
++	    end;
208
++	_ ->
209
++	    jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST)
210
++    end.
211
+--- /dev/null
212
++++ proxy65_listener.erl
213
+@@ -0,0 +1,192 @@
214
++%%%----------------------------------------------------------------------
215
++%%% File    : proxy65_listener.erl
216
++%%% Author  : Magnus Henoch <henoch@dtek.chalmers.se>
217
++%%% Purpose : Handle SOCKS5 connections for JEP-0065 proxy
218
++%%% Created : 27 Dec 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
219
++%%% Id      : $Id: ejabberd_c2s.erl 440 2005-11-22 18:00:56Z alexey $
220
++%%%----------------------------------------------------------------------
221
++
222
++-module(proxy65_listener).
223
++-author('henoch@dtek.chalmers.se').
224
++-vsn('$Revision$ ').
225
++
226
++-export([start/2, handle_connection/2, activate/3]).
227
++
228
++-include("ejabberd.hrl").
229
++-include("jlib.hrl").
230
++
231
++-record(proxy65_connection, {cookie, firstpid = none, secondpid = none}).
232
++
233
++start({SockMod, Socket}, Opts) ->
234
++    {ok, proc_lib:spawn(?MODULE, handle_connection, [{SockMod, Socket}, Opts])}.
235
++
236
++read_bytes(_SockOpts, 0, Data) ->
237
++    lists:flatten(Data);
238
++read_bytes({SockMod, Socket} = SockOpts, N, Data) ->
239
++    Timeout = 1000,
240
++    case SockMod:recv(Socket, N, Timeout) of
241
++	{error, closed} ->
242
++	    %% On closed connection, return everything we have,
243
++	    %% but not if we have nothing.
244
++	    if Data == [] ->
245
++		    erlang:error(closed);
246
++	       true ->
247
++		    lists:flatten(Data)
248
++	    end;
249
++	{ok, MoreData} ->
250
++	    ?DEBUG("read ~p", [MoreData]),
251
++	    DataList = binary_to_list(MoreData),
252
++	    read_bytes(SockOpts, N - length(DataList), [Data, DataList])
253
++    end.
254
++
255
++handle_connection({SockMod, Socket}, Opts) ->
256
++    ?DEBUG("in handle_connection", []),
257
++    case catch handle_auth({SockMod, Socket}, Opts) of
258
++	{'EXIT', Reason} ->
259
++	    ?ERROR_MSG("~p abnormal termination:~n\t~p~n",
260
++		       [?MODULE, Reason]),
261
++	    SockMod:close(Socket);
262
++	_ ->
263
++	    ok
264
++    end.
265
++
266
++handle_auth({SockMod, Socket} = SockOpts, Opts) ->
267
++    ?DEBUG("in handle_auth", []),
268
++    %% SOCKS protocol stuff...
269
++    [5, NAuthMethods] = read_bytes(SockOpts, 2, []),
270
++    AuthMethods = read_bytes(SockOpts, NAuthMethods, []),
271
++    SupportsNoAuth = lists:member(0, AuthMethods),
272
++
273
++    %% Must support no authentication, otherwise crash
274
++    true = SupportsNoAuth,
275
++
276
++    SockMod:send(Socket, [5, 0]),
277
++
278
++    %% And done.
279
++    handle_connect(SockOpts, Opts).
280
++
281
++handle_connect({SockMod, Socket} = SockOpts, Opts) ->
282
++    ?DEBUG("in handle_connect", []),
283
++    %% Expect a CONNECT command and nothing else
284
++    [5, 1, _, 3, AddressLength] = read_bytes(SockOpts, 5, []),
285
++    Cookie = read_bytes(SockOpts, AddressLength, []),
286
++    [0, 0] = read_bytes(SockOpts, 2, []),
287
++
288
++    %% Make sure no more than two connections claim the same cookie.
289
++    F = fun() ->
290
++		case mnesia:read({proxy65_connection, Cookie}) of
291
++		    [] ->
292
++			mnesia:write(#proxy65_connection{cookie = Cookie,
293
++							 firstpid = self()}),
294
++			ok;
295
++		    [#proxy65_connection{secondpid = none} = C] ->
296
++			mnesia:write(C#proxy65_connection{secondpid = self()}),
297
++			ok
298
++		end
299
++	end,
300
++
301
++    case mnesia:transaction(F) of
302
++	{atomic, ok} ->
303
++	    SockMod:send(Socket, [5, 0, 0, 3, AddressLength, Cookie, 0, 0]),
304
++	    wait_for_activation(SockOpts, Opts);
305
++	Error ->
306
++	    %% conflict.  send "general SOCKS server failure".
307
++	    SockMod:send(Socket, [5, 1, 0, 3, AddressLength, Cookie, 0, 0]),
308
++	    erlang:error({badconnect, Error})
309
++    end.
310
++
311
++wait_for_activation(SockOpts, Opts) ->
312
++    ?DEBUG("in wait_for_activation", []),
313
++    receive
314
++	{get_socket, ReplyTo} ->
315
++	    ReplyTo ! SockOpts,
316
++	    wait_for_activation(SockOpts, Opts);
317
++	{activate, TargetSocket, Initiator, Target} ->
318
++	    ?DEBUG("activated", []),
319
++
320
++	    %% We have no way of knowing which connection belongs to
321
++	    %% which participant, so give both the maximum traffic
322
++	    %% allowed to either.
323
++	    Shapers = case lists:keysearch(shaper, 1, Opts) of
324
++			  {value, {_, S}} -> S;
325
++			  _ -> none
326
++		      end,
327
++	    ?DEBUG("we have shapers: ~p", [Shapers]),
328
++	    Shaper1 = acl:match_rule(global, Shapers, jlib:string_to_jid(Initiator)),
329
++	    Shaper2 = acl:match_rule(global, Shapers, jlib:string_to_jid(Target)),
330
++	    if Shaper1 == none; Shaper2 == none ->
331
++		    MaxShaper = none;
332
++	       true ->
333
++		    ShaperValue1 = ejabberd_config:get_global_option({shaper, Shaper1}),
334
++		    ShaperValue2 = ejabberd_config:get_global_option({shaper, Shaper2}),
335
++
336
++		    if ShaperValue1 > ShaperValue2 ->
337
++			    MaxShaper = Shaper1;
338
++		       true ->
339
++			    MaxShaper = Shaper2
340
++		    end,
341
++		    ?DEBUG("shapers have values ~p and ~p~nusing ~p", [ShaperValue1, ShaperValue2, MaxShaper]),
342
++		    ok
343
++	    end,
344
++
345
++	    transfer_data(SockOpts, TargetSocket, shaper:new(MaxShaper))
346
++    end.
347
++
348
++transfer_data({SockMod, Socket} = SockOpts, {TargetSockMod, TargetSocket} = TargetSockOpts,
349
++	      Shaper) ->
350
++    case SockMod:recv(Socket, 0, infinity) of
351
++	{ok, Data} ->
352
++	    if Data /= <<>> ->
353
++		    NewShaper = case Shaper of
354
++				    none -> none;
355
++				    _ ->
356
++					shaper:update(Shaper, size(Data))
357
++				end,
358
++		    ok = TargetSockMod:send(TargetSocket, Data);
359
++	       true ->
360
++		    NewShaper = Shaper
361
++	    end,
362
++	    transfer_data(SockOpts, TargetSockOpts, NewShaper);
363
++	{error, _} ->
364
++	    TargetSockMod:shutdown(TargetSocket, read_write)
365
++    end.
366
++
367
++get_socket(PID) ->
368
++    PID ! {get_socket, self()},
369
++    receive
370
++	{_SockMod, _Socket} = SockOpts ->
371
++	    SockOpts
372
++    end.
373
++
374
++%% If any argument is a jid record, convert it to normalized string form...
375
++activate(#jid{} = Initiator, Target, SessionID) ->
376
++    NormalizedInitiator = jlib:jid_to_string(jlib:make_jid(jlib:jid_tolower(Initiator))),
377
++    activate(NormalizedInitiator, Target, SessionID);
378
++activate(Initiator, #jid{} = Target, SessionID) ->
379
++    NormalizedTarget = jlib:jid_to_string(jlib:make_jid(jlib:jid_tolower(Target))),
380
++    activate(Initiator, NormalizedTarget, SessionID);
381
++%% ...and get on with the activation.
382
++activate(Initiator, Target, SessionID) ->
383
++    Cookie = sha:sha(SessionID ++ Initiator ++ Target),
384
++    F = fun() ->
385
++		case mnesia:read({proxy65_connection, Cookie}) of
386
++		    [#proxy65_connection{firstpid = FirstPID,
387
++					 secondpid = SecondPID}]
388
++		    when is_pid(FirstPID), is_pid(SecondPID) ->
389
++			mnesia:delete({proxy65_connection, Cookie}),
390
++			{FirstPID, SecondPID};
391
++		    _ ->
392
++			error
393
++		end
394
++	end,
395
++    case mnesia:transaction(F) of
396
++	{atomic, {FirstPID, SecondPID}} ->
397
++	    FirstSocket = get_socket(FirstPID),
398
++	    SecondSocket = get_socket(SecondPID),
399
++	    FirstPID ! {activate, SecondSocket, Initiator, Target},
400
++	    SecondPID ! {activate, FirstSocket, Initiator, Target},
401
++	    ok;
402
++	Error ->
403
++	    ?ERROR_MSG("Proxy activation failed: ~p", [Error]),
404
++	    error
405
++    end.
406
+
... ...
@@ -0,0 +1,17 @@
1
+# Copyright 1999-2005 Gentoo Foundation
2
+# Distributed under the terms of the GNU General Public License v2
3
+# $Header: $
4
+
5
+# Name of your ejabberd node. Used by ejabberdctl to determine which
6
+# node to communicate with. Default is "ejabberd@`hostname -s`".
7
+#EJABBERD_NODE="ejabberd@`hostname -s`"
8
+
9
+# Max number of open network connections. Default is 1024. Increasing
10
+# this will slightly increase memory usage.
11
+#ERL_MAX_PORTS=1024
12
+
13
+# Return memory to the system after using it, instead of keeping it
14
+# allocated for future use. Decreases the memory required by ejabberd,
15
+# but makes it run slower.  Default is unset, set to any value to
16
+# activate.
17
+#ERL_FULLSWEEP_AFTER=0
... ...
@@ -0,0 +1,60 @@
1
+#!/sbin/runscript
2
+# Copyright 1999-2005 Gentoo Foundation
3
+# Distributed under the terms of the GNU General Public License v2
4
+# $Header: $
5
+
6
+opts="${opts} reload"
7
+
8
+depend() {
9
+	use dns
10
+	need net
11
+	provide jabber-server
12
+}
13
+
14
+checkconfig() {
15
+	if [ ! -e /etc/jabber/ejabberd.cfg ] ; then
16
+		eerror "You need an /etc/jabber/ejabberd.cfg file to run ejabberd"
17
+		return 1
18
+	fi
19
+}
20
+
21
+start() {
22
+	checkconfig || return 1
23
+	ebegin "Starting eJabberd"
24
+	start-stop-daemon --start --quiet --chuid jabber:jabber \
25
+		--exec /usr/bin/env HOME=/var/run/jabber /usr/bin/ejabberd -- -noshell -detached
26
+	eend $?
27
+}
28
+
29
+stop() {
30
+	ebegin "Stopping eJabberd"
31
+	if [ -z "$EJABBERD_NODE" ];
32
+	then
33
+		EJABBERD_NODE="ejabberd@`hostname -s`"
34
+	fi
35
+	/usr/bin/ejabberdctl $EJABBERD_NODE stop
36
+	eend $?
37
+}
38
+
39
+# Work around a bug in /sbin/runscript.sh - it won't run our custom
40
+# restart() unless it finds these two strings in the file.
41
+# svc_start svc_stop
42
+restart() {
43
+	ebegin "Restarting eJabberd"
44
+	if [ -z "$EJABBERD_NODE" ];
45
+	then
46
+		EJABBERD_NODE="ejabberd@`hostname -s`"
47
+	fi
48
+	/usr/bin/ejabberdctl $EJABBERD_NODE restart
49
+	eend $?
50
+}
51
+
52
+reload() {
53
+	ebegin "Reloading eJabberd"
54
+	if [ -z "$EJABBERD_NODE" ];
55
+	then
56
+		EJABBERD_NODE="ejabberd@`hostname -s`"
57
+	fi
58
+	/usr/bin/ejabberdctl $EJABBERD_NODE reopen-log
59
+	eend $?
60
+}
0 61