Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120836488
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/netnews/remotepurge.c b/netnews/remotepurge.c
index 9941dda6d..f4837ad2d 100644
--- a/netnews/remotepurge.c
+++ b/netnews/remotepurge.c
@@ -1,731 +1,730 @@
/* Remotely purge old/too big articles
*
* Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sysexits.h>
#include <syslog.h>
#include <pwd.h>
#include "prot.h"
#include "lib/times.h"
#include "imclient.h"
#include "util.h"
#include "xmalloc.h"
#include "readconfig.h"
#define SECS_IN_DAY (24*60*60)
#define NOTFINISHED 0
#define IMAP_OK 1
#define IMAP_NO 2
#define IMAP_BAD 3
#define IMAP_EOF 4
/* for statistical purposes */
typedef struct mbox_stats_s {
int total; /* total including those deleted */
int total_bytes;
int deleted;
int deleted_bytes;
} mbox_stats_t;
typedef struct uid_list_s {
unsigned long *list;
int allocsize;
int size;
} uid_list_t;
/* globals for callback functions */
static int days = -1;
int size = -1;
static int current_mbox_exists = 0;
static int verbose = 0;
static int noop = 0;
static char *username = NULL;
//char *authname = NULL;
static char *realm = NULL;
static struct imclient *imclient_conn;
static int cmd_done;
static char *cmd_resp = NULL;
static FILE *configstream;
static void spew(int level, const char *fmt, ...)
{
va_list ap;
char buf[1024];
if (verbose < level) return;
va_start(ap, fmt);
vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
if (verbose) {
printf("%s\n", buf);
}
syslog(LOG_DEBUG, "%s", buf);
}
/* libcyrus makes us define this */
EXPORTED void fatal(const char *s, int code)
{
if (cmd_resp) {
syslog(LOG_ERR, "fatal error: %s (%s)", s, cmd_resp);
fprintf(stderr, "fatal error: %s (%s)\n", s, cmd_resp);
} else {
syslog(LOG_ERR, "fatal error: %s", s);
fprintf(stderr, "fatal error: %s\n", s);
}
exit(code);
}
/***********************
* Parse a mech list of the form: ... AUTH=foo AUTH=bar ...
*
* Return: string with mechs separated by spaces
*
***********************/
typedef struct capabilities_s {
char *mechs;
/* 0 = false; 1 = true */
int starttls;
int logindisabled;
} capabilities_t;
static capabilities_t *parsecapabilitylist(char *str)
{
char *tmp;
int num=0;
capabilities_t *ret=(capabilities_t *) xmalloc(sizeof(capabilities_t));
ret->mechs = (char *)xmalloc(strlen(str)+1);
ret->starttls=0;
ret->logindisabled=0;
/* check for stattls */
if (strstr(str,"STARTTLS")!=NULL) {
ret->starttls=1;
}
/* check for login being disabled */
if (strstr(str,"LOGINDISABLED")!=NULL) {
ret->logindisabled=1;
}
strcpy(ret->mechs,"");
while ((tmp=strstr(str,"AUTH="))!=NULL) {
char *end=tmp+5;
tmp+=5;
while(((*end)!=' ') && ((*end)!='\0'))
end++;
(*end)='\0';
/* add entry to list */
if (num>0)
strcat(ret->mechs," ");
strcat(ret->mechs, tmp);
num++;
/* reset the string */
str=end+1;
}
return ret;
}
/*
* IMAP command completion callback
*/
static void callback_capability(struct imclient *imclient,
void *rock,
struct imclient_reply *reply)
{
(void)imclient;
char *s;
capabilities_t **caps = (capabilities_t **) rock;
s = reply->text;
*caps = parsecapabilitylist(s);
}
/*
* IMAP command completion callback
*/
static void
callback_finish(struct imclient *imclient,
void *rock,
struct imclient_reply *reply)
{
(void)imclient; (void)rock;
if (!strcmp(reply->keyword, "OK")) {
cmd_done = IMAP_OK;
} else if (!strcmp(reply->keyword, "NO")) {
cmd_resp = reply->text;
cmd_done = IMAP_NO;
}
else if (!strcmp(reply->keyword, "BAD")) {
cmd_resp = reply->text;
cmd_done = IMAP_BAD;
}
else if (!strcmp(reply->keyword, "EOF")) {
syslog(LOG_ERR, "connection closed prematurely");
cmd_done = IMAP_EOF;
}
else {
printf("Huh?\n");
cmd_done = IMAP_BAD;
}
}
/*
* Callback to deal with untagged LIST/LSUB data
*/
extern void
callback_list(struct imclient *imclient,
void *rock,
struct imclient_reply *reply);
/*
void print_stats(mbox_stats_t *stats)
{
syslog(LOG_INFO, "total messages considered %d deleted %d",
stats->total, stats->deleted);
printf("total messages \t\t %d\n",stats->total);
printf("deleted messages \t\t %d\n",stats->deleted);
printf("remaining messages\t\t %d\n\n",stats->total - stats->deleted);
}
*/
static void
callback_exists(struct imclient *imclient,
void *rock,
struct imclient_reply *reply)
{
(void)imclient; (void)rock;
current_mbox_exists = reply->msgno;
}
static void
callback_search(struct imclient *imclient,
void *rock,
struct imclient_reply *reply)
{
(void)imclient;
uid_list_t *uids = (uid_list_t *) rock;
const char *s;
uint32_t num;
s = reply->text;
while (Uisdigit(*s)) {
if (parseuint32(s, &s, &num)) break;
if (uids->size >= uids->allocsize)
{
if (uids->allocsize) uids->allocsize *= 2;
else uids->allocsize = 250;
uids->list = xrealloc(uids->list,
sizeof(unsigned long) * uids->allocsize);
}
uids->list[uids->size] = num;
uids->size++;
if (*s == '\0') break;
s++;
}
}
static int send_delete(const char *mbox, const char *uidlist)
{
imclient_send(imclient_conn, callback_finish, imclient_conn,
"UID STORE %a +FLAGS.SILENT (\\Deleted)", uidlist);
cmd_done = NOTFINISHED;
while (cmd_done == NOTFINISHED) {
imclient_processoneevent(imclient_conn);
}
if (cmd_done == IMAP_OK) return 0;
else if (cmd_done == IMAP_NO) {
syslog(LOG_ERR, "%s can't mark messages deleted: %s",
mbox, cmd_resp ? cmd_resp : "");
return -1;
}
else fatal("marking message deleted", EX_TEMPFAIL);
}
static void mark_all_deleted(const char *mbox, uid_list_t *list, mbox_stats_t *stats)
{
int i;
char buf[1024];
int pos;
unsigned long run_start;
int first_time;
unsigned long *A = list->list;
int r;
if (list->size == 0) return;
/* we send blocks of 500 or so characters */
i = 0;
pos = 0; first_time = 1;
run_start = A[i++];
r = 0;
for (; i < list->size && r == 0; i++) {
if (A[i] == A[i-1] + 1)
continue; /* continue this run */
if (first_time) {
first_time = 0;
} else {
buf[pos++] = ',';
}
if (run_start != A[i-1]) {
/* run contains more than one entry */
pos += sprintf(buf + pos, "%lu:%lu", run_start, A[i-1]);
} else {
/* singleton */
pos += sprintf(buf + pos, "%lu", A[i-1]);
}
if (pos > 500) {
r = send_delete(mbox, buf);
pos = 0; first_time = 1;
}
run_start = A[i];
}
if (!r) {
/* handle the last entry */
if (!first_time) {
buf[pos++] = ',';
}
if (run_start != A[i-1]) {
sprintf(buf + pos, "%lu:%lu", run_start, A[i-1]);
} else {
sprintf(buf + pos, "%lu", A[i-1]);
}
/* send out the last one */
send_delete(mbox, buf);
stats->deleted += list->size;
}
}
/* we don't check what comes in on matchlen and category, should we? */
static int purge_me(char *name, time_t when)
{
mbox_stats_t stats;
char search_string[200];
static uid_list_t uidlist;
struct tm *my_tm;
if (when == 0) return 0;
my_tm = gmtime(&when);
snprintf(search_string,sizeof(search_string),
"BEFORE %d-%s-%d",
my_tm->tm_mday,
monthname[my_tm->tm_mon],
1900+my_tm->tm_year);
if (noop) {
printf("%s: %s\n", name, search_string);
return 0;
}
memset(&stats, '\0', sizeof(mbox_stats_t));
spew(2, "%s selecting", name);
/* select mailbox */
imclient_addcallback(imclient_conn,
"EXISTS", CALLBACK_NUMBERED, callback_exists,
(void *)0, (char *)0);
imclient_send(imclient_conn, callback_finish, (void *)imclient_conn,
"%a %s", "SELECT", name);
cmd_done = NOTFINISHED;
while (cmd_done == NOTFINISHED) {
imclient_processoneevent(imclient_conn);
}
spew(2, "%s selecting", name);
if (cmd_done == IMAP_NO) {
syslog(LOG_ERR, "unable to select %s: %s", name, cmd_resp);
return 0;
} else if (cmd_done != IMAP_OK) {
fatal("selecting mailbox", EX_TEMPFAIL);
}
stats.total = current_mbox_exists;
spew(2, "%s exists %d", name, current_mbox_exists);
/* Only search if there are actually messages in the mailbox! */
if(current_mbox_exists) {
/* make out list of uids */
uidlist.size = 0; /* reset to 0 */
spew(3, "%s searching for messages %s", name, search_string);
imclient_addcallback(imclient_conn,
"SEARCH", 0, callback_search,
(void *)&uidlist, (char *)0);
imclient_send(imclient_conn, callback_finish, (void *)imclient_conn,
"UID SEARCH %a", search_string);
cmd_done = NOTFINISHED;
while (cmd_done == NOTFINISHED) {
imclient_processoneevent(imclient_conn);
}
if (cmd_done != IMAP_OK) {
fatal("UID Search failed", EX_TEMPFAIL);
}
if (uidlist.size > 0) {
mark_all_deleted(name, &uidlist, &stats);
}
}
- after_search:
/* close mailbox */
imclient_send(imclient_conn, callback_finish, (void *)imclient_conn,
"CLOSE");
cmd_done = NOTFINISHED;
while (cmd_done == NOTFINISHED) {
imclient_processoneevent(imclient_conn);
}
if (cmd_done != IMAP_OK) {
fatal("unable to CLOSE mailbox", EX_TEMPFAIL);
}
if(current_mbox_exists) {
spew(1, "%s exists %d deleted %d",
name, current_mbox_exists, uidlist.size);
} else {
spew(1, "%s exists %d (skipped)",
name, current_mbox_exists, uidlist.size);
}
return 0;
}
static int purge_all(void)
{
int num = 0;
int ret = 0;
while (ret == 0) {
ret = ExpireExists(num);
if (ret == 0)
purge_me(GetExpireName(num), GetExpireTime(num));
num++;
}
return 0;
}
static void do_list(char *matchstr)
{
imclient_send(imclient_conn, callback_finish, (void *)imclient_conn,
"%a %s %s", "LIST", "*",
matchstr);
cmd_done = NOTFINISHED;
while (cmd_done == NOTFINISHED) {
imclient_processoneevent(imclient_conn);
}
if (cmd_done!=IMAP_OK) fatal("unable to LIST mailboxes", EX_TEMPFAIL);
}
/*
* What we were given on the command line might just be a path or might not have an extension etc...
*/
static char *parseconfigpath(char *str)
{
char *ret;
/* if it ends with a '/' add expire.ctl */
if (str[strlen(str)-1] == '/')
{
ret = (char *) xmalloc(strlen(str)+strlen("expire.ctl")+1);
strcpy(ret,str);
strcat(ret,"expire.ctl");
return ret;
}
return str;
}
static void remote_purge(char *configpath, char **matches)
{
char *name;
imclient_addcallback(imclient_conn,
"LIST", 0, callback_list,
(void *)0, (char *)0);
if (matches[0]==NULL) {
syslog(LOG_WARNING, "matching all mailboxes for possible purge");
spew(1, "matching all mailboxes");
do_list("*");
} else {
while (matches[0]!=NULL) {
spew(0, "matching %s", matches[0]);
do_list(matches[0]);
matches++;
}
}
spew(1, "completed list");
if (configpath!=NULL) {
name = parseconfigpath(configpath);
configstream = fopen(name,"r");
if (configstream == NULL)
fatal("unable to open config file", EX_CONFIG);
EXPreadfile(configstream);
/* ret val */
} else {
artificial_matchall(days);
}
purge_all();
}
/* didn't give correct parameters; let's exit */
static void usage(void)
{
printf("Usage: remotepurge [options] hostname [[match1] ... ]\n");
printf(" -p port : port to use\n");
printf(" -k # : minimum protection layer required\n");
printf(" -l # : max protection layer (0=none; 1=integrity; etc)\n");
printf(" -u user : authorization name to use\n");
printf(" -v : verbose\n");
printf(" -n : don't actually purge\n");
printf(" -m mech : SASL mechanism to use (\"login\" for LOGIN)\n");
printf(" -r realm : realm\n");
printf(" -e expire.ctl : use expire.ctl file (specify full path)\n");
printf(" -d days : purge all message <days> old\n");
exit(EX_USAGE);
}
int main(int argc, char **argv)
{
char *mechanism=NULL;
char servername[1024];
char *expirectlfile = NULL;
int maxssf = 128;
int minssf = 0;
int c;
char *tls_keyfile="";(void)tls_keyfile;
char *port = "imap";
int dotls=0;
int r;
capabilities_t *capabilitylist;
/* look at all the extra args */
while ((c = getopt(argc, argv, "d:vne:k:l:p:u:a:m:t:")) != EOF)
switch (c) {
case 'd':
days = atoi(optarg);
break;
case 'e':
expirectlfile = optarg;
break;
case 'v':
verbose++;
break;
case 'k':
minssf=atoi(optarg);
break;
case 'l':
maxssf=atoi(optarg);
break;
case 'p':
port = optarg;
break;
case 'u':
username = optarg;
break;
case 'm':
mechanism=optarg;
break;
case 'r':
realm=optarg;
break;
case 't':
dotls=1;
tls_keyfile=optarg;
break;
case 'n':
noop = 1;
break;
case '?':
default:
usage();
break;
}
(void)dotls;(void)mechanism;
if (optind >= argc) usage();
if ((days==-1) && (expirectlfile == NULL))
{
printf("Must specify expire.ctl file OR days old OR bytes large\n\n");
usage();
}
/* next to last arg is server name */
strncpy(servername, argv[optind], 1023);
r = imclient_connect (&imclient_conn, servername, port, NULL);
if (r!=0) {
fatal("imclient_connect()", EX_TEMPFAIL);
}
spew(0, "connected");
/* get capabilities */
imclient_addcallback(imclient_conn, "CAPABILITY", 0,
callback_capability, (void *) &capabilitylist,
(char *) 0);
imclient_send(imclient_conn, callback_finish, NULL,
"CAPABILITY");
cmd_done = 0;
while (cmd_done == 0) {
imclient_processoneevent(imclient_conn);
}
r = imclient_authenticate(imclient_conn,
capabilitylist->mechs,
"imap",
username,
minssf,
maxssf);
if (r!=0) {
fatal("imclient_authenticate()\n", EX_CONFIG);
}
spew(0, "authenticated");
readconfig_init();
remote_purge(expirectlfile, argv+(optind+1));
spew(0, "done");
exit(0);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Apr 24, 1:31 PM (14 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18896718
Default Alt Text
(18 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline