TclApps Library Source Code
Check-in [3cc18c6759]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:A few modifications to run ijbridge with Tcl 9. Added tls support for both jabber and IRC sides.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | ijbridge-tcl9
Files: files | file ages | folders
SHA3-256: 3cc18c6759e05cbd3526f81c410597eff473fe548bc8387ec2786448498bb8d7
User & Date: emiliano 2025-01-25 17:32:43.992
Context
2025-01-25
17:32
A few modifications to run ijbridge with Tcl 9. Added tls support for both jabber and IRC sides. Leaf check-in: 3cc18c6759 user: emiliano tags: ijbridge-tcl9
2025-01-21
15:49
Removed wrong utf-8 char from comments check-in: 9c28b69758 user: emiliano tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to apps/ijbridge/ijbridge.conf.sample.
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28

29
30








31
32
33
34

35
36
37
38
39
40
41
42
#
# Comments begin with # and blank lines are ignored. 
# No additional options are permitted.

# Jabber connection information
#
JabberServer   YOUR.JABBER.SERVER
JabberPort     5222

JabberResource ijbridge
JabberUser     VALIDJABBERUSERID
JabberPassword VALIDJABBERPASSWORD

# Details for the Jabber conference room to join to.
#
Conference      [email protected]k
ConferenceNick  ijchain

# IRC connection details
#
IrcServer      IRC.SERVER.NAME
IrcPort        6667

IrcUser        IRCNICK
IrcChannel     #IRCCHANNEL









# FreeNode restricts PRIVMSG to identified users.
# Provide your IrcUser nick's pasword for use in identifying this
# client to FreenNode's nickserv.

#
IrcNickPasswd  IRCNICKSERVPASSWORD
#
# If you want to maintain a running statistics count between invocations
# then specify a file path to maintain the counters. If this is an empty
# string then no file will be used.
#
StatisticsFile ""







|
>






|






>


>
>
>
>
>
>
>
>




>
|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#
# Comments begin with # and blank lines are ignored. 
# No additional options are permitted.

# Jabber connection information
#
JabberServer   YOUR.JABBER.SERVER
JabberPort     5223
JabberTLS      1
JabberResource ijbridge
JabberUser     VALIDJABBERUSERID
JabberPassword VALIDJABBERPASSWORD

# Details for the Jabber conference room to join to.
#
Conference      [email protected]
ConferenceNick  ijchain

# IRC connection details
#
IrcServer      IRC.SERVER.NAME
IrcPort        6667
IrcTLS         1
IrcUser        IRCNICK
IrcChannel     #IRCCHANNEL
SuppressTraffic 1

# Values for LiberaChat
# IrcServer       irc.libera.chat
# IrcPort         6697
# IrcTLS          1
# IrcUser         Ijchain
# IrcChannel      #tcl

# FreeNode restricts PRIVMSG to identified users.
# Provide your IrcUser nick's pasword for use in identifying this
# client to FreenNode's nickserv.
# On LiberaChat, set this to the empty string if joining as an
# unidentified user
IrcNickPasswd  IRCNICKSERVPASSWORD
#
# If you want to maintain a running statistics count between invocations
# then specify a file path to maintain the counters. If this is an empty
# string then no file will be used.
#
StatisticsFile ""
Changes to apps/ijbridge/ijbridge.tcl.
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47

48
49

50
51
52
53
54
55
56
57
58
59
60
#
# -------------------------------------------------------------------------
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# -------------------------------------------------------------------------

package require jlib;                   # jabberlib

package require browse;                 # jabberlib
package require muc;                    # jabberlib
package require wrapper;                # jabberlib
package require sha1;                   # tcllib
package require log;                    # tcllib
package require irc 0.4;                # tcllib

namespace eval client {}

namespace eval ::ijbridge {

    variable version 1.1.1
    variable rcsid {$Id: ijbridge.tcl,v 1.37 2009/01/29 20:45:44 patthoyts Exp $}

