libpushover.c 7.68 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

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

	res = calloc(1, sizeof(*res));
62
	if (res == NULL) {
Shawn Webb's avatar
Shawn Webb committed
63
		return (NULL);
64
	}
Shawn Webb's avatar
Shawn Webb committed
65
66
67
68
69
70
71
72

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

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

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

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

Shawn Webb's avatar
Shawn Webb committed
84
85
86
87
88
89
EXPORTED_SYM
void
pushover_free_ctx(pushover_ctx_t **ctx)
{
	pushover_ctx_t *ctxp;

90
	if (ctx == NULL || *ctx == NULL) {
Shawn Webb's avatar
Shawn Webb committed
91
		return;
92
	}
Shawn Webb's avatar
Shawn Webb committed
93
94
95
96
97

	ctxp = *ctx;

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

Shawn Webb's avatar
Shawn Webb committed
103
104
105
106
107
108
109
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
110
	assert(ctx->psh_uri == NULL);
Shawn Webb's avatar
Shawn Webb committed
111
112
113
114
115

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

116
117
118
119
120
121
122
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
123
	assert(ctx->psh_token == NULL);
124
125
126
127
128

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

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

135
136
137
	flags = 0;

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

144
	if (msg == NULL) {
Shawn Webb's avatar
Shawn Webb committed
145
		return (NULL);
146
	}
Shawn Webb's avatar
Shawn Webb committed
147

Shawn Webb's avatar
Shawn Webb committed
148
149
	msg->psh_flags = flags;

Shawn Webb's avatar
Shawn Webb committed
150
151
152
	return (msg);
}

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

160
	if (msg == NULL || *msg == NULL) {
161
		return;
162
	}
163
164

	msgp = *msg;
Shawn Webb's avatar
Shawn Webb committed
165
	flags = msgp->psh_flags;
166

167
	free(msgp->psh_dest);
168
169
170
	free(msgp->psh_msg);
	free(msgp->psh_title);
	free(msgp->psh_device);
Shawn Webb's avatar
Shawn Webb committed
171
	memset(msgp, 0, sizeof(*msgp));
172

Shawn Webb's avatar
Shawn Webb committed
173
	if (flags & PUSHOVER_FLAGS_ALLOC) {
174
175
176
177
178
		free(msgp);
		*msg = NULL;
	}
}

Shawn Webb's avatar
Shawn Webb committed
179
180
181
182
183
184
185
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
186
	assert(msg->psh_msg == NULL);
Shawn Webb's avatar
Shawn Webb committed
187
188
189
190
191
192
193

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

EXPORTED_SYM
bool
194
pushover_message_set_dest(pushover_message_t *msg, char *dest)
Shawn Webb's avatar
Shawn Webb committed
195
196
197
{

	assert(msg != NULL);
198
	assert(dest != NULL);
Shawn Webb's avatar
Shawn Webb committed
199
	assert(msg->psh_dest == NULL);
Shawn Webb's avatar
Shawn Webb committed
200

201
202
	msg->psh_dest = strdup(dest);
	return (msg->psh_dest != NULL);
Shawn Webb's avatar
Shawn Webb committed
203
204
205
206
207
208
209
210
211
}

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
212
	assert(msg->psh_title == NULL);
Shawn Webb's avatar
Shawn Webb committed
213
214
215
216
217
218
219
220
221
222
223
224

	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
225
	assert(msg->psh_device == NULL);
Shawn Webb's avatar
Shawn Webb committed
226
227
228
229
230
231
232
233
234
235
236
237

	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);
238

239
	if (!pushover_message_priority_sane(prio)) {
Shawn Webb's avatar
Shawn Webb committed
240
		return (false);
241
	}
Shawn Webb's avatar
Shawn Webb committed
242
243
244
245

	msg->psh_priority = prio;

	return (true);
Shawn Webb's avatar
Shawn Webb committed
246
247
248
249
250
251
252
253
}

