source: code/icb.c@ 176b6ef

Last change on this file since 176b6ef was 176b6ef, checked in by Mike Belopuhov <mike@…>, 10 years ago

Use new tokenizer to parse ICB commands

  • Property mode set to 100644
File size: 16.9 KB
RevLine 
[cd7b81d]1/*
[626f420]2 * Copyright (c) 2009, 2010, 2013, 2014 Mike Belopuhov
[cd7b81d]3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
[0519a87]17#include <sys/types.h>
[cd7b81d]18#include <sys/queue.h>
[0519a87]19#include <netdb.h>
[cd7b81d]20#include <stdarg.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <syslog.h>
25#include <unistd.h>
[626f420]26#include <ctype.h>
[cd7b81d]27#include <event.h>
[b7bc432]28#include <vis.h>
[cd7b81d]29
30#include "icb.h"
31#include "icbd.h"
32
33extern int creategroups;
[0519a87]34extern char srvname[NI_MAXHOST];
[cd7b81d]35
36void icb_command(struct icb_session *, char *, char *);
37void icb_groupmsg(struct icb_session *, char *);
[d45051e]38int icb_login(struct icb_session *, char *, char *, char *);
[4284008]39int icb_dowho(struct icb_session *, struct icb_group *);
[cd7b81d]40
41/*
42 * icb_init: initializes pointers to callbacks
43 */
44void
[7882a6f]45icb_init(void)
[cd7b81d]46{
47 LIST_INIT(&groups);
[a785c27]48
49 if (strlen(srvname) == 0)
50 (void)gethostname(srvname, sizeof srvname);
51 /*
[0519a87]52 * NI_MAXHOST is usually greater than what we
[a785c27]53 * can actually send, hence truncation:
54 */
55 if (strlen(srvname) > 200)
56 srvname[200] = '\0';
[cd7b81d]57}
58
59/*
60 * icb_start: called upon accepting a new connection, greets new client
61 */
62void
63icb_start(struct icb_session *is)
64{
[a785c27]65 icb_sendfmt(is, "%c%c%c%s%c%s", ICB_M_PROTO, '1', ICB_M_SEP, srvname,
[cd7b81d]66 ICB_M_SEP, "icbd");
67 SETF(is->flags, ICB_SF_PROTOSENT);
68}
69
70/*
71 * icb_input: main input processing routine
72 */
[d45051e]73int
[cd7b81d]74icb_input(struct icb_session *is)
75{
76 char *msg = is->buffer;
[176b6ef]77 int msglen = is->length;
[1bcb666]78 unsigned char type;
[176b6ef]79 char *wptr = NULL;
[d45051e]80 int res = 0;
[cd7b81d]81
82 is->last = getmonotime();
[a2fadb4]83 type = msg[0];
84 msg++;
[cd7b81d]85 if (!ISSETF(is->flags, ICB_SF_LOGGEDIN) && type != ICB_M_LOGIN) {
86 icb_error(is, "Not logged in");
[d45051e]87 return (0);
[cd7b81d]88 }
89 switch (type) {
90 case ICB_M_LOGIN: {
[176b6ef]91 char client[ICB_MAXNICKLEN];
92 char nick[ICB_MAXNICKLEN];
93 char group[ICB_MAXGRPLEN];
94 char cmd[ICB_MAXCMDLEN];
95
96 if (icb_token(msg, msglen, &wptr, client, ICB_MAXNICKLEN,
97 ICB_M_SEP, 1) < 0) {
98 icb_error(is, "Invalid client");
99 icbd_drop(is, NULL);
100 return (1);
101 }
102 if (icb_token(msg, msglen, &wptr, nick, ICB_MAXNICKLEN,
103 ICB_M_SEP, 1) <= 0) {
104 icb_error(is, "Invalid nick");
105 icbd_drop(is, NULL);
106 return (1);
107 }
108 if (icb_token(msg, msglen, &wptr, group, ICB_MAXGRPLEN,
109 ICB_M_SEP, 1) < 0) {
110 icb_error(is, "Invalid group");
111 icbd_drop(is, NULL);
112 return (1);
113 }
114 if (icb_token(msg, msglen, &wptr, cmd, ICB_MAXCMDLEN,
115 ICB_M_SEP, 1) < 0) {
116 icb_error(is, "Invalid command");
117 icbd_drop(is, NULL);
118 return (1);
119 }
[cd7b81d]120 if (strlen(cmd) > 0 && cmd[0] == 'w') {
121 icb_error(is, "Command not implemented");
[7882a6f]122 icbd_drop(is, NULL);
[d45051e]123 return (1);
[cd7b81d]124 }
[1da9ee5]125 if (strlen(cmd) == 0 || strcmp(cmd, "login") != 0) {
126 icb_error(is, "Malformed login packet");
[7882a6f]127 icbd_drop(is, NULL);
[d45051e]128 return (1);
[1da9ee5]129 }
[d45051e]130 res = icb_login(is, group, nick, client);
[cd7b81d]131 break;
132 }
133 case ICB_M_OPEN: {
[176b6ef]134 icb_groupmsg(is, msg);
[cd7b81d]135 break;
136 }
137 case ICB_M_COMMAND: {
[176b6ef]138 char cmd[ICB_MAXCMDLEN];
139 char arg[ICB_MAXTOPICLEN];
[cd7b81d]140
[176b6ef]141 if (icb_token(msg, msglen, &wptr, cmd, ICB_MAXCMDLEN,
142 ICB_M_SEP, 1) <= 0) {
143 icb_error(is, "Invalid command");
144 icbd_drop(is, NULL);
145 return (1);
146 }
147 if (icb_token(msg, msglen, &wptr, arg, ICB_MAXTOPICLEN,
148 ICB_M_SEP, 1) < 0) {
149 icb_error(is, "Invalid argument");
150 icbd_drop(is, NULL);
151 return (1);
152 }
[cd7b81d]153 icb_command(is, cmd, arg);
154 break;
155 }
[1fb1bbe]156 case ICB_M_PONG: {
157 icb_sendfmt(is, "%c", ICB_M_PING);
158 break;
159 }
[cd7b81d]160 case ICB_M_PROTO:
161 case ICB_M_NOOP:
162 /* ignore */
163 break;
164 default:
165 /* everything else is not valid */
[1bcb666]166 icb_error(is, "Undefined message type %u", type);
[cd7b81d]167 }
[d45051e]168 return (res);
[cd7b81d]169}
170
171/*
172 * icb_login: handles login ('a') packets
173 */
[d45051e]174int
[626f420]175icb_login(struct icb_session *is, char *grp, char *nick, char *client)
[cd7b81d]176{
[cba908b]177 const char *defgrp = "1";
[cd7b81d]178 struct icb_group *ig;
179 struct icb_session *s;
[626f420]180 char group[ICB_MAXGRPLEN];
[cd7b81d]181
[626f420]182 if (!nick || strlen(nick) == 0 ||
[b7bc432]183 icb_vis(is->nick, nick, ICB_MAXNICKLEN, VIS_SP)) {
[cd7b81d]184 icb_error(is, "Invalid nick");
[7882a6f]185 icbd_drop(is, NULL);
[d45051e]186 return (1);
[cd7b81d]187 }
[626f420]188 if (!grp || strlen(grp) == 0)
189 strlcpy(group, defgrp, ICB_MAXGRPLEN);
190 else
[cb7c494]191 icb_vis(group, grp, ICB_MAXGRPLEN, VIS_SP);
[cd7b81d]192 LIST_FOREACH(ig, &groups, entry) {
193 if (strcmp(ig->name, group) == 0)
194 break;
195 }
196 if (ig == NULL) {
197 if (!creategroups) {
198 icb_error(is, "Invalid group %s", group);
[7882a6f]199 icbd_drop(is, NULL);
[d45051e]200 return (1);
[cd7b81d]201 } else {
[677a45b]202 if ((ig = icb_addgroup(is, group)) == NULL) {
[cd7b81d]203 icb_error(is, "Can't create group %s", group);
[d45051e]204 return (0);
[cd7b81d]205 }
[7882a6f]206 icbd_log(NULL, LOG_DEBUG, "%s created group %s",
[626f420]207 is->nick, group);
[cd7b81d]208 }
209 }
210 LIST_FOREACH(s, &ig->sess, entry) {
[626f420]211 if (strcmp(s->nick, is->nick) == 0) {
[cd7b81d]212 icb_error(is, "Nick is already in use");
[7882a6f]213 icbd_drop(is, NULL);
[d45051e]214 return (1);
[cd7b81d]215 }
216 }
217
218 if (client && strlen(client) > 0)
[b7bc432]219 icb_vis(is->client, client, sizeof is->client, VIS_SP);
[0519a87]220 else
221 strlcpy(is->client, is->nick, sizeof is->client);
[cd7b81d]222 is->group = ig;
223 is->login = time(NULL);
224 is->last = getmonotime();
225
226 /* notify group */
227 icb_status_group(ig, NULL, STATUS_SIGNON, "%s (%s@%s) entered group",
228 is->nick, is->client, is->host);
229
230 CLRF(is->flags, ICB_SF_PROTOSENT);
231 SETF(is->flags, ICB_SF_LOGGEDIN);
232
233 LIST_INSERT_HEAD(&ig->sess, is, entry);
234
235 /* acknowledge successful login */
236 icb_sendfmt(is, "%c", ICB_M_LOGIN);
237
238 /* notify user */
239 icb_status(is, STATUS_STATUS, "You are now in group %s%s", ig->name,
[4284008]240 icb_ismod(ig, is) ? " as moderator" : "");
[cd7b81d]241
242 /* send user a topic name */
243 if (strlen(ig->topic) > 0)
244 icb_status(is, STATUS_TOPIC, "Topic for %s is \"%s\"",
245 ig->name, ig->topic);
[d45051e]246 return (0);
[cd7b81d]247}
248
249/*
250 * icb_groupmsg: handles open message ('b') packets
251 */
252void
253icb_groupmsg(struct icb_session *is, char *msg)
254{
255 char buf[ICB_MSGSIZE];
256 struct icb_group *ig = is->group;
257 struct icb_session *s;
258 int buflen = 1;
259
260 if (strlen(msg) == 0) {
261 icb_error(is, "Empty message");
262 return;
263 }
264
265 buflen += snprintf(&buf[1], sizeof buf - 1, "%c%s%c%s", ICB_M_OPEN,
266 is->nick, ICB_M_SEP, msg);
267 buf[0] = buflen;
268
[45bd56a]269 logger(ig->name, is->nick, msg);
[55923b7]270
[cd7b81d]271 LIST_FOREACH(s, &ig->sess, entry) {
272 if (s == is)
273 continue;
[7882a6f]274 icbd_send(s, buf, buflen + 1);
[cd7b81d]275 }
276}
277
278/*
279 * icb_privmsg: handles personal message ('c') packets
280 */
281void
[626f420]282icb_privmsg(struct icb_session *is, char *to, char *msg)
[cd7b81d]283{
284 struct icb_group *ig = is->group;
285 struct icb_session *s;
[626f420]286 char whom[ICB_MAXNICKLEN];
287
[b7bc432]288 icb_vis(whom, to, ICB_MAXNICKLEN, VIS_SP);
[cd7b81d]289
[efa8586]290 /* try home group first */
[cd7b81d]291 LIST_FOREACH(s, &ig->sess, entry) {
292 if (strcmp(s->nick, whom) == 0)
293 break;
294 }
295 if (!s) {
[efa8586]296 /* try all groups until the first match */
297 LIST_FOREACH(ig, &groups, entry) {
298 LIST_FOREACH(s, &ig->sess, entry) {
299 if (strcmp(s->nick, whom) == 0)
300 break;
301 }
302 if (s)
303 break;
304 }
305 if (!s) {
306 icb_error(is, "No such user %s", whom);
307 return;
308 }
[cd7b81d]309 }
310 icb_sendfmt(s, "%c%s%c%s", ICB_M_PERSONAL, is->nick, ICB_M_SEP, msg);
311}
312
313/*
314 * icb_command: handles command ('h') packets
315 */
316void
317icb_command(struct icb_session *is, char *cmd, char *arg)
318{
319 void (*handler)(struct icb_session *, char *);
[176b6ef]320 char command[ICB_MAXCMDLEN];
[cd7b81d]321
[b7bc432]322 icb_vis(command, cmd, sizeof command, VIS_SP);
[626f420]323
324 if ((handler = icb_cmd_lookup(command)) == NULL) {
325 icb_error(is, "Unsupported command: %s", command);
[cd7b81d]326 return;
327 }
328 handler(is, arg);
329}
330
331/*
332 * icb_cmdout: sends out command output ('i') packets, called from the
333 * command handlers
334 */
335void
336icb_cmdout(struct icb_session *is, int type, char *outmsg)
337{
338 char *otype = NULL;
339
340 switch (type) {
341 case CMDOUT_CO:
342 otype = "co";
343 break;
344 case CMDOUT_EC:
345 otype = "ec";
346 break;
347 case CMDOUT_WG:
348 otype = "wg";
349 break;
[4284008]350 case CMDOUT_WH:
351 otype = "wh";
352 break;
353 case CMDOUT_WL:
354 otype = "wl";
355 break;
[cd7b81d]356 default:
[7882a6f]357 icbd_log(is, LOG_ERR, "unknown cmdout type %d", type);
[cd7b81d]358 return;
359 }
[4284008]360 if (outmsg)
361 icb_sendfmt(is, "%c%s%c%s", ICB_M_CMDOUT, otype, ICB_M_SEP,
362 outmsg);
363 else
364 icb_sendfmt(is, "%c%s", ICB_M_CMDOUT, otype);
[cd7b81d]365}
366
367/*
368 * icb_status: sends a status message ('d') to the client
369 */
370void
371icb_status(struct icb_session *is, int type, const char *fmt, ...)
372{
373 va_list ap;
374 char buf[ICB_MSGSIZE];
375 int buflen = 1;
376 static const struct {
377 int type;
378 const char *msg;
379 } msgtab[] = {
380 { STATUS_ARRIVE, "Arrive" },
381 { STATUS_BOOT, "Boot" },
382 { STATUS_DEPART, "Depart" },
[9195a6a]383 { STATUS_HELP, "Help" },
[cd7b81d]384 { STATUS_NAME, "Name" },
[bf02a60]385 { STATUS_NOBEEP, "No-Beep" },
[cd7b81d]386 { STATUS_NOTIFY, "Notify" },
387 { STATUS_SIGNON, "Sign-on" },
388 { STATUS_SIGNOFF, "Sign-off" },
389 { STATUS_STATUS, "Status" },
390 { STATUS_TOPIC, "Topic" },
391 { STATUS_WARNING, "Warning" },
[1022119]392 { 0, NULL }
[cd7b81d]393 };
394
395 if (type < 0 || type > (int)nitems(msgtab) - 1)
396 return;
397 va_start(ap, fmt);
398 buflen += snprintf(&buf[1], sizeof buf - 1, "%c%s%c", ICB_M_STATUS,
399 msgtab[type].msg, ICB_M_SEP);
400 buflen += vsnprintf(&buf[buflen], sizeof buf - buflen, fmt, ap);
401 buf[0] = buflen;
402 va_end(ap);
[7882a6f]403 icbd_send(is, buf, buflen + 1);
[cd7b81d]404}
405
406/*
407 * icb_status: sends a status message ('d') to the group except of the
408 * "ex" if it's not NULL
409 */
410void
411icb_status_group(struct icb_group *ig, struct icb_session *ex, int type,
412 const char *fmt, ...)
413{
[a2fadb4]414 char buf[ICB_MSGSIZE - 10]; /* truncate to make sure all fits */
[cd7b81d]415 va_list ap;
416 struct icb_session *s;
417
418 va_start(ap, fmt);
419 (void)vsnprintf(buf, sizeof buf, fmt, ap);
420 LIST_FOREACH(s, &ig->sess, entry) {
421 if (ex && s == ex)
422 continue;
423 icb_status(s, type, buf);
424 }
[23ca6f1]425 logger(ig->name, "", buf);
[7882a6f]426 icbd_log(NULL, LOG_DEBUG, "%s", buf);
[cd7b81d]427 va_end(ap);
428}
429
430/*
431 * icb_error: sends an error message ('e') to the client
432 */
433void
434icb_error(struct icb_session *is, const char *fmt, ...)
435{
436 char buf[ICB_MSGSIZE];
437 va_list ap;
438 int buflen = 1;
439
440 va_start(ap, fmt);
441 buflen += vsnprintf(&buf[2], sizeof buf - 2, fmt, ap);
442 va_end(ap);
443 buf[0] = ++buflen; /* account for ICB_M_ERROR */
444 buf[1] = ICB_M_ERROR;
[7882a6f]445 icbd_send(is, buf, buflen + 1);
446 icbd_log(is, LOG_DEBUG, "%s", buf + 2);
[cd7b81d]447}
448
449/*
450 * icb_remove: removes a session from the associated group
451 */
452void
453icb_remove(struct icb_session *is, char *reason)
454{
455 if (is->group) {
[4284008]456 if (icb_ismod(is->group, is))
[cd7b81d]457 (void)icb_pass(is->group, is, NULL);
458 LIST_REMOVE(is, entry);
459 if (reason)
460 icb_status_group(is->group, NULL, STATUS_SIGNOFF,
461 "%s (%s@%s) just left: %s", is->nick, is->client,
462 is->host, reason);
463 else
464 icb_status_group(is->group, NULL, STATUS_SIGNOFF,
465 "%s (%s@%s) just left", is->nick, is->client,
466 is->host);
[96a2e31]467 is->group = NULL;
[cd7b81d]468 }
469}
470
471/*
472 * icb_addgroup: adds a new group to the list
473 */
474struct icb_group *
[677a45b]475icb_addgroup(struct icb_session *is, char *name)
[cd7b81d]476{
477 struct icb_group *ig;
478
479 if ((ig = calloc(1, sizeof *ig)) == NULL)
480 return (NULL);
481 strlcpy(ig->name, name, sizeof ig->name);
482 if (is)
[4284008]483 ig->mod = is;
[cd7b81d]484 LIST_INIT(&ig->sess);
485 LIST_INSERT_HEAD(&groups, ig, entry);
486 return (ig);
487}
488
489#ifdef notused
490/*
491 * icb_delgroup: removes a group from the list
492 */
493void
494icb_delgroup(struct icb_group *ig)
495{
496 struct icb_session *s;
497
498 /* well, i guess we should kick out participants! ;-) */
499 LIST_FOREACH(s, &ig->sess, entry) {
500 icb_status(s, STATUS_WARNING, "Group dismissed");
501 s->group = NULL;
502 }
503 LIST_REMOVE(ig, entry);
504 bzero(ig, sizeof ig); /* paranoic thing, obviously */
505 free(ig);
506}
507#endif
508
509/*
[4284008]510 * icb_dowho: a helper function that sends out a group header as a command
511 * output and user information in the "wl" format
[cd7b81d]512 */
[4284008]513int
514icb_dowho(struct icb_session *is, struct icb_group *ig)
[cd7b81d]515{
[a2fadb4]516 char buf[ICB_MSGSIZE - 10]; /* truncate to make sure all fits */
[cd7b81d]517 struct icb_session *s;
[5dfeb4c]518 time_t now;
[4284008]519 int nusers = 0;
[cd7b81d]520
[5dfeb4c]521 now = getmonotime();
[4284008]522 icb_cmdout(is, CMDOUT_CO, " ");
523 snprintf(buf, sizeof buf, "Group: %-8s (%cvl) Mod: %-13s Topic: %s",
524 ig->name, ig->mod ? 'm' : 'p', ig->mod ? ig->mod->nick : "(None)",
525 strlen(ig->topic) > 0 ? ig->topic : "(None)");
526 icb_cmdout(is, CMDOUT_CO, buf);
[cd7b81d]527 LIST_FOREACH(s, &ig->sess, entry) {
528 (void)snprintf(buf, sizeof buf,
[bf02a60]529 "%c%c%s%c%lld%c0%c%lld%c%s%c%s%c%s",
[4284008]530 icb_ismod(ig, s) ? 'm' : ' ', ICB_M_SEP,
[5dfeb4c]531 s->nick, ICB_M_SEP, now - s->last,
[cd7b81d]532 ICB_M_SEP, ICB_M_SEP, s->login, ICB_M_SEP,
[c102bbf]533 s->client, ICB_M_SEP, s->host, ICB_M_SEP, " ");
[cd7b81d]534 icb_cmdout(is, CMDOUT_WL, buf);
[4284008]535 nusers++;
[cd7b81d]536 }
[4284008]537 return (nusers);
538}
539
540/*
541 * icb_who: sends a list of users of either the specified group or all
542 * groups found on the server
543 */
544void
545icb_who(struct icb_session *is, struct icb_group *ig)
546{
[a2fadb4]547 char buf[ICB_MSGSIZE - 10]; /* truncate to make sure all fits */
[4284008]548 struct icb_group *g;
549
550 if (!ig) {
551 int nusers = 0, ngroups = 0;
552
553 LIST_FOREACH(g, &groups, entry) {
554 nusers += icb_dowho(is, g);
555 ngroups++;
556 }
557 if (nusers > 0) {
558 (void)snprintf(buf, sizeof buf,
559 "Total: %d %s in %d %s",
560 nusers, nusers > 1 ? "users" : "user",
561 ngroups, ngroups > 1 ? "groups" : "group");
562 } else
563 (void)snprintf(buf, sizeof buf, "No users found.");
564 icb_cmdout(is, CMDOUT_CO, buf);
565 } else
566 (void)icb_dowho(is, ig);
[cd7b81d]567}
568
569/*
[4284008]570 * icb_ismod: checks whether group is moderated by "is"
[cd7b81d]571 */
[626f420]572inline int
[4284008]573icb_ismod(struct icb_group *ig, struct icb_session *is)
[cd7b81d]574{
[626f420]575 return (ig->mod == is);
[cd7b81d]576}
577
[1d2125a]578/*
579 * icb_modpermit: checks user against the moderators table if it has
580 * been populated
581 */
582int
[f3c60e6]583icb_modpermit(struct icb_session *is, int enforce)
[1d2125a]584{
585 extern char modtab[ICB_MTABLEN][ICB_MAXNICKLEN];
586 extern int modtabcnt;
587
[82d3c1f]588 icbd_modupdate();
[f3c60e6]589 if ((enforce ? 0 : modtabcnt == 0) ||
[1d2125a]590 bsearch(is->nick, modtab, modtabcnt, ICB_MAXNICKLEN,
591 (int (*)(const void *, const void *))strcmp))
592 return (1);
593 return (0);
594}
595
[cd7b81d]596/*
597 * icb_pass: passes moderation of group "ig" from "from" to "to",
598 * returns -1 if "from" is not a moderator, 1 if passed
599 * to "to" and 0 otherwise (no moderator or passed to the
600 * internal bot)
601 */
602int
603icb_pass(struct icb_group *ig, struct icb_session *from,
604 struct icb_session *to)
605{
[4284008]606 if (ig->mod && ig->mod != from)
[cd7b81d]607 return (-1);
608 if (!from && !to)
609 return (-1);
[4284008]610 ig->mod = to;
[cd7b81d]611 if (to)
612 icb_status(to, STATUS_NOTIFY, "%s just passed you moderation"
613 " of %s", from ? from->nick : "server", ig->name);
614 icb_status_group(ig, to, STATUS_NOTIFY, "%s has passed moderation "
615 "to %s", from ? from->nick : "server", to ? to->nick : "server");
616 return (1);
617}
618
619/*
620 * icb_sendfmt: formats a string and sends it over
621 */
622void
623icb_sendfmt(struct icb_session *is, const char *fmt, ...)
624{
625 char buf[ICB_MSGSIZE];
626 va_list ap;
627 int buflen = 1;
628
629 va_start(ap, fmt);
630 buflen += vsnprintf(&buf[1], sizeof buf - 1, fmt, ap);
631 va_end(ap);
632 buf[0] = buflen;
[7882a6f]633 icbd_send(is, buf, buflen + 1);
[cd7b81d]634}
[626f420]635
[677a45b]636/*
637 * icb_token: copies a sequence of characters delimited by the 'sep' character
638 * from the source buffer 'buf' at offset indicated by 'bufptr' to
639 * the destination buffer 'dst' and sets 'bufptr' to the next byte
640 * after 'sep'.
641 */
642int
[176b6ef]643icb_token(char *buf, int len, char **bufptr, char *dst, int dstlen, int sep,
644 int trim)
[677a45b]645{
646 char *start;
647 int i, ret;
648
[176b6ef]649 if (buf == NULL || len <= 0 || dst == NULL || dstlen <= 0)
[677a45b]650 return (0);
651 if (*bufptr == NULL)
652 *bufptr = buf;
653 start = *bufptr;
654 for (i = *bufptr - buf; i < len; i++, (*bufptr)++) {
655 if (**bufptr == sep || **bufptr == '\0') {
656 /* copy and null terminate the token */
657 ret = strlcpy(dst, start,
[176b6ef]658 MIN(*bufptr - start + 1, dstlen));
[677a45b]659 if (**bufptr != '\0')
660 (*bufptr)++;
[176b6ef]661 if (ret > 0 && trim)
662 ret = icb_trim(dst, dstlen);
[677a45b]663 return (ret);
664 }
665 }
666 /*
667 * Reached the end of the buffer without finding a field separator
668 * nor the end of line character. If we have advanced our pointer
669 * we should copy the resulting single field out.
670 */
671 if (*bufptr - start > 0) {
[176b6ef]672 ret = strlcpy(dst, start, MIN(*bufptr - start + 1, dstlen));
673 if (ret > 0 && trim)
674 ret = icb_trim(dst, dstlen);
[677a45b]675 return (ret);
676 }
677 return (0);
678}
679
[176b6ef]680/*
681 * icb_trim: trims trailing whitespace
682 */
683int
684icb_trim(char *buf, int len)
685{
686 char *p = buf;
687 int i;
688
689 for (i = 0; i < len && *p != '\0'; i++)
690 p++;
691 if (*p == '\0' && p - buf > 0)
692 p--;
693 while (p >= buf && isspace(*p)) {
694 *p = '\0';
695 i--;
696 if (p > buf)
697 p--;
698 }
699 return (i);
700}
701
[626f420]702/*
703 * icb_vis: strnvis-like function that escapes percentages as well
704 */
705int
[b7bc432]706icb_vis(char *dst, const char *src, size_t dstsize, int flags)
[626f420]707{
708 int si = 0, di = 0, td;
709
[8f5ba64]710 while ((size_t)di < dstsize - 1 && src[si] != '\0') {
[f73b386]711 if (src[si] == '%') {
712 if ((size_t)di + 1 >= dstsize - 1)
[8d30e02]713 break;
[626f420]714 dst[di++] = '%', dst[di] = '%';
[f73b386]715 } else if (src[si] == ' ' && flags & VIS_SP)
[b7bc432]716 dst[di] = '_';
717 else if (isgraph(src[si]) || src[si] == ' ')
[626f420]718 dst[di] = src[si];
719 else {
720 td = snprintf(&dst[di], dstsize - di,
721 "\\%03o", (unsigned char)src[si]);
[f73b386]722 if (td == -1 || (size_t)td >= dstsize - di)
[8d30e02]723 break;
[626f420]724 di += td - 1;
725 }
726 si++, di++;
727 }
[8f5ba64]728 dst[MIN((size_t)di, dstsize - 1)] = '\0';
[626f420]729 return (0);
730}
Note: See TracBrowser for help on using the repository browser.