    # This array MUST be set up by reading the configuration file. The
    # member names given here define the settings permitted in the 
    # config file.
    # This script will not work by default - you MUST provide suitable 
    # connection details.
    #
    variable Options
    if {![info exists Options]} {
        array set Options {
            JabberServer   {}
            JabberPort     5222
            JabberUser     {}
            JabberPassword {}
            JabberResource ijbridge

            Conference     {}
            ConferenceNick {}
            
            IrcServer      irc.freenode.net
            IrcPort        6667

            IrcUser        {}
            IrcChannel     {}

            IrcNickPasswd  {}
            StatisticsFile {}
            NotificationPort {}
            ColorsFile     {}
        }
    }

    # State variable used in rate-limiting communications with IRC.
    variable Limit
    if {![info exists Limit]} {
        array set Limit {}







>












<


|

|





|
|
|
|
|
>
|
|
|
|
|
>
|
|
>
|
|

|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#
# -------------------------------------------------------------------------
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# -------------------------------------------------------------------------

package require jlib;                   # jabberlib
catch {package require tls;}
package require browse;                 # jabberlib
package require muc;                    # jabberlib
package require wrapper;                # jabberlib
package require sha1;                   # tcllib
package require log;                    # tcllib
package require irc 0.4;                # tcllib

namespace eval client {}

namespace eval ::ijbridge {

    variable version 1.1.1


    # This array MUST be set up by reading the configuration file. The
    # member names given here define the settings permitted in the
    # config file.
    # This script will not work by default - you MUST provide suitable
    # connection details.
    #
    variable Options
    if {![info exists Options]} {
        array set Options {
            JabberServer     {}
            JabberPort       5222
            JabberUser       {}
            JabberPassword   {}
            JabberResource   ijbridge
            JabberTLS        0
            Conference       {}
            ConferenceNick   {}

            IrcServer        irc.freenode.net
            IrcPort          6667
            IrcTLS           0
            IrcUser          {}
            IrcChannel       {}
            SuppressTraffic  0
            IrcNickPasswd    {}
            StatisticsFile   {}
            NotificationPort {}
            ColorsFile       {}
        }
    }

    # State variable used in rate-limiting communications with IRC.
    variable Limit
    if {![info exists Limit]} {
        array set Limit {}
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#	to your jabber server, then you would give 'connect localhost 5222'
#	to set up this connection.
#
proc ::ijbridge::connect {{server {}} {port {}}} {
    variable Options
    variable conn
    array set conn {}
    
    # If roster is unset this is a new connection.
    if {![info exists conn(roster)]} {
        log::log debug "Creating new jabber client instance"
        set conn(roster) [roster::roster [namespace origin OnRoster]]
        set conn(jabber) [jlib::new $conn(roster) \
                              [namespace origin OnClient] \
                              -iqcommand       [namespace origin OnIq] \







|







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#	to your jabber server, then you would give 'connect localhost 5222'
#	to set up this connection.
#
proc ::ijbridge::connect {{server {}} {port {}}} {
    variable Options
    variable conn
    array set conn {}

    # If roster is unset this is a new connection.
    if {![info exists conn(roster)]} {
        log::log debug "Creating new jabber client instance"
        set conn(roster) [roster::roster [namespace origin OnRoster]]
        set conn(jabber) [jlib::new $conn(roster) \
                              [namespace origin OnClient] \
                              -iqcommand       [namespace origin OnIq] \
111
112
113
114
115
116
117




118
119
120
121
122
123
124
125
126

        if {$server == {}} { set server $Options(JabberServer) }
        if {$port == {}} { set port $Options(JabberPort) }
        set conn(server) $server
        set conn(port) $port
    }





    set conn(sock) [socket $conn(server) $conn(port)]
    $conn(jabber) setsockettransport $conn(sock)    
    $conn(jabber) openstream $Options(JabberServer) \
        -cmd [namespace current]::OnConnect -socket $conn(sock)

    return
}

# ijbridge::disconnect --







>
>
>
>
|
|







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

        if {$server == {}} { set server $Options(JabberServer) }
        if {$port == {}} { set port $Options(JabberPort) }
        set conn(server) $server
        set conn(port) $port
    }

    set socketcmd socket
    if {$Options(JabberTLS)} {
	set socketcmd tls::socket
    }
    set conn(sock) [$socketcmd $conn(server) $conn(port)]
    $conn(jabber) setsockettransport $conn(sock)
    $conn(jabber) openstream $Options(JabberServer) \
        -cmd [namespace current]::OnConnect -socket $conn(sock)

    return
}

# ijbridge::disconnect --
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#
#	Called when the Jabber stream is initialised. Here we authenticate
#	with the Jabber server.
#
proc ::ijbridge::OnConnect {token args} {
    variable Options
    variable conn
    
    log::log debug "OnConnect $token $args"
    array set info $args
    set conn(info) $args
    set conn(id) [$token send_auth \
                      $Options(JabberUser) $Options(JabberResource) \
                      [namespace origin OnLogin] \
                      -digest [sha1::sha1 $info(id)$Options(JabberPassword)]]







|







219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#
#	Called when the Jabber stream is initialised. Here we authenticate
#	with the Jabber server.
#
proc ::ijbridge::OnConnect {token args} {
    variable Options
    variable conn

    log::log debug "OnConnect $token $args"
    array set info $args
    set conn(info) $args
    set conn(id) [$token send_auth \
                      $Options(JabberUser) $Options(JabberResource) \
                      [namespace origin OnLogin] \
                      -digest [sha1::sha1 $info(id)$Options(JabberPassword)]]
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
                    lappend c [list query [list xmlns $iqns node $iqnode] 0 {} $p]
                    set r [list iq [list xmlns jabber:client type result \
                                        id $a(-id) to $a(-from) from $a(-to)] 0 {} $c]
                }
                default {
                    set r [IqError $iqns feature-not-implemented \
                               $a(-id) $a(-to) $a(-from) cancel 501]
                }                    
            }
        }
        jabber:iq:version {
            variable version
            lappend p [list name {} 0 "IRC-Jabber Bridge" {}]
            lappend p [list version {} 0 $version {}]
            lappend p [list os {} 0 "Tcl/[info patchlevel]" {}]







|







423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
                    lappend c [list query [list xmlns $iqns node $iqnode] 0 {} $p]
                    set r [list iq [list xmlns jabber:client type result \
                                        id $a(-id) to $a(-from) from $a(-to)] 0 {} $c]
                }
                default {
                    set r [IqError $iqns feature-not-implemented \
                               $a(-id) $a(-to) $a(-from) cancel 501]
                }
            }
        }
        jabber:iq:version {
            variable version
            lappend p [list name {} 0 "IRC-Jabber Bridge" {}]
            lappend p [list version {} 0 $version {}]
            lappend p [list os {} 0 "Tcl/[info patchlevel]" {}]
715
716
717
718
719
720
721
722

723

724
725
726
727

728

729
730
731
732
733
734
735
    jlib::splitjid $a(-from) room nick
    log::log debug [info level 0]
    if {[jlib::jidequal $room $Options(Conference)]} {
        log::log debug "message from $room, user $nick"
        switch -exact -- $type {
            available {
                # Ignore the presence state change messages
                if {[lsearch -exact $MucUserList $a(-from)] == -1} {

                    xmit "PRIVMSG $::client::channel :\001ACTION $nick has joined the jabber conference\001"

                    lappend MucUserList $a(-from)
                }
            }
            unavailable {

                xmit "PRIVMSG $::client::channel :\001ACTION $nick left the jabber conference\001"

                if {[set ndx [lsearch -exact $MucUserList $a(-from)]] != -1} {
                    set MucUserList [lreplace $MucUserList $ndx $ndx]
                }
            }
        }
    }
}







|
>
|
>




>
|
>







722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
    jlib::splitjid $a(-from) room nick
    log::log debug [info level 0]
    if {[jlib::jidequal $room $Options(Conference)]} {
        log::log debug "message from $room, user $nick"
        switch -exact -- $type {
            available {
                # Ignore the presence state change messages
                if {$a(-from) ni $MucUserList} {
		    if {! $Options(SuppressTraffic)} {
			xmit "PRIVMSG $::client::channel :\001ACTION $nick has joined the jabber conference\001"
		    }
                    lappend MucUserList $a(-from)
                }
            }
            unavailable {
		if {! $Options(SuppressTraffic)} {
		    xmit "PRIVMSG $::client::channel :\001ACTION $nick left the jabber conference\001"
		}
                if {[set ndx [lsearch -exact $MucUserList $a(-from)]] != -1} {
                    set MucUserList [lreplace $MucUserList $ndx $ndx]
                }
            }
        }
    }
}
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
        for {set n 0} {$n < 32} {incr n} {
            if {$n == 9 || $n == 10 || $n == 13} continue
            lappend xmlmap [format %c $n] [format "&#x%x;" $n]
        }
    }

