]>
jfr.im git - solanum.git/blob - ircd/msgbuf.c
2 * charybdis - an advanced ircd.
3 * Copyright (c) 2016 William Pitcock <nenolod@dereferenced.org>.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice is present in all copies.
9 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
10 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
12 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
13 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
14 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
16 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
17 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
18 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
19 * POSSIBILITY OF SUCH DAMAGE.
23 #include "ircd_defs.h"
28 static const char tag_escape_table
[256] = {
29 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
30 /* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'n', 0, 0, 'r', 0, 0,
31 /* 1x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32 /* 2x */ 's', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33 /* 3x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ':', 0, 0, 0, 0,
34 /* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35 /* 5x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
38 static const char tag_unescape_table
[256] = {
39 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
40 /* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41 /* 1x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42 /* 2x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43 /* 3x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ';', 0, 0, 0, 0, 0,
44 /* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45 /* 5x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
46 /* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\n', 0,
47 /* 7x */ 0, 0,'\r', ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 msgbuf_unescape_value(char *value
)
61 const char unescape
= tag_unescape_table
[(unsigned char)*++in
];
63 /* "\\\0" is unescaped to the character itself, "\0" */
83 * parse a message into a MsgBuf.
84 * returns 0 on success, 1 on error.
87 msgbuf_parse(struct MsgBuf
*msgbuf
, char *line
)
98 /* truncate tags if they're too long */
99 if ((ch
!= NULL
&& (ch
- line
) + 1 > TAGSLEN
) || (ch
== NULL
&& strlen(line
) >= TAGSLEN
)) {
100 ch
= &line
[TAGSLEN
- 1];
104 /* NULL terminate the tags string */
108 char *next
= strchr(t
, ';');
109 char *eq
= strchr(t
, '=');
122 msgbuf_unescape_value(eq
);
123 msgbuf_append_tag(msgbuf
, t
, eq
, 0);
137 /* truncate message if it's too long */
138 if (strlen(ch
) > DATALEN
) {
146 char *end
= strchr(ch
, ' ');
157 msgbuf
->n_para
= rb_string_to_array(ch
, (char **)msgbuf
->para
, MAXPARA
);
158 if (msgbuf
->n_para
== 0)
161 msgbuf
->cmd
= msgbuf
->para
[0];
166 * Unparse msgbuf tags into a buffer
167 * returns the length of the tags written
170 msgbuf_unparse_tags(char *buf
, size_t buflen
, const struct MsgBuf
*msgbuf
, unsigned int capmask
)
172 bool has_tags
= false;
175 const char * const end
= &buf
[buflen
- 2]; /* this is where the final ' ' goes */
177 for (size_t i
= 0; i
< msgbuf
->n_tags
; i
++) {
180 if ((msgbuf
->tags
[i
].capmask
& capmask
) == 0)
193 if (msgbuf
->tags
[i
].key
== NULL
)
196 len
= strlen(msgbuf
->tags
[i
].key
);
200 if (output
+ len
> end
)
202 strcat(output
, msgbuf
->tags
[i
].key
);
205 if (msgbuf
->tags
[i
].value
!= NULL
) {
210 len
= strlen(msgbuf
->tags
[i
].value
);
211 /* this only checks the unescaped length,
212 * but the escaped length could be longer
214 if (output
+ len
> end
)
217 for (size_t n
= 0; n
< len
; n
++) {
218 const unsigned char c
= msgbuf
->tags
[i
].value
[n
];
219 const char escape
= tag_escape_table
[c
];
222 if (output
+ 2 > end
)
248 * unparse a MsgBuf and message prefix into a buffer
249 * if origin is NULL, me.name will be used.
250 * cmd should not be NULL.
251 * updates buflen to correctly allow remaining message data to be added
254 msgbuf_unparse_prefix(char *buf
, size_t *buflen
, const struct MsgBuf
*msgbuf
, unsigned int capmask
)
257 size_t tags_used
= 0;
259 memset(buf
, 0, *buflen
);
261 tags_buflen
= *buflen
;
262 if (tags_buflen
> TAGSLEN
+ 1)
263 tags_buflen
= TAGSLEN
+ 1;
265 if (msgbuf
->n_tags
> 0)
266 tags_used
= msgbuf_unparse_tags(buf
, tags_buflen
, msgbuf
, capmask
);
268 const size_t data_bufmax
= (tags_used
+ DATALEN
+ 1);
269 if (*buflen
> data_bufmax
)
270 *buflen
= data_bufmax
;
272 rb_snprintf_append(buf
, *buflen
, ":%s ", msgbuf
->origin
!= NULL
? msgbuf
->origin
: me
.name
);
274 if (msgbuf
->cmd
!= NULL
)
275 rb_snprintf_append(buf
, *buflen
, "%s ", msgbuf
->cmd
);
277 if (msgbuf
->target
!= NULL
)
278 rb_snprintf_append(buf
, *buflen
, "%s ", msgbuf
->target
);
282 * unparse a pure MsgBuf into a buffer.
283 * if origin is NULL, me.name will be used.
284 * cmd should not be NULL.
285 * returns 0 on success, 1 on error.
288 msgbuf_unparse(char *buf
, size_t buflen
, const struct MsgBuf
*msgbuf
, unsigned int capmask
)
290 size_t buflen_copy
= buflen
;
292 msgbuf_unparse_prefix(buf
, &buflen_copy
, msgbuf
, capmask
);
294 for (size_t i
= 0; i
< msgbuf
->n_para
; i
++) {
297 if (i
== (msgbuf
->n_para
- 1) && strchr(msgbuf
->para
[i
], ' ') != NULL
) {
298 fmt
= (i
== 0) ? ":%s" : " :%s";
300 fmt
= (i
== 0) ? "%s" : " %s";
303 rb_snprintf_append(buf
, buflen_copy
, fmt
, msgbuf
->para
[i
]);
310 * unparse a MsgBuf stem + format string into a buffer
311 * if origin is NULL, me.name will be used.
312 * cmd may not be NULL.
313 * returns 0 on success, 1 on error.
316 msgbuf_vunparse_fmt(char *buf
, size_t buflen
, const struct MsgBuf
*head
, unsigned int capmask
, const char *fmt
, va_list va
)
318 size_t buflen_copy
= buflen
;
322 msgbuf_unparse_prefix(buf
, &buflen_copy
, head
, capmask
);
323 prefixlen
= strlen(buf
);
325 ws
= buf
+ prefixlen
;
326 vsnprintf(ws
, buflen_copy
- prefixlen
, fmt
, va
);
332 * unparse a MsgBuf stem + format string into a buffer (with va_list handling)
333 * if origin is NULL, me.name will be used.
334 * cmd may not be NULL.
335 * returns 0 on success, 1 on error.
338 msgbuf_unparse_fmt(char *buf
, size_t buflen
, const struct MsgBuf
*head
, unsigned int capmask
, const char *fmt
, ...)
344 res
= msgbuf_vunparse_fmt(buf
, buflen
, head
, capmask
, fmt
, va
);