src/main.c \
src/ServerSocket.c \
src/tools.c \
+ src/UserSession.c \
src/UserClient.c
transirc_LDADD = $(MYSQL_LIBS) $(SYSTEM_LIBS)
--- /dev/null
+<?php
+
+$username = $argv[1];
+$password = $argv[2];
+$nick = $argv[3];
+
+if($username == "pk910")
+ echo "ACCEPT\n";
+else
+ echo "REJECT\n";
+
+?>
\ No newline at end of file
FD_SET(iofd->fd, &read_fds);
if(iohandler_wants_writes(iofd))
FD_SET(iofd->fd, &write_fds);
- } else if(iofd->type == IOTYPE_TIMER) {
+ }
+ if(iofd->type == IOTYPE_TIMER || iofd->timeout.tv_sec || iofd->timeout.tv_usec) {
tdiff.tv_sec = iofd->timeout.tv_sec - now.tv_sec;
tdiff.tv_usec = iofd->timeout.tv_usec - now.tv_usec;
if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
//exec timer
- iofd->state = IO_CLOSED;
- iohandler_events(iofd, 1, 0);
- iohandler_close(iofd);
+ iohandler_events(iofd, 0, 0);
+ if(iofd->type == IOTYPE_TIMER)
+ iohandler_close(iofd);
continue;
} else if(tdiff.tv_usec < 0) {
tdiff.tv_sec--;
}
}
+ gettimeofday(&now, NULL);
+
//check all descriptors
for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
tmp_iofd = iofd->next;
if(FD_ISSET(iofd->fd, &read_fds) || FD_ISSET(iofd->fd, &write_fds)) {
iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds));
}
- } else if(iofd->type == IOTYPE_TIMER) {
+ }
+ if(iofd->type == IOTYPE_TIMER || iofd->timeout.tv_sec || iofd->timeout.tv_usec) {
tdiff.tv_sec = iofd->timeout.tv_sec - now.tv_sec;
tdiff.tv_usec = iofd->timeout.tv_usec - now.tv_usec;
if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
//exec timer
- iofd->state = IO_CLOSED;
- iohandler_events(iofd, 1, 0);
+ iohandler_events(iofd, 0, 0);
+ if(iofd->type == IOTYPE_TIMER)
+ iohandler_close(iofd);
continue;
}
}
return descriptor;
}
-static void iohandler_remove(struct IODescriptor *descriptor) {
+static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) {
//remove IODescriptor from the list
if(descriptor->prev)
descriptor->prev->next = descriptor->next;
first_descriptor = descriptor->next;
if(descriptor->next)
descriptor->next->prev = descriptor->prev;
- engine->remove(descriptor);
+ if(engine_remove)
+ engine->remove(descriptor);
if(descriptor->readbuf.buffer)
free(descriptor->readbuf.buffer);
if(descriptor->writebuf.buffer)
}
void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) {
- if(iofd->type == IOTYPE_TIMER) return; //can not write to timer? :D
+ if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) return; //can not write to timer? :D
if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
iohandler_increase_iobuf(&iofd->writebuf, iofd->writebuf.bufpos + datalen);
if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen)
}
void iohandler_close(struct IODescriptor *iofd) {
+ int engine_remove = 1;
+ if(iofd->writebuf.bufpos) {
+ //try to send everything before closing
+#if defined(F_GETFL)
+ flags = fcntl(sockfd, F_GETFL);
+ fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK);
+ flags = fcntl(sockfd, F_GETFD);
+ fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC);
+#else
+ engine_remove = 0;
+ engine->remove(iofd);
+#endif
+ iohandler_try_write(iofd);
+ }
//close IODescriptor
if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN)
close(iofd->fd);
- iohandler_remove(iofd);
+ iohandler_remove(iofd, engine_remove);
}
void iohandler_update(struct IODescriptor *iofd) {
callback_event.type = IOEVENT_TIMEOUT;
break;
case IO_LISTENING:
- callback_event.data.accept_fd = accept(iofd->fd, NULL, 0);
- if(callback_event.data.accept_fd < 0) {
- //error: could not accept
- } else
- callback_event.type = IOEVENT_ACCEPT;
+ if(readable) {
+ callback_event.data.accept_fd = accept(iofd->fd, NULL, 0);
+ if(callback_event.data.accept_fd < 0) {
+ //error: could not accept
+ } else
+ callback_event.type = IOEVENT_ACCEPT;
+ }
break;
case IO_CONNECTING:
if(readable) { //could not connect
int bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
if(bytes <= 0) {
if (errno != EAGAIN) {
+ iofd->state = IO_CLOSED;
callback_event.type = IOEVENT_CLOSED;
callback_event.data.errid = errno;
}
}
break;
}
+ if(callback_event.type == IOEVENT_IGNORE && !readable && !writeable)
+ callback_event.type = IOEVENT_TIMEOUT;
if(callback_event.type != IOEVENT_IGNORE)
iohandler_trigger_event(&callback_event);
}
struct IODescriptor {
int fd;
+ FILE *file;
enum IOType type;
enum IOStatus state;
struct timeval timeout;
};
struct IODescriptor *iohandler_add(int sockfd, enum IOType type, iohandler_callback *callback);
+struct IODescriptor *iohandler_file(FILE *file, iohandler_callback *callback);
struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback);
struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, const char *bind, iohandler_callback *callback);
struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback);
#include "UserClient.h"
#include "IOHandler.h"
#include "ServerSocket.h"
+#include "UserSession.h"
+#include "tools.h"
static void userclient_callback(struct IOEvent *event);
iofd->state = IO_CONNECTED;
iofd->read_lines = 1;
iohandler_update(iofd);
- client = malloc(sizeof(*client));
+ client = calloc(1, sizeof(*client));
client->iofd = iofd;
iofd->data = client;
client->server = server;
server->clientcount++;
+ struct UserLogin *login = calloc(1, sizeof(*login));
+ client->user = login;
+ login->client = client;
+
+
//add UserClient to the list
client->prev = NULL;
client->next = userclients;
userclients = client;
//let's say hello to the client
- iohandler_printf(iofd, "NOTICE AUTH :*** TransparentIRC " TRANSIRC_VERSION " (use /quote transirc for more information)");
+ iohandler_printf(iofd, "NOTICE AUTH :*** [TransparentIRC] TransparentIRC v" TRANSIRC_VERSION " (use /quote transirc for more information)");
}
void userclient_close(struct UserClient *client) {
+ if(client->flags & USERCLIENT_LOGGED_IN) {
+ usersession_client_close(client->user);
+ } else {
+ struct UserLogin *login = client->user;
+ if(client->flags & USERCLIENT_LOGIN_PROCESSING) {
+ usersession_login_abort(login);
+ }
+ if(login->username)
+ free(login->username);
+ if(login->password)
+ free(login->password);
+ if(login->nick)
+ free(login->nick);
+ if(login->reject_reason)
+ free(login->reject_reason);
+ if(login->session_class)
+ free(login->session_class);
+ free(login);
+ }
iohandler_close(client->iofd);
client->server->clientcount--;
if(client->prev)
userclients = client->next;
if(client->next)
client->next->prev = client->prev;
+
free(client);
}
}
static void userclient_recv(struct UserClient *client, char *line) {
- iohandler_printf(client->iofd, "reply: %s", line);
+ if(!stricmplen(line, "TRANSIRC ", 9)) {
+ /*
+ char *argv[MAXNUMPARAMS];
+ int argc = parse_line(line, argv, 1);
+ */
+
+ } else if(!(client->flags & USERCLIENT_LOGGED_IN)) {
+ struct UserLogin *login = client->user;
+ char *argv[MAXNUMPARAMS];
+ int argc = parse_line(line, argv, 1);
+ if(argc < 3) return;
+ if(!stricmp(argv[1], "PASS")) {
+ char *delimiter = strchr(argv[2], ':');
+ if(login->password)
+ free(login->password);
+ if(delimiter) {
+ *delimiter = '\0';
+ delimiter++;
+ if(login->username)
+ free(login->username);
+ login->username = strdup(argv[2]);
+ login->password = strdup(delimiter);
+ } else
+ login->password = strdup(argv[2]);
+ } else if(!stricmp(argv[1], "USER")) {
+ if(!login->username)
+ login->username = strdup(argv[2]);
+ } else if(!stricmp(argv[1], "NICK")) {
+ if(login->nick)
+ free(login->nick);
+ login->nick = strdup(argv[2]);
+ if(!login->password) {
+ iohandler_printf(client->iofd, "NOTICE AUTH :*** [TransparentIRC] You need to send your LOC data. Try /quote PASS <username>:<password>");
+ }
+ }
+ if(login->username && login->password && login->nick && !(client->flags & USERCLIENT_LOGIN_PROCESSING)) {
+ //try to login
+ iohandler_printf(client->iofd, "NOTICE AUTH :*** [TransparentIRC] Checking login...");
+ usersession_login(login);
+ }
+ } else {
+
+ }
+}
+
+void userclient_login_failed(struct UserLogin *login, char *reason) {
+ iohandler_printf(login->client->iofd, "NOTICE AUTH :*** [TransparentIRC] Login rejected");
+ userclient_close(login->client);
+}
+
+void userclient_login_successful(struct UserLogin *login, struct UserSession *session, int recover) {
+ struct UserClient *client = login->client;
+ iohandler_printf(client->iofd, "NOTICE AUTH :*** [TransparentIRC] Login accepted.");
+ if(login->username)
+ free(login->username);
+ if(login->password)
+ free(login->password);
+ if(login->nick)
+ free(login->nick);
+ free(login);
+ client->user = session;
+ if(recover) {
+ iohandler_printf(client->iofd, "NOTICE AUTH :*** [TransparentIRC] Recovering previous link (Nick: %s).", session->nick);
+
+
+ }
}
static void userclient_callback(struct IOEvent *event) {
struct IODescriptor;
struct ServerSocket;
-//struct UserSession;
+struct UserSession;
+
+#define USERCLIENT_LOGIN_PROCESSING 0x01
+#define USERCLIENT_LOGGED_IN 0x02
+
+struct UserLogin {
+ struct UserClient *client;
+ char *username;
+ char *password;
+ char *nick;
+
+ int login_accepted : 1;
+ char *reject_reason;
+
+ char *session_class;
+
+ struct IODescriptor *login_iofd;
+};
struct UserClient {
struct IODescriptor *iofd;
struct ServerSocket *server;
- //struct UserSession *user;
+ int flags;
+ void *user; /* struct UserSession / struct UserLogin */
+
+ char *username;
+ char *password;
+ char *nick;
struct UserClient *next, *prev;
};
void userclient_accepted(struct ServerSocket *server, int sockfd);
void userclient_close(struct UserClient *client);
void userclient_close_server(struct ServerSocket *server, int keep_clients);
+void userclient_login_failed(struct UserLogin *login, char *reason);
+void userclient_login_successful(struct UserLogin *login, struct UserSession *session, int recover);
#endif
--- /dev/null
+/* UserSession.c - TransparentIRC 0.1
+ * Copyright (C) 2011-2012 Philipp Kreil (pk910)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "UserSession.h"
+#include "IOHandler.h"
+#include "UserClient.h"
+#include "ConfigParser.h"
+#include "tools.h"
+
+static struct UserSession *usersessions = NULL;
+
+static struct UserSession *usersession_add(char *username, char *password, char *nick) {
+ struct UserSession *session;
+ session = calloc(1, sizeof(*session));
+ session->username = strdup(username);
+ session->password = strdup(password);
+ session->nick = strdup(nick);
+
+ //add UserSession to the list
+ session->prev = NULL;
+ session->next = usersessions;
+ if(usersessions)
+ usersessions->prev = session;
+ usersessions = session;
+
+ return session;
+}
+
+/*
+static void usersession_close(struct UserSession *session) {
+ if(session->client)
+ userclient_close(session->client);
+
+ if(session->prev)
+ session->prev->next = session->next;
+ else
+ usersessions = session->next;
+ if(session->next)
+ session->next->prev = session->prev;
+ free(session->username);
+ free(session->password);
+ free(session->nick);
+ free(session);
+}
+*/
+
+/* ****************** SESSION FUNCTIONS ****************** */
+
+static void usersession_initialize_session(struct UserSession *session) {
+
+}
+
+/* ****************** EXTERNAL EVENTS ****************** */
+
+void usersession_client_close(struct UserSession *session) {
+ session->client = NULL;
+ session->idle_since = time(0);
+
+}
+
+void usersession_client_notification(struct UserSession *session, char *notification) {
+ if(session->client) {
+ iohandler_printf(session->client->iofd, ":*TransparentIRC!TransIRC@TransparentIRC.system.notification NOTICE %s :[TransparentIRC] %s", session->nick, notification);
+ }
+}
+
+/* ****************** LOGIN FUNCTIONS ****************** */
+
+static void usersession_login_accept(struct UserLogin *login);
+static void usersession_login_callback(struct IOEvent *event);
+
+void usersession_login(struct UserLogin *login) {
+ if(get_int_field("auth.external.enabled")) {
+ char *execute = get_string_field("auth.external.execute");
+ char *parameters = get_string_field("auth.external.parameters");
+
+ struct variable_replace_map map[] = {
+ {'U', login->username},
+ {'P', login->password},
+ {'N', login->nick},
+ {0, NULL}
+ };
+ char paramstr[CMDLEN+1];
+ build_var_string(paramstr, parameters, map);
+ char *argv[MAXNUMPARAMS];
+ int argc = parse_line(paramstr, argv+1, 0);
+ argv[0] = execute;
+ argv[argc+1] = NULL;
+ int fp = run_external_process(execute, argv);
+ if(fp < 0) {
+ userclient_login_failed(login, "Login Script error.");
+ return;
+ }
+ struct IODescriptor *iofd = iohandler_add(fp, IOTYPE_CLIENT, usersession_login_callback);
+ if(iofd) {
+ iofd->read_lines = 1;
+ iofd->state = IO_CONNECTED;
+ int timeout = get_int_field("auth.external.timeout");
+ if(timeout) {
+ gettimeofday(&iofd->timeout, NULL);
+ iofd->timeout.tv_sec += timeout;
+ }
+ iofd->data = login;
+ login->login_iofd = iofd;
+ login->client->flags |= USERCLIENT_LOGIN_PROCESSING;
+ } else
+ userclient_login_failed(login, "Internal error.");
+ } else
+ usersession_login_accept(login);
+}
+
+static void usersession_login_accept(struct UserLogin *login) {
+ //search session for user (or create a new one)
+ struct UserSession *session, *active_session = NULL;
+ int sessioncount = 0;
+ for(session = usersessions; session; session = session->next) {
+ if(!stricmp(login->username, session->username)) {
+ sessioncount++;
+ if(!strcmp(login->password, session->password) && !stricmp(login->nick, session->nick)) {
+ active_session = session;
+ }
+ }
+ }
+ if(active_session) {
+ if(active_session->client) {
+ iohandler_printf(active_session->client->iofd, "ERROR :[TransparentIRC] Another client logged in.");
+ userclient_close(active_session->client);
+ } //active_session->client is now NULL
+ active_session->client = login->client;
+ userclient_login_successful(login, active_session, 1);
+ } else {
+ int sessionlimit = get_int_field("auth.session_limit");
+ if(sessionlimit && sessioncount >= sessionlimit) {
+ userclient_login_failed(login, "Session limit reached.");
+ return;
+ }
+ active_session = usersession_add(login->username, login->password, login->nick);
+ if(!active_session) {
+ userclient_login_failed(login, "Could not create Session.");
+ return;
+ }
+ userclient_login_successful(login, active_session, 0);
+ usersession_initialize_session(active_session);
+ }
+}
+
+void usersession_login_abort(struct UserLogin *login) {
+ struct IODescriptor *iofd = login->login_iofd;
+ iohandler_close(iofd);
+}
+
+static void usersession_login_callback(struct IOEvent *event) {
+ struct UserLogin *login = event->iofd->data;
+ login->client->flags &= ~USERCLIENT_LOGIN_PROCESSING;
+ char *reply;
+ int login_finished = 0;
+ switch(event->type) {
+ case IOEVENT_RECV:
+ reply = event->data.recv_str;
+ if(!stricmplen(reply, "REJECT", 6)) {
+ if((reply = strchr(reply, ' '))) {
+ char reason[LINELEN];
+ sprintf(reason, "Access denied: %s", reply + 7);
+ login->reject_reason = strdup(reason);
+ } else
+ login->reject_reason = strdup("Access denied.");
+ } else if(!stricmplen(reply, "ACCEPT", 6)) {
+ if((reply = strchr(reply, ' '))) {
+ char *passwd = strchr(reply, ' ');
+ if(passwd) {
+ *passwd = '\0';
+ passwd++;
+ free(login->password);
+ login->password = strdup(passwd);
+ }
+ free(login->username);
+ login->username = strdup(reply);
+ }
+ login->login_accepted = 1;
+ } else {
+ char *argv[MAXNUMPARAMS];
+ int argc = parse_line(reply, argv, 0);
+ if(argc < 2) return;
+ if(!stricmp(argv[0], "CLASS")) {
+ login->session_class = strdup(argv[1]);
+ }
+
+ }
+ break;
+ case IOEVENT_TIMEOUT:
+ login->reject_reason = strdup("Login Script timeout.");
+ login_finished = 1;
+ break;
+ case IOEVENT_CLOSED:
+ login->reject_reason = strdup("Login Script error (no reply).");
+ login_finished = 1;
+ break;
+ default:
+ return;
+ }
+ if(login_finished) {
+ iohandler_close(event->iofd);
+ if(login->login_accepted)
+ usersession_login_accept(login);
+ else
+ userclient_login_failed(login, login->reject_reason);
+ }
+}
--- /dev/null
+/* UserSession.h - TransparentIRC 0.1
+ * Copyright (C) 2011-2012 Philipp Kreil (pk910)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _UserSession_h
+#define _UserSession_h
+#include "overall.h"
+
+struct UserClient;
+struct UserLogin;
+
+struct UserSession {
+ char *username;
+ char *password;
+ char *nick;
+
+ struct UserClient *client;
+ time_t idle_since;
+ time_t connected_since;
+
+ struct UserSession *next, *prev;
+};
+
+void usersession_client_notification(struct UserSession *session, char *notification);
+void usersession_login(struct UserLogin *login);
+void usersession_login_abort(struct UserLogin *login);
+void usersession_client_close(struct UserSession *session);
+
+#endif
#include "IOHandler.h"
#include "ServerSocket.h"
+#include "ConfigParser.h"
char *configFile = "transirc.conf";
}
#endif
parseParameters(argc, argv);
+ loadConfig(configFile);
serversocket_listen("0.0.0.0", 9001);
#define IO_READ_BUFLEN 1024
#define IO_MAX_TIMEOUT 10
#define LINELEN 512
+#define CMDLEN 512
+#define MAXNUMPARAMS 200
#include <stdio.h>
#include <stdlib.h>
if(i == len) break;
}
return c1 - c2;
-}
\ No newline at end of file
+}
+
+int parse_line(char *line, char **argv, int irc_raw) {
+ int argc = 0;
+ if(irc_raw) {
+ if(line[0] == ':')
+ line++;
+ else
+ argv[argc++] = NULL;
+ }
+ while(*line) {
+ //skip leading spaces
+ while (*line == ' ')
+ *line++ = 0;
+ if (*line == ':' && irc_raw) {
+ //the rest is a single parameter
+ argv[argc++] = line + 1;
+ break;
+ }
+ argv[argc++] = line;
+ if (argc >= MAXNUMPARAMS)
+ break;
+ while (*line != ' ' && *line)
+ line++;
+ }
+ return argc;
+}
+
+void build_var_string(char *buffer, char *format, struct variable_replace_map *map) {
+ int bufferpos = 0;
+ int i, escape = 0;
+ char *p = format;
+ char *tmp;
+ while(*p) {
+ if(escape) {
+ escape = 0;
+ goto build_var_string_addchar;
+ }
+ if(*p == '\\') {
+ escape = 1;
+ p++;
+ continue;
+ }
+ if(*p == '%') {
+ p++;
+ for(i = 0; ; i++) {
+ if(!map[i].character) {
+ tmp = NULL;
+ break;
+ }
+ if(map[i].character == *p) {
+ tmp = map[i].value;
+ break;
+ }
+ }
+ if(!tmp) {
+ p--;
+ goto build_var_string_addchar;
+ }
+ for(i = 0; tmp[i] && bufferpos < CMDLEN; i++) {
+ buffer[bufferpos++] = tmp[i];
+ }
+ } else {
+ build_var_string_addchar:
+ buffer[bufferpos++] = *p;
+ if(bufferpos == CMDLEN)
+ break;
+ }
+ p++;
+ }
+ buffer[bufferpos++] = '\0';
+}
+
+int run_external_process(char *command, char **parameters) { //win32 incompatible
+ int sockets[2], child;
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0)
+ return -1;
+ if ((child = fork()) == -1) {
+ close(sockets[0]);
+ close(sockets[1]);
+ return -1;
+ }
+ else if (child) { // This is the parent.
+ close(sockets[0]);
+ wait(NULL);
+ return sockets[1];
+ } else { // This is the child.
+ close(sockets[1]);
+ child = fork(); //double fork to prevent zombies
+ if(child < 0) exit(EXIT_FAILURE);
+ if(child != 0) exit(EXIT_SUCCESS);
+ if(dup2(sockets[0], 1) != -1) {
+ execvp(command, parameters);
+ }
+ close(sockets[0]);
+ exit(EXIT_FAILURE);
+ }
+}
+
#define _tools_h
#include "overall.h"
+struct variable_replace_map {
+ char character;
+ char *value;
+};
+
int stricmp(const char *s1, const char *s2);
int stricmplen(const char *s1, const char *s2, int len);
+int parse_line(char *line, char **argv, int irc_raw);
+void build_var_string(char *buffer, char *format, struct variable_replace_map *map);
+int run_external_process(char *command, char **parameters);
#endif
--- /dev/null
+
+"auth" {
+ "session_limit" = 2;
+
+ "external" {
+ "enabled" = 1;
+ "execute" = "./login.php";
+ // %U = Username
+ // %P = Password
+ // %N = Nick
+ "parameters" = "%U %P %N";
+ "timeout" = 5;
+ };
+};
\ No newline at end of file