    # Eliminate known bridge prefixes (ircbridge is a webchat to irc bridge)
    if {[string equal $who "azbridge"] 
	|| [string equal $who "webchain"]
	|| [string equal $who "wubchain"]
	|| [string equal $who "ircbridge"]} {
        regexp {^<(.*?)> (.*)$}  $msg -> who msg
        set emote [regexp {^\*{1,3} (\w+) (.*)$} $msg -> who msg]
    }

    # Are they speaking to the bot? If so look for bot commands.
    if {[string match -nocase "${::client::nick}\[:,;. |\]*" $msg]} {
        set n [string length ${::client::nick}]







|
<
<
<







761
762
763
764
765
766
767
768



769
770
771
772
773
774
775
        for {set n 0} {$n < 32} {incr n} {
            if {$n == 9 || $n == 10 || $n == 13} continue
            lappend xmlmap [format %c $n] [format "&#x%x;" $n]
        }
    }

    # Eliminate known bridge prefixes (ircbridge is a webchat to irc bridge)
    if { $who in {azbridge webchain wubchain ircbridge ischain} } {



        regexp {^<(.*?)> (.*)$}  $msg -> who msg
        set emote [regexp {^\*{1,3} (\w+) (.*)$} $msg -> who msg]
    }

    # Are they speaking to the bot? If so look for bot commands.
    if {[string match -nocase "${::client::nick}\[:,;. |\]*" $msg]} {
        set n [string length ${::client::nick}]
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
        }
        Pop args
    }
    if {[llength $args] != 1} {
        return -code error "wrong \# args:\
            should be \"send ?option value? message\""
    }
    if {[string length $opts(-id)] < 1} {
        set opts(-id) $conn(id)
    }
    if {[string length $opts(user)] < 1} {
        set opts(user) $Options(Conference)
        set opts(-type) groupchat
    }
    
    log::log debug "send: [lindex $args 0]"
    eval [linsert [array get opts -*] 0 $conn(jabber) \
              send_message $opts(user) -body [lindex $args 0]]
}

