package main import ( "encoding/json" "fmt" "io/ioutil" "local/args" "local/oauth2" "local/storage" "log" "net/http" "strings" ) type Uploader int const ( UploaderTodo = Uploader(iota) UploaderLedger ) var uploaders = map[string]Uploader{ "todo": UploaderTodo, "ledger": UploaderLedger, } type Config struct { EmailUser string EmailPass string EmailIMAP string TodoAddr string TodoToken string TodoList string TodoTag string Uploader Uploader Storage storage.DB Banks map[Bank]bool AccountsPattern string AccountsAntiPattern string } var config Config func NewConfig() Config { as := args.NewArgSet() as.Append(args.STRING, "emailuser", "email username", "breellocaldev@gmail.com") as.Append(args.STRING, "emailpass", "email password", "diblloewfncwssof") as.Append(args.STRING, "emailimap", "email imap", "imap.gmail.com:993") as.Append(args.STRING, "uploader", "todo, ledger", "todo") as.Append(args.STRING, "todoaddr", "todo addr", "https://todo-server.remote.blapointe.com") as.Append(args.STRING, "todopass", "todo pass", "gJtEXbbLHLf54yS9EdujtVN2n6Y") as.Append(args.STRING, "todotoken", "todo token", "") as.Append(args.STRING, "todolist", "todo list", "") as.Append(args.STRING, "todotag", "todo tag", "expense") as.Append(args.STRING, "banks", "uccu,citi,chase,bankofamerica", "uccu,citi,chase,bankofamerica") as.Append(args.STRING, "accounts", "regex to include filter accounts", ".*") as.Append(args.STRING, "not-accounts", "regex to exclude filter accounts", "zzzzzz") as.Append(args.STRING, "authaddr", "auth addr", "https://auth.remote.blapointe.com") as.Append(args.STRING, "store", "store type", "map") as.Append(args.STRING, "storeaddr", "store addr", "/tmp/store") as.Append(args.STRING, "storeuser", "store user", "") as.Append(args.STRING, "storepass", "store pass", "") if err := as.Parse(); err != nil { panic(err) } uploader := as.GetString("uploader") ul, ok := uploaders[uploader] if !ok { panic("invalid uploader: " + uploader) } storage, err := storage.New(storage.TypeFromString(as.GetString("store")), as.GetString("storeaddr"), as.GetString("storeuser"), as.GetString("storepass")) if err != nil { panic(err) } config = Config{ EmailUser: as.GetString("emailuser"), EmailPass: as.GetString("emailpass"), EmailIMAP: as.GetString("emailimap"), TodoAddr: as.GetString("todoaddr"), TodoTag: as.GetString("todotag"), AccountsPattern: as.GetString("accounts"), AccountsAntiPattern: as.GetString("not-accounts"), Storage: storage, Uploader: ul, Banks: map[Bank]bool{ BankOfAmerica: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(BankOfAmerica.String())), Chase: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(Chase.String())), Citi: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(Citi.String())), UCCU: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(UCCU.String())), }, } log.Printf("config: %+v", config) if config.Uploader == UploaderTodo { token := as.GetString("todotoken") if len(token) == 0 { token = getToken(as) } list := as.GetString("todolist") if len(list) == 0 { list = getList(as, token) } config.TodoToken = token config.TodoList = list } return config } func getToken(as *args.ArgSet) string { c := &http.Client{CheckRedirect: func(r *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }} body := "username=" + as.GetString("todopass") name := strings.Split(as.GetString("todoaddr"), ".")[0] name = strings.TrimPrefix(name, "http://") name = strings.TrimPrefix(name, "https://") req, err := http.NewRequest("POST", as.GetString("authaddr")+"/authorize/"+name+"?"+oauth2.REDIRECT+"=127.0.0.1", strings.NewReader(body)) if err != nil { panic(err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := c.Do(req) if err != nil { panic(err) } defer resp.Body.Close() if resp.StatusCode > 399 { panic("bad status getting token: " + resp.Status) } cookie := resp.Header.Get("Set-Cookie") token := cookie[strings.Index(cookie, "=")+1:] token = strings.Split(token, "; ")[0] if len(token) == 0 { panic(fmt.Sprintf("no token found: (%v) %v", resp.StatusCode, resp.Header)) } return token } func getList(as *args.ArgSet, token string) string { req, err := http.NewRequest("GET", as.GetString("todoaddr")+"/ajax.php?loadLists", nil) if err != nil { panic(err) } req.Header.Set("Cookie", oauth2.COOKIE+"="+token) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() var r struct { List []struct { ID string `json:"id"` } `json:"list"` } b, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } if err := json.Unmarshal(b, &r); err != nil { panic(fmt.Errorf("%v: %s", err, b)) } if len(r.List) == 0 { panic("no lists found") } list := r.List[0].ID if len(list) == 0 { panic("empty list found") } return list }