libpushover.c 7.57 KB
Newer Older
Shawn Webb's avatar
Shawn Webb committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*-
 * Copyright (c) 2020 HardenedBSD Foundation Corp.
 * Author: Shawn Webb <shawn.webb@hardenedbsd.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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 AUTHOR 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 AUTHOR 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.
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

32
33
34
#include <sys/types.h>
#include <sys/sbuf.h>

Shawn Webb's avatar
Shawn Webb committed
35
36
#include "libpushover.h"

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct _pushover_ctx {
	uint64_t	 psh_version;
	char		*psh_token;
	char		*psh_uri;
};

struct _pushover_message {
	char			*psh_dest;
	char			*psh_msg;
	char			*psh_title;
	char			*psh_device;
	pushover_priority_t	 psh_priority;
	uint64_t		 psh_flags;
};

Shawn Webb's avatar
Shawn Webb committed
52
static char *msg_to_str(pushover_ctx_t *, pushover_message_t *, CURL *);
Shawn Webb's avatar
Shawn Webb committed
53
static size_t pushover_curl_write_data(void *, size_t, size_t, void *);
Shawn Webb's avatar
Shawn Webb committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

EXPORTED_SYM
pushover_ctx_t *
pushover_init_ctx(const char *token)
{
	pushover_ctx_t *res;

	res = calloc(1, sizeof(*res));
	if (res == NULL)
		return (NULL);

	res->psh_uri = strdup(PUSHOVER_URI);
	if (res->psh_uri == NULL) {
		free(res);
		res = NULL;
		goto out;
	}

72
	if (token != NULL) {
Shawn Webb's avatar
Shawn Webb committed
73
		res->psh_token = strdup(token);
74
75
		assert(res->psh_token != NULL);
	}
Shawn Webb's avatar
Shawn Webb committed
76

Shawn Webb's avatar
Shawn Webb committed
77
78
	res->psh_version = LIBPUSHOVER_VERSION;

Shawn Webb's avatar
Shawn Webb committed
79
80
81
82
out:
	return (res);
}

Shawn Webb's avatar
Shawn Webb committed
83
84
85
86
87
88
89
90
91
92
93
94
95
EXPORTED_SYM
void
pushover_free_ctx(pushover_ctx_t **ctx)
{
	pushover_ctx_t *ctxp;

	if (ctx == NULL || *ctx == NULL)
		return;

	ctxp = *ctx;

	free(ctxp->psh_uri);
	free(ctxp->psh_token);
Shawn Webb's avatar
Shawn Webb committed
96
	memset(ctxp, 0, sizeof(*ctxp));
Shawn Webb's avatar
Shawn Webb committed
97
98
99
100
	free(ctxp);
	*ctx = NULL;
}

Shawn Webb's avatar
Shawn Webb committed
101
102
103
104
105
106
107
EXPORTED_SYM
bool
pushover_set_uri(pushover_ctx_t *ctx, const char *uri)
{

	assert(ctx != NULL);
	assert(uri != NULL);
Shawn Webb's avatar
Shawn Webb committed
108
	assert(ctx->psh_uri == NULL);
Shawn Webb's avatar
Shawn Webb committed
109
110
111
112
113

	ctx->psh_uri = strdup(uri);
	return (ctx->psh_uri != NULL);
}

114
115
116
117
118
119
120
EXPORTED_SYM
bool
pushover_set_token(pushover_ctx_t *ctx, const char *token)
{

	assert(ctx != NULL);
	assert(token != NULL);
Shawn Webb's avatar
Shawn Webb committed
121
	assert(ctx->psh_token == NULL);
122
123
124
125
126

	ctx->psh_token = strdup(token);
	return (ctx->psh_token != NULL);
}

Shawn Webb's avatar
Shawn Webb committed
127
128
129
130
EXPORTED_SYM
pushover_message_t *
pushover_init_message(pushover_message_t *msg)
{
131
	uint64_t flags;
Shawn Webb's avatar
Shawn Webb committed
132

133
134
135
	flags = 0;

	if (msg == NULL) {
Shawn Webb's avatar
Shawn Webb committed
136
		msg = calloc(1, sizeof(*msg));
137
138
		flags |= PUSHOVER_FLAGS_ALLOC;
	} else {
Shawn Webb's avatar
Shawn Webb committed
139
		memset(msg, 0, sizeof(*msg));
140
	}
Shawn Webb's avatar
Shawn Webb committed
141
142
143
144

	if (msg == NULL)
		return (NULL);

Shawn Webb's avatar
Shawn Webb committed
145
146
	msg->psh_flags = flags;

Shawn Webb's avatar
Shawn Webb committed
147
148
149
	return (msg);
}

