gtsocial-umbx

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

rfc3164.go (5570B)


      1 package rfc3164
      2 
      3 import (
      4 	"bytes"
      5 	"os"
      6 	"time"
      7 
      8 	"gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser"
      9 )
     10 
     11 type Parser struct {
     12 	buff     []byte
     13 	cursor   int
     14 	l        int
     15 	priority syslogparser.Priority
     16 	version  int
     17 	header   header
     18 	message  rfc3164message
     19 	location *time.Location
     20 	skipTag  bool
     21 }
     22 
     23 type header struct {
     24 	timestamp time.Time
     25 	hostname  string
     26 }
     27 
     28 type rfc3164message struct {
     29 	tag     string
     30 	content string
     31 }
     32 
     33 func NewParser(buff []byte) *Parser {
     34 	return &Parser{
     35 		buff:     buff,
     36 		cursor:   0,
     37 		l:        len(buff),
     38 		location: time.UTC,
     39 	}
     40 }
     41 
     42 func (p *Parser) Location(location *time.Location) {
     43 	p.location = location
     44 }
     45 
     46 func (p *Parser) Parse() error {
     47 	tcursor := p.cursor
     48 	pri, err := p.parsePriority()
     49 	if err != nil {
     50 		// RFC3164 sec 4.3.3
     51 		p.priority = syslogparser.Priority{13, syslogparser.Facility{Value: 1}, syslogparser.Severity{Value: 5}}
     52 		p.cursor = tcursor
     53 		content, err := p.parseContent()
     54 		p.header.timestamp = time.Now().Round(time.Second)
     55 		if err != syslogparser.ErrEOL {
     56 			return err
     57 		}
     58 		p.message = rfc3164message{content: content}
     59 		return nil
     60 	}
     61 
     62 	tcursor = p.cursor
     63 	hdr, err := p.parseHeader()
     64 	if err == syslogparser.ErrTimestampUnknownFormat {
     65 		// RFC3164 sec 4.3.2.
     66 		hdr.timestamp = time.Now().Round(time.Second)
     67 		// No tag processing should be done
     68 		p.skipTag = true
     69 		// Reset cursor for content read
     70 		p.cursor = tcursor
     71 	} else if err != nil {
     72 		return err
     73 	} else {
     74 		p.cursor++
     75 	}
     76 
     77 	msg, err := p.parsemessage()
     78 	if err != syslogparser.ErrEOL {
     79 		return err
     80 	}
     81 
     82 	p.priority = pri
     83 	p.version = syslogparser.NO_VERSION
     84 	p.header = hdr
     85 	p.message = msg
     86 
     87 	return nil
     88 }
     89 
     90 func (p *Parser) Dump() syslogparser.LogParts {
     91 	return syslogparser.LogParts{
     92 		"timestamp": p.header.timestamp,
     93 		"hostname":  p.header.hostname,
     94 		"tag":       p.message.tag,
     95 		"content":   p.message.content,
     96 		"priority":  p.priority.P,
     97 		"facility":  p.priority.F.Value,
     98 		"severity":  p.priority.S.Value,
     99 	}
    100 }
    101 
    102 func (p *Parser) parsePriority() (syslogparser.Priority, error) {
    103 	return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
    104 }
    105 
    106 func (p *Parser) parseHeader() (header, error) {
    107 	hdr := header{}
    108 	var err error
    109 
    110 	ts, err := p.parseTimestamp()
    111 	if err != nil {
    112 		return hdr, err
    113 	}
    114 
    115 	hostname, err := p.parseHostname()
    116 	if err != nil {
    117 		return hdr, err
    118 	}
    119 
    120 	hdr.timestamp = ts
    121 	hdr.hostname = hostname
    122 
    123 	return hdr, nil
    124 }
    125 
    126 func (p *Parser) parsemessage() (rfc3164message, error) {
    127 	msg := rfc3164message{}
    128 	var err error
    129 
    130 	if !p.skipTag {
    131 		tag, err := p.parseTag()
    132 		if err != nil {
    133 			return msg, err
    134 		}
    135 		msg.tag = tag
    136 	}
    137 
    138 	content, err := p.parseContent()
    139 	if err != syslogparser.ErrEOL {
    140 		return msg, err
    141 	}
    142 
    143 	msg.content = content
    144 
    145 	return msg, err
    146 }
    147 
    148 // https://tools.ietf.org/html/rfc3164#section-4.1.2
    149 func (p *Parser) parseTimestamp() (time.Time, error) {
    150 	var ts time.Time
    151 	var err error
    152 	var tsFmtLen int
    153 	var sub []byte
    154 
    155 	tsFmts := []string{
    156 		time.Stamp,
    157 		time.RFC3339,
    158 	}
    159 	// if timestamps starts with numeric try formats with different order
    160 	// it is more likely that timestamp is in RFC3339 format then
    161 	if c := p.buff[p.cursor]; c > '0' && c < '9' {
    162 		tsFmts = []string{
    163 			time.RFC3339,
    164 			time.Stamp,
    165 		}
    166 	}
    167 
    168 	found := false
    169 	for _, tsFmt := range tsFmts {
    170 		tsFmtLen = len(tsFmt)
    171 
    172 		if p.cursor+tsFmtLen > p.l {
    173 			continue
    174 		}
    175 
    176 		sub = p.buff[p.cursor : tsFmtLen+p.cursor]
    177 		ts, err = time.ParseInLocation(tsFmt, string(sub), p.location)
    178 		if err == nil {
    179 			found = true
    180 			break
    181 		}
    182 	}
    183 
    184 	if !found {
    185 		p.cursor = len(time.Stamp)
    186 
    187 		// XXX : If the timestamp is invalid we try to push the cursor one byte
    188 		// XXX : further, in case it is a space
    189 		if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
    190 			p.cursor++
    191 		}
    192 
    193 		return ts, syslogparser.ErrTimestampUnknownFormat
    194 	}
    195 
    196 	fixTimestampIfNeeded(&ts)
    197 
    198 	p.cursor += tsFmtLen
    199 
    200 	if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
    201 		p.cursor++
    202 	}
    203 
    204 	return ts, nil
    205 }
    206 
    207 func (p *Parser) parseHostname() (string, error) {
    208 	oldcursor := p.cursor
    209 	hostname, err := syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
    210 	if err == nil && len(hostname) > 0 && string(hostname[len(hostname)-1]) == ":" { // not an hostname! we found a GNU implementation of syslog()
    211 		p.cursor = oldcursor - 1
    212 		myhostname, err := os.Hostname()
    213 		if err == nil {
    214 			return myhostname, nil
    215 		}
    216 		return "", nil
    217 	}
    218 	return hostname, err
    219 }
    220 
    221 // http://tools.ietf.org/html/rfc3164#section-4.1.3
    222 func (p *Parser) parseTag() (string, error) {
    223 	var b byte
    224 	var endOfTag bool
    225 	var bracketOpen bool
    226 	var tag []byte
    227 	var err error
    228 	var found bool
    229 
    230 	from := p.cursor
    231 
    232 	for {
    233 		if p.cursor == p.l {
    234 			// no tag found, reset cursor for content
    235 			p.cursor = from
    236 			return "", nil
    237 		}
    238 
    239 		b = p.buff[p.cursor]
    240 		bracketOpen = (b == '[')
    241 		endOfTag = (b == ':' || b == ' ')
    242 
    243 		// XXX : parse PID ?
    244 		if bracketOpen {
    245 			tag = p.buff[from:p.cursor]
    246 			found = true
    247 		}
    248 
    249 		if endOfTag {
    250 			if !found {
    251 				tag = p.buff[from:p.cursor]
    252 				found = true
    253 			}
    254 
    255 			p.cursor++
    256 			break
    257 		}
    258 
    259 		p.cursor++
    260 	}
    261 
    262 	if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
    263 		p.cursor++
    264 	}
    265 
    266 	return string(tag), err
    267 }
    268 
    269 func (p *Parser) parseContent() (string, error) {
    270 	if p.cursor > p.l {
    271 		return "", syslogparser.ErrEOL
    272 	}
    273 
    274 	content := bytes.Trim(p.buff[p.cursor:p.l], " ")
    275 	p.cursor += len(content)
    276 
    277 	return string(content), syslogparser.ErrEOL
    278 }
    279 
    280 func fixTimestampIfNeeded(ts *time.Time) {
    281 	now := time.Now()
    282 	y := ts.Year()
    283 
    284 	if ts.Year() == 0 {
    285 		y = now.Year()
    286 	}
    287 
    288 	newTs := time.Date(y, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
    289 		ts.Second(), ts.Nanosecond(), ts.Location())
    290 
    291 	*ts = newTs
    292 }