ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
webhook.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file webhook.cpp
6 * @brief IFTTT URL / payload builders (pure) + fire over the http_client.
7 */
8
10#include "shared_primitives/strbuf.h" // pc_sb frame builder
11
12#if PC_ENABLE_WEBHOOK
13
15
16#include <stdio.h>
17#include <string.h>
18
19#if PC_ENABLE_HTTP_CLIENT
21#endif
22namespace
23{
24bool put(char *out, size_t cap, size_t *pos, const char *s)
25{
26 size_t n = strnlen(s, cap + 1);
27 if (*pos + n >= cap)
28 {
29 return false;
30 }
31 memcpy(out + *pos, s, n);
32 *pos += n;
33 out[*pos] = '\0';
34 return true;
35}
36
37// Append a JSON string value, escaping '"' and '\'.
38bool put_escaped(char *out, size_t cap, size_t *pos, const char *s)
39{
40 for (; *s; s++)
41 {
42 char c = *s;
43 if (c == '"' || c == '\\')
44 {
45 if (*pos + 2 >= cap)
46 {
47 return false;
48 }
49 out[(*pos)++] = '\\';
50 out[(*pos)++] = c;
51 }
52 else
53 {
54 if (*pos + 1 >= cap)
55 {
56 return false;
57 }
58 out[(*pos)++] = c;
59 }
60 out[*pos] = '\0';
61 }
62 return true;
63}
64} // namespace
65
66int pc_ifttt_url(const char *event, const char *key, char *out, size_t cap)
67{
68 if (!out || cap == 0 || !event || !key)
69 {
70 if (out && cap)
71 {
72 out[0] = '\0';
73 }
74 return 0;
75 }
76 pc_sb sb_out = {out, cap, 0, true};
77 pc_sb_put(&sb_out, "https://maker.ifttt.com/trigger/");
78 pc_sb_put(&sb_out, event);
79 pc_sb_put(&sb_out, "/with/key/");
80 pc_sb_put(&sb_out, key);
81 int w = (int)pc_sb_finish(&sb_out);
82 // The builder is the overflow signal: it refuses the frame rather than truncating, so there is
83 // no would-be length to compare against cap.
84 if (!sb_out.ok)
85 {
86 out[0] = '\0';
87 return 0;
88 }
89 return w;
90}
91
92int pc_ifttt_payload(const char *v1, const char *v2, const char *v3, char *out, size_t cap)
93{
94 if (!out || cap == 0)
95 {
96 return 0;
97 }
98 out[0] = '\0';
99 size_t pos = 0;
100 bool ok = put(out, cap, &pos, "{");
101 const char *names[3] = {"value1", "value2", "value3"};
102 const char *vals[3] = {v1, v2, v3};
103 bool first = true;
104 for (int i = 0; i < 3 && ok; i++)
105 {
106 if (!vals[i])
107 {
108 continue;
109 }
110 // ok is always true on entry here: the loop condition (`&& ok`) was just re-checked to
111 // reach this iteration, and nothing between loop entry and this statement can clear it.
112 if (!first)
113 {
114 ok = ok && put(out, cap, &pos, ","); // GCOVR_EXCL_BR_LINE ok-false half is unreachable (see above)
115 }
116 first = false;
117 ok = ok && put(out, cap, &pos, "\"");
118 ok = ok && put(out, cap, &pos, names[i]);
119 ok = ok && put(out, cap, &pos, "\":\"");
120 ok = ok && put_escaped(out, cap, &pos, vals[i]);
121 ok = ok && put(out, cap, &pos, "\"");
122 }
123 ok = ok && put(out, cap, &pos, "}");
124 if (!ok)
125 {
126 out[0] = '\0';
127 return 0;
128 }
129 return (int)pos;
130}
131
132#if PC_ENABLE_HTTP_CLIENT
133
134int pc_webhook_post(const char *url, const char *json)
135{
136 if (!url || !json)
137 {
138 return (int)HttpClientError::HTTP_CLIENT_ERR_URL;
139 }
140 HttpClientResult r;
141 return http_post(url, PC_MIME_JSON, (const uint8_t *)json, strnlen(json, PC_HTTP_CLIENT_BUF_SIZE), &r);
142}
143
144#else // http_client not enabled in this build
145
146int pc_webhook_post(const char *, const char *)
147{
148 return -1;
149}
150
151#endif // PC_ENABLE_HTTP_CLIENT
152
153int pc_ifttt_trigger(const char *event, const char *key, const char *v1, const char *v2, const char *v3)
154{
155 char url[160];
156 char body[256];
157 if (pc_ifttt_url(event, key, url, sizeof(url)) == 0)
158 {
159 return -1;
160 }
161 if (pc_ifttt_payload(v1, v2, v3, body, sizeof(body)) == 0)
162 {
163 return -1;
164 }
165 return pc_webhook_post(url, body);
166}
167
168#endif // PC_ENABLE_WEBHOOK
Zero-heap outbound HTTP(S) client over raw lwIP (PC_ENABLE_HTTP_CLIENT).
Shared HTTP Content-Type ("MIME") string constants (one source of truth).
#define PC_HTTP_CLIENT_BUF_SIZE
Receive buffer (and max response size) for the outbound HTTP client, bytes.
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
Definition strbuf.h:648
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Definition strbuf.h:60
Bump-append target; ok latches false once an append would overflow cap.
Definition strbuf.h:30
bool ok
Definition strbuf.h:34
Outbound webhooks / IFTTT (PC_ENABLE_WEBHOOK).