openidec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

main.go (9713B)


      1 package main
      2 
      3 import (
      4 	"bufio"
      5 	"flag"
      6 	"fmt"
      7 	"io"
      8 	"io/ioutil"
      9 	"os"
     10 	"sort"
     11 	"strings"
     12 
     13 	"git.openbsd.org.ru/vasyahacker/openidec/ii"
     14 )
     15 
     16 func open_db(path string) *ii.DB {
     17 	db := ii.OpenDB(path)
     18 	if db == nil {
     19 		fmt.Printf("Can no open db: %s\n", path)
     20 		os.Exit(1)
     21 	}
     22 	return db
     23 }
     24 
     25 func open_users_db(path string) *ii.UDB {
     26 	db := ii.OpenUsers(path)
     27 	if err := db.LoadUsers(); err != nil {
     28 		fmt.Printf("Can no load db: %s\n", path)
     29 		os.Exit(1)
     30 	}
     31 	return db
     32 }
     33 
     34 func GetFile(path string) string {
     35 	var file *os.File
     36 	var err error
     37 	if path == "-" {
     38 		file = os.Stdin
     39 	} else {
     40 		file, err = os.Open(path)
     41 		if err != nil {
     42 			fmt.Printf("Can not open file %s: %s\n", path, err)
     43 			os.Exit(1)
     44 		}
     45 		defer file.Close()
     46 	}
     47 	b, err := ioutil.ReadAll(file)
     48 	if err != nil {
     49 		fmt.Printf("Can not read file %s: %s\n", path, err)
     50 		os.Exit(1)
     51 	}
     52 	return string(b)
     53 }
     54 
     55 func main() {
     56 	ii.OpenLog(ioutil.Discard, os.Stdout, os.Stderr)
     57 
     58 	db_opt := flag.String("db", "./db", "II database path (directory)")
     59 	lim_opt := flag.Int("lim", 0, "Fetch last N messages")
     60 	verbose_opt := flag.Bool("v", false, "Verbose")
     61 	force_opt := flag.Bool("f", false, "Force full sync")
     62 	users_opt := flag.String("u", "points.txt", "Users database")
     63 	conns_opt := flag.Int("j", 6, "Maximum parallel jobs")
     64 	topics_opt := flag.Bool("t", false, "Select topics only")
     65 	from_opt := flag.String("from", "", "Select from")
     66 	to_opt := flag.String("to", "", "Select to")
     67 	flag.Parse()
     68 	ii.MaxConnections = *conns_opt
     69 	if *verbose_opt {
     70 		ii.OpenLog(os.Stdout, os.Stdout, os.Stderr)
     71 	}
     72 
     73 	args := flag.Args()
     74 	if len(args) < 1 {
     75 		fmt.Printf(`Help: %s [options] command [arguments]
     76 Commands:
     77 	search <string> [echo]        - search in base
     78 	send <server> <pauth> <msg|-> - send message
     79 	clean                         - cleanup database
     80 	fetch <url> [echofile|-]      - fetch
     81 	store <bundle|->              - import bundle to database
     82 	get <msgid>                   - show message from database
     83 	select <echo> [[start]:lim]   - get slice from echo
     84 	index                         - recreate index
     85 	blacklist <msgid>             - blacklist msg
     86 	useradd <name> <e-mail> <password> - adduser
     87 Options:
     88         -db=<path>                    - database path
     89         -lim=<lim>                    - fetch lim last messages
     90         -u=<path>                     - points account file
     91         -t                            - topics only (select,get)
     92         -from=<user>                  - select from
     93         -to=<user>                    - select to
     94 `, os.Args[0])
     95 		os.Exit(1)
     96 	}
     97 	switch cmd := args[0]; cmd {
     98 	case "search":
     99 		echo := ""
    100 		if len(args) < 2 {
    101 			fmt.Printf("No string supplied\n")
    102 			os.Exit(1)
    103 		}
    104 		if len(args) > 2 {
    105 			echo = args[2]
    106 		}
    107 		db := open_db(*db_opt)
    108 		db.Lock()
    109 		defer db.Unlock()
    110 		if err := db.LoadIndex(); err != nil {
    111 			fmt.Printf("Can not load index: %s\n", err)
    112 			os.Exit(1)
    113 		}
    114 		for _, v := range db.Idx.List {
    115 			if echo != "" {
    116 				mi := db.Idx.Hash[v]
    117 				if mi.Echo != echo {
    118 					continue
    119 				}
    120 			}
    121 			m := db.GetFast(v)
    122 			if m == nil {
    123 				continue
    124 			}
    125 			if strings.Contains(m.Text, args[1]) {
    126 				fmt.Printf("%s\n", v)
    127 				if *verbose_opt {
    128 					fmt.Printf("%s\n", m)
    129 				}
    130 			}
    131 		}
    132 	case "blacklist":
    133 		if len(args) < 2 {
    134 			fmt.Printf("No msgid supplied\n")
    135 			os.Exit(1)
    136 		}
    137 		db := open_db(*db_opt)
    138 		m := db.Get(args[1])
    139 		if m != nil {
    140 			if err := db.Blacklist(m); err != nil {
    141 				fmt.Printf("Can not blacklist: %s\n", err)
    142 				os.Exit(1)
    143 			}
    144 		} else {
    145 			fmt.Printf("No such msg")
    146 		}
    147 	case "send":
    148 		if len(args) < 4 {
    149 			fmt.Printf("No argumnet(s) supplied\nShould be: <server> <pauth> and <file|->.\n")
    150 			os.Exit(1)
    151 		}
    152 		msg := GetFile(args[3])
    153 		if _, err := ii.DecodeMsgline(string(msg), false); err != nil {
    154 			fmt.Printf("Wrong message format\n")
    155 			os.Exit(1)
    156 		}
    157 		n, err := ii.Connect(args[1])
    158 		if err != nil {
    159 			fmt.Printf("Can not connect to %s: %s\n", args[1], err)
    160 			os.Exit(1)
    161 		}
    162 		if err := n.Post(args[2], msg); err != nil {
    163 			fmt.Printf("Can not send message: %s\n", err)
    164 			os.Exit(1)
    165 		}
    166 	case "useradd":
    167 		if len(args) < 4 {
    168 			fmt.Printf("No argumnet(s) supplied\nShould be: name, e-mail and password.\n")
    169 			os.Exit(1)
    170 		}
    171 		db := open_users_db(*users_opt)
    172 		if err := db.Add(args[1], args[2], args[3]); err != nil {
    173 			fmt.Printf("Can not add user: %s\n", err)
    174 			os.Exit(1)
    175 		}
    176 	case "clean":
    177 		hash := make(map[string]int)
    178 		last := make(map[string]string)
    179 		nr := 0
    180 		dup := 0
    181 		fmt.Printf("Pass 1...\n")
    182 		err := ii.FileLines(*db_opt, func(line string) bool {
    183 			nr++
    184 			a := strings.Split(line, ":")
    185 			if len(a) != 2 {
    186 				ii.Error.Printf("Error in line: %d", nr)
    187 				return true
    188 			}
    189 			if !ii.IsMsgId(a[0]) {
    190 				ii.Error.Printf("Error in line: %d", nr)
    191 				return true
    192 			}
    193 			if _, ok := hash[a[0]]; ok {
    194 				hash[a[0]]++
    195 				dup++
    196 				last[a[0]] = line
    197 			} else {
    198 				hash[a[0]] = 1
    199 			}
    200 			return true
    201 		})
    202 		if err != nil {
    203 			fmt.Printf("Error: %s\n", err)
    204 			os.Exit(1)
    205 		}
    206 		fmt.Printf("%d lines... %d dups...\n", nr, dup)
    207 		if dup == 0 {
    208 			os.Exit(0)
    209 		}
    210 		fmt.Printf("Pass 2...\n")
    211 		nr = 0
    212 		f, err := os.OpenFile(*db_opt+".new", os.O_CREATE|os.O_WRONLY, 0644)
    213 		if err != nil {
    214 			fmt.Printf("Error: %s\n", err)
    215 			os.Exit(1)
    216 		}
    217 		skip := 0
    218 		err = ii.FileLines(*db_opt, func(line string) bool {
    219 			nr++
    220 			a := strings.Split(line, ":")
    221 			id := a[0]
    222 			if len(a) != 2 {
    223 				fmt.Printf("Error in line: %d\n", nr)
    224 				skip++
    225 				return true
    226 			}
    227 			if !ii.IsMsgId(id) {
    228 				fmt.Printf("Error in line: %d\n", nr)
    229 				skip++
    230 				return true
    231 			}
    232 			if v, ok := hash[id]; !ok || v == 0 {
    233 				fmt.Printf("Error. DB has changed. Aborted.\n")
    234 				os.Exit(1)
    235 			}
    236 			if hash[id] > 0 { // first record
    237 				hash[id] = -hash[id]
    238 				l := line
    239 				if hash[id] < -1 {
    240 					l = last[id]
    241 				}
    242 				if _, err := f.WriteString(l + "\n"); err != nil {
    243 					fmt.Printf("Error: %s\n", err)
    244 					os.Exit(1)
    245 				}
    246 			} else {
    247 				skip++
    248 			}
    249 			hash[id] += 1
    250 			if hash[id] > 0 {
    251 				fmt.Println("Error. DB has changed. Aborted.")
    252 				os.Exit(1)
    253 			}
    254 			return true
    255 		})
    256 		f.Close()
    257 		if err != nil {
    258 			fmt.Println("Error:", err)
    259 			os.Exit(1)
    260 		}
    261 		for _, v := range hash {
    262 			if v != 0 {
    263 				fmt.Println("Error. DB shrinked. Aborted.")
    264 				os.Exit(1)
    265 			}
    266 		}
    267 		fmt.Printf("%d messages removed. File %s created.\n", skip, *db_opt+".new")
    268 	case "fetch":
    269 		var echolist []string
    270 		if len(args) < 2 {
    271 			fmt.Println("No url supplied")
    272 			os.Exit(1)
    273 		}
    274 		db := open_db(*db_opt)
    275 		n, err := ii.Connect(args[1])
    276 		if err != nil {
    277 			fmt.Printf("Can not connect to %s: %s\n", args[1], err)
    278 			os.Exit(1)
    279 		}
    280 		if *force_opt {
    281 			n.Force = true
    282 		}
    283 		if len(args) > 2 {
    284 			str := GetFile(args[2])
    285 			for _, v := range strings.Split(str, "\n") {
    286 				echolist = append(echolist, strings.Split(v, ":")[0])
    287 			}
    288 		}
    289 		err = n.Fetch(db, echolist, *lim_opt)
    290 		if err != nil {
    291 			fmt.Printf("Can not fetch from %s: %s\n", args[1], err)
    292 			os.Exit(1)
    293 		}
    294 	case "store":
    295 		if len(args) < 2 {
    296 			fmt.Printf("No bundle file supplied\n")
    297 			os.Exit(1)
    298 		}
    299 		db := open_db(*db_opt)
    300 		var f *os.File
    301 		var err error
    302 		if args[1] == "-" {
    303 			f = os.Stdin
    304 		} else {
    305 			f, err = os.Open(args[1])
    306 		}
    307 		if err != nil {
    308 			fmt.Printf("Can no open bundle: %s\n", args[1])
    309 			os.Exit(1)
    310 		}
    311 		defer f.Close()
    312 		reader := bufio.NewReader(f)
    313 		for {
    314 			line, err := reader.ReadString('\n')
    315 			if err != nil && err != io.EOF {
    316 				fmt.Printf("Can read input (%s)\n", err)
    317 				os.Exit(1)
    318 			}
    319 			line = strings.TrimSuffix(line, "\n")
    320 			if err == io.EOF {
    321 				break
    322 			}
    323 			m, err := ii.DecodeBundle(line)
    324 			if m == nil {
    325 				fmt.Printf("Can not parse message: %s (%s)\n", line, err)
    326 				continue
    327 			}
    328 			if db.Lookup(m.MsgId) == nil {
    329 				if err := db.Store(m); err != nil {
    330 					fmt.Printf("Can not store message: %s\n", err)
    331 					os.Exit(1)
    332 				}
    333 			}
    334 		}
    335 	case "get":
    336 		if len(args) < 2 {
    337 			fmt.Printf("No msgid supplied\n")
    338 			os.Exit(1)
    339 		}
    340 		db := open_db(*db_opt)
    341 
    342 		if *topics_opt {
    343 			mi := db.Lookup(args[1])
    344 			if mi == nil {
    345 				return
    346 			}
    347 			mis := db.LookupIDS(db.SelectIDS(ii.Query{Echo: mi.Echo}))
    348 			topic := mi.Id
    349 			for p := mi; p != nil; p = db.LookupFast(p.Repto, false) {
    350 				if p.Repto == p.Id {
    351 					break
    352 				}
    353 				if p.Echo != mi.Echo {
    354 					continue
    355 				}
    356 				topic = p.Id
    357 			}
    358 			ids := db.GetTopics(mis)[topic]
    359 			if len(ids) == 0 {
    360 				ids = append(ids, args[1])
    361 			}
    362 			for _, m := range ids {
    363 				fmt.Println(m)
    364 			}
    365 			return
    366 		}
    367 
    368 		m := db.Get(args[1])
    369 		if m != nil {
    370 			fmt.Println(m)
    371 		}
    372 	case "select":
    373 		if len(args) < 2 {
    374 			fmt.Printf("No echo supplied\n")
    375 			os.Exit(1)
    376 		}
    377 		db := open_db(*db_opt)
    378 		req := ii.Query{Echo: args[1]}
    379 		if *from_opt != "" {
    380 			req.From = *from_opt
    381 		}
    382 		if *to_opt != "" {
    383 			req.To = *to_opt
    384 		}
    385 
    386 		if *topics_opt {
    387 			req.Repto = "!"
    388 		}
    389 		if len(args) > 2 {
    390 			fmt.Sscanf(args[2], "%d:%d", &req.Start, &req.Lim)
    391 		}
    392 		resp := db.SelectIDS(req)
    393 		for _, v := range resp {
    394 			if *verbose_opt {
    395 				fmt.Println(db.Get(v))
    396 			} else {
    397 				fmt.Println(v)
    398 			}
    399 		}
    400 	case "sort":
    401 		db := open_db(*db_opt)
    402 		if err := db.LoadIndex(); err != nil {
    403 			fmt.Printf("Can not load index: %s\n", err)
    404 			os.Exit(1)
    405 		}
    406 		scanner := bufio.NewScanner(os.Stdin)
    407 		var mm []*ii.Msg
    408 		for scanner.Scan() {
    409 			mi := db.LookupFast(scanner.Text(), false)
    410 			if mi != nil {
    411 				mm = append(mm, db.Get(mi.Id))
    412 			}
    413 		}
    414 		sort.SliceStable(mm, func(i, j int) bool {
    415 			return mm[i].Date < mm[j].Date
    416 		})
    417 		for _, v := range mm {
    418 			if *verbose_opt {
    419 				fmt.Println(v)
    420 			} else {
    421 				fmt.Println(v.MsgId)
    422 			}
    423 		}
    424 	case "index":
    425 		db := open_db(*db_opt)
    426 		if err := db.CreateIndex(); err != nil {
    427 			fmt.Printf("Can not rebuild index: %s\n", err)
    428 			os.Exit(1)
    429 		}
    430 	default:
    431 		fmt.Printf("Wrong cmd: %s\n", cmd)
    432 		os.Exit(1)
    433 	}
    434 }