# ijbridge::xmit --
#
#	This is where we send messages to the IRC channel. IRC requires 
#	rate limiting. A client can be kicked for sending too much in
#	too short a period so here we deal with this.
#
proc ::ijbridge::xmit {str} {
    variable Options
    variable Limit
    # if we are over the sending limit just push line into the queue







|


|



|







|







943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
        }
        Pop args
    }
    if {[llength $args] != 1} {
        return -code error "wrong \# args:\
            should be \"send ?option value? message\""
    }
    if { $opts(-id) eq "" } {
        set opts(-id) $conn(id)
    }
    if { $opts(user) eq "" } {
        set opts(user) $Options(Conference)
        set opts(-type) groupchat
    }

    log::log debug "send: [lindex $args 0]"
    eval [linsert [array get opts -*] 0 $conn(jabber) \
              send_message $opts(user) -body [lindex $args 0]]
}

# ijbridge::xmit --
#
#	This is where we send messages to the IRC channel. IRC requires
#	rate limiting. A client can be kicked for sending too much in
#	too short a period so here we deal with this.
#
proc ::ijbridge::xmit {str} {
    variable Options
    variable Limit
    # if we are over the sending limit just push line into the queue
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
    # if we are over the line limit kick off the queued sends
    if { $Limit(lines) > 4 } {
        ::log::log info "flood started"
        lappend Limit(queue) $str
        after 1000 [namespace origin XmitFromQueue]
        return
    }
    $client::cn send $str
}