EXPORTED_SYM
bool
pushover_submit_message(pushover_ctx_t *ctx, pushover_message_t *msg)
{
	CURLcode curl_code;
	char *post_str;
254
	CURL *curl;
Shawn Webb's avatar
Shawn Webb committed
255
256
257
	bool res;

	assert(ctx != NULL);
Shawn Webb's avatar
Shawn Webb committed
258
	assert(ctx->psh_uri != NULL);
Shawn Webb's avatar
Shawn Webb committed
259
	assert(ctx->psh_token != NULL);
Shawn Webb's avatar
Shawn Webb committed
260
261

	assert(msg != NULL);
262
	assert(msg->psh_dest != NULL);
Shawn Webb's avatar
Shawn Webb committed
263
	assert(msg->psh_msg != NULL);
264
	assert(pushover_message_priority_sane(msg->psh_priority));
Shawn Webb's avatar
Shawn Webb committed
265
266

	res = false;
Shawn Webb's avatar
Shawn Webb committed
267
	post_str = NULL;
Shawn Webb's avatar
Shawn Webb committed
268
269

	curl = curl_easy_init();
270
	if (curl == NULL) {
Shawn Webb's avatar
Shawn Webb committed
271
		goto end;
272
	}
Shawn Webb's avatar
Shawn Webb committed
273
274
275
276

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

	post_str = msg_to_str(ctx, msg, curl);
277
	if (post_str == NULL)
Shawn Webb's avatar
Shawn Webb committed
278
279
		goto end;
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_str);
280
281
	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
	curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
Shawn Webb's avatar
Shawn Webb committed
282
283
	curl_easy_setopt(curl, CURLOPT_STDERR, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, pushover_curl_write_data);
Shawn Webb's avatar
Shawn Webb committed
284
285
286
287
288
289
290
291
292
293
294
295
296

	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)
{
297
	struct sbuf *sb;
Shawn Webb's avatar
Shawn Webb committed
298
	char *p, *res;
Shawn Webb's avatar
Shawn Webb committed
299
300
301
302

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

303
304
305
306
307
	sb = sbuf_new_auto();
	if (sb == NULL) {
		goto end;
	}

Shawn Webb's avatar
Shawn Webb committed
308
	res = NULL;
309
310
311
312
313
314
	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)) {
315
			curl_free(p);
316
317
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
318
		curl_free(p);
319
320
321
322
323
324
325
	}
	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)) {
326
			curl_free(p);
327
328
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
329
		curl_free(p);
330
331
332
333
334
335
336
	}
	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)) {
337
			curl_free(p);
338
339
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
340
		curl_free(p);
341
342
343
344
345
346
347
	}
	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)) {
348
			curl_free(p);
349
350
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
351
		curl_free(p);
352
353
354
355
356
357
358
	}
	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)) {
359
			curl_free(p);
360
361
			goto end;
		}
Shawn Webb's avatar
Shawn Webb committed
362
		curl_free(p);
363
	}
Shawn Webb's avatar
Shawn Webb committed
364

365
	if (sbuf_printf(sb, "&priority=%d", msg->psh_priority)) {
Shawn Webb's avatar
Shawn Webb committed
366
367
368
369
		goto end;
	}

end:
370
371
	if (sb != NULL) {
		if (sbuf_finish(sb)) {
Shawn Webb's avatar
Shawn Webb committed
372
			sbuf_delete(sb);
373
374
375
			return (NULL);
		}
		res = strdup(sbuf_data(sb));
Shawn Webb's avatar
Shawn Webb committed
376
		sbuf_delete(sb);
377
	}
Shawn Webb's avatar
Shawn Webb committed
378
379
380
	return (res);
}

Shawn Webb's avatar
Shawn Webb committed
381
382
383
384
385
386
387
388
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
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
__attribute__((constructor))
static void
init_libpushover(void)
{

	curl_global_init(CURL_GLOBAL_ALL);
}

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

	curl_global_cleanup();
}