| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package IRC::Utils; |
|
2
|
|
|
|
|
|
|
BEGIN { |
|
3
|
2
|
|
|
2
|
|
29196
|
$IRC::Utils::AUTHORITY = 'cpan:HINRIK'; |
|
4
|
|
|
|
|
|
|
} |
|
5
|
|
|
|
|
|
|
BEGIN { |
|
6
|
2
|
|
|
2
|
|
39
|
$IRC::Utils::VERSION = '0.12'; |
|
7
|
|
|
|
|
|
|
} |
|
8
|
|
|
|
|
|
|
|
|
9
|
2
|
|
|
2
|
|
17
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
64
|
|
|
10
|
2
|
|
|
2
|
|
9
|
use warnings FATAL => 'all'; |
|
|
2
|
|
|
|
|
13
|
|
|
|
2
|
|
|
|
|
101
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
889
|
use Encode qw(decode); |
|
|
2
|
|
|
|
|
11268
|
|
|
|
2
|
|
|
|
|
127
|
|
|
13
|
2
|
|
|
2
|
|
12945
|
use Encode::Guess; |
|
|
2
|
|
|
|
|
14948
|
|
|
|
2
|
|
|
|
|
16
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
require Exporter; |
|
16
|
2
|
|
|
2
|
|
154
|
use base qw(Exporter); |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
781
|
|
|
17
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
18
|
|
|
|
|
|
|
uc_irc lc_irc parse_mode_line normalize_mask matches_mask matches_mask_array |
|
19
|
|
|
|
|
|
|
unparse_mode_line gen_mode_change parse_user is_valid_nick_name eq_irc |
|
20
|
|
|
|
|
|
|
decode_irc is_valid_chan_name has_color has_formatting strip_color |
|
21
|
|
|
|
|
|
|
strip_formatting NORMAL BOLD UNDERLINE REVERSE ITALIC FIXED WHITE BLACK |
|
22
|
|
|
|
|
|
|
BLUE GREEN RED BROWN PURPLE ORANGE YELLOW LIGHT_GREEN TEAL LIGHT_CYAN |
|
23
|
|
|
|
|
|
|
LIGHT_BLUE PINK GREY LIGHT_GREY numeric_to_name name_to_numeric |
|
24
|
|
|
|
|
|
|
); |
|
25
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( ALL => [@EXPORT_OK] ); |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use constant { |
|
28
|
|
|
|
|
|
|
# cancel all formatting and colors |
|
29
|
2
|
|
|
|
|
8868
|
NORMAL => "\x0f", |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# formatting |
|
32
|
|
|
|
|
|
|
BOLD => "\x02", |
|
33
|
|
|
|
|
|
|
UNDERLINE => "\x1f", |
|
34
|
|
|
|
|
|
|
REVERSE => "\x16", |
|
35
|
|
|
|
|
|
|
ITALIC => "\x1d", |
|
36
|
|
|
|
|
|
|
FIXED => "\x11", |
|
37
|
|
|
|
|
|
|
BLINK => "\x06", |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# mIRC colors |
|
40
|
|
|
|
|
|
|
WHITE => "\x0300", |
|
41
|
|
|
|
|
|
|
BLACK => "\x0301", |
|
42
|
|
|
|
|
|
|
BLUE => "\x0302", |
|
43
|
|
|
|
|
|
|
GREEN => "\x0303", |
|
44
|
|
|
|
|
|
|
RED => "\x0304", |
|
45
|
|
|
|
|
|
|
BROWN => "\x0305", |
|
46
|
|
|
|
|
|
|
PURPLE => "\x0306", |
|
47
|
|
|
|
|
|
|
ORANGE => "\x0307", |
|
48
|
|
|
|
|
|
|
YELLOW => "\x0308", |
|
49
|
|
|
|
|
|
|
LIGHT_GREEN => "\x0309", |
|
50
|
|
|
|
|
|
|
TEAL => "\x0310", |
|
51
|
|
|
|
|
|
|
LIGHT_CYAN => "\x0311", |
|
52
|
|
|
|
|
|
|
LIGHT_BLUE => "\x0312", |
|
53
|
|
|
|
|
|
|
PINK => "\x0313", |
|
54
|
|
|
|
|
|
|
GREY => "\x0314", |
|
55
|
|
|
|
|
|
|
LIGHT_GREY => "\x0315", |
|
56
|
2
|
|
|
2
|
|
13
|
}; |
|
|
2
|
|
|
|
|
4
|
|
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# list originally snatched from AnyEvent::IRC::Util |
|
59
|
|
|
|
|
|
|
our %NUMERIC2NAME = ( |
|
60
|
|
|
|
|
|
|
'001' => 'RPL_WELCOME', # RFC2812 |
|
61
|
|
|
|
|
|
|
'002' => 'RPL_YOURHOST', # RFC2812 |
|
62
|
|
|
|
|
|
|
'003' => 'RPL_CREATED', # RFC2812 |
|
63
|
|
|
|
|
|
|
'004' => 'RPL_MYINFO', # RFC2812 |
|
64
|
|
|
|
|
|
|
'005' => 'RPL_ISUPPORT', # draft-brocklesby-irc-isupport-03 |
|
65
|
|
|
|
|
|
|
'008' => 'RPL_SNOMASK', # Undernet |
|
66
|
|
|
|
|
|
|
'009' => 'RPL_STATMEMTOT', # Undernet |
|
67
|
|
|
|
|
|
|
'010' => 'RPL_STATMEM', # Undernet |
|
68
|
|
|
|
|
|
|
'020' => 'RPL_CONNECTING', # IRCnet |
|
69
|
|
|
|
|
|
|
'014' => 'RPL_YOURCOOKIE', # IRCnet |
|
70
|
|
|
|
|
|
|
'042' => 'RPL_YOURID', # IRCnet |
|
71
|
|
|
|
|
|
|
'043' => 'RPL_SAVENICK', # IRCnet |
|
72
|
|
|
|
|
|
|
'050' => 'RPL_ATTEMPTINGJUNC', # aircd |
|
73
|
|
|
|
|
|
|
'051' => 'RPL_ATTEMPTINGREROUTE', # aircd |
|
74
|
|
|
|
|
|
|
'200' => 'RPL_TRACELINK', # RFC1459 |
|
75
|
|
|
|
|
|
|
'201' => 'RPL_TRACECONNECTING', # RFC1459 |
|
76
|
|
|
|
|
|
|
'202' => 'RPL_TRACEHANDSHAKE', # RFC1459 |
|
77
|
|
|
|
|
|
|
'203' => 'RPL_TRACEUNKNOWN', # RFC1459 |
|
78
|
|
|
|
|
|
|
'204' => 'RPL_TRACEOPERATOR', # RFC1459 |
|
79
|
|
|
|
|
|
|
'205' => 'RPL_TRACEUSER', # RFC1459 |
|
80
|
|
|
|
|
|
|
'206' => 'RPL_TRACESERVER', # RFC1459 |
|
81
|
|
|
|
|
|
|
'207' => 'RPL_TRACESERVICE', # RFC2812 |
|
82
|
|
|
|
|
|
|
'208' => 'RPL_TRACENEWTYPE', # RFC1459 |
|
83
|
|
|
|
|
|
|
'209' => 'RPL_TRACECLASS', # RFC2812 |
|
84
|
|
|
|
|
|
|
'210' => 'RPL_STATS', # aircd |
|
85
|
|
|
|
|
|
|
'211' => 'RPL_STATSLINKINFO', # RFC1459 |
|
86
|
|
|
|
|
|
|
'212' => 'RPL_STATSCOMMANDS', # RFC1459 |
|
87
|
|
|
|
|
|
|
'213' => 'RPL_STATSCLINE', # RFC1459 |
|
88
|
|
|
|
|
|
|
'214' => 'RPL_STATSNLINE', # RFC1459 |
|
89
|
|
|
|
|
|
|
'215' => 'RPL_STATSILINE', # RFC1459 |
|
90
|
|
|
|
|
|
|
'216' => 'RPL_STATSKLINE', # RFC1459 |
|
91
|
|
|
|
|
|
|
'217' => 'RPL_STATSQLINE', # RFC1459 |
|
92
|
|
|
|
|
|
|
'218' => 'RPL_STATSYLINE', # RFC1459 |
|
93
|
|
|
|
|
|
|
'219' => 'RPL_ENDOFSTATS', # RFC1459 |
|
94
|
|
|
|
|
|
|
'221' => 'RPL_UMODEIS', # RFC1459 |
|
95
|
|
|
|
|
|
|
'231' => 'RPL_SERVICEINFO', # RFC1459 |
|
96
|
|
|
|
|
|
|
'233' => 'RPL_SERVICE', # RFC1459 |
|
97
|
|
|
|
|
|
|
'234' => 'RPL_SERVLIST', # RFC1459 |
|
98
|
|
|
|
|
|
|
'235' => 'RPL_SERVLISTEND', # RFC1459 |
|
99
|
|
|
|
|
|
|
'239' => 'RPL_STATSIAUTH', # IRCnet |
|
100
|
|
|
|
|
|
|
'241' => 'RPL_STATSLLINE', # RFC1459 |
|
101
|
|
|
|
|
|
|
'242' => 'RPL_STATSUPTIME', # RFC1459 |
|
102
|
|
|
|
|
|
|
'243' => 'RPL_STATSOLINE', # RFC1459 |
|
103
|
|
|
|
|
|
|
'244' => 'RPL_STATSHLINE', # RFC1459 |
|
104
|
|
|
|
|
|
|
'245' => 'RPL_STATSSLINE', # Bahamut, IRCnet, Hybrid |
|
105
|
|
|
|
|
|
|
'250' => 'RPL_STATSCONN', # ircu, Unreal |
|
106
|
|
|
|
|
|
|
'251' => 'RPL_LUSERCLIENT', # RFC1459 |
|
107
|
|
|
|
|
|
|
'252' => 'RPL_LUSEROP', # RFC1459 |
|
108
|
|
|
|
|
|
|
'253' => 'RPL_LUSERUNKNOWN', # RFC1459 |
|
109
|
|
|
|
|
|
|
'254' => 'RPL_LUSERCHANNELS', # RFC1459 |
|
110
|
|
|
|
|
|
|
'255' => 'RPL_LUSERME', # RFC1459 |
|
111
|
|
|
|
|
|
|
'256' => 'RPL_ADMINME', # RFC1459 |
|
112
|
|
|
|
|
|
|
'257' => 'RPL_ADMINLOC1', # RFC1459 |
|
113
|
|
|
|
|
|
|
'258' => 'RPL_ADMINLOC2', # RFC1459 |
|
114
|
|
|
|
|
|
|
'259' => 'RPL_ADMINEMAIL', # RFC1459 |
|
115
|
|
|
|
|
|
|
'261' => 'RPL_TRACELOG', # RFC1459 |
|
116
|
|
|
|
|
|
|
'262' => 'RPL_TRACEEND', # RFC2812 |
|
117
|
|
|
|
|
|
|
'263' => 'RPL_TRYAGAIN', # RFC2812 |
|
118
|
|
|
|
|
|
|
'265' => 'RPL_LOCALUSERS', # aircd, Bahamut, Hybrid |
|
119
|
|
|
|
|
|
|
'266' => 'RPL_GLOBALUSERS', # aircd, Bahamut, Hybrid |
|
120
|
|
|
|
|
|
|
'267' => 'RPL_START_NETSTAT', # aircd |
|
121
|
|
|
|
|
|
|
'268' => 'RPL_NETSTAT', # aircd |
|
122
|
|
|
|
|
|
|
'269' => 'RPL_END_NETSTAT', # aircd |
|
123
|
|
|
|
|
|
|
'270' => 'RPL_PRIVS', # ircu |
|
124
|
|
|
|
|
|
|
'271' => 'RPL_SILELIST', # ircu |
|
125
|
|
|
|
|
|
|
'272' => 'RPL_ENDOFSILELIST', # ircu |
|
126
|
|
|
|
|
|
|
'300' => 'RPL_NONE', # RFC1459 |
|
127
|
|
|
|
|
|
|
'301' => 'RPL_AWAY', # RFC1459 |
|
128
|
|
|
|
|
|
|
'302' => 'RPL_USERHOST', # RFC1459 |
|
129
|
|
|
|
|
|
|
'303' => 'RPL_ISON', # RFC1459 |
|
130
|
|
|
|
|
|
|
'305' => 'RPL_UNAWAY', # RFC1459 |
|
131
|
|
|
|
|
|
|
'306' => 'RPL_NOWAWAY', # RFC1459 |
|
132
|
|
|
|
|
|
|
'307' => 'RPL_WHOISREGNICK', # Bahamut, Unreal, Plexus |
|
133
|
|
|
|
|
|
|
'310' => 'RPL_WHOISMODES', # Plexus |
|
134
|
|
|
|
|
|
|
'311' => 'RPL_WHOISUSER', # RFC1459 |
|
135
|
|
|
|
|
|
|
'312' => 'RPL_WHOISSERVER', # RFC1459 |
|
136
|
|
|
|
|
|
|
'313' => 'RPL_WHOISOPERATOR', # RFC1459 |
|
137
|
|
|
|
|
|
|
'314' => 'RPL_WHOWASUSER', # RFC1459 |
|
138
|
|
|
|
|
|
|
'315' => 'RPL_ENDOFWHO', # RFC1459 |
|
139
|
|
|
|
|
|
|
'317' => 'RPL_WHOISIDLE', # RFC1459 |
|
140
|
|
|
|
|
|
|
'318' => 'RPL_ENDOFWHOIS', # RFC1459 |
|
141
|
|
|
|
|
|
|
'319' => 'RPL_WHOISCHANNELS', # RFC1459 |
|
142
|
|
|
|
|
|
|
'321' => 'RPL_LISTSTART', # RFC1459 |
|
143
|
|
|
|
|
|
|
'322' => 'RPL_LIST', # RFC1459 |
|
144
|
|
|
|
|
|
|
'323' => 'RPL_LISTEND', # RFC1459 |
|
145
|
|
|
|
|
|
|
'324' => 'RPL_CHANNELMODEIS', # RFC1459 |
|
146
|
|
|
|
|
|
|
'325' => 'RPL_UNIQOPIS', # RFC2812 |
|
147
|
|
|
|
|
|
|
'328' => 'RPL_CHANNEL_URL', # Bahamut, AustHex |
|
148
|
|
|
|
|
|
|
'329' => 'RPL_CREATIONTIME', # Bahamut |
|
149
|
|
|
|
|
|
|
'330' => 'RPL_WHOISACCOUNT', # ircu |
|
150
|
|
|
|
|
|
|
'331' => 'RPL_NOTOPIC', # RFC1459 |
|
151
|
|
|
|
|
|
|
'332' => 'RPL_TOPIC', # RFC1459 |
|
152
|
|
|
|
|
|
|
'333' => 'RPL_TOPICWHOTIME', # ircu |
|
153
|
|
|
|
|
|
|
'338' => 'RPL_WHOISACTUALLY', # Bahamut, ircu |
|
154
|
|
|
|
|
|
|
'340' => 'RPL_USERIP', # ircu |
|
155
|
|
|
|
|
|
|
'341' => 'RPL_INVITING', # RFC1459 |
|
156
|
|
|
|
|
|
|
'342' => 'RPL_SUMMONING', # RFC1459 |
|
157
|
|
|
|
|
|
|
'345' => 'RPL_INVITED', # GameSurge |
|
158
|
|
|
|
|
|
|
'346' => 'RPL_INVITELIST', # RFC2812 |
|
159
|
|
|
|
|
|
|
'347' => 'RPL_ENDOFINVITELIST', # RFC2812 |
|
160
|
|
|
|
|
|
|
'348' => 'RPL_EXCEPTLIST', # RFC2812 |
|
161
|
|
|
|
|
|
|
'349' => 'RPL_ENDOFEXCEPTLIST', # RFC2812 |
|
162
|
|
|
|
|
|
|
'351' => 'RPL_VERSION', # RFC1459 |
|
163
|
|
|
|
|
|
|
'352' => 'RPL_WHOREPLY', # RFC1459 |
|
164
|
|
|
|
|
|
|
'353' => 'RPL_NAMREPLY', # RFC1459 |
|
165
|
|
|
|
|
|
|
'354' => 'RPL_WHOSPCRPL', # ircu |
|
166
|
|
|
|
|
|
|
'355' => 'RPL_NAMREPLY_', # QuakeNet |
|
167
|
|
|
|
|
|
|
'361' => 'RPL_KILLDONE', # RFC1459 |
|
168
|
|
|
|
|
|
|
'362' => 'RPL_CLOSING', # RFC1459 |
|
169
|
|
|
|
|
|
|
'363' => 'RPL_CLOSEEND', # RFC1459 |
|
170
|
|
|
|
|
|
|
'364' => 'RPL_LINKS', # RFC1459 |
|
171
|
|
|
|
|
|
|
'365' => 'RPL_ENDOFLINKS', # RFC1459 |
|
172
|
|
|
|
|
|
|
'366' => 'RPL_ENDOFNAMES', # RFC1459 |
|
173
|
|
|
|
|
|
|
'367' => 'RPL_BANLIST', # RFC1459 |
|
174
|
|
|
|
|
|
|
'368' => 'RPL_ENDOFBANLIST', # RFC1459 |
|
175
|
|
|
|
|
|
|
'369' => 'RPL_ENDOFWHOWAS', # RFC1459 |
|
176
|
|
|
|
|
|
|
'371' => 'RPL_INFO', # RFC1459 |
|
177
|
|
|
|
|
|
|
'372' => 'RPL_MOTD', # RFC1459 |
|
178
|
|
|
|
|
|
|
'373' => 'RPL_INFOSTART', # RFC1459 |
|
179
|
|
|
|
|
|
|
'374' => 'RPL_ENDOFINFO', # RFC1459 |
|
180
|
|
|
|
|
|
|
'375' => 'RPL_MOTDSTART', # RFC1459 |
|
181
|
|
|
|
|
|
|
'376' => 'RPL_ENDOFMOTD', # RFC1459 |
|
182
|
|
|
|
|
|
|
'381' => 'RPL_YOUREOPER', # RFC1459 |
|
183
|
|
|
|
|
|
|
'382' => 'RPL_REHASHING', # RFC1459 |
|
184
|
|
|
|
|
|
|
'383' => 'RPL_YOURESERVICE', # RFC2812 |
|
185
|
|
|
|
|
|
|
'384' => 'RPL_MYPORTIS', # RFC1459 |
|
186
|
|
|
|
|
|
|
'385' => 'RPL_NOTOPERANYMORE', # AustHex, Hybrid, Unreal |
|
187
|
|
|
|
|
|
|
'391' => 'RPL_TIME', # RFC1459 |
|
188
|
|
|
|
|
|
|
'392' => 'RPL_USERSSTART', # RFC1459 |
|
189
|
|
|
|
|
|
|
'393' => 'RPL_USERS', # RFC1459 |
|
190
|
|
|
|
|
|
|
'394' => 'RPL_ENDOFUSERS', # RFC1459 |
|
191
|
|
|
|
|
|
|
'395' => 'RPL_NOUSERS', # RFC1459 |
|
192
|
|
|
|
|
|
|
'396' => 'RPL_HOSTHIDDEN', # Undernet |
|
193
|
|
|
|
|
|
|
'401' => 'ERR_NOSUCHNICK', # RFC1459 |
|
194
|
|
|
|
|
|
|
'402' => 'ERR_NOSUCHSERVER', # RFC1459 |
|
195
|
|
|
|
|
|
|
'403' => 'ERR_NOSUCHCHANNEL', # RFC1459 |
|
196
|
|
|
|
|
|
|
'404' => 'ERR_CANNOTSENDTOCHAN', # RFC1459 |
|
197
|
|
|
|
|
|
|
'405' => 'ERR_TOOMANYCHANNELS', # RFC1459 |
|
198
|
|
|
|
|
|
|
'406' => 'ERR_WASNOSUCHNICK', # RFC1459 |
|
199
|
|
|
|
|
|
|
'407' => 'ERR_TOOMANYTARGETS', # RFC1459 |
|
200
|
|
|
|
|
|
|
'408' => 'ERR_NOSUCHSERVICE', # RFC2812 |
|
201
|
|
|
|
|
|
|
'409' => 'ERR_NOORIGIN', # RFC1459 |
|
202
|
|
|
|
|
|
|
'411' => 'ERR_NORECIPIENT', # RFC1459 |
|
203
|
|
|
|
|
|
|
'412' => 'ERR_NOTEXTTOSEND', # RFC1459 |
|
204
|
|
|
|
|
|
|
'413' => 'ERR_NOTOPLEVEL', # RFC1459 |
|
205
|
|
|
|
|
|
|
'414' => 'ERR_WILDTOPLEVEL', # RFC1459 |
|
206
|
|
|
|
|
|
|
'415' => 'ERR_BADMASK', # RFC2812 |
|
207
|
|
|
|
|
|
|
'421' => 'ERR_UNKNOWNCOMMAND', # RFC1459 |
|
208
|
|
|
|
|
|
|
'422' => 'ERR_NOMOTD', # RFC1459 |
|
209
|
|
|
|
|
|
|
'423' => 'ERR_NOADMININFO', # RFC1459 |
|
210
|
|
|
|
|
|
|
'424' => 'ERR_FILEERROR', # RFC1459 |
|
211
|
|
|
|
|
|
|
'425' => 'ERR_NOOPERMOTD', # Unreal |
|
212
|
|
|
|
|
|
|
'429' => 'ERR_TOOMANYAWAY', # Bahamut |
|
213
|
|
|
|
|
|
|
'430' => 'ERR_EVENTNICKCHANGE', # AustHex |
|
214
|
|
|
|
|
|
|
'431' => 'ERR_NONICKNAMEGIVEN', # RFC1459 |
|
215
|
|
|
|
|
|
|
'432' => 'ERR_ERRONEUSNICKNAME', # RFC1459 |
|
216
|
|
|
|
|
|
|
'433' => 'ERR_NICKNAMEINUSE', # RFC1459 |
|
217
|
|
|
|
|
|
|
'436' => 'ERR_NICKCOLLISION', # RFC1459 |
|
218
|
|
|
|
|
|
|
'439' => 'ERR_TARGETTOOFAST', # ircu |
|
219
|
|
|
|
|
|
|
'440' => 'ERR_SERCVICESDOWN', # Bahamut, Unreal |
|
220
|
|
|
|
|
|
|
'441' => 'ERR_USERNOTINCHANNEL', # RFC1459 |
|
221
|
|
|
|
|
|
|
'442' => 'ERR_NOTONCHANNEL', # RFC1459 |
|
222
|
|
|
|
|
|
|
'443' => 'ERR_USERONCHANNEL', # RFC1459 |
|
223
|
|
|
|
|
|
|
'444' => 'ERR_NOLOGIN', # RFC1459 |
|
224
|
|
|
|
|
|
|
'445' => 'ERR_SUMMONDISABLED', # RFC1459 |
|
225
|
|
|
|
|
|
|
'446' => 'ERR_USERSDISABLED', # RFC1459 |
|
226
|
|
|
|
|
|
|
'447' => 'ERR_NONICKCHANGE', # Unreal |
|
227
|
|
|
|
|
|
|
'449' => 'ERR_NOTIMPLEMENTED', # Undernet |
|
228
|
|
|
|
|
|
|
'451' => 'ERR_NOTREGISTERED', # RFC1459 |
|
229
|
|
|
|
|
|
|
'455' => 'ERR_HOSTILENAME', # Unreal |
|
230
|
|
|
|
|
|
|
'459' => 'ERR_NOHIDING', # Unreal |
|
231
|
|
|
|
|
|
|
'460' => 'ERR_NOTFORHALFOPS', # Unreal |
|
232
|
|
|
|
|
|
|
'461' => 'ERR_NEEDMOREPARAMS', # RFC1459 |
|
233
|
|
|
|
|
|
|
'462' => 'ERR_ALREADYREGISTRED', # RFC1459 |
|
234
|
|
|
|
|
|
|
'463' => 'ERR_NOPERMFORHOST', # RFC1459 |
|
235
|
|
|
|
|
|
|
'464' => 'ERR_PASSWDMISMATCH', # RFC1459 |
|
236
|
|
|
|
|
|
|
'465' => 'ERR_YOUREBANNEDCREEP', # RFC1459 |
|
237
|
|
|
|
|
|
|
'466' => 'ERR_YOUWILLBEBANNED', # RFC1459 |
|
238
|
|
|
|
|
|
|
'467' => 'ERR_KEYSET', # RFC1459 |
|
239
|
|
|
|
|
|
|
'469' => 'ERR_LINKSET', # Unreal |
|
240
|
|
|
|
|
|
|
'471' => 'ERR_CHANNELISFULL', # RFC1459 |
|
241
|
|
|
|
|
|
|
'472' => 'ERR_UNKNOWNMODE', # RFC1459 |
|
242
|
|
|
|
|
|
|
'473' => 'ERR_INVITEONLYCHAN', # RFC1459 |
|
243
|
|
|
|
|
|
|
'474' => 'ERR_BANNEDFROMCHAN', # RFC1459 |
|
244
|
|
|
|
|
|
|
'475' => 'ERR_BADCHANNELKEY', # RFC1459 |
|
245
|
|
|
|
|
|
|
'476' => 'ERR_BADCHANMASK', # RFC2812 |
|
246
|
|
|
|
|
|
|
'477' => 'ERR_NOCHANMODES', # RFC2812 |
|
247
|
|
|
|
|
|
|
'478' => 'ERR_BANLISTFULL', # RFC2812 |
|
248
|
|
|
|
|
|
|
'481' => 'ERR_NOPRIVILEGES', # RFC1459 |
|
249
|
|
|
|
|
|
|
'482' => 'ERR_CHANOPRIVSNEEDED', # RFC1459 |
|
250
|
|
|
|
|
|
|
'483' => 'ERR_CANTKILLSERVER', # RFC1459 |
|
251
|
|
|
|
|
|
|
'484' => 'ERR_RESTRICTED', # RFC2812 |
|
252
|
|
|
|
|
|
|
'485' => 'ERR_UNIQOPPRIVSNEEDED', # RFC2812 |
|
253
|
|
|
|
|
|
|
'488' => 'ERR_TSLESSCHAN', # IRCnet |
|
254
|
|
|
|
|
|
|
'491' => 'ERR_NOOPERHOST', # RFC1459 |
|
255
|
|
|
|
|
|
|
'492' => 'ERR_NOSERVICEHOST', # RFC1459 |
|
256
|
|
|
|
|
|
|
'493' => 'ERR_NOFEATURE', # ircu |
|
257
|
|
|
|
|
|
|
'494' => 'ERR_BADFEATURE', # ircu |
|
258
|
|
|
|
|
|
|
'495' => 'ERR_BADLOGTYPE', # ircu |
|
259
|
|
|
|
|
|
|
'496' => 'ERR_BADLOGSYS', # ircu |
|
260
|
|
|
|
|
|
|
'497' => 'ERR_BADLOGVALUE', # ircu |
|
261
|
|
|
|
|
|
|
'498' => 'ERR_ISOPERLCHAN', # ircu |
|
262
|
|
|
|
|
|
|
'501' => 'ERR_UMODEUNKNOWNFLAG', # RFC1459 |
|
263
|
|
|
|
|
|
|
'502' => 'ERR_USERSDONTMATCH', # RFC1459 |
|
264
|
|
|
|
|
|
|
'503' => 'ERR_GHOSTEDCLIENT', # Hybrid |
|
265
|
|
|
|
|
|
|
); |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
our %NAME2NUMERIC; |
|
268
|
|
|
|
|
|
|
while (my ($key, $val) = each %NUMERIC2NAME) { |
|
269
|
|
|
|
|
|
|
$NAME2NUMERIC{$val} = $key; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub numeric_to_name { |
|
273
|
1
|
|
|
1
|
1
|
2315
|
my ($code) = @_; |
|
274
|
1
|
|
|
|
|
13
|
return $NUMERIC2NAME{$code}; |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub name_to_numeric { |
|
278
|
1
|
|
|
1
|
1
|
3
|
my ($name) = @_; |
|
279
|
1
|
|
|
|
|
7
|
return $NAME2NUMERIC{$name}; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub uc_irc { |
|
283
|
12
|
|
|
12
|
1
|
31
|
my ($value, $type) = @_; |
|
284
|
12
|
50
|
|
|
|
24
|
return if !defined $value; |
|
285
|
12
|
100
|
|
|
|
29
|
$type = 'rfc1459' if !defined $type; |
|
286
|
12
|
|
|
|
|
22
|
$type = lc $type; |
|
287
|
|
|
|
|
|
|
|
|
288
|
12
|
100
|
|
|
|
36
|
if ($type eq 'ascii') { |
|
|
|
100
|
|
|
|
|
|
|
289
|
1
|
|
|
|
|
3
|
$value =~ tr/a-z/A-Z/; |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
elsif ($type eq 'strict-rfc1459') { |
|
292
|
1
|
|
|
|
|
3
|
$value =~ tr/a-z{}|/A-Z[]\\/; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
else { |
|
295
|
10
|
|
|
|
|
18
|
$value =~ tr/a-z{}|^/A-Z[]\\~/; |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
12
|
|
|
|
|
47
|
return $value; |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub lc_irc { |
|
302
|
6
|
|
|
6
|
1
|
14
|
my ($value, $type) = @_; |
|
303
|
6
|
50
|
|
|
|
17
|
return if !defined $value; |
|
304
|
6
|
100
|
|
|
|
15
|
$type = 'rfc1459' if !defined $type; |
|
305
|
6
|
|
|
|
|
11
|
$type = lc $type; |
|
306
|
|
|
|
|
|
|
|
|
307
|
6
|
100
|
|
|
|
21
|
if ($type eq 'ascii') { |
|
|
|
100
|
|
|
|
|
|
|
308
|
1
|
|
|
|
|
5
|
$value =~ tr/A-Z/a-z/; |
|
309
|
|
|
|
|
|
|
} |
|
310
|
|
|
|
|
|
|
elsif ($type eq 'strict-rfc1459') { |
|
311
|
1
|
|
|
|
|
3
|
$value =~ tr/A-Z[]\\/a-z{}|/; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
else { |
|
314
|
4
|
|
|
|
|
8
|
$value =~ tr/A-Z[]\\~/a-z{}|^/; |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
|
|
317
|
6
|
|
|
|
|
31
|
return $value; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub eq_irc { |
|
321
|
1
|
|
|
1
|
1
|
3
|
my ($first, $second, $type) = @_; |
|
322
|
1
|
50
|
33
|
|
|
8
|
return if !defined $first || !defined $second; |
|
323
|
1
|
50
|
|
|
|
3
|
return 1 if lc_irc($first, $type) eq lc_irc($second, $type); |
|
324
|
0
|
|
|
|
|
0
|
return; |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub parse_mode_line { |
|
328
|
3
|
|
|
3
|
1
|
2250
|
my @args = @_; |
|
329
|
|
|
|
|
|
|
|
|
330
|
3
|
|
|
|
|
10
|
my $chanmodes = [qw(beI k l imnpstaqr)]; |
|
331
|
3
|
|
|
|
|
6
|
my $statmodes = 'ohv'; |
|
332
|
3
|
|
|
|
|
7
|
my $hashref = { }; |
|
333
|
3
|
|
|
|
|
5
|
my $count = 0; |
|
334
|
|
|
|
|
|
|
|
|
335
|
3
|
|
|
|
|
12
|
while (my $arg = shift @args) { |
|
336
|
3
|
50
|
66
|
|
|
35
|
if ( ref $arg eq 'ARRAY' ) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
337
|
0
|
|
|
|
|
0
|
$chanmodes = $arg; |
|
338
|
0
|
|
|
|
|
0
|
next; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
elsif (ref $arg eq 'HASH') { |
|
341
|
0
|
|
|
|
|
0
|
$statmodes = join '', keys %{ $arg }; |
|
|
0
|
|
|
|
|
0
|
|
|
342
|
0
|
|
|
|
|
0
|
next; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
elsif ($arg =~ /^[-+]/ or $count == 0) { |
|
345
|
3
|
|
|
|
|
5
|
my $action = '+'; |
|
346
|
3
|
|
|
|
|
11
|
for my $char (split //, $arg) { |
|
347
|
6
|
100
|
100
|
|
|
27
|
if ($char eq '+' or $char eq '-') { |
|
348
|
2
|
|
|
|
|
4
|
$action = $char; |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
else { |
|
351
|
4
|
|
|
|
|
6
|
push @{ $hashref->{modes} }, $action . $char; |
|
|
4
|
|
|
|
|
16
|
|
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
6
|
100
|
33
|
|
|
97
|
if (length $chanmodes->[0] && length $chanmodes->[1] && length $statmodes |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
355
|
|
|
|
|
|
|
&& $char =~ /[$statmodes$chanmodes->[0]$chanmodes->[1]]/) { |
|
356
|
4
|
|
|
|
|
14
|
push @{ $hashref->{args} }, shift @args; |
|
|
4
|
|
|
|
|
13
|
|
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
|
|
359
|
6
|
50
|
66
|
|
|
74
|
if (length $chanmodes->[2] && $action eq '+' && $char =~ /[$chanmodes->[2]]/) { |
|
|
|
|
66
|
|
|
|
|
|
360
|
0
|
|
|
|
|
0
|
push @{ $hashref->{args} }, shift @args; |
|
|
0
|
|
|
|
|
0
|
|
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
else { |
|
365
|
0
|
|
|
|
|
0
|
push @{ $hashref->{args} }, $arg; |
|
|
0
|
|
|
|
|
0
|
|
|
366
|
|
|
|
|
|
|
} |
|
367
|
3
|
|
|
|
|
11
|
$count++; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
3
|
|
|
|
|
12
|
return $hashref; |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
sub normalize_mask { |
|
374
|
2
|
|
|
2
|
1
|
1247
|
my ($arg) = @_; |
|
375
|
2
|
50
|
|
|
|
6
|
return if !defined $arg; |
|
376
|
|
|
|
|
|
|
|
|
377
|
2
|
|
|
|
|
5
|
$arg =~ s/\*{2,}/*/g; |
|
378
|
2
|
|
|
|
|
3
|
my @mask; |
|
379
|
|
|
|
|
|
|
my $remainder; |
|
380
|
2
|
100
|
66
|
|
|
13
|
if ($arg !~ /!/ and $arg =~ /@/) { |
|
381
|
1
|
|
|
|
|
2
|
$remainder = $arg; |
|
382
|
1
|
|
|
|
|
2
|
$mask[0] = '*'; |
|
383
|
|
|
|
|
|
|
} |
|
384
|
|
|
|
|
|
|
else { |
|
385
|
1
|
|
|
|
|
5
|
($mask[0], $remainder) = split /!/, $arg, 2; |
|
386
|
|
|
|
|
|
|
} |
|
387
|
|
|
|
|
|
|
|
|
388
|
2
|
100
|
|
|
|
8
|
$remainder =~ s/!//g if defined $remainder; |
|
389
|
2
|
100
|
|
|
|
7
|
@mask[1..2] = split(/@/, $remainder, 2) if defined $remainder; |
|
390
|
2
|
100
|
|
|
|
7
|
$mask[2] =~ s/@//g if defined $mask[2]; |
|
391
|
|
|
|
|
|
|
|
|
392
|
2
|
|
|
|
|
5
|
for my $i (1..2) { |
|
393
|
4
|
100
|
|
|
|
13
|
$mask[$i] = '*' if !defined $mask[$i]; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
2
|
|
|
|
|
9
|
return $mask[0] . '!' . $mask[1] . '@' . $mask[2]; |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub unparse_mode_line { |
|
399
|
4
|
|
|
4
|
1
|
8
|
my ($line) = @_; |
|
400
|
4
|
50
|
33
|
|
|
26
|
return if !defined $line || !length $line; |
|
401
|
|
|
|
|
|
|
|
|
402
|
4
|
|
|
|
|
8
|
my $action; my $return; |
|
403
|
4
|
|
|
|
|
17
|
for my $mode ( split(//,$line) ) { |
|
404
|
48
|
100
|
100
|
|
|
310
|
if ($mode =~ /^(\+|-)$/ && (!$action || $mode ne $action)) { |
|
|
|
|
66
|
|
|
|
|
|
405
|
9
|
|
|
|
|
12
|
$return .= $mode; |
|
406
|
9
|
|
|
|
|
12
|
$action = $mode; |
|
407
|
9
|
|
|
|
|
13
|
next; |
|
408
|
|
|
|
|
|
|
} |
|
409
|
39
|
100
|
100
|
|
|
155
|
$return .= $mode if ($mode ne '+' and $mode ne '-'); |
|
410
|
|
|
|
|
|
|
} |
|
411
|
4
|
|
|
|
|
18
|
$return =~ s/[+-]$//; |
|
412
|
4
|
|
|
|
|
32
|
return $return; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
sub gen_mode_change { |
|
416
|
3
|
|
|
3
|
1
|
9
|
my ($before, $after) = @_; |
|
417
|
3
|
50
|
|
|
|
10
|
$before = '' if !defined $before; |
|
418
|
3
|
50
|
|
|
|
8
|
$after = '' if !defined $after; |
|
419
|
|
|
|
|
|
|
|
|
420
|
3
|
|
|
|
|
12
|
my @before = split //, $before; |
|
421
|
3
|
|
|
|
|
11
|
my @after = split //, $after; |
|
422
|
3
|
|
|
|
|
6
|
my $string = ''; |
|
423
|
3
|
|
|
|
|
9
|
my @hunks = _diff(\@before, \@after); |
|
424
|
3
|
|
|
|
|
20
|
$string .= $_->[0] . $_->[1] for @hunks; |
|
425
|
|
|
|
|
|
|
|
|
426
|
3
|
|
|
|
|
8
|
return unparse_mode_line($string); |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub is_valid_nick_name { |
|
430
|
2
|
|
|
2
|
1
|
25
|
my ($nickname) = @_; |
|
431
|
2
|
50
|
33
|
|
|
12
|
return if !defined $nickname || !length $nickname; |
|
432
|
2
|
100
|
|
|
|
14
|
return 1 if $nickname =~ /^[A-Za-z_`\-^\|\\\{}\[\]][A-Za-z_0-9`\-^\|\\\{}\[\]]*$/; |
|
433
|
1
|
|
|
|
|
4
|
return; |
|
434
|
|
|
|
|
|
|
} |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub is_valid_chan_name { |
|
437
|
3
|
|
|
3
|
1
|
7
|
my $channel = shift; |
|
438
|
3
|
|
50
|
|
|
21
|
my $chantypes = shift || ['#', '&']; |
|
439
|
3
|
50
|
|
|
|
10
|
return if !@$chantypes; |
|
440
|
3
|
|
|
|
|
7
|
my $chanprefix = join '', @$chantypes; |
|
441
|
3
|
50
|
33
|
|
|
25
|
return if !defined $channel || !length $channel; |
|
442
|
|
|
|
|
|
|
|
|
443
|
3
|
100
|
|
|
|
14
|
return if bytes::length($channel) > 200; |
|
444
|
2
|
100
|
|
|
|
7164
|
return 1 if $channel =~ /^[$chanprefix][^ \a\0\012\015,:]+$/; |
|
445
|
1
|
|
|
|
|
5
|
return; |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
sub matches_mask_array { |
|
449
|
2
|
|
|
2
|
1
|
4
|
my ($masks, $matches, $mapping) = @_; |
|
450
|
|
|
|
|
|
|
|
|
451
|
2
|
50
|
33
|
|
|
9
|
return if !defined $masks || !defined $matches; |
|
452
|
2
|
50
|
|
|
|
7
|
return if ref $masks ne 'ARRAY'; |
|
453
|
2
|
50
|
|
|
|
4
|
return if ref $matches ne 'ARRAY'; |
|
454
|
2
|
|
|
|
|
3
|
my $ref = { }; |
|
455
|
|
|
|
|
|
|
|
|
456
|
2
|
|
|
|
|
4
|
for my $mask (@$masks) { |
|
457
|
2
|
|
|
|
|
3
|
for my $match (@$matches) { |
|
458
|
2
|
100
|
|
|
|
3
|
if (matches_mask($mask, $match, $mapping)) { |
|
459
|
1
|
|
|
|
|
3
|
push @{ $ref->{ $mask } }, $match; |
|
|
1
|
|
|
|
|
12
|
|
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
} |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
|
|
464
|
2
|
|
|
|
|
10
|
return $ref; |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
sub matches_mask { |
|
468
|
4
|
|
|
4
|
1
|
404
|
my ($mask, $match, $mapping) = @_; |
|
469
|
4
|
50
|
33
|
|
|
15
|
return if !defined $mask || !length $mask; |
|
470
|
4
|
50
|
33
|
|
|
15
|
return if !defined $match || !length $match; |
|
471
|
|
|
|
|
|
|
|
|
472
|
4
|
|
|
|
|
8
|
my $umask = quotemeta uc_irc($mask, $mapping); |
|
473
|
4
|
|
|
|
|
14
|
$umask =~ s/\\\*/[\x01-\xFF]{0,}/g; |
|
474
|
4
|
|
|
|
|
6
|
$umask =~ s/\\\?/[\x01-\xFF]{1,1}/g; |
|
475
|
4
|
|
|
|
|
8
|
$match = uc_irc($match, $mapping); |
|
476
|
|
|
|
|
|
|
|
|
477
|
4
|
100
|
|
|
|
61
|
return 1 if $match =~ /^$umask$/; |
|
478
|
2
|
|
|
|
|
8
|
return; |
|
479
|
|
|
|
|
|
|
} |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub parse_user { |
|
482
|
2
|
|
|
2
|
1
|
12
|
my ($user) = @_; |
|
483
|
2
|
50
|
|
|
|
6
|
return if !defined $user; |
|
484
|
|
|
|
|
|
|
|
|
485
|
2
|
|
|
|
|
11
|
my ($n, $u, $h) = split /[!@]/, $user; |
|
486
|
2
|
100
|
|
|
|
8
|
return ($n, $u, $h) if wantarray(); |
|
487
|
1
|
|
|
|
|
4
|
return $n; |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub has_color { |
|
491
|
3
|
|
|
3
|
1
|
1713
|
my ($string) = @_; |
|
492
|
3
|
50
|
|
|
|
10
|
return if !defined $string; |
|
493
|
3
|
100
|
|
|
|
18
|
return 1 if $string =~ /[\x03\x04\x1B]/; |
|
494
|
1
|
|
|
|
|
7
|
return; |
|
495
|
|
|
|
|
|
|
} |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
sub has_formatting { |
|
498
|
5
|
|
|
5
|
1
|
7
|
my ($string) = @_; |
|
499
|
5
|
50
|
|
|
|
13
|
return if !defined $string; |
|
500
|
5
|
100
|
|
|
|
24
|
return 1 if $string =~/[\x02\x1f\x16\x1d\x11\x06]/; |
|
501
|
3
|
|
|
|
|
10
|
return; |
|
502
|
|
|
|
|
|
|
} |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
sub strip_color { |
|
505
|
4
|
|
|
4
|
1
|
643
|
my ($string) = @_; |
|
506
|
4
|
50
|
|
|
|
13
|
return if !defined $string; |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
# mIRC colors |
|
509
|
4
|
|
|
|
|
32
|
$string =~ s/\x03(?:,\d{1,2}|\d{1,2}(?:,\d{1,2})?)?//g; |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
# RGB colors supported by some clients |
|
512
|
4
|
|
|
|
|
49
|
$string =~ s/\x04[0-9a-fA-F]{0,6}//ig; |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# see ECMA-48 + advice by urxvt author |
|
515
|
4
|
|
|
|
|
8
|
$string =~ s/\x1B\[.*?[\x00-\x1F\x40-\x7E]//g; |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# strip cancellation codes too if there are no formatting codes |
|
518
|
4
|
100
|
|
|
|
11
|
$string =~ s/\x0f//g if !has_formatting($string); |
|
519
|
4
|
|
|
|
|
16
|
return $string; |
|
520
|
|
|
|
|
|
|
} |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
sub strip_formatting { |
|
523
|
2
|
|
|
2
|
1
|
7
|
my ($string) = @_; |
|
524
|
2
|
50
|
|
|
|
8
|
return if !defined $string; |
|
525
|
2
|
|
|
|
|
18
|
$string =~ s/[\x02\x1f\x16\x1d\x11\x06]//g; |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# strip cancellation codes too if there are no color codes |
|
528
|
2
|
100
|
|
|
|
8
|
$string =~ s/\x0f//g if !has_color($string); |
|
529
|
|
|
|
|
|
|
|
|
530
|
2
|
|
|
|
|
8
|
return $string; |
|
531
|
|
|
|
|
|
|
} |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub decode_irc { |
|
534
|
2
|
|
|
2
|
1
|
29848
|
my ($line) = @_; |
|
535
|
2
|
|
|
|
|
18
|
my $utf8 = guess_encoding($line, 'utf8'); |
|
536
|
2
|
100
|
|
|
|
532
|
return ref $utf8 ? decode('utf8', $line) : decode('cp1252', $line); |
|
537
|
|
|
|
|
|
|
} |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub _diff { |
|
540
|
3
|
|
|
3
|
|
6
|
my ($before, $after) = @_; |
|
541
|
3
|
|
|
|
|
6
|
my %in_before; |
|
542
|
3
|
|
|
|
|
12
|
@in_before{@$before} = (); |
|
543
|
3
|
|
|
|
|
11
|
my %in_after; |
|
544
|
3
|
|
|
|
|
10
|
@in_after{@$after} = (); |
|
545
|
3
|
|
|
|
|
4
|
my (@diff, %seen); |
|
546
|
|
|
|
|
|
|
|
|
547
|
3
|
|
|
|
|
5
|
for my $seen (@$before) { |
|
548
|
8
|
100
|
66
|
|
|
40
|
next if exists $seen{$seen} || exists $in_after{$seen}; |
|
549
|
6
|
|
|
|
|
8
|
$seen{$seen} = 1; |
|
550
|
6
|
|
|
|
|
17
|
push @diff, ['-', $seen]; |
|
551
|
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
|
|
553
|
3
|
|
|
|
|
7
|
%seen = (); |
|
554
|
|
|
|
|
|
|
|
|
555
|
3
|
|
|
|
|
6
|
for my $seen (@$after) { |
|
556
|
12
|
100
|
66
|
|
|
59
|
next if exists $seen{$seen} || exists $in_before{$seen}; |
|
557
|
10
|
|
|
|
|
15
|
$seen{$seen} = 1; |
|
558
|
10
|
|
|
|
|
28
|
push @diff, ['+', $seen]; |
|
559
|
|
|
|
|
|
|
} |
|
560
|
|
|
|
|
|
|
|
|
561
|
3
|
|
|
|
|
18
|
return @diff; |
|
562
|
|
|
|
|
|
|
} |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
1; |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=encoding utf8 |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head1 NAME |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
IRC::Utils - Common utilities for IRC-related tasks |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
use strict; |
|
575
|
|
|
|
|
|
|
use warnings; |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
use IRC::Utils ':ALL'; |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
my $nickname = '^Lame|BOT[moo]'; |
|
580
|
|
|
|
|
|
|
my $uppercase_nick = uc_irc($nickname); |
|
581
|
|
|
|
|
|
|
my $lowercase_nick = lc_irc($nickname); |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
print "They're equivalent\n" if eq_irc($uppercase_nick, $lowercase_nick); |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
my $mode_line = 'ov+b-i Bob sue stalin*!*@*'; |
|
586
|
|
|
|
|
|
|
my $hashref = parse_mode_line($mode_line); |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
my $banmask = 'stalin*'; |
|
589
|
|
|
|
|
|
|
my $full_banmask = normalize_mask($banmask); |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
if (matches_mask($full_banmask, 'stalin!joe@kremlin.ru')) { |
|
592
|
|
|
|
|
|
|
print "EEK!"; |
|
593
|
|
|
|
|
|
|
} |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
my $decoded = irc_decode($raw_irc_message); |
|
596
|
|
|
|
|
|
|
print $decoded, "\n"; |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
if (has_color($message)) { |
|
599
|
|
|
|
|
|
|
print 'COLOR CODE ALERT!\n"; |
|
600
|
|
|
|
|
|
|
} |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my $results_hashref = matches_mask_array(\@masks, \@items_to_match_against); |
|
603
|
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
my $nick = parse_user('stalin!joe@kremlin.ru'); |
|
605
|
|
|
|
|
|
|
my ($nick, $user, $host) = parse_user('stalin!joe@kremlin.ru'); |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
The functions in this module take care of many of the tasks you are faced |
|
610
|
|
|
|
|
|
|
with when working with IRC. Mode lines, ban masks, message encoding and |
|
611
|
|
|
|
|
|
|
formatting, etc. |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=head2 C |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
Takes one mandatory parameter, a string to convert to IRC uppercase, and one |
|
618
|
|
|
|
|
|
|
optional parameter, the casemapping of the ircd (which can be B<'rfc1459'>, |
|
619
|
|
|
|
|
|
|
B<'strict-rfc1459'> or B<'ascii'>. Default is B<'rfc1459'>). Returns the IRC |
|
620
|
|
|
|
|
|
|
uppercase equivalent of the passed string. |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head2 C |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Takes one mandatory parameter, a string to convert to IRC lowercase, and one |
|
625
|
|
|
|
|
|
|
optional parameter, the casemapping of the ircd (which can be B<'rfc1459'>, |
|
626
|
|
|
|
|
|
|
B<'strict-rfc1459'> or B<'ascii'>. Default is B<'rfc1459'>). Returns the IRC |
|
627
|
|
|
|
|
|
|
lowercase equivalent of the passed string. |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=head2 C |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
Takes two mandatory parameters, IRC strings (channels or nicknames) to |
|
632
|
|
|
|
|
|
|
compare. A third, optional parameter specifies the casemapping. Returns true |
|
633
|
|
|
|
|
|
|
if the two strings are equivalent, false otherwise |
|
634
|
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
# long version |
|
636
|
|
|
|
|
|
|
lc_irc($one, $map) eq lc_irc($two, $map) |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# short version |
|
639
|
|
|
|
|
|
|
eq_irc($one, $two, $map) |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=head2 C |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
Takes a list representing an IRC mode line. Returns a hashref. Optionally |
|
644
|
|
|
|
|
|
|
you can also supply an arrayref and a hashref to specify valid channel |
|
645
|
|
|
|
|
|
|
modes (default: C<[qw(beI k l imnpstaqr)]>) and status modes (default: |
|
646
|
|
|
|
|
|
|
C<< {o => '@', h => '%', v => '+'} >>), respectively. |
|
647
|
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
If the modeline |
|
649
|
|
|
|
|
|
|
couldn't be parsed the hashref will be empty. On success the following keys |
|
650
|
|
|
|
|
|
|
will be available in the hashref: |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
B<'modes'>, an arrayref of normalised modes; |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
B<'args'>, an arrayref of applicable arguments to the modes; |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Example: |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
my $hashref = parse_mode_line( 'ov+b-i', 'Bob', 'sue', 'stalin*!*@*' ); |
|
659
|
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# $hashref will be: |
|
661
|
|
|
|
|
|
|
{ |
|
662
|
|
|
|
|
|
|
modes => [ '+o', '+v', '+b', '-i' ], |
|
663
|
|
|
|
|
|
|
args => [ 'Bob', 'sue', 'stalin*!*@*' ], |
|
664
|
|
|
|
|
|
|
} |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=head2 C |
|
667
|
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
Takes one parameter, a string representing an IRC mask. Returns a normalised |
|
669
|
|
|
|
|
|
|
full mask. |
|
670
|
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
Example: |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
$fullbanmask = normalize_mask( 'stalin*' ); |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# $fullbanmask will be: 'stalin*!*@*'; |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head2 C |
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Takes two parameters, a string representing an IRC mask and something to |
|
680
|
|
|
|
|
|
|
match against the IRC mask, such as a nick!user@hostname string. Returns |
|
681
|
|
|
|
|
|
|
a true value if they match, a false value otherwise. Optionally, one may |
|
682
|
|
|
|
|
|
|
pass the casemapping (see L|/uc_irc>), as this function uses |
|
683
|
|
|
|
|
|
|
C internally. |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head2 C |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Takes two array references, the first being a list of strings representing |
|
688
|
|
|
|
|
|
|
IRC masks, the second a list of somethings to test against the masks. Returns |
|
689
|
|
|
|
|
|
|
an empty hashref if there are no matches. Otherwise, the keys will be the |
|
690
|
|
|
|
|
|
|
masks matched, each value being an arrayref of the strings that matched it. |
|
691
|
|
|
|
|
|
|
Optionally, one may pass the casemapping (see L|/uc_irc>), as |
|
692
|
|
|
|
|
|
|
this function uses C internally. |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=head2 C |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
Takes one argument, a string representing a number of mode changes. Returns |
|
697
|
|
|
|
|
|
|
a condensed version of the changes. |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
my $mode_line = unparse_mode_line('+o+o+o-v+v'); |
|
700
|
|
|
|
|
|
|
$mode_line is now '+ooo-v+v' |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=head2 C |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Takes two arguments, strings representing a set of IRC user modes before and |
|
705
|
|
|
|
|
|
|
after a change. Returns a string representing what changed. |
|
706
|
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
my $mode_change = gen_mode_change('abcde', 'befmZ'); |
|
708
|
|
|
|
|
|
|
$mode_change is now '-acd+fmZ' |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
=head2 C |
|
711
|
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
Takes one parameter, a string representing a user in the form |
|
713
|
|
|
|
|
|
|
nick!user@hostname. In a scalar context it returns just the nickname. |
|
714
|
|
|
|
|
|
|
In a list context it returns a list consisting of the nick, user and hostname, |
|
715
|
|
|
|
|
|
|
respectively. |
|
716
|
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 C |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
Takes one argument, a channel name to validate. Returns true or false if the |
|
720
|
|
|
|
|
|
|
channel name is valid or not. You can supply a second argument, an array of |
|
721
|
|
|
|
|
|
|
characters of allowed channel prefixes. Defaults to C<['#', '&']>. |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=head2 C |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
Takes one argument, a nickname to validate. Returns true or false if the |
|
726
|
|
|
|
|
|
|
nickname is valid or not. |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=head2 C |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
Takes an IRC server numerical reply code (e.g. '001') as an argument, and |
|
731
|
|
|
|
|
|
|
returns the corresponding name (e.g. 'RPL_WELCOME'). |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=head2 C |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Takes an IRC server reply name (e.g. 'RPL_WELCOME') as an argument, and returns the |
|
736
|
|
|
|
|
|
|
corresponding numerical code (e.g. '001'). |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=head2 C |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns true if it contains any IRC |
|
741
|
|
|
|
|
|
|
color codes, false otherwise. Useful if you want your bot to kick users for |
|
742
|
|
|
|
|
|
|
(ab)using colors. :) |
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
=head2 C |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns true if it contains any IRC |
|
747
|
|
|
|
|
|
|
formatting codes, false otherwise. |
|
748
|
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=head2 C |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns the string stripped of all |
|
752
|
|
|
|
|
|
|
IRC color codes. |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=head2 C |
|
755
|
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns the string stripped of all |
|
757
|
|
|
|
|
|
|
IRC formatting codes. |
|
758
|
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
=head2 C |
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
This function takes a byte string (i.e. an unmodified IRC message) and |
|
762
|
|
|
|
|
|
|
returns a text string. Since the source encoding might have been UTF-8, |
|
763
|
|
|
|
|
|
|
you should store it with UTF-8 or some other Unicode encoding in your |
|
764
|
|
|
|
|
|
|
file/database/whatever to be safe. For a more detailed discussion, see |
|
765
|
|
|
|
|
|
|
L. |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
use IRC::Utils qw(decode_irc); |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
sub message_handler { |
|
770
|
|
|
|
|
|
|
my ($nick, $channel, $message) = @_; |
|
771
|
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# not wise, $message is a byte string of unkown encoding |
|
773
|
|
|
|
|
|
|
print $message, "\n"; |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
$message = decode_irc($what); |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
# good, $message is a text string |
|
778
|
|
|
|
|
|
|
print $message, "\n"; |
|
779
|
|
|
|
|
|
|
} |
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=head1 CONSTANTS |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
Use the following constants to add formatting and mIRC color codes to IRC |
|
784
|
|
|
|
|
|
|
messages. |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
Normal text: |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
NORMAL |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
Formatting: |
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
BOLD |
|
793
|
|
|
|
|
|
|
UNDERLINE |
|
794
|
|
|
|
|
|
|
REVERSE |
|
795
|
|
|
|
|
|
|
ITALIC |
|
796
|
|
|
|
|
|
|
FIXED |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
Colors: |
|
799
|
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
WHITE |
|
801
|
|
|
|
|
|
|
BLACK |
|
802
|
|
|
|
|
|
|
BLUE |
|
803
|
|
|
|
|
|
|
GREEN |
|
804
|
|
|
|
|
|
|
RED |
|
805
|
|
|
|
|
|
|
BROWN |
|
806
|
|
|
|
|
|
|
PURPLE |
|
807
|
|
|
|
|
|
|
ORANGE |
|
808
|
|
|
|
|
|
|
YELLOW |
|
809
|
|
|
|
|
|
|
LIGHT_GREEN |
|
810
|
|
|
|
|
|
|
TEAL |
|
811
|
|
|
|
|
|
|
LIGHT_CYAN |
|
812
|
|
|
|
|
|
|
LIGHT_BLUE |
|
813
|
|
|
|
|
|
|
PINK |
|
814
|
|
|
|
|
|
|
GREY |
|
815
|
|
|
|
|
|
|
LIGHT_GREY |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
Individual non-color formatting codes can be cancelled with their |
|
818
|
|
|
|
|
|
|
corresponding constant, but you can also cancel all of them at once with |
|
819
|
|
|
|
|
|
|
C. To cancel the effect of color codes, you must use C. |
|
820
|
|
|
|
|
|
|
which of course has the side effect of cancelling all other formatting codes |
|
821
|
|
|
|
|
|
|
as well. |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
$msg = 'This word is '.YELLOW.'yellow'.NORMAL.' while this word is'.BOLD.'bold'.BOLD; |
|
824
|
|
|
|
|
|
|
$msg = UNDERLINE.BOLD.'This sentence is both underlined and bold.'.NORMAL; |
|
825
|
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
=head1 ENCODING |
|
827
|
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=head2 Messages |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
The only encoding requirement the IRC protocol places on its messages is |
|
831
|
|
|
|
|
|
|
that they be 8-bits and ASCII-compatible. This has resulted in most of the |
|
832
|
|
|
|
|
|
|
Western world settling on ASCII-compatible Latin-1 (usually Microsoft's |
|
833
|
|
|
|
|
|
|
CP1252, a Latin-1 variant) as a convention. Recently, popular IRC clients |
|
834
|
|
|
|
|
|
|
(mIRC, xchat, certain irssi configurations) have begun sending a mixture of |
|
835
|
|
|
|
|
|
|
CP1252 and UTF-8 over the wire to allow more characters without breaking |
|
836
|
|
|
|
|
|
|
backward compatibility (too much). They send CP1252 encoded messages if the |
|
837
|
|
|
|
|
|
|
characters fit within that encoding, otherwise falling back to UTF-8, and |
|
838
|
|
|
|
|
|
|
likewise autodetecting the encoding (UTF-8 or CP1252) of incoming messages. |
|
839
|
|
|
|
|
|
|
Since writing text with mixed encoding to a file, terminal, or database is |
|
840
|
|
|
|
|
|
|
not a good idea, you need a way to decode messages from IRC. |
|
841
|
|
|
|
|
|
|
L|/decode_irc> will do that. |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
=head2 Channel names |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
The matter is complicated further by the fact that some servers allow |
|
846
|
|
|
|
|
|
|
non-ASCII characters in channel names. IRC modules generally don't |
|
847
|
|
|
|
|
|
|
explicitly encode or decode any IRC traffic, but they do have to |
|
848
|
|
|
|
|
|
|
concatenate parts of a message (e.g. a channel name and a message) before |
|
849
|
|
|
|
|
|
|
sending it over the wire. So when you do something like |
|
850
|
|
|
|
|
|
|
C<< privmsg($channel, 'æði') >>, where C<$channel> is the unmodified |
|
851
|
|
|
|
|
|
|
channel name (a byte string) you got from an earlier IRC message, the |
|
852
|
|
|
|
|
|
|
channel name will get double-encoded when concatenated with your message (a |
|
853
|
|
|
|
|
|
|
non-ASCII text string) if the channel name contains non-ASCII bytes. |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
To prevent this, you can't simply L the channel name and |
|
856
|
|
|
|
|
|
|
then use it. C<'#æði'> in CP1252 is not the same channel as C<'#æði'> in |
|
857
|
|
|
|
|
|
|
UTF-8, since they are encoded as different sequences of bytes, and the IRC |
|
858
|
|
|
|
|
|
|
server only cares about the byte representation. Therefore, when using a |
|
859
|
|
|
|
|
|
|
channel name you got from the server (e.g. when replying to message), you |
|
860
|
|
|
|
|
|
|
should use the original byte string (before it has been decoded with |
|
861
|
|
|
|
|
|
|
L|/decode_irc>), and encode any other parameters (with |
|
862
|
|
|
|
|
|
|
L|Encode>) so that your message will be concatenated |
|
863
|
|
|
|
|
|
|
correctly. At some point, you'll probably want to print the channel name, |
|
864
|
|
|
|
|
|
|
write it to a log file or use it in a filename, so you'll eventually have to |
|
865
|
|
|
|
|
|
|
decode it, at which point the UTF-8 C<#æði> and CP1252 C<#æði> will have to |
|
866
|
|
|
|
|
|
|
be considered equivalent. |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
use Encode qw(encode_utf8 encode); |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
sub message_handler { |
|
871
|
|
|
|
|
|
|
# these three are all byte strings |
|
872
|
|
|
|
|
|
|
my ($nick, $channel, $message) = @_; |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
# bad: if $channel has any non-ASCII bytes, they will get double-encoded |
|
875
|
|
|
|
|
|
|
privmsg($channel, 'æði'); |
|
876
|
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
# bad: if $message has any non-ASCII bytes, they will get double-encoded |
|
878
|
|
|
|
|
|
|
privmsg('#æði', $message); |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
# good: both are byte strings already, so they will concatenate correctly |
|
881
|
|
|
|
|
|
|
privmsg($channel, $message); |
|
882
|
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
# good: both are text strings (Latin1 as per Perl's default), so |
|
884
|
|
|
|
|
|
|
# they'll be concatenated correctly |
|
885
|
|
|
|
|
|
|
privmsg('#æði', 'æði'); |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
# good: similar to the last one, except now they're using UTF-8, which |
|
888
|
|
|
|
|
|
|
# means that the channel is actually not the same as above |
|
889
|
|
|
|
|
|
|
use utf8; |
|
890
|
|
|
|
|
|
|
privmsg('#æði', 'æði'); |
|
891
|
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
# good: $channel and $msg_bytes are both byte strings |
|
893
|
|
|
|
|
|
|
my $msg_bytes = encode_utf8('æði'); |
|
894
|
|
|
|
|
|
|
privmsg($channel, $msg_bytes); |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
# good: $chan_bytes and $message are both byte strings |
|
897
|
|
|
|
|
|
|
# here we're sending a message to the utf8-encoded #æði |
|
898
|
|
|
|
|
|
|
my $utf8_bytes = encode_utf8('#æði'); |
|
899
|
|
|
|
|
|
|
privmsg($utf8_bytes, $message); |
|
900
|
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
# good: $chan_bytes and $message are both byte strings |
|
902
|
|
|
|
|
|
|
# here we're sending a message to the cp1252-encoded #æði |
|
903
|
|
|
|
|
|
|
my $cp1252_bytes = encode('cp1252', '#æði'); |
|
904
|
|
|
|
|
|
|
privmsg($cp1252_bytes, $message); |
|
905
|
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
# bad: $channel is in an undetermined encoding |
|
907
|
|
|
|
|
|
|
log_message("Got message from $channel"); |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
# good: using the decoded version of $channel |
|
910
|
|
|
|
|
|
|
log_message("Got message from ".decode_irc($channel)); |
|
911
|
|
|
|
|
|
|
} |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
See also L, L, |
|
914
|
|
|
|
|
|
|
L, L, and |
|
915
|
|
|
|
|
|
|
L. |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=head1 AUTHOR |
|
918
|
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Hinrik Ern SigurEsson (C irc.perl.org, or C @ FreeNode). |
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
Chris C Williams |
|
922
|
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
924
|
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
L |
|
926
|
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
L |
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
=cut |