# ijbridge::XmitFromQueue --
#
#	If we had to limit messages sent from 'xmit' then we handle the
#	queued messages from here on a timer.
#
proc ::ijbridge::XmitFromQueue {} {
    variable Options
    variable Limit
    # return if the queue is empty
    if { [string length [set str [lindex $Limit(queue) 0]]] < 1 } {
        set Limit(last) 0
        ::log::log info "flood ended"
        return
    }
    set Limit(queue) [lreplace $Limit(queue) 0 0]
    ::log::log debug "sending from queue"
    $client::cn send $str
    # send next line
    after 1200 [namespace origin XmitFromQueue]
}

# ijbridge::Pop --
#
#	Utility function used in option processing.







|


















|







987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
    # if we are over the line limit kick off the queued sends
    if { $Limit(lines) > 4 } {
        ::log::log info "flood started"
        lappend Limit(queue) $str
        after 1000 [namespace origin XmitFromQueue]
        return
    }
    $::client::cn send $str
}

# ijbridge::XmitFromQueue --
#
#	If we had to limit messages sent from 'xmit' then we handle the
#	queued messages from here on a timer.
#
proc ::ijbridge::XmitFromQueue {} {
    variable Options
    variable Limit
    # return if the queue is empty
    if { [string length [set str [lindex $Limit(queue) 0]]] < 1 } {
        set Limit(last) 0
        ::log::log info "flood ended"
        return
    }
    set Limit(queue) [lreplace $Limit(queue) 0 0]
    ::log::log debug "sending from queue"
    $::client::cn send $str
    # send next line
    after 1200 [namespace origin XmitFromQueue]
}

