| /* Secure Shout Host Oriented Unified Talk |
| * Copyright 2015-2019 Rivoreo |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation, either version 3 of the License, or (at your |
| * option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| */ |
| |
| #ifdef ENABLE_IRC_FRONTEND |
| |
| #include "common.h" |
| #include "client.h" |
| #include "irc.h" |
| #include "syncrw.h" |
| #include <unistd.h> |
| #include <string.h> |
| #include <syslog.h> |
| #include <fcntl.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <time.h> |
| |
| /* Some IRC cilents expecting a prefix before every server reply, make them |
| * happy by sending an empty prefix... */ |
| #define IRC_SEND_EMPTY_PREFIX |
| #define IRC_SERVER_NAME "sshout.sourceforge.net" |
| |
| static const char *sshout_user_name; |
| static int is_irc_nick_name_set; |
| static char irc_user_name[10]; |
| static int is_irc_registered; |
| static char irc_channel_name[51]; |
| //static int is_irc_joined; |
| static int need_send_luser; |
| |
| static void send_irc_line(const char *line) { |
| size_t len = strnlen(line, 510); |
| if(sync_write(STDOUT_FILENO, line, len) < 0) { |
| perror("write"); |
| exit(1); |
| } |
| if(sync_write(STDOUT_FILENO, "\r\n", 2) < 0) { |
| perror("write"); |
| exit(1); |
| } |
| } |
| |
| #if 0 |
| static void send_irc_line_format(const char *format, ...) { |
| va_list ap; |
| va_start(ap, format); |
| vprintf(format, ap); |
| va_end(ap); |
| fputs("\r\n", stdout); |
| } |
| #endif |
| |
| static void send_irc_reply(const char *command, ...) { |
| va_list ap; |
| #ifdef IRC_SEND_EMPTY_PREFIX |
| fputs(": ", stdout); |
| #endif |
| fputs(command, stdout); |
| //if(isdigit(command[0]) && isdigit(command[1]) && isdigit(command[2]) && !command[3]) { |
| if(is_irc_registered) { |
| putchar(' '); |
| fputs(sshout_user_name, stdout); |
| } |
| va_start(ap, command); |
| int is_trailing = 0; |
| #if 1 |
| const char *arg, *last_arg = NULL; |
| #else |
| const char *arg; |
| char *last_arg = NULL; |
| #endif |
| while((arg = va_arg(ap, const char *))) { |
| if(last_arg) { |
| putchar(' '); |
| if(is_trailing) putchar(':'); |
| else if(strchr(last_arg, ' ')) { |
| is_trailing = 1; |
| putchar(':'); |
| } |
| fputs(last_arg, stdout); |
| //free(last_arg); |
| } |
| #if 1 |
| last_arg = arg; |
| #else |
| last_arg = strdup(arg); |
| if(!last_arg) { |
| syslog(LOG_ERR, "send_irc_reply: out of memory"); |
| return; |
| } |
| #endif |
| //syslog(LOG_DEBUG, "send_irc_reply: last_arg = %p<%s>", last_arg, last_arg); |
| } |
| va_end(ap); |
| if(last_arg) { |
| fputs(" :", stdout); |
| fputs(last_arg, stdout); |
| //free(last_arg); |
| } |
| fputs("\r\n", stdout); |
| } |
| |
| static void send_irc_welcome() { |
| send_irc_reply(IRC_RPL_WELCOME, _("Welcome to SSHOUT IRC frontend"), NULL); |
| } |
| |
| static void send_irc_myinfo() { |
| //send_irc_reply(IRC_RPL_MYINFO, "SSHOUT IRC frontend", SSHOUT_VERSION_STRING, "wr", "n", NULL); |
| #ifdef IRC_SEND_EMPTY_PREFIX |
| fputs(": ", stdout); |
| #endif |
| printf(IRC_RPL_MYINFO " %s " SSHOUT_VERSION_STRING " wr n\r\n", sshout_user_name); |
| } |
| |
| static void send_irc_luser(unsigned int user_count) { |
| #ifdef IRC_SEND_EMPTY_PREFIX |
| fputs(": ", stdout); |
| #endif |
| printf(IRC_RPL_LUSERCLIENT " %s :There are %u users and 0 invisible on 1 servers\n", sshout_user_name, user_count); |
| } |
| |
| static void do_registered(int fd) { |
| is_irc_registered = 1; |
| send_irc_welcome(); |
| send_irc_myinfo(); |
| //send_irc_luser(); |
| need_send_luser = 1; |
| client_send_request_get_online_users(fd); |
| } |
| |
| static void send_irc_motd() { |
| char buffer[503]; |
| int fd = open(SSHOUT_MOTD_FILE, O_RDONLY); |
| if(fd == -1) { |
| int e = errno; |
| if(e != ENOENT) syslog(LOG_WARNING, "irc_command_motd: " SSHOUT_MOTD_FILE ": %s", strerror(e)); |
| send_irc_reply(IRC_ERR_NOMOTD, strerror(e), NULL); |
| errno = e; |
| return; |
| } |
| int s = sync_read(fd, buffer, sizeof buffer); |
| if(s < 0) { |
| int e = errno; |
| if(e != ENOENT) syslog(LOG_WARNING, "irc_command_motd: read: %s", strerror(e)); |
| send_irc_reply(IRC_ERR_NOMOTD, strerror(e), NULL); |
| errno = e; |
| return; |
| } |
| //if(!s) return -1; |
| |
| size_t user_name_len = strlen(sshout_user_name); |
| #ifdef IRC_SEND_EMPTY_PREFIX |
| fputs(": ", stdout); |
| #endif |
| //sync_write(STDOUT_FILENO, IRC_RPL_MOTDSTART " :- sshout Message of the day -\r\n", 36); |
| sync_write(STDOUT_FILENO, IRC_RPL_MOTDSTART " ", 4); |
| sync_write(STDOUT_FILENO, sshout_user_name, user_name_len); |
| sync_write(STDOUT_FILENO, " :- sshout Message of the day -\r\n", 33); |
| #ifdef IRC_SEND_EMPTY_PREFIX |
| fputs(": ", stdout); |
| #endif |
| //sync_write(STDOUT_FILENO, IRC_RPL_MOTD " :- ", 7); |
| sync_write(STDOUT_FILENO, IRC_RPL_MOTD " ", 4); |
| sync_write(STDOUT_FILENO, sshout_user_name, user_name_len); |
| sync_write(STDOUT_FILENO, " :- ", 4); |
| sync_write(STDOUT_FILENO, buffer, s); |
| sync_write(STDOUT_FILENO, "\r\n", 2); |
| #ifdef IRC_SEND_EMPTY_PREFIX |
| fputs(": ", stdout); |
| #endif |
| //sync_write(STDOUT_FILENO, IRC_RPL_ENDOFMOTD " :End of MOTD\r\n", 18); |
| sync_write(STDOUT_FILENO, IRC_RPL_ENDOFMOTD " ", 4); |
| sync_write(STDOUT_FILENO, sshout_user_name, user_name_len); |
| #ifdef NO_NLS |
| sync_write(STDOUT_FILENO, " :End of MOTD\r\n", 15); |
| #else |
| const char *s_end_of_motd = _("End of MOTD"); |
| sync_write(STDOUT_FILENO, " :", 2); |
| sync_write(STDOUT_FILENO, s_end_of_motd, strlen(s_end_of_motd)); |
| sync_write(STDOUT_FILENO, "\r\n", 2); |
| #endif |
| } |
| |
| static void send_irc_message(const struct local_message *msg) { |
| if(!is_irc_registered) return; |
| char *text = NULL; |
| switch(msg->msg_type) { |
| case SSHOUT_MSG_RICH: |
| text = strdup("[HTML]"); |
| break; |
| case SSHOUT_MSG_IMAGE: |
| text = strdup(_("[Image]")); |
| break; |
| } |
| if(!text) { |
| text = malloc(msg->msg_length + 1); |
| if(!text) { |
| syslog(LOG_ERR, "send_irc_message: out of memory"); |
| return; |
| } |
| memcpy(text, msg->msg, msg->msg_length); |
| text[msg->msg_length] = 0; |
| } |
| printf(":%s PRIVMSG %s :%s\r\n", msg->msg_from, |
| strcmp(msg->msg_to, GLOBAL_NAME) == 0 || strcmp(msg->msg_to, "*") == 0 ? |
| irc_channel_name : msg->msg_to, text); |
| } |
| |
| static void send_irc_online_users(const struct local_online_users_info *info) { |
| int i = 0; |
| #ifdef IRC_SEND_EMPTY_PREFIX |
| fputs(": ", stdout); |
| #endif |
| fputs(IRC_RPL_NAMREPLY " ", stdout); |
| fputs(sshout_user_name, stdout); |
| fputs(" = #sshout :", stdout); |
| while(i < info->count) { |
| const struct local_online_user *u = info->user + i++; |
| fputs(u->user_name, stdout); |
| putchar(' '); |
| } |
| fputs("\r\n", stdout); |
| send_irc_reply(IRC_RPL_ENDOFNAMES, "#sshout", _("End of NAMES"), NULL); |
| } |
| |
| static void send_irc_user_join(const char *user_name) { |
| if(strcmp(user_name, sshout_user_name) == 0) return; |
| printf(":%s JOIN :#sshout\r\n", user_name); |
| } |
| |
| static void send_irc_user_quit(const char *user_name) { |
| printf(":%s QUIT :Quit: %s\r\n", user_name, user_name); |
| } |
| |
| /*static void irc_command_not_implemented(int fd, int argc, */struct fixed_length_string { |
| size_t len; |
| const char *p; |
| }/* *argv) { |
| }*/; |
| |
| static void irc_command_nick(int fd, int argc, struct fixed_length_string *argv) { |
| syslog(LOG_DEBUG, "function: irc_command_nick(%d, %d, %p)", fd, argc, argv); |
| if(argc < 1) { |
| send_irc_reply(IRC_ERR_NONICKNAMEGIVEN, _("Missing nick name"), NULL); |
| return; |
| } |
| if(argv->len != strlen(sshout_user_name) || memcmp(argv->p, sshout_user_name, argv->len)) { |
| send_irc_reply(IRC_ERR_RESTRICTED, _("Nick name didn't match the registered one"), NULL); |
| return; |
| } |
| is_irc_nick_name_set = 1; |
| if(*irc_user_name) do_registered(fd); |
| } |
| |
| static void irc_command_user(int fd, int argc, struct fixed_length_string *argv) { |
| syslog(LOG_DEBUG, "function: irc_command_user(%d, %d, %p)", fd, argc, argv); |
| if(argc < 4) { |
| send_irc_reply(IRC_ERR_NEEDMOREPARAMS, "USER", _("Not enough parameters"), NULL); |
| return; |
| } |
| |
| /* |
| int i = 0; |
| while(i <= argc) { |
| syslog(LOG_DEBUG, "argv[%d].len = %zu", i, argv[i].len); |
| if(argv[i].len) syslog(LOG_DEBUG, "argv[%d].p[0] = %hhu'%c'", i, argv[i].p[0], argv[i].p[0]); |
| i++; |
| } |
| */ |
| if(is_irc_registered) { |
| send_irc_reply(IRC_ERR_ALREADYREGISTRED, _("You cannot register again"), NULL); |
| return; |
| } |
| size_t user_name_len = argv[0].len > 9 ? 9 : argv[0].len; |
| memcpy(irc_user_name, argv[0].p, user_name_len); |
| irc_user_name[user_name_len] = 0; |
| //syslog(LOG_DEBUG, "argv[1].len = %zu", argv[1].len); |
| /* |
| if(argv[1].len != 1) { |
| send_irc_reply(IRC_ERR_UNKNOWNMODE, "?", "User mode takes only 1 numeric", NULL); |
| } else if(argv[1].p[0] != '0') { |
| char c[2] = { argv[1].p[0], 0 }; |
| send_irc_reply(IRC_ERR_UNKNOWNMODE, c, "User modes other than 0 are not supported", NULL); |
| } |
| */ |
| if(is_irc_nick_name_set) do_registered(fd); |
| } |
| |
| static void irc_command_oper(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc < 2) { |
| send_irc_reply(IRC_ERR_NEEDMOREPARAMS, "OPER", _("Not enough parameters"), NULL); |
| return; |
| } |
| send_irc_reply(IRC_ERR_NOOPERHOST, _("operator is not supported"), NULL); |
| } |
| |
| static void irc_command_mode(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc < 1) { |
| send_irc_reply(IRC_ERR_NEEDMOREPARAMS, "MODE", _("Not enough parameters"), NULL); |
| return; |
| } |
| //if(is_irc_joined) return; |
| if(*irc_channel_name) return; |
| if(argc == 1) { |
| send_irc_reply(IRC_RPL_UMODEIS, "+wr", NULL); |
| return; |
| } |
| if(argv->len != strlen(sshout_user_name) || memcmp(argv->p, sshout_user_name, argv->len)) { |
| send_irc_reply(IRC_ERR_USERSDONTMATCH, _("You cannot change mode for other users"), NULL); |
| return; |
| } |
| } |
| |
| static void irc_command_quit(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc > 0 && *irc_channel_name) { |
| char buffer[22 + 504 + 1] = "Leaving IRC frontend: "; |
| size_t len = argv->len; |
| if(len > 504) len = 504; |
| memcpy(buffer + 22, argv->p, len); |
| buffer[22 + len] = 0; |
| client_post_plain_text_message(fd, GLOBAL_NAME, buffer); |
| } |
| close(fd); |
| exit(0); |
| } |
| |
| static void irc_command_join(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc < 1) { |
| send_irc_reply(IRC_ERR_NEEDMOREPARAMS, "JOIN", _("Not enough parameters"), NULL); |
| return; |
| } |
| if(argv->len == 1 && argv->p[0] == '0') { |
| //is_irc_joined = 1; |
| strcpy(irc_channel_name, "#sshout"); |
| return; |
| } |
| // &#+! |
| if(argv->len < 2) { |
| //send_irc_reply(IRC_ERR_BADCHANMASK, |
| return; |
| } |
| #if 0 |
| int i = strchr("&#+!", argv->p[0]) ? 1 : 0; |
| if(argv->len != 6 || memcmp(argv->p + i, "sshout", argv->len - i)) { |
| #else |
| if(argv->len != 7 || memcmp(argv->p, "#sshout", argv->len)) { |
| #endif |
| char channel[argv->len + 1]; |
| memcpy(channel, argv->p, argv->len); |
| channel[argv->len] = 0; |
| send_irc_reply(IRC_ERR_NOSUCHCHANNEL, channel, _("Only one channel #sshout is available"), NULL); |
| return; |
| } |
| //is_irc_joined = 1; |
| strcpy(irc_channel_name, "#sshout"); |
| //send_irc_topic(); |
| client_send_request_get_online_users(fd); |
| printf(":%s JOIN :#sshout\r\n", sshout_user_name); |
| } |
| |
| static void irc_command_names(int fd, int argc, struct fixed_length_string *argv) { |
| //if(!*irc_channel_name) return; |
| client_send_request_get_online_users(fd); |
| } |
| |
| static void irc_command_list(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc > 1) return; |
| if(argc == 1) { |
| #if 0 |
| int i = strchr("&#+!", argv->p[0]) ? 1 : 0; |
| if(argv->len != 6 || memcmp(argv->p + i, "sshout", argv->len - i)) return; |
| #else |
| if(argv->len != 7 || memcmp(argv->p, "#sshout", argv->len)) return; |
| #endif |
| } |
| send_irc_reply(IRC_RPL_LISTSTART, _("Channel"), _("Users Name"), NULL); |
| send_irc_reply(IRC_RPL_LIST, "#sshout", "?", _("There is only one"), NULL); |
| send_irc_reply(IRC_RPL_LISTEND, _("End of LIST"), NULL); |
| } |
| |
| static void irc_command_privmsg(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc < 1) { |
| send_irc_reply(IRC_ERR_NORECIPIENT, _("Missing recipient"), NULL); |
| return; |
| } |
| if(argc < 2) { |
| send_irc_reply(IRC_ERR_NOTEXTTOSEND, _("Missing message"), NULL); |
| return; |
| } |
| if(!is_irc_registered) { |
| send_irc_reply(IRC_ERR_NOTREGISTERED, "PRIVMSG", _("You have not registered"), NULL); |
| return; |
| } |
| struct local_message *message = malloc(sizeof(struct local_message) + argv[1].len); |
| if(!message) { |
| syslog(LOG_ERR, "irc_command_privmsg: out of memory"); |
| return; |
| } |
| #if 0 |
| if(argv[0].len == 7 && memcmp(argv[0].p, "#sshout", 7) == 0) { |
| #else |
| if(argv[0].len > 0 && strchr("&#+!", argv[0].p[0])) { |
| #endif |
| strcpy(message->msg_to, GLOBAL_NAME); |
| } else { |
| size_t receiver_len = argv[0].len; |
| if(receiver_len > USER_NAME_MAX_LENGTH - 1) receiver_len = USER_NAME_MAX_LENGTH - 1; |
| memcpy(message->msg_to, argv[0].p, receiver_len); |
| message->msg_to[receiver_len] = 0; |
| } |
| message->msg_type = SSHOUT_MSG_PLAIN; |
| message->msg_length = argv[1].len; |
| memcpy(message->msg, argv[1].p, argv[1].len); |
| client_post_message(fd, message); |
| free(message); |
| return; |
| } |
| |
| static void irc_command_motd(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc > 0) return; |
| send_irc_motd(); |
| } |
| |
| static void irc_command_version(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc > 0) return; |
| char buffer[506]; |
| #ifdef NO_NLS |
| snprintf(buffer, sizeof buffer, SSHOUT_VERSION_STRING "\n" |
| "IRC frontend\n" |
| SSHOUT_COPYRIGHT_LINE "\n" |
| SSHOUT_LICENSE_INFORMATION "\n" |
| "Project page: https://sourceforge.net/projects/sshout/"); |
| #else |
| snprintf(buffer, sizeof buffer, SSHOUT_VERSION_STRING "\n" |
| "%s\n%s\n%s\n%s%s", _("IRC frontend"), |
| SSHOUT_COPYRIGHT_LINE, SSHOUT_LICENSE_INFORMATION, |
| _("Project page: "), " https://sourceforge.net/projects/sshout/"); |
| #endif |
| send_irc_reply(IRC_RPL_VERSION, buffer, NULL); |
| } |
| |
| static void irc_command_time(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc > 0) return; |
| time_t t = time(NULL); |
| struct tm *tm = localtime(&t); |
| char buffer[512]; |
| size_t date_str_len = strftime(buffer, sizeof buffer, "%x %T %Z", tm); |
| if(date_str_len) send_irc_reply(IRC_RPL_TIME, IRC_SERVER_NAME, buffer, NULL); |
| else send_irc_reply(IRC_ERR_UNKNOWNERROR, _("Error: cannot format current date time")); |
| } |
| |
| static void irc_command_ping(int fd, int argc, struct fixed_length_string *argv) { |
| if(argc > 1) return; |
| #if 0 |
| char from_host[argv->len + 1]; |
| memcpy(from_host, argv->p, argv->len); |
| from_host[argv->len] = 0; |
| send_irc_reply("PONG", from_host, NULL); |
| #else |
| sync_write(STDOUT_FILENO, "PONG :", 6); |
| sync_write(STDOUT_FILENO, argv->p, argv->len); |
| sync_write(STDOUT_FILENO, "\r\n", 2); |
| #endif |
| } |
| |
| static struct command { |
| const char *name; |
| #if 0 |
| void (*do_command)(int, int, struct fixed_length_string { |
| size_t len; |
| const char *p; |
| } *); |
| #else |
| void (*do_command)(int, int, struct fixed_length_string *); |
| #endif |
| } irc_commands[] = { |
| { "PASS", NULL }, |
| { "NICK", irc_command_nick }, |
| { "USER", irc_command_user }, |
| { "OPER", irc_command_oper }, |
| { "MODE", irc_command_mode }, |
| { "QUIT", irc_command_quit }, |
| { "JOIN", irc_command_join }, |
| { "PART", NULL }, |
| { "TOPIC", NULL }, |
| { "NAMES", irc_command_names }, |
| { "LIST", irc_command_list }, |
| { "PRIVMSG", irc_command_privmsg }, |
| { "NOTICE", irc_command_privmsg }, |
| { "MOTD", irc_command_motd }, |
| { "VERSION", irc_command_version }, |
| { "TIME", irc_command_time }, |
| { "PING", irc_command_ping }, |
| { "PONG", NULL }, |
| { NULL, NULL } |
| }; |
| |
| static void parse_irc_arg(const char *line, size_t len, int *argc, struct fixed_length_string **argv) { |
| const char *space = "BUG!", *colon = line; |
| unsigned int i = 0; |
| do { |
| while(line[i] == ' ') i++; |
| if(line[i] == ':') do { |
| i++; |
| colon = memchr(line + i, ':', len - i); |
| if(colon && (colon == line + i + 1 || colon[-1] != ' ')) { |
| //syslog(LOG_INFO, "parse_irc_arg: malformed IRC line"); |
| //return; |
| //continue; |
| colon = NULL; |
| } |
| size_t a_len = (colon ? (colon - 1 - line) : len) - i; |
| (*argc)++; |
| *argv = realloc(*argv, sizeof(struct fixed_length_string) * (*argc + 1)); |
| if(!*argv) { |
| syslog(LOG_ERR, "parse_irc_arg: out of memory"); |
| exit(1); |
| } |
| (*argv)[*argc - 1].len = a_len; |
| (*argv)[*argc - 1].p = line + i; |
| } while(colon && (i = colon - line)); |
| else { |
| space = memchr(line + i, ' ', len - i); |
| size_t a_len = (space ? (space - line) : len) - i; |
| (*argc)++; |
| *argv = realloc(*argv, sizeof(struct fixed_length_string) * (*argc + 1)); |
| if(!*argv) { |
| syslog(LOG_ERR, "parse_irc_arg: out of memory"); |
| exit(1); |
| } |
| (*argv)[*argc - 1].len = a_len; |
| (*argv)[*argc - 1].p = line + i; |
| } |
| } while(colon && space && (i = space - line + 1)); |
| (*argv)[*argc].len = 0; |
| (*argv)[*argc].p = NULL; |
| } |
| |
| static void do_irc_line(int fd, const char *line, size_t len) { |
| //fprintf(stderr, "function: do_irc_line(%d, %p, %zu)\n", fd, line, len); |
| //syslog(LOG_DEBUG, "function: do_irc_line(%d, %p, %zu)", fd, line, len); |
| if(!len) return; |
| const char *prefix = NULL; |
| size_t prefix_len; |
| const char *space; |
| if(*line == ':') { |
| space = memchr(line + 1, ' ', len - 1); |
| if(!space) return; |
| prefix = line + 1; |
| prefix_len = space - line - 1; |
| space++; |
| len -= space - line; |
| line = space; |
| } |
| if(prefix && strncmp(prefix, sshout_user_name, prefix_len)) { |
| // Invalid prefix |
| syslog(LOG_INFO, "do_irc_line: invalid prefix from client"); |
| return; |
| } |
| int argc = 0; |
| struct fixed_length_string *argv = malloc(sizeof(struct fixed_length_string)); |
| if(!argv) { |
| syslog(LOG_ERR, "do_irc_line: out of memory"); |
| exit(1); |
| } |
| space = memchr(line, ' ', len); |
| if(space && space + 1 == line + len) { |
| space = NULL; |
| len--; |
| } |
| size_t command_len = space ? space - line : len; |
| if(space) parse_irc_arg(space + 1, len - command_len - 1, &argc, &argv); |
| else memset(argv, 0, sizeof(struct fixed_length_string)); |
| struct command *c = irc_commands; |
| while(c->name) { |
| if(strlen(c->name) == command_len && strncasecmp(c->name, line, command_len) == 0) { |
| if(c->do_command) c->do_command(fd, argc, argv); |
| free(argv); |
| return; |
| } |
| c++; |
| } |
| char buffer[command_len + 1]; |
| memcpy(buffer, line, command_len); |
| buffer[command_len] = 0; |
| syslog(LOG_INFO, "do_irc_line: %s: command not found", buffer); |
| } |
| |
| static void client_irc_init(const char *user_name) { |
| setvbuf(stdout, NULL, _IOLBF, 0); |
| sshout_user_name = user_name; |
| syslog(LOG_INFO, "IRC server started"); |
| } |
| |
| static void client_irc_do_local_packet(int fd) { |
| static struct private_buffer buffer; |
| struct local_packet *packet; |
| int e = get_local_packet(fd, &packet, &buffer); |
| switch(e) { |
| case GET_PACKET_EOF: |
| close(fd); |
| exit(0); |
| case GET_PACKET_ERROR: |
| send_irc_reply(IRC_ERR_UNKNOWNERROR, _("Local packet read error"), strerror(errno), NULL); |
| close(fd); |
| exit(1); |
| case GET_PACKET_SHORT_READ: |
| send_irc_reply(IRC_ERR_UNKNOWNERROR, _("Local packet short read"), NULL); |
| close(fd); |
| exit(1); |
| case GET_PACKET_TOO_LARGE: |
| send_irc_reply(IRC_ERR_UNKNOWNERROR, _("Received local packet too large"), NULL); |
| close(fd); |
| exit(1); |
| case GET_PACKET_OUT_OF_MEMORY: |
| send_irc_reply(IRC_ERR_UNKNOWNERROR, _("Out of memory"), NULL); |
| close(fd); |
| exit(1); |
| case GET_PACKET_INCOMPLETE: |
| syslog(LOG_DEBUG, "incomplete local packet received, read %zu bytes, total %zu bytes; will continue later\n", |
| buffer.read_length, buffer.total_length); |
| return; |
| case 0: |
| break; |
| default: |
| send_irc_reply(IRC_ERR_UNKNOWNERROR, ("Internal error"), NULL); |
| syslog(LOG_ERR, "Unknown error %d from get_local_packet", e); |
| abort(); |
| } |
| switch(packet->type) { |
| case SSHOUT_LOCAL_DISPATCH_MESSAGE: |
| send_irc_message((struct local_message *)packet->data); |
| break; |
| case SSHOUT_LOCAL_ONLINE_USERS_INFO: |
| if(need_send_luser) { |
| send_irc_luser(((struct local_online_users_info *)packet->data)->count); |
| need_send_luser = 0; |
| } else send_irc_online_users((struct local_online_users_info *)packet->data); |
| break; |
| case SSHOUT_LOCAL_USER_ONLINE: |
| send_irc_user_join((char *)packet->data); |
| //send_irc_user_mode_change((char *)packet->data, "+wr"); |
| break; |
| case SSHOUT_LOCAL_USER_OFFLINE: |
| send_irc_user_quit((char *)packet->data); |
| break; |
| case SSHOUT_LOCAL_USER_NOT_FOUND: |
| send_irc_reply(IRC_ERR_NOSUCHNICK, (char *)packet->data, "User not found", NULL); |
| break; |
| default: |
| syslog(LOG_WARNING, "Unknown local packet type %d", packet->type); |
| break; |
| } |
| free(packet); |
| } |
| |
| static char input_buffer[512]; |
| static int ss; |
| |
| static void client_irc_do_stdin(int fd) { |
| int s; |
| do { |
| s = read(STDIN_FILENO, input_buffer + ss, sizeof input_buffer - ss); |
| } while(s < 0 && errno == EINTR); |
| if(s < 0) { |
| if(errno == EAGAIN) return; |
| perror("read"); |
| exit(1); |
| } |
| if(!s) { |
| exit(0); |
| } |
| int skip_len = 0; |
| char *lf, *last_lf = NULL; |
| while((lf = memchr(input_buffer + ss + skip_len, '\n', s - skip_len)) && lf > input_buffer + 1 && lf[-1] == '\r') { |
| int line_len = lf - input_buffer - skip_len - 1; // Does not include \r\n |
| do_irc_line(fd, input_buffer + skip_len, line_len); |
| skip_len += line_len + 2; |
| last_lf = lf; |
| } |
| ss += s - skip_len; |
| if(last_lf) memmove(input_buffer, last_lf + 1, ss); |
| if(ss == sizeof input_buffer) { |
| //sync_write(STDOUT_FILENO, "ERROR :Closing Link: line too long\r\n", 35); |
| send_irc_line("ERROR :Closing Link: line too long"); |
| exit(1); |
| } |
| } |
| |
| void client_irc_get_actions(struct client_frontend_actions *a) { |
| a->init_io = client_irc_init; |
| a->do_local_packet = client_irc_do_local_packet; |
| a->do_stdin = client_irc_do_stdin; |
| a->do_after_signal = NULL; |
| a->do_tick = NULL; |
| } |
| |
| #endif |