Index: daemon/dispatch.cc =================================================================== --- daemon/dispatch.cc (revision 332) +++ daemon/dispatch.cc (working copy) @@ -74,7 +74,7 @@ ENTRY(adduser3, 5, -1, true), ENTRY(autoresponse, 4, 5, true), ENTRY(deluser, 3, 3, true), - ENTRY(stat, 3, 3, true), + ENTRY(stat, 3, 4, true), ENTRY(listdomain, 2, 2, false), #ifdef TEST_DAEMON ENTRY(echo, 0, -1, false), // For testing purposes only Index: daemon/stat.cc =================================================================== --- daemon/stat.cc (revision 332) +++ daemon/stat.cc (working copy) @@ -35,7 +35,9 @@ count_size unseen_new; count_size unseen; count_size seen; - + bool recurse; + unsigned long depth; + mystring rep() const; }; @@ -55,6 +57,7 @@ DIR* dir; struct dirent* curr; struct stat statbuf; + bool m_isdir, m_ismaildir, m_isnew, m_unseen; public: statdir(const mystring& dirname); ~statdir() { close(); } @@ -65,6 +68,10 @@ const char* currname() const { return curr->d_name; } void operator++() { advance(); } void advance(); + bool isdir() const { return m_isdir; } + bool ismaildir() const { return m_ismaildir; } + bool isnew() const { return m_isnew; } + bool unseen() const { return m_unseen; } }; statdir::statdir(const mystring& dirname) @@ -83,12 +90,10 @@ void statdir::advance() { + bool ignore = false; + if(dir) { - while((curr = readdir(dir)) != 0) { - if(curr->d_name[0] == '.') - continue; - break; - } + curr = readdir(dir); } if(!curr) close(); @@ -96,57 +101,75 @@ mystring fullpath = path + "/" + curr->d_name; if(stat(fullpath.c_str(), &statbuf) == -1) close(); - } -} - -bool stat_new_dir(const mystring& basename, stats& stats) -{ - statdir dir(basename + "/new"); - if(!dir) - return false; - while(dir) { - if(S_ISREG(dir->st_mode)) { - ++stats.unseen_new.count; - stats.unseen_new.size += dir->st_blocks * 512; + + m_isdir = m_isnew = false; + if(S_ISDIR(statbuf.st_mode)) { + m_isdir = true; + m_isnew = !strcmp(curr->d_name, "new"); + if(!m_isnew) + ignore = !strcmp(curr->d_name, ".") || + !strcmp(curr->d_name, "..") || + !strcmp(curr->d_name, "tmp"); + m_ismaildir = m_isnew || + (!ignore && !strcmp(curr->d_name, "cur")); + } + else if(!S_ISREG(statbuf.st_mode)) + ignore = true; + + m_unseen = true; + const char* colon = strchr(curr->d_name, ':'); + if(colon) { + ++colon; + if(*colon++ == '2' && *colon++ == ',' && !strchr(colon, 'S')) + m_unseen = false; } - ++dir; } - return true; + if (ignore) + advance(); } -bool stat_cur_dir(const mystring& basename, stats& stats) +bool stat_dir(const mystring& basename, stats& stats, bool isnew = false) { - statdir dir(basename + "/cur"); + statdir dir(basename); + if(!dir) - return false; + return true; + while(dir) { - if(S_ISREG(dir->st_mode)) { - count_size* stat = &stats.unseen; - const char* colon = strchr(dir.currname(), ':'); - if(colon) { - ++colon; - if(*colon++ == '2' && *colon++ == ',' && !strchr(colon, 'S')) + if(!dir.isdir()) { + count_size* stat = &stats.unseen_new; + if(!isnew) { + if (dir.unseen()) + stat = &stats.unseen; + else stat = &stats.seen; } ++stat->count; stat->size += dir->st_blocks * 512; } + else if(stats.recurse || (stats.depth < 1 && dir.ismaildir())) { + stats.depth++; + if (!stat_dir(basename + "/" + dir.currname(), stats, dir.isnew())) + return false; + stats.depth--; + } ++dir; } return true; } -bool stat_dir(const mystring& basename, stats& stats) -{ - return stat_new_dir(basename, stats) && - stat_cur_dir(basename, stats); -} CMD(stat) - // Usage: stat baseuser-virtuser pass + // Usage: stat baseuser-virtuser pass [recurse] { mystring user = args[0]; mystring pass = args[1]; + args[1] = LOG_PASSWORD; + bool recurse = 0; + if(args.count() == 3) { + recurse = (args[2] == "recurse"); + } + logcommand(args); pwentry* pw; vpwentry* vpw; @@ -158,6 +181,8 @@ mystring dirname = pw->home + "/" + vpw->directory; stats stats; + stats.depth = 0; + stats.recurse = recurse; if(!stat_dir(dirname, stats)) RETURN(err, "Failed to stat maildir");