# ijbridge::Pop --
#
#	Utility function used in option processing.
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
	::log::log notice "UsersOnline [msg]"
	set users [split [string map {@ "" % "" + ""} [string trim [msg]]] " "]
	foreach bridge {ircbridge azbridge ijbridge ijchain} {
	    set users [lsearch -all -inline -exact -not $users $bridge]
	}
        foreach u $users {lappend users_tmp $u}
    }
    
    $cn registerevent 366 {
        # End of NAMES list
        # Copy the new user list to the main list.
        variable users_tmp
        if {[info exists users_tmp]} {
            set ::ijbridge::IrcUserList $users_tmp
            unset users_tmp







|







1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
	::log::log notice "UsersOnline [msg]"
	set users [split [string map {@ "" % "" + ""} [string trim [msg]]] " "]
	foreach bridge {ircbridge azbridge ijbridge ijchain} {
	    set users [lsearch -all -inline -exact -not $users $bridge]
	}
        foreach u $users {lappend users_tmp $u}
    }

    $cn registerevent 366 {
        # End of NAMES list
        # Copy the new user list to the main list.
        variable users_tmp
        if {[info exists users_tmp]} {
            set ::ijbridge::IrcUserList $users_tmp
            unset users_tmp
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
        } err]} { puts stderr $err }
    }

    $cn registerevent 333 {
        # topicinfo - a handy marker for 'we just joined a channel'
        # Ask Chanserv to op us.
        cmd-send "PRIVMSG Chanserv :op $::client::channel $::client::nick"
    } 
   
    # WHOIS handling from Jabber user 
    $cn registerevent 311 {set ::client::whois(realname) [msg];set ::client::whois(address) [additional]}
    $cn registerevent 312 {set ::client::whois(url) [msg]}
    $cn registerevent 319 {set ::client::whois(channels) [msg]}
    $cn registerevent 320 {set ::client::whois(status) [msg]}
    $cn registerevent 302 {set ::client::whois(host) [msg]}
    $cn registerevent 318 {
        variable ::client::whois







|
|
|







1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
        } err]} { puts stderr $err }
    }

    $cn registerevent 333 {
        # topicinfo - a handy marker for 'we just joined a channel'
        # Ask Chanserv to op us.
        cmd-send "PRIVMSG Chanserv :op $::client::channel $::client::nick"
    }

    # WHOIS handling from Jabber user
    $cn registerevent 311 {set ::client::whois(realname) [msg];set ::client::whois(address) [additional]}
    $cn registerevent 312 {set ::client::whois(url) [msg]}
    $cn registerevent 319 {set ::client::whois(channels) [msg]}
    $cn registerevent 320 {set ::client::whois(status) [msg]}
    $cn registerevent 302 {set ::client::whois(host) [msg]}
    $cn registerevent 318 {
        variable ::client::whois
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
    }

    $cn registerevent defaultevent {
        switch -exact -- [action] {
            PONG {}
            KICK {
                #::ijbridge::send "[who] kicked someone \"[msg]\""
                ::log::log notice "[action]:[who]:[target]:[msg]" 
            }
            MODE -
            default {
                ::log::log notice "[action]:[who]:[target]:[msg]" 
            }
        }
    }

    $cn registerevent PRIVMSG {
        if {[catch {
            if { [string equal [target] $client::channel] } {
                set emote 0
                set msg [msg]
                if { [string match "\001*\001" [msg]] } {
                    if { [regexp {^\001ACTION (.+)\001$} [msg] -> msg] } {
                        set emote 1
                    }
                }
                ijbridge::IrcToJabber [who] $msg $emote
            } elseif {[string equal [target] $client::nick]} {
                ::ijbridge::BotCommand [who] [who] [msg]
            } else {
                log::log debug "[action]:[who]:[target]:[msg]"
            }
        } err]} { log::log notice $err }
    }

    $cn registerevent PART {
        variable ::ijbridge::IrcUserList
	if { [target] eq $client::channel && [who] ne $client::nick } {
	    set IrcUserList \
		    [lsearch -all -inline -exact -not $IrcUserList [who]]
	    ijbridge::send "*** [who] leaves"
	}

    }

    $cn registerevent JOIN {
        variable ::ijbridge::IrcUserList
	if { [lsearch -exact {ircbridge azbridge ijbridge ijchain} [who]] == -1 \
		&& [who] ne $client::nick } {
	    if { [lsearch -exact $IrcUserList [who]] == -1 } {
		lappend IrcUserList [who]
	    }
	    ijbridge::send "*** [who] joins"
	}
    }

    $cn registerevent NICK {
        variable ::ijbridge::IrcUserList
	if { [who] eq $::client::nick } {
	    set ::client::nick [msg]
	} else {
	    set IrcUserList \
		    [lsearch -all -inline -exact -not $IrcUserList [who]]
	    if { [lsearch -exact $IrcUserList [msg]] == -1 } {
		lappend IrcUserList [msg]
	    }
	    ijbridge::send "*** [who] is now known as [msg]"
	}
    }

    $cn registerevent QUIT {







|



|






|








|









|









|
|
|













|







1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
    }

    $cn registerevent defaultevent {
        switch -exact -- [action] {
            PONG {}
            KICK {
                #::ijbridge::send "[who] kicked someone \"[msg]\""
                ::log::log notice "[action]:[who]:[target]:[msg]"
            }
            MODE -
            default {
                ::log::log notice "[action]:[who]:[target]:[msg]"
            }
        }
    }

    $cn registerevent PRIVMSG {
        if {[catch {
            if { [string equal [target] $::client::channel] } {
                set emote 0
                set msg [msg]
                if { [string match "\001*\001" [msg]] } {
                    if { [regexp {^\001ACTION (.+)\001$} [msg] -> msg] } {
                        set emote 1
                    }
                }
                ijbridge::IrcToJabber [who] $msg $emote
            } elseif {[string equal [target] $::client::nick]} {
                ::ijbridge::BotCommand [who] [who] [msg]
            } else {
                log::log debug "[action]:[who]:[target]:[msg]"
            }
        } err]} { log::log notice $err }
    }

    $cn registerevent PART {
        variable ::ijbridge::IrcUserList
	if { [target] eq $::client::channel && [who] ne $::client::nick } {
	    set IrcUserList \
		    [lsearch -all -inline -exact -not $IrcUserList [who]]
	    ijbridge::send "*** [who] leaves"
	}

    }

    $cn registerevent JOIN {
        variable ::ijbridge::IrcUserList
	if {    [who] ni {ircbridge azbridge ijbridge ijchain} &&
		[who] ne $::client::nick } {
	    if { [who] ni $IrcUserList } {
		lappend IrcUserList [who]
	    }
	    ijbridge::send "*** [who] joins"
	}
    }

    $cn registerevent NICK {
        variable ::ijbridge::IrcUserList
	if { [who] eq $::client::nick } {
	    set ::client::nick [msg]
	} else {
	    set IrcUserList \
		    [lsearch -all -inline -exact -not $IrcUserList [who]]
	    if { [msg] ni $IrcUserList } {
		lappend IrcUserList [msg]
	    }
	    ijbridge::send "*** [who] is now known as [msg]"
	}
    }

    $cn registerevent QUIT {
1250
1251
1252
1253
1254
1255
1256

1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267



1268
1269
1270
1271
1272
1273
1274
	}
    }
}

