/* IRCfs - IRC FileServ for *nix. * Copyright (C) 2002 Nick 'Zaf' Clifford * For licensing details, refer to the LICENSE file in the source * code directory. */ #include "runtime.h" #include "module.h" #include "misc.h" #include "fserv.h" #include "file.h" #include "debug.h" #include "dccchat.h" #include "dccfile.h" #include "list.h" #include "magic.h" #include "fserv.h" #include "str_hash.h" #include "appconf/appconf.h" #include "session.h" #include "channel.h" #include "ctcp.h" #include "timer.h" enum fserv_state { fserv_active=1, fserv_closing, fserv_closed }; #define MAGIC_FSERV 0x72dcd4a1 struct fserv { MAGIC magic; POOL pool; char *name; /* Name of the fserv */ struct dir *root; /* Root of file system */ enum fserv_state state; char *includes,*excludes; enum fserv_triggers trigger_type; char *trigger_match; struct list *session_list; /* List of struct session's */ struct list *where_list; /* List of where to run */ }; #define MAGIC_FSERV_WHERE 0x834a6dff struct fserv_where { MAGIC magic; struct fserv *fserv; char *network; char *server; struct list *channel_list; /* (char *) list of channels */ struct server *current_server; /* Current server */ char *last_tried; /* Last server tried */ }; int fserv_foreach_where(struct conf_group *parent, const char *name, struct conf_value *entry, void *d); int fserv_foreach_conf_entry(struct conf_group *parent, const char *name, struct conf_value *entry, void *d); void fserv_dump(); void fserv_dump_fserv(struct fserv *fs); static int fserv_start(); static void fserv_autoload_fserv(struct fserv *fs); static int fserv_connect_where(struct list *l, struct fserv_where *w, struct fserv *fs); static int fserv_load_parse_trigger_type(struct fserv *fs); static int fserv_dump_where(struct list *l, struct fserv_where *w, struct fserv *fs); static int fserv_dump_where_chans(char *channame); static int fserv_dump_session(struct list *l, struct session *s, struct fserv *fs); static int fserv_server_connected(struct eventmsg *em); static int fserv_server_disconnected(struct eventmsg *em); static int fserv_server_error(struct eventmsg *em); static int fserv_join_channels(struct list *l, char *channel, struct fserv_where *w); static int fserv_load(); static int fserv_load_config(const char *name); static int fserv_channel_msg(struct eventmsg *em); static int fserv_user_msg(struct eventmsg *em); static int fserv_ctcp_msg(struct eventmsg *em); static int fserv_retry_timeout(struct timer *t, void *appdata); MODULE_NAME("fserv"); MODULE_INIT(fserv_init); MODULE_DESTROY(fserv_unload); MODULE_DEPENDS("server", "socket", "channel", "ctcp", "user", "appconf", "file", "timer"); MODULE_REGISTER(fserv); DEBUG fserv_debug; static POOL pool; struct eventobj *fserv_eventobj; struct list *fserv_list; enum server_use_ip { server_use_ip_hostname, server_use_ip_default_route, server_use_ip_from_server, server_use_ip_manual }; struct conf_enum_list fserv_valid_triggers[] = { { .num = fserv_trigger_private, .str = "private" }, { .num = fserv_trigger_channel, .str = "channel" }, { .num = fserv_trigger_ctcp, .str = "ctcp" }, { .num = -1, .str = NULL } }; int fserv_init() { fserv_debug = debug_register("fserv"); pool = pool_new(NULL); fserv_eventobj = event_create_obj(NULL,NULL); // event_set_filter_func(fserv_eventobj,fserv_event_filter); fserv_list = list_create(pool); if (fserv_load() == -1) return -1; /* dump fserv list */ fserv_dump(); /* start fservs */ fserv_start(); return 0; } int fserv_start() { list_foreach_simple(fserv_list,(list_simple_callback_t) fserv_autoload_fserv); return 0; } void fserv_autoload_fserv(struct fserv *fs) { ASSERT(is_magic(fs,MAGIC_FSERV)); if (fs->state != fserv_active) return; notice(fserv_debug,"Starting fserv %s",fs->name); list_foreach(fs->where_list, (list_callback_t)fserv_connect_where, fs); } int fserv_connect_where(struct list *l, struct fserv_where *w, struct fserv *fs) { struct server *s; ASSERT(is_magic(w, MAGIC_FSERV_WHERE)); ASSERT(is_magic(fs, MAGIC_FSERV)); info(fserv_debug," Connecting to server %s on network %s", w->server,w->network); s = server_find_network_server(w->network,w->server); if (s == NULL) { s = server_open(w->network, w->server); } if (s == NULL) { warn(fserv_debug,"fserv %s: can't open a " "connection to %s on %s", fs->name,w->server,w->network); return 0; } w->current_server = s; event_add_listener(server_get_event_obj(), server_event_connected, s, fserv_server_connected, w); event_add_listener(server_get_event_obj(), server_event_disconnected, s, fserv_server_disconnected, w); event_add_listener(server_get_event_obj(), server_event_error, s, fserv_server_error, w); event_add_listener(user_get_event_obj(), user_event_msg, NULL, fserv_user_msg, w); event_add_listener(ctcp_get_event_obj(), ctcp_event_query, NULL, fserv_ctcp_msg, w); if (server_is_connected(s) == 0) { list_foreach(w->channel_list,(list_callback_t) fserv_join_channels,w); } return 0; } int fserv_server_connected(struct eventmsg *em) { struct fserv_where *w; w = em->appdata; ASSERT(is_magic(w,MAGIC_FSERV_WHERE)); info(fserv_debug,"fserv %s on %s server connected", w->fserv->name, server_get_name(w->current_server)); list_foreach(w->channel_list,(list_callback_t) fserv_join_channels,w); return 0; } int fserv_join_channels(struct list *l, char *channel, struct fserv_where *w) { struct channel *c; ASSERT(is_magic(w,MAGIC_FSERV_WHERE)); c = channel_create(w->current_server,channel); event_add_listener(channel_get_eventobj(), channel_event_msg, c, fserv_channel_msg, w); return 0; } int fserv_ctcp_msg(struct eventmsg *em) { struct ctcp_event_msg *cem; struct fserv_where *fw; struct fserv *f; cem = em->msg; ASSERT(is_magic(cem, MAGIC_CTCP_EVENT_MSG)); fw = em->appdata; ASSERT(is_magic(fw, MAGIC_FSERV_WHERE)); f = fw->fserv; debug(fserv_debug,"User %s on %s ctcp'd: %s", user_get_nick(cem->user), server_get_name(cem->server), cem->request); if (!(f->trigger_type & fserv_trigger_ctcp)) return 0; if (strcasecmp(cem->request, f->trigger_match) == 0) { fserv_open(f,cem->user); return 0; } return 0; } int fserv_user_msg(struct eventmsg *em) { struct user_event_msg *uem; struct fserv_where *fw; struct fserv *f; uem = em->msg; ASSERT(is_magic(uem, MAGIC_USER_EVENT_MSG)); fw = em->appdata; ASSERT(is_magic(fw, MAGIC_FSERV_WHERE)); f = fw->fserv; debug(fserv_debug,"User %s on %s said: %s", user_get_nick(uem->user), server_get_name(uem->server), uem->txt); if (!(f->trigger_type & fserv_trigger_private)) return 0; if (strcasecmp(uem->txt, f->trigger_match) == 0) { fserv_open(f,uem->user); return 0; } return 0; } int fserv_channel_msg(struct eventmsg *em) { struct channel_event_msg *cem; struct fserv_where *fw; struct fserv *f; cem = (struct channel_event_msg *)em->msg; ASSERT(is_magic(cem, MAGIC_CHANNEL_EVENT_MSG)); fw = em->appdata; ASSERT(is_magic(fw, MAGIC_FSERV_WHERE)); debug(fserv_debug,"user %s on %s->%s said: %s", user_get_name(cem->from), server_get_name(cem->server), channel_get_name(cem->channel), cem->msg); f = fw->fserv; if (!(f->trigger_type & fserv_trigger_channel)) return 0; if (strcasecmp(cem->msg, f->trigger_match) == 0) { fserv_open(f,cem->from); return 0; } return 0; } int fserv_open(struct fserv *f, struct user *u) { notice(fserv_debug,"User %s!%s@%s on %s requested fserv %s", user_get_nick(u), user_get_username(u), user_get_hostname(u), server_get_name(user_get_server(u)), f->name); session_create(u, f->root); return 0; } int fserv_server_disconnected(struct eventmsg *em) { struct fserv *f; struct fserv_where *w; struct server *s; int timeout; s = (struct server *) em->msg; w = (struct fserv_where *) em->appdata; ASSERT(is_magic(w,MAGIC_FSERV_WHERE)); f = w->fserv; warn(fserv_debug,"fserv: server disconnected"); if (server_is_connected(s) == 0) { /* Ok, its a disconnection after a complete connect, * so we can retry immediatly */ timer_start(1,fserv_retry_timeout, w); } else { /* Whoops, we got disconnected before we even succesfully * logged in, so lets wait a few secs before retrying. */ timeout = 60; conf_value_to_int(conf_get_value(f->pool, "/fserv/server_retry_timeout"),&timeout); timer_start(timeout,fserv_retry_timeout, w); } return 0; } int fserv_retry_timeout(struct timer *t, void *appdata) { struct fserv_where *w; struct fserv *f; w = (struct fserv_where *) appdata; ASSERT(is_magic(w,MAGIC_FSERV_WHERE)); f = w->fserv; info(fserv_debug,"Going to try connection again"); fserv_connect_where(NULL, w, f); return 0; } int fserv_server_error(struct eventmsg *em) { error(fserv_debug,"fserv: server error"); return 0; } void fserv_dump() { debug(fserv_debug,"Fserv list"); list_foreach_simple(fserv_list,(list_simple_callback_t)fserv_dump_fserv); debug(fserv_debug,"End of Fserv list"); } void fserv_dump_fserv(struct fserv *fs) { ASSERT(is_magic(fs,MAGIC_FSERV)); debug(fserv_debug," Name: %s Root: %s", fs->name, fs->root->handler->get_full_name(fs->root)); debug(fserv_debug," State: %d", fs->state); debug(fserv_debug," Includes: %s", fs->includes ? fs->includes : NULL); debug(fserv_debug," Excludes: %s", fs->excludes ? fs->excludes : NULL); debug(fserv_debug," Trigger: Type: %d Match: %s", fs->trigger_type, fs->trigger_match); debug(fserv_debug," Session List:"); list_foreach(fs->session_list, (list_callback_t)fserv_dump_session, fs); debug(fserv_debug," End of session list"); debug(fserv_debug," Where List:"); list_foreach(fs->where_list, (list_callback_t)fserv_dump_where, fs); debug(fserv_debug," End of where list"); } int fserv_dump_where(struct list *l, struct fserv_where *w, struct fserv *fs) { ASSERT(is_magic(w, MAGIC_FSERV_WHERE)); ASSERT(is_magic(fs, MAGIC_FSERV)); debug(fserv_debug," network: %s server: %s", w->network, w->server); debug(fserv_debug," current server: %s last tried: %s", w->current_server ? server_get_name(w->current_server) : "", w->last_tried ? w->last_tried : ""); debug(fserv_debug," Channels: "); list_foreach_simple(w->channel_list,(list_simple_callback_t) fserv_dump_where_chans); return 0; } int fserv_dump_where_chans(char *channame) { debug(fserv_debug," %s", channame); return 0; } int fserv_dump_session(struct list *l, struct session *s, struct fserv *fs) { struct user *u; struct dir *d; u = session_get_user(s); d = session_get_current_dir(s); debug(fserv_debug," %s!%s@%s: %s", user_get_nick(u), user_get_username(u), user_get_hostname(u), d->handler->get_full_name(d)); return 0; } int fserv_load() { struct conf_value *cv; struct conf_group *cg; cv = conf_get_value(pool,"/fileservers"); if (cv == NULL) return 0; cg = conf_value_to_group(cv); if (cg == NULL) { warn(fserv_debug,"Config entry /fileservers isn't a group"); return 0; } return conf_foreach(cg, fserv_foreach_conf_entry,NULL); } int fserv_foreach_conf_entry(struct conf_group *parent, const char *name, struct conf_value *entry, void *d) { struct conf_group *cg; const char *fserv_name; cg = conf_value_to_group(entry); if (cg == NULL) { return 0; } fserv_name = conf_group_name(cg); return fserv_load_config(fserv_name); } int fserv_load_config(const char *name) { struct conf_value *cv; struct conf_group *cg; const char *path; const char *ccp; struct dir_handler *dh; struct fserv *fs; int i; POOL p; p = pool_new(pool); fs = XMALLOC(p, struct fserv); set_magic(fs,MAGIC_FSERV); fs->pool = p; fs->session_list = list_create(p); fs->where_list = list_create(p); fs->name = pstrdup(p,name); info(fserv_debug,"Loading fserv %s settings",fs->name); /* active */ cv = conf_get_value(p,"/fileservers/%s/active",name); if (cv == NULL) fs->state = fserv_active; else { if (conf_value_to_bool(cv,&i) == -1) { error(fserv_debug,"cfg /fileservers/%s/active must be " "a boolean", name); pool_destroy(p); return -1; } if (i) fs->state = fserv_active; else fs->state = fserv_closed; } debug(fserv_debug,"fserv %s %s active",fs->name, fs->state == fserv_active ? "is" : "isn't"); /* Where to listen */ cv = conf_get_value(p,"/fileservers/%s/where",name); if (cv != NULL) { cg = conf_value_to_group(cv); if (cg == NULL) { warn(fserv_debug,"Config entry /fileservers/%s/where " "isn't a group",name); } else { if (conf_foreach(cg, fserv_foreach_where,fs) == -1) { pool_destroy(p); return -1; } } } cv = conf_get_value(p,"/fileservers/%s/share",name); if (cv == NULL) { error(fserv_debug,"Fileserver %s doesn't share anything!", name); pool_destroy(p); return -1; } cg = conf_value_to_group(cv); if (cg == NULL) { error(fserv_debug,"Config entry /fileservers/%s/share " "isn't a group",name); pool_destroy(p); return -1; } /* TODO Read excludes and includes */ cv = conf_get_value(p,"/fileservers/%s/share/type"); if (cv == NULL) { error(fserv_debug,"Fileserver %s missing share/type", name); pool_destroy(p); return -1; } ccp = conf_value_to_str_const(cv); if (ccp == NULL) { error(fserv_debug,"Config entry /fileservers/%s/share/type " "is invalid",name); pool_destroy(p); return -1; } dh = find_dir_handler(ccp); if (dh == NULL) { module_check_load(ccp); dh = find_dir_handler(ccp); } if (dh == NULL) { error(fserv_debug,"Can't find directory handler named '%s'", ccp); pool_destroy(p); return -1; } cv = conf_get_value(p,"/fileservers/%s/share/path"); if (cv == NULL || (path=conf_value_to_str_const(cv)) == NULL) { error(fserv_debug,"Config entry /fileservers/%s/share/path " "is either missing or invalid", name); pool_destroy(p); return -1; } fs->root = dh->open(NULL,path); if (fs->root == NULL) { error(fserv_debug,"Can't open root directory of fileserver %s", name); pool_destroy(p); return -1; } fserv_load_parse_trigger_type(fs); cv = conf_get_value(fs->pool,"/fileservers/%s/trigger/match",fs->name); if (cv == NULL) { notice(fserv_debug,"Fileserver %s doesn't have a trigger " "match!", fs->name); return 0; } fs->trigger_match = pstrdup(fs->pool, conf_value_to_str_const(cv)); list_add_end(fserv_list,fs); return 0; } int fserv_load_parse_trigger_type(struct fserv *fs) { struct conf_value *cv; int i,result; char **type_list; cv = conf_get_value(fs->pool,"/fileservers/%s/trigger/type",fs->name); if (cv == NULL) { notice(fserv_debug,"Fileserver %s doesn't have a trigger!", fs->name); return 0; } if (conf_value_to_strarray(fs->pool, cv, &type_list) == -1) { error(fserv_debug,"Error parsing configuration entry " "/fileservers/%s/trigger/type. Expecting " "a string list of fserv trigger types", fs->name); return -1; } for (i = 0; type_list[i] != NULL; i++) { if (parse_enum_list(type_list[i],fserv_valid_triggers, &result) == -1) { error(fserv_debug,"Error parsing configuration entry " "/fileservers/%s/trigger/type. " "Expecting a string list of fserv " "trigger types.", fs->name); return -1; } fs->trigger_type |= result; } return 0; } int fserv_foreach_where(struct conf_group *parent, const char *name, struct conf_value *entry, void *d) { struct fserv *fs; struct fserv_where *fsw; char **a; char *chan; const char *cp; int len,i; fs = (struct fserv *) d; ASSERT(is_magic(fs,MAGIC_FSERV)); if (conf_value_to_strarray(fs->pool,entry,&a) == -1) { error(fserv_debug,"cfg /fileservers/%s/where/%s " "should be a list of channel names ", fs->name,name); return -1; } fsw = XMALLOC(fs->pool, struct fserv_where); set_magic(fsw,MAGIC_FSERV_WHERE); fsw->fserv = fs; cp = index(name,'.'); if (cp) { len = cp - name; fsw->network = palloc(fs->pool, sizeof(char) * (len+1)); memcpy(fsw->network,name,len); fsw->network[len]=0; fsw->server = pstrdup(fs->pool, cp+1); } else { fsw->network = pstrdup(fs->pool,name); fsw->server = NULL; } debug(fserv_debug," fserv %s on network %s server %s",fs->name, fsw->network ? fsw->network : "*", fsw->server ? fsw->server : "*"); fsw->channel_list = list_create(fs->pool); for(i = 0; a[i] != NULL; i++) { chan = pstrdup(fs->pool,a[i]); /* don't care if we don't find it */ debug(fserv_debug," on chan %s",chan); list_add_end(fsw->channel_list,chan); } list_add_end(fs->where_list, fsw); return 0; } int fserv_unload() { return -1; }