150
151
152
153
154
EXPORTED_SYM
void
pushover_free_message(pushover_message_t **msg)
{
	pushover_message_t *msgp;
Shawn Webb's avatar
Shawn Webb committed
155
	uint64_t flags;
156
157
158
159
160

	if (msg == NULL || *msg == NULL)
		return;

	msgp = *msg;
Shawn Webb's avatar
Shawn Webb committed
161
	flags = msgp->psh_flags;
162

163
	free(msgp->psh_dest);
164
165
166
	free(msgp->psh_msg);
	free(msgp->psh_title);
	free(msgp->psh_device);
Shawn Webb's avatar
Shawn Webb committed
167
	memset(msgp, 0, sizeof(*msgp));
168

Shawn Webb's avatar
Shawn Webb committed
169
	if (flags & PUSHOVER_FLAGS_ALLOC) {
170
171
172
173
174
		free(msgp);
		*msg = NULL;
	}
}

Shawn Webb's avatar
Shawn Webb committed
175
176
177
178
179
180
181
EXPORTED_SYM
bool
pushover_message_set_msg(pushover_message_t *msg, char *data)
{

	assert(msg != NULL);
	assert(data != NULL);
Shawn Webb's avatar
Shawn Webb committed
182
	assert(msg->psh_msg == NULL);
Shawn Webb's avatar
Shawn Webb committed
183
184
185
186
187
188
189

	msg->psh_msg = strdup(data);
	return (msg->psh_msg != NULL);
}

EXPORTED_SYM
bool
190
pushover_message_set_dest(pushover_message_t *msg, char *dest)
Shawn Webb's avatar
Shawn Webb committed
191
192
193
{

	assert(msg != NULL);
194
	assert(dest != NULL);
Shawn Webb's avatar
Shawn Webb committed
195
	assert(msg->psh_dest == NULL);
Shawn Webb's avatar
Shawn Webb committed
196

197
198
	msg->psh_dest = strdup(dest);
	return (msg->psh_dest != NULL);
Shawn Webb's avatar
Shawn Webb committed
199
200
201
202
203
204
205
206
207
}

EXPORTED_SYM
bool
pushover_message_set_title(pushover_message_t *msg, char *title)
{

	assert(msg != NULL);
	assert(title != NULL);
Shawn Webb's avatar
Shawn Webb committed
208
	assert(msg->psh_title == NULL);
Shawn Webb's avatar
Shawn Webb committed
209
210
211
212
213
214
215
216
217
218
219
220

	msg->psh_title = strdup(title);
	return (msg->psh_title != NULL);
}

EXPORTED_SYM
bool
pushover_message_set_device(pushover_message_t *msg, char *device)
{

	assert(msg != NULL);
	assert(device != NULL);
Shawn Webb's avatar
Shawn Webb committed
221
	assert(msg->psh_device == NULL);
Shawn Webb's avatar
Shawn Webb committed
222
223
224
225
226
227
228
229
230
231
232
233

	msg->psh_device = strdup(device);
	return (msg->psh_device != NULL);
}

EXPORTED_SYM
bool
pushover_message_set_priority(pushover_message_t *msg,
    pushover_priority_t prio)
{

	assert(msg != NULL);
234

Shawn Webb's avatar
Shawn Webb committed
235
	if (!pushover_message_priority_sane(prio))
Shawn Webb's avatar
Shawn Webb committed
236
237
238
239
240
		return (false);

	msg->psh_priority = prio;

	return (true);
Shawn Webb's avatar
Shawn Webb committed
241
242
243
244
245
246
247
248
}

EXPORTED_SYM
bool
pushover_submit_message(pushover_ctx_t *ctx, pushover_message_t *msg)
{
	CURLcode curl_code;
	char *post_str;
249
	CURL *curl;
Shawn Webb's avatar
Shawn Webb committed
250
251
252
	bool res;

	assert(ctx != NULL);
Shawn Webb's avatar
Shawn Webb committed
253
	assert(ctx->psh_uri != NULL);
Shawn Webb's avatar
Shawn Webb committed
254
	assert(ctx->psh_token != NULL);
Shawn Webb's avatar
Shawn Webb committed
255
256

	assert(msg != NULL);
257
	assert(msg->psh_dest != NULL);
Shawn Webb's avatar
Shawn Webb committed
258
	assert(msg->psh_msg != NULL);
259
	assert(pushover_message_priority_sane(msg->psh_priority));
Shawn Webb's avatar
Shawn Webb committed
260
261

	res = false;
Shawn Webb's avatar
Shawn Webb committed
262
	post_str = NULL;
Shawn Webb's avatar
Shawn Webb committed
263
264
265
266
267
268
269
270

	curl = curl_easy_init();
	if (curl == NULL)
		goto end;

	curl_easy_setopt(curl, CURLOPT_URL, ctx->psh_uri);

	post_str = msg_to_str(ctx, msg, curl);
271
	if (post_str == NULL)
Shawn Webb's avatar
Shawn Webb committed
272
273
		goto end;
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_str);
274
275
	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
	curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