# connect to the server and register

proc client::connect {cn server port} {

    variable ::ijbridge::Limit
    variable nick
    # set up variable for rate limiting
    array set Limit [list last [clock seconds] queue {} lines 0]
    ::log::log notice "Connecting to $server on port $port"
    if {[catch {$cn connect $server $port} err]} {
        ::log::log notice "Could not connect: $err"
        after 10000 [list [namespace current]::connect $cn $server $port]
        return
    }
    fconfigure [$cn socket] -encoding utf-8 ;# We shall always be utf-8



    $cn user $nick localhost domain "Tcl Jabber-IRC bridge"
    $cn nick $nick
    ping
    ::log::log notice "Connected to $server"
}

proc client::ping {} {







>










|
>
>
>







1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
	}
    }
}

# connect to the server and register

proc client::connect {cn server port} {
    variable ::ijbridge::Options
    variable ::ijbridge::Limit
    variable nick
    # set up variable for rate limiting
    array set Limit [list last [clock seconds] queue {} lines 0]
    ::log::log notice "Connecting to $server on port $port"
    if {[catch {$cn connect $server $port} err]} {
        ::log::log notice "Could not connect: $err"
        after 10000 [list [namespace current]::connect $cn $server $port]
        return
    }
    fconfigure [$cn socket] -encoding utf-8 -profile replace ;# We shall always be utf-8
    if {$Options(IrcTLS)} {
	tls::import [$cn socket]
    }
    $cn user $nick localhost domain "Tcl Jabber-IRC bridge"
    $cn nick $nick
    ping
    ::log::log notice "Connected to $server"
}

