1package soju 2 3import ( 4 "context" 5 "fmt" 6 "net/url" 7 "strings" 8 "time" 9 10 "github.com/prometheus/client_golang/prometheus" 11) 12 13type Database interface { 14 Close() error 15 Stats(ctx context.Context) (*DatabaseStats, error) 16 17 ListUsers(ctx context.Context) ([]User, error) 18 GetUser(ctx context.Context, username string) (*User, error) 19 StoreUser(ctx context.Context, user *User) error 20 DeleteUser(ctx context.Context, id int64) error 21 22 ListNetworks(ctx context.Context, userID int64) ([]Network, error) 23 StoreNetwork(ctx context.Context, userID int64, network *Network) error 24 DeleteNetwork(ctx context.Context, id int64) error 25 ListChannels(ctx context.Context, networkID int64) ([]Channel, error) 26 StoreChannel(ctx context.Context, networKID int64, ch *Channel) error 27 DeleteChannel(ctx context.Context, id int64) error 28 29 ListDeliveryReceipts(ctx context.Context, networkID int64) ([]DeliveryReceipt, error) 30 StoreClientDeliveryReceipts(ctx context.Context, networkID int64, client string, receipts []DeliveryReceipt) error 31} 32 33type MetricsCollectorDatabase interface { 34 Database 35 MetricsCollector() prometheus.Collector 36} 37 38func OpenDB(driver, source string) (Database, error) { 39 switch driver { 40 case "sqlite3": 41 return OpenSqliteDB(source) 42 case "postgres": 43 return OpenPostgresDB(source) 44 default: 45 return nil, fmt.Errorf("unsupported database driver: %q", driver) 46 } 47} 48 49type DatabaseStats struct { 50 Users int64 51 Networks int64 52 Channels int64 53} 54 55type User struct { 56 ID int64 57 Username string 58 Password string // hashed 59 Realname string 60 Admin bool 61} 62 63type SASL struct { 64 Mechanism string 65 66 Plain struct { 67 Username string 68 Password string 69 } 70 71 // TLS client certificate authentication. 72 External struct { 73 // X.509 certificate in DER form. 74 CertBlob []byte 75 // PKCS#8 private key in DER form. 76 PrivKeyBlob []byte 77 } 78} 79 80type Network struct { 81 ID int64 82 Name string 83 Addr string 84 Nick string 85 Username string 86 Realname string 87 Pass string 88 ConnectCommands []string 89 SASL SASL 90 Enabled bool 91} 92 93func (net *Network) GetName() string { 94 if net.Name != "" { 95 return net.Name 96 } 97 return net.Addr 98} 99 100func (net *Network) URL() (*url.URL, error) { 101 s := net.Addr 102 if !strings.Contains(s, "://") { 103 // This is a raw domain name, make it an URL with the default scheme 104 s = "ircs://" + s 105 } 106 107 u, err := url.Parse(s) 108 if err != nil { 109 return nil, fmt.Errorf("failed to parse upstream server URL: %v", err) 110 } 111 112 return u, nil 113} 114 115func GetNick(user *User, net *Network) string { 116 if net.Nick != "" { 117 return net.Nick 118 } 119 return user.Username 120} 121 122func GetUsername(user *User, net *Network) string { 123 if net.Username != "" { 124 return net.Username 125 } 126 return GetNick(user, net) 127} 128 129func GetRealname(user *User, net *Network) string { 130 if net.Realname != "" { 131 return net.Realname 132 } 133 if user.Realname != "" { 134 return user.Realname 135 } 136 return GetNick(user, net) 137} 138 139type MessageFilter int 140 141const ( 142 // TODO: use customizable user defaults for FilterDefault 143 FilterDefault MessageFilter = iota 144 FilterNone 145 FilterHighlight 146 FilterMessage 147) 148 149func parseFilter(filter string) (MessageFilter, error) { 150 switch filter { 151 case "default": 152 return FilterDefault, nil 153 case "none": 154 return FilterNone, nil 155 case "highlight": 156 return FilterHighlight, nil 157 case "message": 158 return FilterMessage, nil 159 } 160 return 0, fmt.Errorf("unknown filter: %q", filter) 161} 162 163type Channel struct { 164 ID int64 165 Name string 166 Key string 167 168 Detached bool 169 DetachedInternalMsgID string 170 171 RelayDetached MessageFilter 172 ReattachOn MessageFilter 173 DetachAfter time.Duration 174 DetachOn MessageFilter 175} 176 177type DeliveryReceipt struct { 178 ID int64 179 Target string // channel or nick 180 Client string 181 InternalMsgID string 182} 183