summaryrefslogtreecommitdiff
path: root/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser')
-rw-r--r--vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE23
-rw-r--r--vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md4
-rw-r--r--vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go292
-rw-r--r--vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go606
-rw-r--r--vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go213
5 files changed, 1138 insertions, 0 deletions
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE
new file mode 100644
index 000000000..3e2dbb6f8
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2013, Jérôme Renard
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md
new file mode 100644
index 000000000..ce59d376f
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md
@@ -0,0 +1,4 @@
+Syslogparser
+============
+
+This is a fork for [github.com/jeromer/syslogparser](https://github.com/jeromer/syslogparser), since this library is intensively used by `go-syslog`, now is integrated as a `internal` package.
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go
new file mode 100644
index 000000000..486a0cb54
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go
@@ -0,0 +1,292 @@
+package rfc3164
+
+import (
+ "bytes"
+ "os"
+ "time"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser"
+)
+
+type Parser struct {
+ buff []byte
+ cursor int
+ l int
+ priority syslogparser.Priority
+ version int
+ header header
+ message rfc3164message
+ location *time.Location
+ skipTag bool
+}
+
+type header struct {
+ timestamp time.Time
+ hostname string
+}
+
+type rfc3164message struct {
+ tag string
+ content string
+}
+
+func NewParser(buff []byte) *Parser {
+ return &Parser{
+ buff: buff,
+ cursor: 0,
+ l: len(buff),
+ location: time.UTC,
+ }
+}
+
+func (p *Parser) Location(location *time.Location) {
+ p.location = location
+}
+
+func (p *Parser) Parse() error {
+ tcursor := p.cursor
+ pri, err := p.parsePriority()
+ if err != nil {
+ // RFC3164 sec 4.3.3
+ p.priority = syslogparser.Priority{13, syslogparser.Facility{Value: 1}, syslogparser.Severity{Value: 5}}
+ p.cursor = tcursor
+ content, err := p.parseContent()
+ p.header.timestamp = time.Now().Round(time.Second)
+ if err != syslogparser.ErrEOL {
+ return err
+ }
+ p.message = rfc3164message{content: content}
+ return nil
+ }
+
+ tcursor = p.cursor
+ hdr, err := p.parseHeader()
+ if err == syslogparser.ErrTimestampUnknownFormat {
+ // RFC3164 sec 4.3.2.
+ hdr.timestamp = time.Now().Round(time.Second)
+ // No tag processing should be done
+ p.skipTag = true
+ // Reset cursor for content read
+ p.cursor = tcursor
+ } else if err != nil {
+ return err
+ } else {
+ p.cursor++
+ }
+
+ msg, err := p.parsemessage()
+ if err != syslogparser.ErrEOL {
+ return err
+ }
+
+ p.priority = pri
+ p.version = syslogparser.NO_VERSION
+ p.header = hdr
+ p.message = msg
+
+ return nil
+}
+
+func (p *Parser) Dump() syslogparser.LogParts {
+ return syslogparser.LogParts{
+ "timestamp": p.header.timestamp,
+ "hostname": p.header.hostname,
+ "tag": p.message.tag,
+ "content": p.message.content,
+ "priority": p.priority.P,
+ "facility": p.priority.F.Value,
+ "severity": p.priority.S.Value,
+ }
+}
+
+func (p *Parser) parsePriority() (syslogparser.Priority, error) {
+ return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
+}
+
+func (p *Parser) parseHeader() (header, error) {
+ hdr := header{}
+ var err error
+
+ ts, err := p.parseTimestamp()
+ if err != nil {
+ return hdr, err
+ }
+
+ hostname, err := p.parseHostname()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.timestamp = ts
+ hdr.hostname = hostname
+
+ return hdr, nil
+}
+
+func (p *Parser) parsemessage() (rfc3164message, error) {
+ msg := rfc3164message{}
+ var err error
+
+ if !p.skipTag {
+ tag, err := p.parseTag()
+ if err != nil {
+ return msg, err
+ }
+ msg.tag = tag
+ }
+
+ content, err := p.parseContent()
+ if err != syslogparser.ErrEOL {
+ return msg, err
+ }
+
+ msg.content = content
+
+ return msg, err
+}
+
+// https://tools.ietf.org/html/rfc3164#section-4.1.2
+func (p *Parser) parseTimestamp() (time.Time, error) {
+ var ts time.Time
+ var err error
+ var tsFmtLen int
+ var sub []byte
+
+ tsFmts := []string{
+ time.Stamp,
+ time.RFC3339,
+ }
+ // if timestamps starts with numeric try formats with different order
+ // it is more likely that timestamp is in RFC3339 format then
+ if c := p.buff[p.cursor]; c > '0' && c < '9' {
+ tsFmts = []string{
+ time.RFC3339,
+ time.Stamp,
+ }
+ }
+
+ found := false
+ for _, tsFmt := range tsFmts {
+ tsFmtLen = len(tsFmt)
+
+ if p.cursor+tsFmtLen > p.l {
+ continue
+ }
+
+ sub = p.buff[p.cursor : tsFmtLen+p.cursor]
+ ts, err = time.ParseInLocation(tsFmt, string(sub), p.location)
+ if err == nil {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ p.cursor = len(time.Stamp)
+
+ // XXX : If the timestamp is invalid we try to push the cursor one byte
+ // XXX : further, in case it is a space
+ if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
+ p.cursor++
+ }
+
+ return ts, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ fixTimestampIfNeeded(&ts)
+
+ p.cursor += tsFmtLen
+
+ if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
+ p.cursor++
+ }
+
+ return ts, nil
+}
+
+func (p *Parser) parseHostname() (string, error) {
+ oldcursor := p.cursor
+ hostname, err := syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
+ if err == nil && len(hostname) > 0 && string(hostname[len(hostname)-1]) == ":" { // not an hostname! we found a GNU implementation of syslog()
+ p.cursor = oldcursor - 1
+ myhostname, err := os.Hostname()
+ if err == nil {
+ return myhostname, nil
+ }
+ return "", nil
+ }
+ return hostname, err
+}
+
+// http://tools.ietf.org/html/rfc3164#section-4.1.3
+func (p *Parser) parseTag() (string, error) {
+ var b byte
+ var endOfTag bool
+ var bracketOpen bool
+ var tag []byte
+ var err error
+ var found bool
+
+ from := p.cursor
+
+ for {
+ if p.cursor == p.l {
+ // no tag found, reset cursor for content
+ p.cursor = from
+ return "", nil
+ }
+
+ b = p.buff[p.cursor]
+ bracketOpen = (b == '[')
+ endOfTag = (b == ':' || b == ' ')
+
+ // XXX : parse PID ?
+ if bracketOpen {
+ tag = p.buff[from:p.cursor]
+ found = true
+ }
+
+ if endOfTag {
+ if !found {
+ tag = p.buff[from:p.cursor]
+ found = true
+ }
+
+ p.cursor++
+ break
+ }
+
+ p.cursor++
+ }
+
+ if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
+ p.cursor++
+ }
+
+ return string(tag), err
+}
+
+func (p *Parser) parseContent() (string, error) {
+ if p.cursor > p.l {
+ return "", syslogparser.ErrEOL
+ }
+
+ content := bytes.Trim(p.buff[p.cursor:p.l], " ")
+ p.cursor += len(content)
+
+ return string(content), syslogparser.ErrEOL
+}
+
+func fixTimestampIfNeeded(ts *time.Time) {
+ now := time.Now()
+ y := ts.Year()
+
+ if ts.Year() == 0 {
+ y = now.Year()
+ }
+
+ newTs := time.Date(y, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
+ ts.Second(), ts.Nanosecond(), ts.Location())
+
+ *ts = newTs
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go
new file mode 100644
index 000000000..bea8e5879
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go
@@ -0,0 +1,606 @@
+// Note to self : never try to code while looking after your kids
+// The result might look like this : https://pbs.twimg.com/media/BXqSuYXIEAAscVA.png
+
+package rfc5424
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "time"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser"
+)
+
+const (
+ NILVALUE = '-'
+)
+
+var (
+ ErrYearInvalid = &syslogparser.ParserError{"Invalid year in timestamp"}
+ ErrMonthInvalid = &syslogparser.ParserError{"Invalid month in timestamp"}
+ ErrDayInvalid = &syslogparser.ParserError{"Invalid day in timestamp"}
+ ErrHourInvalid = &syslogparser.ParserError{"Invalid hour in timestamp"}
+ ErrMinuteInvalid = &syslogparser.ParserError{"Invalid minute in timestamp"}
+ ErrSecondInvalid = &syslogparser.ParserError{"Invalid second in timestamp"}
+ ErrSecFracInvalid = &syslogparser.ParserError{"Invalid fraction of second in timestamp"}
+ ErrTimeZoneInvalid = &syslogparser.ParserError{"Invalid time zone in timestamp"}
+ ErrInvalidTimeFormat = &syslogparser.ParserError{"Invalid time format"}
+ ErrInvalidAppName = &syslogparser.ParserError{"Invalid app name"}
+ ErrInvalidProcId = &syslogparser.ParserError{"Invalid proc ID"}
+ ErrInvalidMsgId = &syslogparser.ParserError{"Invalid msg ID"}
+ ErrNoStructuredData = &syslogparser.ParserError{"No structured data"}
+)
+
+type Parser struct {
+ buff []byte
+ cursor int
+ l int
+ header header
+ structuredData string
+ message string
+}
+
+type header struct {
+ priority syslogparser.Priority
+ version int
+ timestamp time.Time
+ hostname string
+ appName string
+ procId string
+ msgId string
+}
+
+type partialTime struct {
+ hour int
+ minute int
+ seconds int
+ secFrac float64
+}
+
+type fullTime struct {
+ pt partialTime
+ loc *time.Location
+}
+
+type fullDate struct {
+ year int
+ month int
+ day int
+}
+
+func NewParser(buff []byte) *Parser {
+ return &Parser{
+ buff: buff,
+ cursor: 0,
+ l: len(buff),
+ }
+}
+
+func (p *Parser) Location(location *time.Location) {
+ // Ignore as RFC5424 syslog always has a timezone
+}
+
+func (p *Parser) Parse() error {
+ hdr, err := p.parseHeader()
+ if err != nil {
+ return err
+ }
+
+ p.header = hdr
+
+ sd, err := p.parseStructuredData()
+ if err != nil {
+ return err
+ }
+
+ p.structuredData = sd
+ p.cursor++
+
+ if p.cursor < p.l {
+ p.message = string(p.buff[p.cursor:])
+ }
+
+ return nil
+}
+
+func (p *Parser) Dump() syslogparser.LogParts {
+ return syslogparser.LogParts{
+ "priority": p.header.priority.P,
+ "facility": p.header.priority.F.Value,
+ "severity": p.header.priority.S.Value,
+ "version": p.header.version,
+ "timestamp": p.header.timestamp,
+ "hostname": p.header.hostname,
+ "app_name": p.header.appName,
+ "proc_id": p.header.procId,
+ "msg_id": p.header.msgId,
+ "structured_data": p.structuredData,
+ "message": p.message,
+ }
+}
+
+// HEADER = PRI VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID
+func (p *Parser) parseHeader() (header, error) {
+ hdr := header{}
+
+ pri, err := p.parsePriority()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.priority = pri
+
+ ver, err := p.parseVersion()
+ if err != nil {
+ return hdr, err
+ }
+ hdr.version = ver
+ p.cursor++
+
+ ts, err := p.parseTimestamp()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.timestamp = ts
+ p.cursor++
+
+ host, err := p.parseHostname()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.hostname = host
+ p.cursor++
+
+ appName, err := p.parseAppName()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.appName = appName
+ p.cursor++
+
+ procId, err := p.parseProcId()
+ if err != nil {
+ return hdr, nil
+ }
+
+ hdr.procId = procId
+ p.cursor++
+
+ msgId, err := p.parseMsgId()
+ if err != nil {
+ return hdr, nil
+ }
+
+ hdr.msgId = msgId
+ p.cursor++
+
+ return hdr, nil
+}
+
+func (p *Parser) parsePriority() (syslogparser.Priority, error) {
+ return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
+}
+
+func (p *Parser) parseVersion() (int, error) {
+ return syslogparser.ParseVersion(p.buff, &p.cursor, p.l)
+}
+
+// https://tools.ietf.org/html/rfc5424#section-6.2.3
+func (p *Parser) parseTimestamp() (time.Time, error) {
+ var ts time.Time
+
+ if p.cursor >= p.l {
+ return ts, ErrInvalidTimeFormat
+ }
+
+ if p.buff[p.cursor] == NILVALUE {
+ p.cursor++
+ return ts, nil
+ }
+
+ fd, err := parseFullDate(p.buff, &p.cursor, p.l)
+ if err != nil {
+ return ts, err
+ }
+
+ if p.cursor >= p.l || p.buff[p.cursor] != 'T' {
+ return ts, ErrInvalidTimeFormat
+ }
+
+ p.cursor++
+
+ ft, err := parseFullTime(p.buff, &p.cursor, p.l)
+ if err != nil {
+ return ts, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ nSec, err := toNSec(ft.pt.secFrac)
+ if err != nil {
+ return ts, err
+ }
+
+ ts = time.Date(
+ fd.year,
+ time.Month(fd.month),
+ fd.day,
+ ft.pt.hour,
+ ft.pt.minute,
+ ft.pt.seconds,
+ nSec,
+ ft.loc,
+ )
+
+ return ts, nil
+}
+
+// HOSTNAME = NILVALUE / 1*255PRINTUSASCII
+func (p *Parser) parseHostname() (string, error) {
+ return syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
+}
+
+// APP-NAME = NILVALUE / 1*48PRINTUSASCII
+func (p *Parser) parseAppName() (string, error) {
+ return parseUpToLen(p.buff, &p.cursor, p.l, 48, ErrInvalidAppName)
+}
+
+// PROCID = NILVALUE / 1*128PRINTUSASCII
+func (p *Parser) parseProcId() (string, error) {
+ return parseUpToLen(p.buff, &p.cursor, p.l, 128, ErrInvalidProcId)
+}
+
+// MSGID = NILVALUE / 1*32PRINTUSASCII
+func (p *Parser) parseMsgId() (string, error) {
+ return parseUpToLen(p.buff, &p.cursor, p.l, 32, ErrInvalidMsgId)
+}
+
+func (p *Parser) parseStructuredData() (string, error) {
+ return parseStructuredData(p.buff, &p.cursor, p.l)
+}
+
+// ----------------------------------------------
+// https://tools.ietf.org/html/rfc5424#section-6
+// ----------------------------------------------
+
+// XXX : bind them to Parser ?
+
+// FULL-DATE : DATE-FULLYEAR "-" DATE-MONTH "-" DATE-MDAY
+func parseFullDate(buff []byte, cursor *int, l int) (fullDate, error) {
+ var fd fullDate
+
+ year, err := parseYear(buff, cursor, l)
+ if err != nil {
+ return fd, err
+ }
+
+ if *cursor >= l || buff[*cursor] != '-' {
+ return fd, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ *cursor++
+
+ month, err := parseMonth(buff, cursor, l)
+ if err != nil {
+ return fd, err
+ }
+
+ if *cursor >= l || buff[*cursor] != '-' {
+ return fd, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ *cursor++
+
+ day, err := parseDay(buff, cursor, l)
+ if err != nil {
+ return fd, err
+ }
+
+ fd = fullDate{
+ year: year,
+ month: month,
+ day: day,
+ }
+
+ return fd, nil
+}
+
+// DATE-FULLYEAR = 4DIGIT
+func parseYear(buff []byte, cursor *int, l int) (int, error) {
+ yearLen := 4
+
+ if *cursor+yearLen > l {
+ return 0, syslogparser.ErrEOL
+ }
+
+ // XXX : we do not check for a valid year (ie. 1999, 2013 etc)
+ // XXX : we only checks the format is correct
+ sub := string(buff[*cursor : *cursor+yearLen])
+
+ *cursor += yearLen
+
+ year, err := strconv.Atoi(sub)
+ if err != nil {
+ return 0, ErrYearInvalid
+ }
+
+ return year, nil
+}
+
+// DATE-MONTH = 2DIGIT ; 01-12
+func parseMonth(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 1, 12, ErrMonthInvalid)
+}
+
+// DATE-MDAY = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
+func parseDay(buff []byte, cursor *int, l int) (int, error) {
+ // XXX : this is a relaxed constraint
+ // XXX : we do not check if valid regarding February or leap years
+ // XXX : we only checks that day is in range [01 -> 31]
+ // XXX : in other words this function will not rant if you provide Feb 31th
+ return syslogparser.Parse2Digits(buff, cursor, l, 1, 31, ErrDayInvalid)
+}
+
+// FULL-TIME = PARTIAL-TIME TIME-OFFSET
+func parseFullTime(buff []byte, cursor *int, l int) (fullTime, error) {
+ var loc = new(time.Location)
+ var ft fullTime
+
+ pt, err := parsePartialTime(buff, cursor, l)
+ if err != nil {
+ return ft, err
+ }
+
+ loc, err = parseTimeOffset(buff, cursor, l)
+ if err != nil {
+ return ft, err
+ }
+
+ ft = fullTime{
+ pt: pt,
+ loc: loc,
+ }
+
+ return ft, nil
+}
+
+// PARTIAL-TIME = TIME-HOUR ":" TIME-MINUTE ":" TIME-SECOND[TIME-SECFRAC]
+func parsePartialTime(buff []byte, cursor *int, l int) (partialTime, error) {
+ var pt partialTime
+
+ hour, minute, err := getHourMinute(buff, cursor, l)
+ if err != nil {
+ return pt, err
+ }
+
+ if *cursor >= l || buff[*cursor] != ':' {
+ return pt, ErrInvalidTimeFormat
+ }
+
+ *cursor++
+
+ // ----
+
+ seconds, err := parseSecond(buff, cursor, l)
+ if err != nil {
+ return pt, err
+ }
+
+ pt = partialTime{
+ hour: hour,
+ minute: minute,
+ seconds: seconds,
+ }
+
+ // ----
+
+ if *cursor >= l || buff[*cursor] != '.' {
+ return pt, nil
+ }
+
+ *cursor++
+
+ secFrac, err := parseSecFrac(buff, cursor, l)
+ if err != nil {
+ return pt, nil
+ }
+ pt.secFrac = secFrac
+
+ return pt, nil
+}
+
+// TIME-HOUR = 2DIGIT ; 00-23
+func parseHour(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 0, 23, ErrHourInvalid)
+}
+
+// TIME-MINUTE = 2DIGIT ; 00-59
+func parseMinute(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 0, 59, ErrMinuteInvalid)
+}
+
+// TIME-SECOND = 2DIGIT ; 00-59
+func parseSecond(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 0, 59, ErrSecondInvalid)
+}
+
+// TIME-SECFRAC = "." 1*6DIGIT
+func parseSecFrac(buff []byte, cursor *int, l int) (float64, error) {
+ maxDigitLen := 6
+
+ max := *cursor + maxDigitLen
+ from := *cursor
+ to := from
+
+ for to = from; to < max; to++ {
+ if to >= l {
+ break
+ }
+
+ c := buff[to]
+ if !syslogparser.IsDigit(c) {
+ break
+ }
+ }
+
+ sub := string(buff[from:to])
+ if len(sub) == 0 {
+ return 0, ErrSecFracInvalid
+ }
+
+ secFrac, err := strconv.ParseFloat("0."+sub, 64)
+ *cursor = to
+ if err != nil {
+ return 0, ErrSecFracInvalid
+ }
+
+ return secFrac, nil
+}
+
+// TIME-OFFSET = "Z" / TIME-NUMOFFSET
+func parseTimeOffset(buff []byte, cursor *int, l int) (*time.Location, error) {
+
+ if *cursor >= l || buff[*cursor] == 'Z' {
+ *cursor++
+ return time.UTC, nil
+ }
+
+ return parseNumericalTimeOffset(buff, cursor, l)
+}
+
+// TIME-NUMOFFSET = ("+" / "-") TIME-HOUR ":" TIME-MINUTE
+func parseNumericalTimeOffset(buff []byte, cursor *int, l int) (*time.Location, error) {
+ var loc = new(time.Location)
+
+ sign := buff[*cursor]
+
+ if (sign != '+') && (sign != '-') {
+ return loc, ErrTimeZoneInvalid
+ }
+
+ *cursor++
+
+ hour, minute, err := getHourMinute(buff, cursor, l)
+ if err != nil {
+ return loc, err
+ }
+
+ tzStr := fmt.Sprintf("%s%02d:%02d", string(sign), hour, minute)
+ tmpTs, err := time.Parse("-07:00", tzStr)
+ if err != nil {
+ return loc, err
+ }
+
+ return tmpTs.Location(), nil
+}
+
+func getHourMinute(buff []byte, cursor *int, l int) (int, int, error) {
+ hour, err := parseHour(buff, cursor, l)
+ if err != nil {
+ return 0, 0, err
+ }
+
+ if *cursor >= l || buff[*cursor] != ':' {
+ return 0, 0, ErrInvalidTimeFormat
+ }
+
+ *cursor++
+
+ minute, err := parseMinute(buff, cursor, l)
+ if err != nil {
+ return 0, 0, err
+ }
+
+ return hour, minute, nil
+}
+
+func toNSec(sec float64) (int, error) {
+ _, frac := math.Modf(sec)
+ fracStr := strconv.FormatFloat(frac, 'f', 9, 64)
+ fracInt, err := strconv.Atoi(fracStr[2:])
+ if err != nil {
+ return 0, err
+ }
+
+ return fracInt, nil
+}
+
+// ------------------------------------------------
+// https://tools.ietf.org/html/rfc5424#section-6.3
+// ------------------------------------------------
+
+func parseStructuredData(buff []byte, cursor *int, l int) (string, error) {
+ var sdData string
+ var found bool
+
+ if *cursor >= l {
+ return "-", nil
+ }
+
+ if buff[*cursor] == NILVALUE {
+ *cursor++
+ return "-", nil
+ }
+
+ if buff[*cursor] != '[' {
+ return sdData, ErrNoStructuredData
+ }
+
+ from := *cursor
+ to := from
+
+ for to = from; to < l; to++ {
+ if found {
+ break
+ }
+
+ b := buff[to]
+
+ if b == ']' {
+ switch t := to + 1; {
+ case t == l:
+ found = true
+ case t <= l && buff[t] == ' ':
+ found = true
+ }
+ }
+ }
+
+ if found {
+ *cursor = to
+ return string(buff[from:to]), nil
+ }
+
+ return sdData, ErrNoStructuredData
+}
+
+func parseUpToLen(buff []byte, cursor *int, l int, maxLen int, e error) (string, error) {
+ var to int
+ var found bool
+ var result string
+
+ max := *cursor + maxLen
+
+ for to = *cursor; (to <= max) && (to < l); to++ {
+ if buff[to] == ' ' {
+ found = true
+ break
+ }
+ }
+
+ if found {
+ result = string(buff[*cursor:to])
+ } else if to > max {
+ to = max // don't go past max
+ }
+
+ *cursor = to
+
+ if found {
+ return result, nil
+ }
+
+ return "", e
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go
new file mode 100644
index 000000000..7a10820c7
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go
@@ -0,0 +1,213 @@
+package syslogparser
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ PRI_PART_START = '<'
+ PRI_PART_END = '>'
+
+ NO_VERSION = -1
+)
+
+var (
+ ErrEOL = &ParserError{"End of log line"}
+ ErrNoSpace = &ParserError{"No space found"}
+
+ ErrPriorityNoStart = &ParserError{"No start char found for priority"}
+ ErrPriorityEmpty = &ParserError{"Priority field empty"}
+ ErrPriorityNoEnd = &ParserError{"No end char found for priority"}
+ ErrPriorityTooShort = &ParserError{"Priority field too short"}
+ ErrPriorityTooLong = &ParserError{"Priority field too long"}
+ ErrPriorityNonDigit = &ParserError{"Non digit found in priority"}
+
+ ErrVersionNotFound = &ParserError{"Can not find version"}
+
+ ErrTimestampUnknownFormat = &ParserError{"Timestamp format unknown"}
+
+ ErrHostnameTooShort = &ParserError{"Hostname field too short"}
+)
+
+type LogParser interface {
+ Parse() error
+ Dump() LogParts
+ Location(*time.Location)
+}
+
+type ParserError struct {
+ ErrorString string
+}
+
+type Priority struct {
+ P int
+ F Facility
+ S Severity
+}
+
+type Facility struct {
+ Value int
+}
+
+type Severity struct {
+ Value int
+}
+
+type LogParts map[string]interface{}
+
+// https://tools.ietf.org/html/rfc3164#section-4.1
+func ParsePriority(buff []byte, cursor *int, l int) (Priority, error) {
+ pri := newPriority(0)
+
+ if l <= 0 {
+ return pri, ErrPriorityEmpty
+ }
+
+ if buff[*cursor] != PRI_PART_START {
+ return pri, ErrPriorityNoStart
+ }
+
+ i := 1
+ priDigit := 0
+
+ for i < l {
+ if i >= 5 {
+ return pri, ErrPriorityTooLong
+ }
+
+ c := buff[i]
+
+ if c == PRI_PART_END {
+ if i == 1 {
+ return pri, ErrPriorityTooShort
+ }
+
+ *cursor = i + 1
+ return newPriority(priDigit), nil
+ }
+
+ if IsDigit(c) {
+ v, e := strconv.Atoi(string(c))
+ if e != nil {
+ return pri, e
+ }
+
+ priDigit = (priDigit * 10) + v
+ } else {
+ return pri, ErrPriorityNonDigit
+ }
+
+ i++
+ }
+
+ return pri, ErrPriorityNoEnd
+}
+
+// https://tools.ietf.org/html/rfc5424#section-6.2.2
+func ParseVersion(buff []byte, cursor *int, l int) (int, error) {
+ if *cursor >= l {
+ return NO_VERSION, ErrVersionNotFound
+ }
+
+ c := buff[*cursor]
+ *cursor++
+
+ // XXX : not a version, not an error though as RFC 3164 does not support it
+ if !IsDigit(c) {
+ return NO_VERSION, nil
+ }
+
+ v, e := strconv.Atoi(string(c))
+ if e != nil {
+ *cursor--
+ return NO_VERSION, e
+ }
+
+ return v, nil
+}
+
+func IsDigit(c byte) bool {
+ return c >= '0' && c <= '9'
+}
+
+func newPriority(p int) Priority {
+ // The Priority value is calculated by first multiplying the Facility
+ // number by 8 and then adding the numerical value of the Severity.
+
+ return Priority{
+ P: p,
+ F: Facility{Value: p / 8},
+ S: Severity{Value: p % 8},
+ }
+}
+
+func FindNextSpace(buff []byte, from int, l int) (int, error) {
+ var to int
+
+ for to = from; to < l; to++ {
+ if buff[to] == ' ' {
+ to++
+ return to, nil
+ }
+ }
+
+ return 0, ErrNoSpace
+}
+
+func Parse2Digits(buff []byte, cursor *int, l int, min int, max int, e error) (int, error) {
+ digitLen := 2
+
+ if *cursor+digitLen > l {
+ return 0, ErrEOL
+ }
+
+ sub := string(buff[*cursor : *cursor+digitLen])
+
+ *cursor += digitLen
+
+ i, err := strconv.Atoi(sub)
+ if err != nil {
+ return 0, e
+ }
+
+ if i >= min && i <= max {
+ return i, nil
+ }
+
+ return 0, e
+}
+
+func ParseHostname(buff []byte, cursor *int, l int) (string, error) {
+ from := *cursor
+
+ if from >= l {
+ return "", ErrHostnameTooShort
+ }
+
+ var to int
+
+ for to = from; to < l; to++ {
+ if buff[to] == ' ' {
+ break
+ }
+ }
+
+ hostname := buff[from:to]
+
+ *cursor = to
+
+ return string(hostname), nil
+}
+
+func ShowCursorPos(buff []byte, cursor int) {
+ fmt.Println(string(buff))
+ padding := strings.Repeat("-", cursor)
+ fmt.Println(padding + "↑\n")
+}
+
+func (err *ParserError) Error() string {
+ return err.ErrorString
+}