proc client::ping {} {
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
#::irc::config debug 0

# -------------------------------------------------------------------------

# ijbridge::ReadControl --
#
#	Reads commands from stdin and evaluates them. This permits
#	us to issue commands to the server while it is still 
#	running. Suitable commands are ijbridge::presence and
#	ijbridge::say or ijbridge::xmit.
#
proc ::ijbridge::ReadControl {chan} {
    variable Control
    if {![info exists Control]} {set Control {}}
    if {[eof $chan]} {







|







1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
#::irc::config debug 0

# -------------------------------------------------------------------------

# ijbridge::ReadControl --
#
#	Reads commands from stdin and evaluates them. This permits
#	us to issue commands to the server while it is still
#	running. Suitable commands are ijbridge::presence and
#	ijbridge::say or ijbridge::xmit.
#
proc ::ijbridge::ReadControl {chan} {
    variable Control
    if {![info exists Control]} {set Control {}}
    if {[eof $chan]} {
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
    fileevent $chan readable \
        [namespace code [list NotificationGet $chan]]
}
proc ::ijbridge::NotificationGet {chan} {
    variable Options
    upvar #0 [namespace current]::$chan state
    if {[gets $chan line] != -1} {
        foreach {len hash data} $line break
        set data [base64::decode $data]
        set check [md5::md5 -hex $data]
        if {[string length $data] != $len} {
            puts stderr "failed length check from $state(addr):$state(port)"
        } elseif {$check ne $hash} {
            puts stderr "failed checksum from $state(addr):$state(port)"
        } else {
            foreach {user channel url title} $data break
            if {$channel eq [jid node $Options(Conference)] \
                    || $channel eq $Options(IrcChannel) \
                    || $channel eq [string map [list "#" ""] $Options(IrcChannel)]} {
                BotSay $::client::channel \
                    "$user has created a new paste at $url \"$title\""
            } else {
                puts stderr "channel \"$channel\" ignored"
            }
        }
        NotificationClose $chan







|







|
|
|
|







1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
    fileevent $chan readable \
        [namespace code [list NotificationGet $chan]]
}
proc ::ijbridge::NotificationGet {chan} {
    variable Options
    upvar #0 [namespace current]::$chan state
    if {[gets $chan line] != -1} {
        lassign $line len hash data
        set data [base64::decode $data]
        set check [md5::md5 -hex $data]
        if {[string length $data] != $len} {
            puts stderr "failed length check from $state(addr):$state(port)"
        } elseif {$check ne $hash} {
            puts stderr "failed checksum from $state(addr):$state(port)"
        } else {
            lassign $data user channel url title
            if {$channel eq [jid node $Options(Conference)] ||
		$channel eq $Options(IrcChannel)            ||
		$channel eq [string map [list "#" ""] $Options(IrcChannel)]} {
                BotSay $::client::channel \
                    "$user has created a new paste at $url \"$title\""
            } else {
                puts stderr "channel \"$channel\" ignored"
            }
        }
        NotificationClose $chan
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
    }

    # Connect to IRC
    client::create $ijbridge::Options(IrcServer) \
        $ijbridge::Options(IrcPort) \
        $ijbridge::Options(IrcUser) \
        $ijbridge::Options(IrcChannel)
    
    # Connect to Jabber.
    eval [linsert $args 0 ::ijbridge::connect]
    set ::ijbridge::statid [after 3600000 ::ijbridge::DumpStats]

    # Create the notification service port
    if {$ijbridge::Options(NotificationPort) ne {}} {
        ::ijbridge::NotificationStart







|







1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
    }

    # Connect to IRC
    client::create $ijbridge::Options(IrcServer) \
        $ijbridge::Options(IrcPort) \
        $ijbridge::Options(IrcUser) \
        $ijbridge::Options(IrcChannel)

    # Connect to Jabber.
    eval [linsert $args 0 ::ijbridge::connect]
    set ::ijbridge::statid [after 3600000 ::ijbridge::DumpStats]

    # Create the notification service port
    if {$ijbridge::Options(NotificationPort) ne {}} {
        ::ijbridge::NotificationStart
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
        } else {
            # Permit running as a Windows service.
            if {![info exists tcl_service]} {
                vwait ::forever
            }
        }
    }
    
    # Record the statistics to file
    ::ijbridge::DumpStats
}

if {!$tcl_interactive} {
    set r [catch [linsert $argv 0 Main] err]
    if {$r} {puts $errorInfo}
    exit $r
}







|









1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
        } else {
            # Permit running as a Windows service.
            if {![info exists tcl_service]} {
                vwait ::forever
            }
        }
    }

    # Record the statistics to file
    ::ijbridge::DumpStats
}

if {!$tcl_interactive} {
    set r [catch [linsert $argv 0 Main] err]
    if {$r} {puts $errorInfo}
    exit $r
}