Shawn Webb's avatar
Shawn Webb committed
276
277
	curl_easy_setopt(curl, CURLOPT_STDERR, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, pushover_curl_write_data);
Shawn Webb's avatar
Shawn Webb committed
278
279
280
281
282
283
284
285
286
287
288
289
290

	curl_code = curl_easy_perform(curl);
	res = (curl_code == CURLE_OK);

end:
	curl_easy_cleanup(curl);
	free(post_str);
	return (res);
}

static char *
msg_to_str(pushover_ctx_t *ctx, pushover_message_t *msg, CURL *curl)
{
291
	struct sbuf *sb;
Shawn Webb's avatar
Shawn Webb committed
292
	char *p, *res;
Shawn Webb's avatar
Shawn Webb committed
293
294
295
296

	assert(ctx != NULL);
	assert(msg != NULL);

297
298
299
300
301
	sb = sbuf_new_auto();
	if (sb == NULL) {
		goto end;
	}

Shawn Webb's avatar
Shawn Webb committed
302
	res = NULL;
303
304
305
306
307
308
309
310
	if (msg->psh_device != NULL) {
		p = curl_easy_escape(curl, msg->psh_device, 0);
		if (p == NULL) {
			goto end;
		}
		if (sbuf_printf(sb, "&device=%s", p)) {
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
311
		curl_free(p);
312
313
314
315
316
317
318
319
320
	}
	if (msg->psh_msg != NULL) {
		p = curl_easy_escape(curl, msg->psh_msg, 0);
		if (p == NULL) {
			goto end;
		}
		if (sbuf_printf(sb, "&message=%s", p)) {
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
321
		curl_free(p);
322
323
324
325
326
327
328
329
330
	}
	if (msg->psh_title != NULL) {
		p = curl_easy_escape(curl, msg->psh_title, 0);
		if (p == NULL) {
			goto end;
		}
		if (sbuf_printf(sb, "&title=%s", p)) {
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
331
		curl_free(p);
332
333
334
335
336
337
338
339
340
	}
	if (ctx->psh_token != NULL) {
		p = curl_easy_escape(curl, ctx->psh_token, 0);
		if (p == NULL) {
			goto end;
		}
		if (sbuf_printf(sb, "&token=%s", p)) {
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
341
		curl_free(p);
342
343
344
345
346
347
348
349
350
	}
	if (msg->psh_dest != NULL) {
		p = curl_easy_escape(curl, msg->psh_dest, 0);
		if (p == NULL) {
			goto end;
		}
		if (sbuf_printf(sb, "&user=%s", p)) {
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
351
		curl_free(p);
352
	}
Shawn Webb's avatar
Shawn Webb committed
353

354
	if (sbuf_printf(sb, "&priority=%d", msg->psh_priority)) {
Shawn Webb's avatar
Shawn Webb committed
355
356
357
358
		goto end;
	}

end:
359
360
	if (sb != NULL) {
		if (sbuf_finish(sb)) {
Shawn Webb's avatar
Shawn Webb committed
361
			sbuf_delete(sb);
362
363
364
			return (NULL);
		}
		res = strdup(sbuf_data(sb));
Shawn Webb's avatar
Shawn Webb committed
365
		sbuf_delete(sb);
366
	}
Shawn Webb's avatar
Shawn Webb committed
367
368
369
	return (res);
}

Shawn Webb's avatar
Shawn Webb committed
370
371
372
373
374
375
376
377
static size_t
pushover_curl_write_data(void *buffer, size_t sz, size_t nmemb,
    void *usrp)
{

	return (sz * nmemb);
}

Shawn Webb's avatar
Shawn Webb committed
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
__attribute__((constructor))
static void
init_libpushover(void)
{

	curl_global_init(CURL_GLOBAL_ALL);
}

__attribute__((destructor))
static void
destroy_libpushover(void)
{

	curl_global_cleanup();
}