1/*
2** Zabbix
3** Copyright (C) 2001-2021 Zabbix SIA
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18**/
19package webpage
20
21import (
22	"bufio"
23	"fmt"
24	"net/url"
25	"regexp"
26	"strconv"
27	"strings"
28	"time"
29
30	"zabbix.com/internal/agent"
31	"zabbix.com/pkg/conf"
32	"zabbix.com/pkg/plugin"
33	"zabbix.com/pkg/web"
34	"zabbix.com/pkg/zbxregexp"
35)
36
37type Options struct {
38	Timeout int `conf:"optional,range=1:30"`
39}
40
41type Plugin struct {
42	plugin.Base
43	options Options
44}
45
46var impl Plugin
47
48func (p *Plugin) Configure(global *plugin.GlobalOptions, options interface{}) {
49	if err := conf.Unmarshal(options, &p.options); err != nil {
50		p.Warningf("cannot unmarshal configuration options: %s", err)
51	}
52	if p.options.Timeout == 0 {
53		p.options.Timeout = global.Timeout
54	}
55}
56
57func (p *Plugin) Validate(options interface{}) error {
58	var o Options
59	return conf.Unmarshal(options, &o)
60}
61
62func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (interface{}, error) {
63	if len(params) == 0 || params[0] == "" {
64		return nil, fmt.Errorf("Invalid first parameter.")
65	}
66
67	u, err := url.Parse(params[0])
68	if err != nil {
69		return nil, fmt.Errorf("Cannot parse url: %s", err)
70	}
71
72	if u.Scheme == "" || u.Opaque != "" {
73		params[0] = "http://" + params[0]
74	}
75
76	if len(params) > 2 && params[2] != "" {
77		params[0] += ":" + params[2]
78	}
79
80	if len(params) > 1 && params[1] != "" {
81		if params[1][0] != '/' {
82			params[0] += "/"
83		}
84
85		params[0] += params[1]
86	}
87
88	switch key {
89	case "web.page.regexp":
90		var length *int
91		var output string
92
93		if len(params) > 6 {
94			return nil, fmt.Errorf("Too many parameters.")
95		}
96
97		if len(params) < 4 {
98			return nil, fmt.Errorf("Invalid number of parameters.")
99		}
100
101		rx, err := regexp.Compile(params[3])
102		if err != nil {
103			return nil, fmt.Errorf("Invalid forth parameter: %s", err)
104		}
105
106		if len(params) > 4 && params[4] != "" {
107			if n, err := strconv.Atoi(params[4]); err != nil {
108				return nil, fmt.Errorf("Invalid fifth parameter: %s", err)
109			} else {
110				length = &n
111			}
112		}
113
114		if len(params) > 5 && params[5] != "" {
115			output = params[5]
116		} else {
117			output = "\\0"
118		}
119
120		s, err := web.Get(params[0], time.Duration(p.options.Timeout)*time.Second, true)
121		if err != nil {
122			return nil, err
123		}
124
125		scanner := bufio.NewScanner(strings.NewReader(s))
126		for scanner.Scan() {
127			if out, ok := zbxregexp.ExecuteRegex(scanner.Bytes(), rx, []byte(output)); ok {
128				if length != nil {
129					out = agent.CutAfterN(out, *length)
130				}
131				return out, nil
132			}
133		}
134
135		return "", nil
136	case "web.page.perf":
137		if len(params) > 3 {
138			return nil, fmt.Errorf("Too many parameters.")
139		}
140
141		start := time.Now()
142
143		_, err := web.Get(params[0], time.Duration(p.options.Timeout)*time.Second, false)
144		if err != nil {
145			return nil, err
146		}
147
148		return time.Since(start).Seconds(), nil
149	default:
150		if len(params) > 3 {
151			return nil, fmt.Errorf("Too many parameters.")
152		}
153
154		return web.Get(params[0], time.Duration(p.options.Timeout)*time.Second, true)
155	}
156
157}
158
159func init() {
160	plugin.RegisterMetrics(&impl, "WebPage",
161		"web.page.get", "Get content of a web page.",
162		"web.page.perf", "Loading time of full web page (in seconds).",
163		"web.page.regexp", "Find string on a web page.")
164}
165