static void account_getdirs (const char *login, char *rootdir, char *newsfile, char *dropbox) { struct stat sb; char dir[MAXPATHLEN]; if (snprintf(dir, MAXPATHLEN, "%s/%s/files", hxd_cfg.paths.accounts, login) == -1) { hxd_log("account_getdirs(%s): path '%s' too long?!?", login, dir); goto default_files; } #ifdef HAVE_CORESERVICES if (!resolve_alias_path(dir, rootdir) || stat(rootdir, &sb)) { #else if (!realpath(dir, rootdir) || stat(rootdir, &sb)) { #endif default_files: #ifdef HAVE_CORESERVICES if (!resolve_alias_path(hxd_cfg.paths.files, rootdir)) { #else if (!realpath(hxd_cfg.paths.files, rootdir)) { #endif hxd_log("could not get realpath of files dir '%s'", hxd_cfg.paths.files); snprintf(rootdir, MAXPATHLEN, "%s", hxd_cfg.paths.files); } } if (snprintf(dir, MAXPATHLEN, "%s/%s/news", hxd_cfg.paths.accounts, login) == -1) { hxd_log("account_getdirs(%s): path '%s' too long?!?", login, dir); goto default_news; } #ifdef HAVE_CORESERVICES if (!resolve_alias_path(dir, newsfile) || stat(newsfile, &sb)) { #else if (!realpath(dir, newsfile) || stat(newsfile, &sb)) { #endif default_news: #ifdef HAVE_CORESERVICES if (!resolve_alias_path(hxd_cfg.paths.news, newsfile)) { #else if (!realpath(hxd_cfg.paths.news, newsfile)) { #endif hxd_log("could not get realpath of news file '%s'", hxd_cfg.paths.news); snprintf(newsfile, MAXPATHLEN, "%s", hxd_cfg.paths.news); } } if (snprintf(dir, MAXPATHLEN, "%s/%s/dropbox", hxd_cfg.paths.accounts, login) == -1) { hxd_log("account_getdirs(%s): path '%s' too long?!?", login, dir); goto default_dropbox; } #ifdef HAVE_CORESERVICES if (!resolve_alias_path(dir, dropbox) || stat(dropbox, &sb)) { #else if (!realpath(dir, dropbox) || stat(dropbox, &sb)) { #endif default_dropbox: dropbox[0] = 0; } } void account_getconf (struct htlc_conn *htlc) { struct hxd_config cfg; char file[MAXPATHLEN]; account_getdirs(htlc->login, htlc->rootdir, htlc->newsfile, htlc->dropbox); if (snprintf(file, MAXPATHLEN, "%s/%s/conf", hxd_cfg.paths.accounts, htlc->login) == -1) { hxd_log("account_getconf(%s): path '%s' too long?!?", htlc->login, file); return; } memset(&cfg, 0, sizeof(cfg)); cfg.limits.individual_exec = hxd_cfg.limits.individual_exec; cfg.limits.individual_downloads = hxd_cfg.limits.individual_downloads; cfg.limits.individual_uploads = hxd_cfg.limits.individual_uploads; cfg.limits.out_Bps = hxd_cfg.limits.out_Bps; /* remote server queueing spec */ cfg.limits.can_omit_queue = hxd_cfg.limits.can_omit_queue; hxd_read_config(file, &cfg); htlc->get_limit = cfg.limits.individual_downloads > HTXF_GET_MAX ? HTXF_GET_MAX : cfg.limits.individual_downloads; htlc->put_limit = cfg.limits.individual_uploads > HTXF_PUT_MAX ? HTXF_PUT_MAX : cfg.limits.individual_uploads; htlc->limit_out_Bps = cfg.limits.out_Bps; htlc->exec_limit = cfg.limits.individual_exec; /* remote server queuing spec */ htlc->can_download = cfg.limits.can_omit_queue; } int access_extra_set (struct extra_access_bits *acc, char *p, int val) { switch (p[0]) { case 'a': if (!strcmp(p, "access_volatile")) { acc->access_volatile = val; return 0; } case 'c': if (!strcmp(p, "can_spam")) { acc->can_spam = val; return 0; } else if (!strcmp(p, "chat_private")) { acc->chat_private = val; return 0; } case 'd': if (!strcmp(p, "debug")) { acc->debug = val; return 0; } case 'f': if (!strcmp(p, "file_getinfo")) { acc->file_getinfo = val; return 0; } else if (!strcmp(p, "file_hash")) { acc->file_hash = val; return 0; } else if (!strcmp(p, "file_list")) { acc->file_list = val; return 0; } case 'i': if (!strcmp(p, "is_0wn3d")) { acc->is_0wn3d = val; return 0; } else if (!strcmp(p, "info_get_address")) { acc->info_get_address = val; return 0; } else if (!strcmp(p, "info_get_login")) { acc->info_get_login = val; return 0; } case 'm': if (!strcmp(p, "manage_users")) { acc->manage_users = val; return 0; } else if (!strcmp(p, "msg")) { acc->msg = val; return 0; } case 's': if (!strcmp(p, "set_subject")) { acc->set_subject = val; return 0; } case 'u': if (!strcmp(p, "user_0wn")) { acc->user_0wn = val; return 0; } else if (!strcmp(p, "user_access")) { acc->user_access = val; return 0; } else if (!strcmp(p, "user_color")) { acc->user_color = val; return 0; } else if (!strcmp(p, "user_getlist")) { acc->user_getlist = val; return 0; } else if (!strcmp(p, "user_visibility")) { acc->user_visibility = val; return 0; } } return 1; } void access_extra_set_default (struct login_defaults *def, char *p, u_int16_t val) { if (!strcmp(p, "color")) { def->color = val; def->has_default_color = 1; } else if (!strcmp(p, "icon")) { def->icon = val; def->has_default_icon = 1; } } int set_access_bit (struct hl_access_bits *acc, char *p, int val) { switch (p[0]) { case 'c': if (!strcmp(p, "cant_be_disconnected")) { acc->cant_be_disconnected = val; return 0; } else if (!strcmp(p, "comment_files")) { acc->comment_files = val; return 0; } else if (!strcmp(p, "comment_folders")) { acc->comment_folders = val; return 0; } else if (!strcmp(p, "create_folders")) { acc->create_folders = val; return 0; } else if (!strcmp(p, "create_users")) { acc->create_users = val; return 0; } case 'd': if (!strcmp(p, "delete_files")) { acc->delete_files = val; return 0; } else if (!strcmp(p, "delete_folders")) { acc->delete_folders = val; return 0; } else if (!strcmp(p, "delete_users")) { acc->delete_users = val; return 0; } else if (!strcmp(p, "disconnect_users")) { acc->disconnect_users = val; return 0; } else if (!strcmp(p, "dont_show_agreement")) { acc->dont_show_agreement = val; return 0; } else if (!strcmp(p, "download_files")) { acc->download_files = val; return 0; } else if (!strcmp(p, "download_folders")) { acc->download_folders = val; return 0; } case 'g': if (!strcmp(p, "get_user_info")) { acc->get_user_info = val; return 0; } case 'm': if (!strcmp(p, "make_aliases")) { acc->make_aliases = val; return 0; } else if (!strcmp(p, "modify_users")) { acc->modify_users = val; return 0; } else if (!strcmp(p, "move_files")) { acc->move_files = val; return 0; } else if (!strcmp(p, "move_folders")) { acc->move_folders = val; return 0; } case 'p': if (!strcmp(p, "post_news")) { acc->post_news = val; return 0; } case 'r': if (!strcmp(p, "read_chat")) { acc->read_chat = val; return 0; } else if (!strcmp(p, "read_news")) { acc->read_news = val; return 0; } else if (!strcmp(p, "read_users")) { acc->read_users = val; return 0; } else if (!strcmp(p, "rename_files")) { acc->rename_files = val; return 0; } else if (!strcmp(p, "rename_folders")) { acc->rename_folders = val; return 0; } case 's': if (!strcmp(p, "send_chat")) { acc->send_chat = val; return 0; } case 'u': if (!strcmp(p, "upload_anywhere")) { acc->upload_anywhere = val; return 0; } else if (!strcmp(p, "upload_files")) { acc->upload_files = val; return 0; } else if (!strcmp(p, "use_any_name")) { acc->use_any_name = val; return 0; } case 'v': if (!strcmp(p, "view_drop_boxes")) { acc->view_drop_boxes = val; return 0; } } return 1; } void account_get_access_extra (struct htlc_conn *htlc) { int fd, r, lastlinelen, val; u_int16_t def_val; char buf[1024], *p, *lastlinep, *nextp, *ep; char path[MAXPATHLEN]; /* set the default options before reading in real values */ htlc->access_extra.chat_private = 1; htlc->access_extra.msg = 1; htlc->access_extra.user_getlist = 1; htlc->access_extra.file_list = 1; htlc->access_extra.file_getinfo = 1; htlc->access_extra.file_hash = 1; htlc->access_extra.user_visibility = 0; htlc->access_extra.user_color = 0; htlc->access_extra.set_subject = 0; htlc->access_extra.debug = 0; htlc->access_extra.user_access = 0; htlc->access_extra.access_volatile = 1; htlc->access_extra.user_0wn = 0; htlc->access_extra.is_0wn3d = 1; htlc->access_extra.manage_users = 0; htlc->access_extra.info_get_address = 1; htlc->access_extra.info_get_login = 1; /* the below is used to track if the color/icon has been set in the access * file (better than just testing if it is non-zero) */ htlc->defaults.has_default_color = 0; /* reserved for internal use */ htlc->defaults.has_default_icon = 0; /* reserved for internal use */ if (snprintf(path, MAXPATHLEN, "%s/%s/access", hxd_cfg.paths.accounts, htlc->login) == -1) { hxd_log("account_getconf(%s): path '%s' too long?!?", htlc->login, path); return; } if ((fd = open(path, O_RDONLY)) < 0) return; p = lastlinep = buf; for (;;) { lastlinelen = p - lastlinep; if (lastlinelen) { memcpy(buf, lastlinep, lastlinelen); if (sizeof(buf)-lastlinelen-1 == 0) break; } r = read(fd, buf+lastlinelen, sizeof(buf)-lastlinelen-1); if (r <= 0) break; buf[r] = 0; for (p = buf; *p; p = nextp) { nextp = strchr(p, '\n'); if (nextp) { *nextp = 0; nextp++; lastlinep = nextp; } else { do p++; while (*p); break; } while (isspace(*p)) p++; if (*p == '#') continue; for (ep = p; *ep; ep++) { if (*ep == '=' || isspace(*ep)) { if (*ep == '=') { *ep++ = 0; goto is_eq; } *ep++ = 0; while (isspace(*ep)) ep++; if (*ep != '=') break; else *ep++ = 0; is_eq: while (isspace(*ep)) ep++; if (!*ep) break; val = *ep == '1' ? 1 : 0; access_extra_set(&htlc->access_extra, p, val); def_val = strtoul(ep, 0, 0); access_extra_set_default(&htlc->defaults, p, def_val); } } } } close(fd); } int account_read (const char *login, char *password, char *name, struct hl_access_bits *acc) { struct hl_user_data user_data; int fd, r; u_int16_t len; char path[MAXPATHLEN]; if (strchr(login, DIRCHAR)) return EPERM; if (snprintf(path, sizeof(path), "%s/%s/UserData", hxd_cfg.paths.accounts, login) == -1) { hxd_log("account_read(%s): path '%s' too long?!?", login, path); return ENAMETOOLONG; } fd = open(path, O_RDONLY); if (fd < 0) { hxd_log("account_read(%s): open(%s): %s", login, path, strerror(errno)); return errno; } if ((r = read(fd, &user_data, 734)) != 734) { hxd_log("account_read(%s): read(%d, %x, %u) == %d: %s", login, fd, &user_data, 734, r, strerror(errno)); close(fd); return errno; } close(fd); if (acc) *acc = user_data.access; if (name) { len = ntohs(user_data.nlen) > 31 ? 31 : ntohs(user_data.nlen); memcpy(name, user_data.name, len); name[len] = 0; } if (password) { len = ntohs(user_data.plen) > 31 ? 31 : ntohs(user_data.plen); hl_decode(password, user_data.password, len); password[len] = 0; } memset(&user_data, 0, sizeof(user_data)); return 0; } int account_write (const char *login, const char *password, const char *name, const struct hl_access_bits *acc) { int fd, r, err; char path[MAXPATHLEN]; struct hl_user_data user_data; u_int16_t nlen, llen, plen; struct stat sb; if (strchr(login, '/')) return EPERM; if (snprintf(path, sizeof(path), "%s/%s", hxd_cfg.paths.accounts, login) == -1) { hxd_log("account_write(%s): path '%s' too long?!?", login, path); return ENAMETOOLONG; } if (stat(path, &sb)) { if (mkdir(path, hxd_cfg.permissions.account_directories)) { hxd_log("account_write(%s): mkdir(%s): %s", login, path, strerror(errno)); return errno; } } if (snprintf(path, sizeof(path), "%s/%s/UserData", hxd_cfg.paths.accounts, login) == -1) { hxd_log("account_write(%s): path '%s' too long?!?", login, path); return ENAMETOOLONG; } fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, hxd_cfg.permissions.account_files); if (fd < 0) { hxd_log("account_write(%s): open(%s) %s", login, path, strerror(errno)); return errno; } if ((nlen = strlen(name)) > 134) nlen = 134; if ((llen = strlen(login)) > 34) llen = 34; if ((plen = strlen(password)) > 32) plen = 32; memset(&user_data, 0, sizeof(user_data)); user_data.magic = htonl(0x00010000); user_data.access = *acc; user_data.nlen = htons(nlen); memcpy(user_data.name, name, nlen); user_data.llen = htons(llen); memcpy(user_data.login, login, llen); user_data.plen = htons(plen); hl_encode(user_data.password, password, plen); err = 0; if ((r = write(fd, &user_data, 734)) != 734) { err = errno; hxd_log("account_write(%s): write(%d, %x, %u) == %d: %s", login, fd, &user_data, 734, r, strerror(err)); } else { fsync(fd); } close(fd); memset(&user_data, 0, sizeof(user_data)); return err; } int account_delete (const char *login) { char path[MAXPATHLEN]; /* check if the account name has a '/' in it */ if (strchr(login, '/')) return EPERM; if (snprintf(path, sizeof(path), "%s/%s/UserData", hxd_cfg.paths.accounts, login) == -1) { hxd_log("account_delete: %s: path '%s' too long?!?", login, path); return ENAMETOOLONG; } if (unlink(path)) { hxd_log("account_delete: %s: unlink(%s): %s", login, path, strerror(errno)); return errno; } return 0; }
static void cmd_access (struct htlc_conn *htlc, u_int32_t cid, char *chatbuf) { char *p, *str; u_int32_t uid; int val; struct htlc_conn *htlcp; char errbuf[MAX_CHAT]; char nickbuf[32]; int f[2], i = 1; char abuf[HOSTLEN+1]; if (!htlc->access_extra.user_access) { cmd_denied(htlc, cid, "access"); return; } p = chatbuf; uid = atou32(p); if (!strncmp(p, "0 ", 2)) uid = 0; else if (!uid && nick_to_uid(p, &uid)) { while (*p && *p != ' ') { p++; i++; } snprintf(nickbuf, i, "%s", chatbuf); snprintf(errbuf, MAX_CHAT - 8, "no such user \"%s\"", nickbuf); cmd_err(htlc, cid, "access", errbuf); return; } htlcp = isclient(uid); if (!htlcp) { snprintf(errbuf, MAX_CHAT - 8, "no such user (uid:%u)", uid); cmd_err(htlc, cid, "access", errbuf); return; } if (!htlcp->access_extra.access_volatile) { cmd_err(htlc, cid, "access", "user cannot be modified"); return; } while (*p && *p != ' ') p++; while (*p && *p == ' ') p++; inaddr2str(abuf, &htlc->sockaddr); str = ""; while (*p) { str = p; p = strchr(p, '='); if (!p) break; *p = 0; p++; val = *p == '1' ? 1 : 0; p++; f[0] = access_extra_set(&htlcp->access_extra, str, val); f[1] = set_access_bit(&htlcp->access, str, val); if (f[0] && f[1]) { snprintf(errbuf, MAX_CHAT - 8, "unknown argument \"%s\"", str); cmd_err(htlc, cid, "access", errbuf); return; } while (*p && *p != ' ') p++; while (*p && *p == ' ') p++; } hxd_log("%s@%s:%u - %s:%u:%u:%s modified access of %s:%u:%u:%s - %s", htlc->userid, abuf, ntohs(htlc->sockaddr.SIN_PORT), htlc->name, htlc->icon, htlc->uid, htlc->login, htlcp->name, htlcp->icon, htlcp->uid, htlcp->login, str); user_access_update(htlcp); snd_user_change(htlcp); }