#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include "assert.h"
#include "global.h"
#include "exitcodes.h"
#include "hash.h"
#include "imap_err.h"
#include "mailbox.h"
#include "mpool.h"
#include "mboxlist.h"
#include "convert_code.h"
#include "seen.h"
#include "xmalloc.h"
const int config_need_data = 0;
#define DB (config_seenstate_db)
#define SUBDB (config_subscription_db)
extern int optind;
extern char *optarg;
struct arb_mailbox_data {
int readers;
int subscribers;
};
struct mpool *arb_pool;
hash_table mailbox_table, mboxname_table;
time_t report_time, prune_time = 0;
int code = 0;
int dosubs = 1;
static struct namespace arb_namespace;
void usage(void);
void run_users(void);
void make_report(char *key, void *data, void *rock);
void process_seen(const char *path);
void process_subs(const char *path);
int do_mailbox(const char *name, int matchlen, int maycreate, void *rock);
int main(int argc,char **argv)
{
int opt, r;
int report_days = 30;
int prune_months = 0;
char pattern[MAX_MAILBOX_NAME+1];
char *alt_config = NULL;
strcpy(pattern, "*");
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
while ((opt = getopt(argc, argv, "C:od:p:")) != EOF) {
switch (opt) {
case 'C':
alt_config = optarg;
break;
case 'd':
report_days = atoi(optarg);
if (report_days <= 0) usage();
break;
case 'o':
dosubs = 0;
break;
case 'p':
prune_months = atoi(optarg);
if (prune_months <= 0) usage();
break;
default:
usage();
}
}
cyrus_init(alt_config, "arbitron", 0);
mboxlist_init(0);
mboxlist_open(NULL);
if ((r = mboxname_init_namespace(&arb_namespace, 1)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
if (optind != argc) strlcpy(pattern, argv[optind], sizeof(pattern));
report_time = time(0) - (report_days*60*60*24);
if (prune_months) {
prune_time = time(0) - (prune_months*60*60*24*31);
}
arb_pool = new_mpool(0);
construct_hash_table(&mailbox_table, 2047, 1);
construct_hash_table(&mboxname_table, 2047, 1);
mboxname_hiersep_tointernal(&arb_namespace, pattern, 0);
fprintf(stderr, "Loading Mailboxes...");
(*arb_namespace.mboxlist_findall)(&arb_namespace, pattern, 1, 0, 0,
do_mailbox, NULL);
fprintf(stderr, "Done\nLoading Users");
run_users();
fprintf(stderr, "\n");
hash_enumerate(&mboxname_table, make_report, NULL);
free_hash_table(&mailbox_table, NULL);
free_hash_table(&mboxname_table, NULL);
free_mpool(arb_pool);
mboxlist_close();
mboxlist_done();
cyrus_done();
return code;
}
void usage(void)
{
fprintf(stderr,
"usage: arbitron [-o] [-C alt_config] [-d days]"
" [-p months] [mboxpattern]\n");
exit(EC_USAGE);
}
int do_mailbox(const char *name, int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock __attribute__((unused)))
{
int r;
struct mailbox mbox;
r = mailbox_open_header(name, NULL, &mbox);
if(!r) {
struct arb_mailbox_data *d = mpool_malloc(arb_pool,
sizeof(struct arb_mailbox_data));
d->readers = 0;
d->subscribers = 0;
hash_insert(mbox.uniqueid, d, &mailbox_table);
hash_insert(name, d, &mboxname_table);
mailbox_close(&mbox);
}
return 0;
}
void run_users()
{
char prefix[MAX_MAILBOX_PATH+1],path[MAX_MAILBOX_PATH+1],
file[MAX_MAILBOX_PATH+1];
DIR *dirp, *dirq;
struct dirent *dirent1, *dirent2;
snprintf(prefix, sizeof(prefix), "%s%s", config_dir, FNAME_USERDIR);
dirp = opendir(prefix);
if(!dirp) {
fatal("can't open user directory", EC_SOFTWARE);
}
while((dirent1 = readdir(dirp)) != NULL) {
if(!strcmp(dirent1->d_name, ".") || !strcmp(dirent1->d_name,"..")) {
continue;
}
snprintf(path, sizeof(path), "%s%s", prefix, dirent1->d_name);
dirq = opendir(path);
if(dirq) {
fprintf(stderr, ".");
while(dirq && ((dirent2 = readdir(dirq)) != NULL)) {
size_t len;
if(!strcmp(dirent2->d_name, ".") ||
!strcmp(dirent2->d_name,"..")) {
continue;
}
len = strlen(dirent2->d_name);
if(len > 4) {
snprintf(file, sizeof(file),
"%s/%s", path, dirent2->d_name);
if(len > 5 &&
!strcmp(dirent2->d_name + len - 5, ".seen")) {
process_seen(file);
} else if (dosubs &&
!strcmp(dirent2->d_name + len - 4, ".sub")) {
process_subs(file);
}
}
}
closedir(dirq);
}
}
closedir(dirp);
}
static int process_user_cb(void *rockp,
const char *key, int keylen,
const char *tmpdata __attribute__((unused)),
int tmpdatalen __attribute__((unused)))
{
DB->delete((struct db *)rockp, key, keylen, NULL, 0);
return 0;
}
static int process_user_p(void *rockp __attribute__((unused)),
const char *key,
int keylen,
const char *data,
int datalen __attribute__((unused)))
{
int ret = 0;
long version, lastread;
char *p;
char buf[64];
struct arb_mailbox_data *mbox;
version = strtol(data, &p, 10); data = p;
lastread = strtol(data, &p, 10); data = p;
memcpy(buf, key, keylen);
buf[keylen] = '\0';
mbox = hash_lookup(buf, &mailbox_table);
if(mbox && lastread >= report_time) {
mbox->readers++;
}
if(lastread < prune_time) {
ret = 1;
}
return ret;
}
void process_seen(const char *path)
{
int r;
struct db *tmp = NULL;
r = DB->open(path, 0, &tmp);
if(r) goto done;
DB->foreach(tmp, "", 0, process_user_p, process_user_cb, tmp, NULL);
done:
if(tmp) DB->close(tmp);
}
static int process_subs_cb(void *rockp __attribute__((unused)),
const char *key __attribute__((unused)),
int keylen __attribute__((unused)),
const char *tmpdata __attribute__((unused)),
int tmpdatalen __attribute__((unused)))
{
return 0;
}
static int process_subs_p(void *rockp __attribute__((unused)),
const char *key, int keylen,
const char *tmpdata __attribute__((unused)),
int tmpdatalen __attribute__((unused)))
{
struct arb_mailbox_data *mbox;
char buf[MAX_MAILBOX_NAME+1];
memcpy(buf, key, keylen);
buf[keylen] = '\0';
mbox = hash_lookup(buf, &mboxname_table);
if(mbox) {
mbox->subscribers++;
}
return 0;
}
void process_subs(const char *path)
{
int r;
struct db *tmp = NULL;
r = SUBDB->open(path, 0, &tmp);
if(r) goto done;
SUBDB->foreach(tmp, "", 0, process_subs_p, process_subs_cb, NULL, NULL);
done:
if(tmp) SUBDB->close(tmp);
}
void make_report(char *key, void *data, void *rock __attribute__((unused)))
{
struct arb_mailbox_data *mbox = (struct arb_mailbox_data *)data;
if(!strncasecmp(key, "user.", 5) && mbox->readers <= 1)
return;
mboxname_hiersep_toexternal(&arb_namespace, key, 0);
printf("%s %d", key, mbox->readers);
if(dosubs) printf(" %d", mbox->subscribers);
printf("\n");
}