+
+func (c *Client) SendLusers() {
+ lusers := 0
+ clientsLock.RLock()
+ for client := range clients {
+ if client.registered {
+ lusers++
+ }
+ }
+ clientsLock.RUnlock()
+ c.ReplyNicknamed(
+ "251",
+ fmt.Sprintf("There are %d users and 0 invisible on 1 servers",
+ lusers,
+ ))
+}
+
+func (c *Client) SendMotd() {
+ if motd == nil {
+ c.ReplyNicknamed("422", "MOTD File is missing")
+ return
+ }
+ motdText, err := ioutil.ReadFile(*motd)
+ if err != nil {
+ log.Printf("can not read motd file %s: %v", *motd, err)
+ c.ReplyNicknamed("422", "Error reading MOTD File")
+ return
+ }
+ c.ReplyNicknamed("375", "- "+*hostname+" Message of the day -")
+ for _, s := range strings.Split(strings.TrimSuffix(string(motdText), "\n"), "\n") {
+ c.ReplyNicknamed("372", "- "+s)
+ }
+ c.ReplyNicknamed("376", "End of /MOTD command")
+}
+
+func (c *Client) Join(cmd string) {
+ args := strings.Split(cmd, " ")
+ rs := strings.Split(args[0], ",")
+ keys := []string{}
+ if len(args) > 1 {
+ keys = strings.Split(args[1], ",")
+ }
+RoomCycle:
+ for n, roomName := range rs {
+ if !RERoom.MatchString(roomName) {
+ c.ReplyNoChannel(roomName)
+ continue
+ }
+ var key string
+ if (n < len(keys)) && (keys[n] != "") {
+ key = keys[n]
+ }
+ roomsLock.RLock()
+ for roomNameExisting, room := range rooms {
+ if roomName != roomNameExisting {
+ continue
+ }
+ if (room.key != "") && (room.key != key) {
+ c.ReplyNicknamed("475", roomName, "Cannot join channel (+k)")
+ roomsLock.RUnlock()
+ return
+ }
+ room.events <- ClientEvent{c, EventNew, ""}
+ roomsLock.RUnlock()
+ continue RoomCycle
+ }
+ roomsLock.RUnlock()
+ roomNew := RoomRegister(roomName)
+ if *verbose {
+ log.Println("room", roomName, "created")
+ }
+ if key != "" {
+ roomNew.key = key
+ roomNew.StateSave()
+ }
+ roomNew.events <- ClientEvent{c, EventNew, ""}
+ }
+}
+
+func (client *Client) SendWhois(nicknames []string) {
+ var c *Client
+ for _, nickname := range nicknames {
+ nickname = strings.ToLower(nickname)
+ clientsLock.RLock()
+ for c = range clients {
+ if strings.ToLower(c.nickname) == nickname {
+ clientsLock.RUnlock()
+ goto Found
+ }
+ }
+ clientsLock.RUnlock()
+ client.ReplyNoNickChan(nickname)
+ continue
+ Found:
+ var host string
+ if *cloak != "" {
+ host = *cloak
+ } else {
+ host, _, err := net.SplitHostPort(c.conn.RemoteAddr().String())
+ if err != nil {
+ log.Printf("can't parse RemoteAddr %q: %v", host, err)
+ host = "Unknown"
+ }
+ }
+ client.ReplyNicknamed("311", c.nickname, c.username, host, "*", c.realname)
+ client.ReplyNicknamed("312", c.nickname, *hostname, *hostname)
+ if c.away != "" {
+ client.ReplyNicknamed("301", c.nickname, c.away)
+ }
+ subscriptions := make([]string, 0)
+ roomsLock.RLock()
+ for _, room := range rooms {
+ for subscriber := range room.members {
+ if subscriber.nickname == nickname {
+ subscriptions = append(subscriptions, room.name)
+ }
+ }
+ }
+ roomsLock.RUnlock()
+ sort.Strings(subscriptions)
+ client.ReplyNicknamed("319", c.nickname, strings.Join(subscriptions, " "))
+ client.ReplyNicknamed("318", c.nickname, "End of /WHOIS list")
+ }
+}
+
+func (c *Client) SendList(cols []string) {
+ var rs []string
+ if (len(cols) > 1) && (cols[1] != "") {
+ rs = strings.Split(strings.Split(cols[1], " ")[0], ",")
+ } else {
+ rs = make([]string, 0)
+ roomsLock.RLock()
+ for r := range rooms {
+ rs = append(rs, r)
+ }
+ roomsLock.RUnlock()
+ }
+ sort.Strings(rs)
+ roomsLock.RLock()
+ for _, r := range rs {
+ if room, found := rooms[r]; found {
+ c.ReplyNicknamed(
+ "322",
+ r,
+ fmt.Sprintf("%d", len(room.members)),
+ room.topic,
+ )
+ }
+ }
+ roomsLock.RUnlock()
+ c.ReplyNicknamed("323", "End of /LIST")
+}
+
+func (c *Client) Register(cmd string, cols []string) {
+ switch cmd {
+ case "PASS":
+ if len(cols) == 1 || len(cols[1]) < 1 {
+ c.ReplyNotEnoughParameters("PASS")
+ return
+ }
+ password := strings.TrimPrefix(cols[1], ":")
+ c.password = password
+ case "NICK":
+ if len(cols) == 1 || len(cols[1]) < 1 {
+ c.ReplyParts("431", "No nickname given")
+ return
+ }
+ nickname := cols[1]
+ // Compatibility with some clients prepending colons to nickname
+ nickname = strings.TrimPrefix(nickname, ":")
+ nickname = strings.ToLower(nickname)
+ if !RENickname.MatchString(nickname) {
+ c.ReplyParts("432", "*", cols[1], "Erroneous nickname")
+ return
+ }
+ clientsLock.RLock()
+ for existingClient := range clients {
+ if existingClient.nickname == nickname {
+ clientsLock.RUnlock()
+ c.ReplyParts("433", "*", nickname, "Nickname is already in use")
+ return
+ }
+ }
+ clientsLock.RUnlock()
+ c.nickname = nickname
+ case "USER":
+ if len(cols) == 1 {
+ c.ReplyNotEnoughParameters("USER")
+ return
+ }
+ args := strings.SplitN(cols[1], " ", 4)
+ if len(args) < 4 {
+ c.ReplyNotEnoughParameters("USER")
+ return
+ }
+ c.username = args[0]
+ realname := strings.TrimLeft(args[3], ":")
+ c.realname = realname
+ }
+ if c.nickname != "*" && c.username != "" {
+ if *passwords != "" {
+ authenticated := false
+ if c.password == "" {
+ c.ReplyParts("462", "You may not register")
+ c.Close()
+ return
+ }
+ contents, err := ioutil.ReadFile(*passwords)
+ if err != nil {
+ log.Fatalf("can not read passwords file %s: %s", *passwords, err)
+ return
+ }
+ for n, entry := range strings.Split(string(contents), "\n") {
+ if entry == "" || strings.HasPrefix(entry, "#") {
+ continue
+ }
+ cols := strings.Split(entry, ":")
+ if len(cols) != 2 {
+ log.Fatalf("bad passwords format: %s:%d", *passwords, n)
+ continue
+ }
+ if cols[0] != c.nickname {
+ continue
+ }
+ h := sha256.Sum256([]byte(c.password))
+ authenticated = subtle.ConstantTimeCompare(
+ []byte(hex.EncodeToString(h[:])),
+ []byte(cols[1]),
+ ) == 1
+ break
+ }
+ if !authenticated {
+ c.ReplyParts("462", "You may not register")
+ c.Close()
+ return
+ }
+ }
+ c.registered = true
+ c.ReplyNicknamed("001", "Hi, welcome to IRC")
+ c.ReplyNicknamed("002", "Your host is "+*hostname+", running goircd "+Version)
+ c.ReplyNicknamed("003", "This server was created sometime")
+ c.ReplyNicknamed("004", *hostname+" goircd o o")
+ c.SendLusers()
+ c.SendMotd()
+ if *verbose {
+ log.Println(c, "logged in")
+ }
+ }
+}