1// This program is free software: you can redistribute it and/or modify
2// it under the terms of the GNU Affero General Public License as
3// published by the Free Software Foundation, either version 3 of the
4// License, or (at your option) any later version.
5//
6// This program is distributed in the hope that it will be useful, but
7// WITHOUT ANY WARRANTY; without even the implied warranty of
8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// Affero General Public License for more details.
10//
11// You should have received a copy of the GNU Affero General Public
12// License along with this program. If not, see <http://www.gnu.org/licenses/>.
13
14package main
15
16import (
17 "bufio"
18 "crypto/tls"
19 "crypto/x509"
20 "flag"
21 "fmt"
22 "github.com/nmeum/marvin/irc"
23 "github.com/nmeum/marvin/modules"
24 "io/ioutil"
25 "log"
26 "net"
27 "os"
28 "strings"
29 "time"
30)
31
32const (
33 appName = "marvin"
34)
35
36var (
37 conf = flag.String("c", "marvin.json", "configuration file")
38 verb = flag.Bool("v", false, "verbose output")
39)
40
41func main() {
42 flag.Parse()
43 logger := log.New(os.Stderr, "ERROR: ", 0)
44
45 config, err := readConfig(*conf)
46 if err != nil && !os.IsNotExist(err) {
47 logger.Fatal(err)
48 }
49
50 conn, err := connect(config)
51 if err != nil {
52 logger.Fatal(err)
53 }
54 defer conn.Close()
55
56 errChan := make(chan error)
57 go func() {
58 for err := range errChan {
59 logger.Println(err)
60 }
61 }()
62
63 ircBot, err := setup(conn, config)
64 if err != nil {
65 logger.Fatal(err)
66 }
67
68 reader := bufio.NewReader(conn)
69 for {
70 line, err := reader.ReadString('\n')
71 if err != nil {
72 logger.Println(err)
73 break
74 }
75
76 line = strings.Trim(line, "\n")
77 line = strings.Trim(line, "\r")
78
79 if *verb {
80 fmt.Println(line)
81 }
82
83 ircBot.Handle(line, errChan)
84 }
85}
86
87func setup(conn net.Conn, config config) (client *irc.Client, err error) {
88 client = irc.NewClient(conn)
89 client.CmdHook("001", func(c *irc.Client, m irc.Message) error {
90 time.Sleep(3 * time.Second) // Wait for NickServ etc
91 return c.Write("JOIN %s", strings.Join(config.Chan, ","))
92 })
93
94 moduleSet := modules.NewModuleSet(client, config.Conf)
95 for _, fn := range moduleInits {
96 fn(moduleSet)
97 }
98
99 client.Setup(config.Nick, config.Name, config.Host)
100 return client, moduleSet.LoadAll()
101}
102
103func connect(config config) (conn net.Conn, err error) {
104 netw := "tcp"
105 addr := fmt.Sprintf("%s:%d", config.Host, config.Port)
106
107 if len(config.Cert) >= 1 {
108 certFile, err := ioutil.ReadFile(config.Cert)
109 if err != nil {
110 return nil, err
111 }
112
113 caCertPool := x509.NewCertPool()
114 caCertPool.AppendCertsFromPEM(certFile)
115
116 tlsConfig := &tls.Config{RootCAs: caCertPool}
117 if len(config.ClientCert) >= 1 && len(config.ClientKey) >= 1 {
118 clientCert, err := tls.LoadX509KeyPair(config.ClientCert, config.ClientKey)
119 if err != nil {
120 return nil, err
121 }
122
123 tlsConfig.Certificates = []tls.Certificate{ clientCert }
124 }
125 return tls.Dial(netw, addr, tlsConfig)
126 }
127
128 return net.Dial(netw, addr)
129}