1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2010, James Martelletti <james@nerdc0re.com>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * James Martelletti <james@nerdc0re.com>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Tamas Cseke <tamas.cseke@vcc.live>
27  *
28  * mod_raven.c -- Raven Logging
29  *
30  */
31 #include <switch.h>
32 #include <zlib.h>
33 #include <switch_curl.h>
34 
35 #define RAVEN_ZLIB_CHUNK 16384
36 #define RAVEN_VERSION "5"
37 #define RAVEN_UA "freeswitch-raven/1.0"
38 
39 SWITCH_MODULE_LOAD_FUNCTION(mod_raven_load);
40 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_raven_shutdown);
41 SWITCH_MODULE_DEFINITION(mod_raven, mod_raven_load, mod_raven_shutdown, NULL);
42 
43 static switch_status_t load_config(void);
44 
45 static struct {
46 	char *uri;
47 	char *key;
48 	char *secret;
49 	char *project;
50 	switch_bool_t log_uuid;
51 	switch_log_level_t log_level;
52 } globals;
53 
54 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_uri, globals.uri);
55 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_key, globals.key);
56 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_secret, globals.secret);
57 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_project, globals.project);
58 
59 static switch_loadable_module_interface_t raven_module_interface = {
60 	/*.module_name */ modname,
61 	/*.endpoint_interface */ NULL,
62 	/*.timer_interface */ NULL,
63 	/*.dialplan_interface */ NULL,
64 	/*.codec_interface */ NULL,
65 	/*.application_interface */ NULL,
66 	/*.api_interface */ NULL,
67 	/*.file_interface */ NULL,
68 	/*.speech_interface */ NULL,
69 	/*.directory_interface */ NULL
70 };
71 
encode(const char * raw,int raw_len,char ** encoded_out)72 static switch_status_t encode(const char *raw, int raw_len, char **encoded_out)
73 {
74     z_stream stream;
75     unsigned char *encoded = NULL, *compressed = NULL;
76     int ret;
77 	switch_size_t compressed_size = 0, compressed_len = 0, need_bytes;
78 
79     stream.zalloc = Z_NULL;
80     stream.zfree = Z_NULL;
81     stream.opaque = Z_NULL;
82     ret = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
83     if (ret != Z_OK) {
84 		return SWITCH_STATUS_FALSE;
85     }
86 
87     stream.next_in = (unsigned char *)raw;
88     stream.avail_in = raw_len;
89 
90     do {
91 		compressed_size += RAVEN_ZLIB_CHUNK;
92 		compressed = realloc(compressed, compressed_size + 1);
93 		switch_assert(compressed != NULL);
94 
95 		stream.avail_out = compressed_size - compressed_len;
96 		stream.next_out = compressed + compressed_len;
97 
98 		ret = deflate(&stream, Z_FINISH);
99 		assert(ret != Z_STREAM_ERROR);
100 		compressed_len = compressed_size - stream.avail_out;
101     } while (stream.avail_in != 0);
102 
103 	deflateEnd(&stream);
104 
105 	need_bytes = compressed_len * 3 + 1;
106 	encoded = malloc(need_bytes);
107 	switch_assert(encoded);
108 	memset(encoded, 0, need_bytes);
109 	switch_b64_encode(compressed, compressed_len, encoded, need_bytes);
110 
111 	switch_safe_free(compressed);
112 
113     *encoded_out = (char *)encoded;
114 
115     return SWITCH_STATUS_SUCCESS;
116 }
117 
raven_capture(const char * userdata,const char * message,const char * level,const char * file,const char * func,int line)118 static switch_status_t raven_capture(const char *userdata, const char *message, const char *level, const char *file, const char *func, int line)
119 {
120     cJSON* json, *fingerprint;
121     char *raw_body;
122     char *encoded_body = NULL;
123     switch_time_t timestamp = switch_micro_time_now();
124     switch_status_t status = SWITCH_STATUS_SUCCESS;
125 	char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1];
126 
127 	switch_uuid_str(uuid, sizeof(uuid));
128     json = cJSON_CreateObject();
129     cJSON_AddStringToObject(json, "event_id", (const char *)uuid);
130     cJSON_AddNumberToObject(json, "timestamp", timestamp);
131     cJSON_AddStringToObject(json, "platform", RAVEN_UA);
132     cJSON_AddStringToObject(json, "project", globals.project);
133     cJSON_AddStringToObject(json, "server_name", switch_core_get_hostname());
134     cJSON_AddStringToObject(json, "level", level);
135 
136 	if (globals.log_uuid && !zstr(userdata)) {
137 		cJSON_AddItemToObject(json, "message", cJSON_CreateStringPrintf("%s %s", userdata, message));
138 	} else {
139 		cJSON_AddStringToObject(json, "message", message);
140 	}
141 
142     fingerprint = cJSON_CreateArray();
143     cJSON_AddItemToArray(fingerprint, cJSON_CreateString(file));
144     cJSON_AddItemToArray(fingerprint, cJSON_CreateString(func));
145     cJSON_AddItemToArray(fingerprint, cJSON_CreateNumber(line));
146     cJSON_AddItemToObject(json, "fingerprint", fingerprint);
147 
148     raw_body = cJSON_PrintUnformatted(json);
149 
150     if ((status = encode(raw_body, strlen(raw_body), &encoded_body)) == SWITCH_STATUS_SUCCESS) {
151 		int response;
152 		CURL *curl_handle = switch_curl_easy_init();
153 		switch_curl_slist_t * list = NULL;
154 		char *auth_header = switch_mprintf("X-Sentry-Auth: Sentry sentry_version=%s,"
155 										   " sentry_client=%s,"
156 										   " sentry_timestamp=%d,"
157 										   " sentry_key=%s,"
158 										   " sentry_secret=%s",
159 										   RAVEN_VERSION, RAVEN_UA,
160 										   timestamp, globals.key, globals.secret);
161 
162 		char *url = switch_mprintf( "%s/api/%s/store/", globals.uri, globals.project);
163 		switch_curl_easy_setopt(curl_handle, CURLOPT_URL,url);
164 		switch_curl_easy_setopt(curl_handle, CURLOPT_POST, 1);
165 		switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
166         switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, encoded_body);
167         switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(encoded_body));
168 
169 		switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, RAVEN_UA);
170 
171 		list = switch_curl_slist_append(list, auth_header);
172 		list = switch_curl_slist_append(list, "Content-Type: application/octet-stream");
173 		switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
174 
175 		if (!strncasecmp(globals.uri, "https", 5)) {
176 			switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
177 			switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
178 		}
179 
180 		switch_curl_easy_perform(curl_handle);
181 		switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response);
182 
183 		if (response != 200) {
184 			status = SWITCH_STATUS_FALSE;
185 		}
186 
187 		switch_curl_easy_cleanup(curl_handle);
188 		switch_curl_slist_free_all(list);
189 		switch_safe_free(url);
190 		switch_safe_free(auth_header);
191     }
192 
193     switch_safe_free(raw_body);
194     switch_safe_free(encoded_body);
195     cJSON_Delete(json);
196 
197     return status;
198 }
199 
200 
mod_raven_logger(const switch_log_node_t * node,switch_log_level_t level)201 static switch_status_t mod_raven_logger(const switch_log_node_t *node, switch_log_level_t level)
202 {
203 	switch_status_t status = SWITCH_STATUS_SUCCESS;
204 
205 	if (level != SWITCH_LOG_CONSOLE && !zstr(node->data)) {
206 		const char * raven_level;
207 
208 		switch (level) {
209 		case SWITCH_LOG_DEBUG:
210 			raven_level = "debug";
211 			break;
212 		case SWITCH_LOG_INFO:
213 			raven_level = "info";
214 			break;
215 		case SWITCH_LOG_NOTICE:
216 		case SWITCH_LOG_WARNING:
217 			raven_level = "warning";
218 			break;
219 		case SWITCH_LOG_ERROR:
220 			raven_level = "error";
221 			break;
222 		case SWITCH_LOG_CRIT:
223 		case SWITCH_LOG_ALERT:
224 			raven_level = "fatal";
225 			break;
226 		default:
227 			raven_level = "debug";
228 			break;
229 		}
230 
231 		status = raven_capture(node->userdata, node->data, raven_level, node->file, node->func, node->line);
232 	}
233 
234 	return status;
235 }
236 
load_config(void)237 static switch_status_t load_config(void)
238 {
239 	char *cf = "raven.conf";
240 	switch_xml_t cfg, xml, settings, param;
241 
242 	globals.log_level = SWITCH_LOG_WARNING;
243 	globals.log_uuid = SWITCH_TRUE;
244 
245 	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
246 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
247 		return SWITCH_STATUS_FALSE;
248 	}
249 
250 	if ((settings = switch_xml_child(cfg, "settings"))) {
251 		for (param = switch_xml_child(settings, "param"); param; param = param->next) {
252 			char *var = (char *) switch_xml_attr_soft(param, "name");
253 			char *val = (char *) switch_xml_attr_soft(param, "value");
254 
255 			if (!strcmp(var, "uri")) {
256 				set_global_uri(val);
257 			} else if (!strcmp(var, "key")) {
258 				set_global_key(val);
259 			} else if (!strcmp(var, "secret")) {
260 				set_global_secret(val);
261 			} else if (!strcmp(var, "project")) {
262 				set_global_project(val);
263 			} else if (!strcasecmp(var, "loglevel") && !zstr(val)) {
264 				globals.log_level = switch_log_str2level(val);
265 				if (globals.log_level == SWITCH_LOG_INVALID) {
266 					globals.log_level = SWITCH_LOG_WARNING;
267 				}
268 			} else if (!strcasecmp(var, "uuid")) {
269 				globals.log_uuid = switch_true(val);
270 			}
271 		}
272 	}
273 	switch_xml_free(xml);
274 
275 	if (zstr(globals.uri) || zstr(globals.project) || zstr(globals.key) || zstr(globals.secret)) {
276 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing parameter\n");
277 		return SWITCH_STATUS_FALSE;
278 	}
279 
280 	return SWITCH_STATUS_SUCCESS;
281 }
282 
SWITCH_MODULE_LOAD_FUNCTION(mod_raven_load)283 SWITCH_MODULE_LOAD_FUNCTION(mod_raven_load)
284 {
285 	switch_status_t status;
286 	*module_interface = &raven_module_interface;
287 
288 	memset(&globals, 0, sizeof(globals));
289 
290 	if ((status = load_config()) != SWITCH_STATUS_SUCCESS) {
291 		return status;
292 	}
293 
294 	switch_log_bind_logger(mod_raven_logger, globals.log_level, SWITCH_FALSE);
295 
296 	return SWITCH_STATUS_SUCCESS;
297 }
298 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_raven_shutdown)299 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_raven_shutdown)
300 {
301 	switch_safe_free(globals.uri);
302 	switch_safe_free(globals.key);
303 	switch_safe_free(globals.secret);
304 	switch_safe_free(globals.project);
305 
306 	switch_log_unbind_logger(mod_raven_logger);
307 
308 	return SWITCH_STATUS_SUCCESS;
309 }
310 
311 /* For Emacs:
312  * Local Variables:
313  * mode:c
314  * indent-tabs-mode:t
315  * tab-width:4
316  * c-basic-offset:4
317  * End:
318  * For VIM:
319  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
320  */
321