|
@@ -242,7 +242,7 @@ func accVerifyHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb
|
242
|
242
|
if err == nil {
|
243
|
243
|
sendSuccessfulRegResponse(client, rb, false)
|
244
|
244
|
} else {
|
245
|
|
- rb.Add(nil, server.name, code, client.nick, account, client.t(message))
|
|
245
|
+ rb.Add(nil, server.name, code, client.Nick(), account, client.t(message))
|
246
|
246
|
}
|
247
|
247
|
|
248
|
248
|
return false
|
|
@@ -1873,17 +1873,38 @@ func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
|
1873
|
1873
|
}
|
1874
|
1874
|
|
1875
|
1875
|
// NOTICE <target>{,<target>} <message>
|
1876
|
|
-func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
|
1876
|
+// PRIVMSG <target>{,<target>} <message>
|
|
1877
|
+// TAGMSG <target>{,<target>}
|
|
1878
|
+func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
|
1879
|
+ histType, err := msgCommandToHistType(server, msg.Command)
|
|
1880
|
+ if err != nil {
|
|
1881
|
+ return false
|
|
1882
|
+ }
|
|
1883
|
+
|
|
1884
|
+ cnick := client.Nick()
|
1877
|
1885
|
clientOnlyTags := msg.ClientOnlyTags()
|
|
1886
|
+ if histType == history.Tagmsg && len(clientOnlyTags) == 0 {
|
|
1887
|
+ // nothing to do
|
|
1888
|
+ return false
|
|
1889
|
+ }
|
|
1890
|
+
|
1878
|
1891
|
targets := strings.Split(msg.Params[0], ",")
|
1879
|
|
- message := msg.Params[1]
|
|
1892
|
+ var message string
|
|
1893
|
+ if len(msg.Params) > 1 {
|
|
1894
|
+ message = msg.Params[1]
|
|
1895
|
+ }
|
|
1896
|
+
|
|
1897
|
+ // note that error replies are never sent for NOTICE
|
1880
|
1898
|
|
1881
|
1899
|
if client.isTor && isRestrictedCTCPMessage(message) {
|
1882
|
|
- rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor"))
|
|
1900
|
+ if histType != history.Notice {
|
|
1901
|
+ rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor"))
|
|
1902
|
+ }
|
1883
|
1903
|
return false
|
1884
|
1904
|
}
|
1885
|
1905
|
|
1886
|
1906
|
splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
|
|
1907
|
+ now := time.Now().UTC()
|
1887
|
1908
|
|
1888
|
1909
|
for i, targetString := range targets {
|
1889
|
1910
|
// max of four targets per privmsg
|
|
@@ -1893,52 +1914,66 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
1893
|
1914
|
prefixes, targetString := modes.SplitChannelMembershipPrefixes(targetString)
|
1894
|
1915
|
lowestPrefix := modes.GetLowestChannelModePrefix(prefixes)
|
1895
|
1916
|
|
1896
|
|
- target, cerr := CasefoldChannel(targetString)
|
1897
|
|
- if cerr == nil {
|
1898
|
|
- channel := server.channels.Get(target)
|
|
1917
|
+ if len(targetString) == 0 {
|
|
1918
|
+ continue
|
|
1919
|
+ } else if targetString[0] == '#' {
|
|
1920
|
+ channel := server.channels.Get(targetString)
|
1899
|
1921
|
if channel == nil {
|
1900
|
|
- // errors silently ignored with NOTICE as per RFC
|
1901
|
|
- continue
|
1902
|
|
- }
|
1903
|
|
- if !channel.CanSpeak(client) {
|
1904
|
|
- // errors silently ignored with NOTICE as per RFC
|
|
1922
|
+ if histType != history.Notice {
|
|
1923
|
+ rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, cnick, targetString, client.t("No such channel"))
|
|
1924
|
+ }
|
1905
|
1925
|
continue
|
1906
|
1926
|
}
|
1907
|
|
- channel.SendSplitMessage("NOTICE", lowestPrefix, clientOnlyTags, client, splitMsg, rb)
|
|
1927
|
+ channel.SendSplitMessage(msg.Command, lowestPrefix, clientOnlyTags, client, splitMsg, rb)
|
1908
|
1928
|
} else {
|
1909
|
|
- target, err := CasefoldName(targetString)
|
1910
|
|
- if err != nil {
|
1911
|
|
- continue
|
1912
|
|
- }
|
1913
|
|
-
|
1914
|
|
- // NOTICEs sent to services are ignored
|
1915
|
|
- if _, isService := OragonoServices[target]; isService {
|
|
1929
|
+ if service, isService := OragonoServices[strings.ToLower(targetString)]; isService {
|
|
1930
|
+ // NOTICE and TAGMSG to services are ignored
|
|
1931
|
+ if histType == history.Privmsg {
|
|
1932
|
+ servicePrivmsgHandler(service, server, client, message, rb)
|
|
1933
|
+ }
|
1916
|
1934
|
continue
|
1917
|
1935
|
}
|
1918
|
1936
|
|
1919
|
|
- user := server.clients.Get(target)
|
|
1937
|
+ user := server.clients.Get(targetString)
|
1920
|
1938
|
if user == nil {
|
1921
|
|
- // errors silently ignored with NOTICE as per RFC
|
|
1939
|
+ if histType != history.Notice {
|
|
1940
|
+ rb.Add(nil, server.name, ERR_NOSUCHNICK, cnick, targetString, "No such nick")
|
|
1941
|
+ }
|
1922
|
1942
|
continue
|
1923
|
1943
|
}
|
1924
|
|
- if !user.capabilities.Has(caps.MessageTags) {
|
1925
|
|
- clientOnlyTags = nil
|
|
1944
|
+ tnick := user.Nick()
|
|
1945
|
+
|
|
1946
|
+ if histType == history.Tagmsg && !user.capabilities.Has(caps.MessageTags) {
|
|
1947
|
+ continue // nothing to do
|
1926
|
1948
|
}
|
|
1949
|
+
|
|
1950
|
+ nickMaskString := client.NickMaskString()
|
|
1951
|
+ accountName := client.AccountName()
|
1927
|
1952
|
// restrict messages appropriately when +R is set
|
1928
|
1953
|
// intentionally make the sending user think the message went through fine
|
1929
|
1954
|
allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
|
1930
|
1955
|
allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
|
1931
|
1956
|
if allowedPlusR && allowedTor {
|
1932
|
|
- user.SendSplitMsgFromClient(client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
|
|
1957
|
+ if histType == history.Tagmsg {
|
|
1958
|
+ user.sendFromClientInternal(false, now, splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick)
|
|
1959
|
+ } else {
|
|
1960
|
+ user.SendSplitMsgFromClient(client, clientOnlyTags, msg.Command, tnick, splitMsg)
|
|
1961
|
+ }
|
1933
|
1962
|
}
|
1934
|
|
- nickMaskString := client.NickMaskString()
|
1935
|
|
- accountName := client.AccountName()
|
1936
|
1963
|
if client.capabilities.Has(caps.EchoMessage) {
|
1937
|
|
- rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "NOTICE", user.nick, splitMsg)
|
|
1964
|
+ if histType == history.Tagmsg && client.capabilities.Has(caps.MessageTags) {
|
|
1965
|
+ rb.AddFromClient(splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick)
|
|
1966
|
+ } else {
|
|
1967
|
+ rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, msg.Command, tnick, splitMsg)
|
|
1968
|
+ }
|
|
1969
|
+ }
|
|
1970
|
+ if histType != history.Notice && user.HasMode(modes.Away) {
|
|
1971
|
+ //TODO(dan): possibly implement cooldown of away notifications to users
|
|
1972
|
+ rb.Add(nil, server.name, RPL_AWAY, cnick, tnick, user.AwayMessage())
|
1938
|
1973
|
}
|
1939
|
1974
|
|
1940
|
1975
|
user.history.Add(history.Item{
|
1941
|
|
- Type: history.Notice,
|
|
1976
|
+ Type: histType,
|
1942
|
1977
|
Message: splitMsg,
|
1943
|
1978
|
Nick: nickMaskString,
|
1944
|
1979
|
AccountName: accountName,
|
|
@@ -1988,7 +2023,7 @@ func npcaHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
|
1988
|
2023
|
// OPER <name> <password>
|
1989
|
2024
|
func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
1990
|
2025
|
if client.HasMode(modes.Operator) == true {
|
1991
|
|
- rb.Add(nil, server.name, ERR_UNKNOWNERROR, "OPER", client.t("You're already opered-up!"))
|
|
2026
|
+ rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), "OPER", client.t("You're already opered-up!"))
|
1992
|
2027
|
return false
|
1993
|
2028
|
}
|
1994
|
2029
|
|
|
@@ -1999,7 +2034,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
|
1999
|
2034
|
authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil)
|
2000
|
2035
|
}
|
2001
|
2036
|
if !authorized {
|
2002
|
|
- rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect"))
|
|
2037
|
+ rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect"))
|
2003
|
2038
|
client.Quit(client.t("Password incorrect"))
|
2004
|
2039
|
return true
|
2005
|
2040
|
}
|
|
@@ -2090,90 +2125,6 @@ func isRestrictedCTCPMessage(message string) bool {
|
2090
|
2125
|
return strings.HasPrefix(message, "\x01") && !strings.HasPrefix(message, "\x01ACTION")
|
2091
|
2126
|
}
|
2092
|
2127
|
|
2093
|
|
-// PRIVMSG <target>{,<target>} <message>
|
2094
|
|
-func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
2095
|
|
- clientOnlyTags := msg.ClientOnlyTags()
|
2096
|
|
- targets := strings.Split(msg.Params[0], ",")
|
2097
|
|
- message := msg.Params[1]
|
2098
|
|
-
|
2099
|
|
- if client.isTor && isRestrictedCTCPMessage(message) {
|
2100
|
|
- rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor"))
|
2101
|
|
- return false
|
2102
|
|
- }
|
2103
|
|
-
|
2104
|
|
- // split privmsg
|
2105
|
|
- splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
|
2106
|
|
-
|
2107
|
|
- cnick := client.Nick()
|
2108
|
|
- for i, targetString := range targets {
|
2109
|
|
- // max of four targets per privmsg
|
2110
|
|
- if i > maxTargets-1 {
|
2111
|
|
- break
|
2112
|
|
- }
|
2113
|
|
- prefixes, targetString := modes.SplitChannelMembershipPrefixes(targetString)
|
2114
|
|
- lowestPrefix := modes.GetLowestChannelModePrefix(prefixes)
|
2115
|
|
-
|
2116
|
|
- // eh, no need to notify them
|
2117
|
|
- if len(targetString) < 1 {
|
2118
|
|
- continue
|
2119
|
|
- }
|
2120
|
|
-
|
2121
|
|
- target, err := CasefoldChannel(targetString)
|
2122
|
|
- if err == nil {
|
2123
|
|
- channel := server.channels.Get(target)
|
2124
|
|
- if channel == nil {
|
2125
|
|
- rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, cnick, targetString, client.t("No such channel"))
|
2126
|
|
- continue
|
2127
|
|
- }
|
2128
|
|
- if !channel.CanSpeak(client) {
|
2129
|
|
- rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
2130
|
|
- continue
|
2131
|
|
- }
|
2132
|
|
- channel.SendSplitMessage("PRIVMSG", lowestPrefix, clientOnlyTags, client, splitMsg, rb)
|
2133
|
|
- } else {
|
2134
|
|
- target, err = CasefoldName(targetString)
|
2135
|
|
- if service, isService := OragonoServices[target]; isService {
|
2136
|
|
- servicePrivmsgHandler(service, server, client, message, rb)
|
2137
|
|
- continue
|
2138
|
|
- }
|
2139
|
|
- user := server.clients.Get(target)
|
2140
|
|
- if err != nil || user == nil {
|
2141
|
|
- if len(target) > 0 {
|
2142
|
|
- client.Send(nil, server.name, ERR_NOSUCHNICK, cnick, target, "No such nick")
|
2143
|
|
- }
|
2144
|
|
- continue
|
2145
|
|
- }
|
2146
|
|
- if !user.capabilities.Has(caps.MessageTags) {
|
2147
|
|
- clientOnlyTags = nil
|
2148
|
|
- }
|
2149
|
|
- // restrict messages appropriately when +R is set
|
2150
|
|
- // intentionally make the sending user think the message went through fine
|
2151
|
|
- allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
|
2152
|
|
- allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
|
2153
|
|
- if allowedPlusR && allowedTor {
|
2154
|
|
- user.SendSplitMsgFromClient(client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
|
2155
|
|
- }
|
2156
|
|
- nickMaskString := client.NickMaskString()
|
2157
|
|
- accountName := client.AccountName()
|
2158
|
|
- if client.capabilities.Has(caps.EchoMessage) {
|
2159
|
|
- rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
|
2160
|
|
- }
|
2161
|
|
- if user.HasMode(modes.Away) {
|
2162
|
|
- //TODO(dan): possibly implement cooldown of away notifications to users
|
2163
|
|
- rb.Add(nil, server.name, RPL_AWAY, cnick, user.Nick(), user.AwayMessage())
|
2164
|
|
- }
|
2165
|
|
-
|
2166
|
|
- user.history.Add(history.Item{
|
2167
|
|
- Type: history.Privmsg,
|
2168
|
|
- Message: splitMsg,
|
2169
|
|
- Nick: nickMaskString,
|
2170
|
|
- AccountName: accountName,
|
2171
|
|
- })
|
2172
|
|
- }
|
2173
|
|
- }
|
2174
|
|
- return false
|
2175
|
|
-}
|
2176
|
|
-
|
2177
|
2128
|
// QUIT [<reason>]
|
2178
|
2129
|
func quitHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
2179
|
2130
|
reason := "Quit"
|
|
@@ -2346,71 +2297,6 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
|
2346
|
2297
|
return false
|
2347
|
2298
|
}
|
2348
|
2299
|
|
2349
|
|
-// TAGMSG <target>{,<target>}
|
2350
|
|
-func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
2351
|
|
- clientOnlyTags := msg.ClientOnlyTags()
|
2352
|
|
- // no client-only tags, so we can drop it
|
2353
|
|
- if clientOnlyTags == nil {
|
2354
|
|
- return false
|
2355
|
|
- }
|
2356
|
|
-
|
2357
|
|
- targets := strings.Split(msg.Params[0], ",")
|
2358
|
|
-
|
2359
|
|
- cnick := client.Nick()
|
2360
|
|
- message := utils.MakeSplitMessage("", true) // assign consistent message ID
|
2361
|
|
- for i, targetString := range targets {
|
2362
|
|
- // max of four targets per privmsg
|
2363
|
|
- if i > maxTargets-1 {
|
2364
|
|
- break
|
2365
|
|
- }
|
2366
|
|
- prefixes, targetString := modes.SplitChannelMembershipPrefixes(targetString)
|
2367
|
|
- lowestPrefix := modes.GetLowestChannelModePrefix(prefixes)
|
2368
|
|
-
|
2369
|
|
- // eh, no need to notify them
|
2370
|
|
- if len(targetString) < 1 {
|
2371
|
|
- continue
|
2372
|
|
- }
|
2373
|
|
-
|
2374
|
|
- target, err := CasefoldChannel(targetString)
|
2375
|
|
- if err == nil {
|
2376
|
|
- channel := server.channels.Get(target)
|
2377
|
|
- if channel == nil {
|
2378
|
|
- rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, cnick, targetString, client.t("No such channel"))
|
2379
|
|
- continue
|
2380
|
|
- }
|
2381
|
|
- if !channel.CanSpeak(client) {
|
2382
|
|
- rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
2383
|
|
- continue
|
2384
|
|
- }
|
2385
|
|
- channel.SendSplitMessage("TAGMSG", lowestPrefix, clientOnlyTags, client, message, rb)
|
2386
|
|
- } else {
|
2387
|
|
- target, err = CasefoldName(targetString)
|
2388
|
|
- user := server.clients.Get(target)
|
2389
|
|
- if err != nil || user == nil {
|
2390
|
|
- if len(target) > 0 {
|
2391
|
|
- client.Send(nil, server.name, ERR_NOSUCHNICK, cnick, target, client.t("No such nick"))
|
2392
|
|
- }
|
2393
|
|
- continue
|
2394
|
|
- }
|
2395
|
|
-
|
2396
|
|
- // end user can't receive tagmsgs
|
2397
|
|
- if !user.capabilities.Has(caps.MessageTags) {
|
2398
|
|
- continue
|
2399
|
|
- }
|
2400
|
|
- unick := user.Nick()
|
2401
|
|
- user.SendSplitMsgFromClient(client, clientOnlyTags, "TAGMSG", unick, message)
|
2402
|
|
- if client.capabilities.Has(caps.EchoMessage) {
|
2403
|
|
- rb.AddSplitMessageFromClient(client.NickMaskString(), client.AccountName(), clientOnlyTags, "TAGMSG", unick, message)
|
2404
|
|
- }
|
2405
|
|
- if user.HasMode(modes.Away) {
|
2406
|
|
- //TODO(dan): possibly implement cooldown of away notifications to users
|
2407
|
|
- rb.Add(nil, server.name, RPL_AWAY, cnick, unick, user.AwayMessage())
|
2408
|
|
- }
|
2409
|
|
- }
|
2410
|
|
- }
|
2411
|
|
- return false
|
2412
|
|
-}
|
2413
|
|
-
|
2414
|
2300
|
// TIME
|
2415
|
2301
|
func timeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
2416
|
2302
|
rb.Add(nil, server.name, RPL_TIME, client.nick, server.name, time.Now().Format(time.RFC1123))
|
|
@@ -2502,7 +2388,7 @@ func unKLineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
|
2502
|
2388
|
// USER <username> * 0 <realname>
|
2503
|
2389
|
func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
2504
|
2390
|
if client.registered {
|
2505
|
|
- rb.Add(nil, server.name, ERR_ALREADYREGISTRED, client.nick, client.t("You may not reregister"))
|
|
2391
|
+ rb.Add(nil, server.name, ERR_ALREADYREGISTRED, client.Nick(), client.t("You may not reregister"))
|
2506
|
2392
|
return false
|
2507
|
2393
|
}
|
2508
|
2394
|
|
|
@@ -2513,7 +2399,7 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
|
2513
|
2399
|
if client.preregNick == msg.Params[0] {
|
2514
|
2400
|
client.SetNames("user", msg.Params[3], false)
|
2515
|
2401
|
} else {
|
2516
|
|
- rb.Add(nil, server.name, ERR_INVALIDUSERNAME, client.t("Malformed username"))
|
|
2402
|
+ rb.Add(nil, server.name, ERR_INVALIDUSERNAME, client.Nick(), client.t("Malformed username"))
|
2517
|
2403
|
}
|
2518
|
2404
|
}
|
2519
|
2405
|
|
|
@@ -2631,7 +2517,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
|
2631
|
2517
|
if len(msg.Params) > 0 {
|
2632
|
2518
|
casefoldedMask, err := Casefold(msg.Params[0])
|
2633
|
2519
|
if err != nil {
|
2634
|
|
- rb.Add(nil, server.name, ERR_UNKNOWNERROR, "WHO", client.t("Mask isn't valid"))
|
|
2520
|
+ rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), "WHO", client.t("Mask isn't valid"))
|
2635
|
2521
|
return false
|
2636
|
2522
|
}
|
2637
|
2523
|
mask = casefoldedMask
|