view cmd/sievemgr/internal/config/parser.go @ 22:fc5e6970a0d5 default tip

Add support for specifying an authorization identity on the command line
author Guido Berhoerster <guido+sievemgr@berhoerster.name>
date Wed, 17 Feb 2021 07:50:55 +0100
parents 854167f55839
children
line wrap: on
line source

// Copyright (C) 2020 Guido Berhoerster <guido+sievemgr@berhoerster.name>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package config

import (
	"fmt"
	"strconv"
)

type parser struct {
	s *scanner
}

func (p *parser) scanType(expectedTok token) (string, error) {
	tok, lit, err := p.s.scan()
	if err != nil {
		return lit, NewParserError(err.Error(), p.s.line)
	} else if tok != expectedTok {
		return lit, NewParserError(fmt.Sprintf("expected %s, got %s %q", expectedTok, tok, lit), p.s.line)
	}
	return lit, nil
}

func (p *parser) validateAccount(acct *Account) error {
	if acct.Host == "" {
		return NewParserError(fmt.Sprintf("no host specified for account %q",
			acct.Name), 0)
	} else if acct.User == "" {
		return NewParserError(fmt.Sprintf("no user specified for account %q",
			acct.Name), 0)
	}
	return nil
}

func (p *parser) parse(conf *Configuration) error {
	var acct *Account
	var isDefault bool
	var tok token
	var lit string
	var err error
parsing:
	for {
		tok, lit, err = p.s.scan()
		if err != nil {
			err = NewParserError(err.Error(), p.s.line)
			break parsing
		}
		if tok == tokenIllegal {
			err = NewParserError(fmt.Sprintf("illegal token %q", lit), p.s.line)
			break parsing
		} else if tok == tokenEOF {
			break parsing
		} else if tok != tokenIdent {
			err = NewParserError(fmt.Sprintf("expected identifier, got %s %q", tok, lit), p.s.line)
			break parsing
		}
		switch lit {
		case "account":
			if acct != nil {
				if err = p.validateAccount(acct); err != nil {
					break parsing
				}
				conf.Accounts = append(conf.Accounts, acct)
				if isDefault {
					conf.Default = acct
					isDefault = false
				}
			}

			// account name
			if lit, err = p.scanType(tokenString); err != nil {
				break parsing
			}
			acct = &Account{
				Name: lit,
			}
		case "default":
			isDefault = true
		case "host":
			if lit, err = p.scanType(tokenString); err != nil {
				break parsing
			}
			acct.Host = lit
		case "port":
			if lit, err = p.scanType(tokenNumber); err != nil {
				break parsing
			}
			var port int
			if port, err = strconv.Atoi(lit); err != nil {
				err = NewParserError(fmt.Sprintf("failed to parse port: %s", err), p.s.line)
				break parsing
			} else if port < 1 || port > 65535 {
				err = NewParserError(fmt.Sprintf("invalid port number %d", port), p.s.line)
				break parsing
			}
			acct.Port = lit
		case "user":
			if lit, err = p.scanType(tokenString); err != nil {
				break parsing
			}
			acct.User = lit
		case "pass":
			if lit, err = p.scanType(tokenString); err != nil {
				break parsing
			}
			acct.Password = lit
		case "insecure":
			acct.Insecure = true
		default:
			err = NewParserError(fmt.Sprintf("unknown %s: %q", tok, lit), p.s.line)
			break parsing
		}
	}
	if err != nil {
		return err
	}
	if acct != nil {
		if err = p.validateAccount(acct); err != nil {
			return err
		}
		conf.Accounts = append(conf.Accounts, acct)
		if isDefault {
			conf.Default = acct
		}
	}

	return nil
}