示例#1
0
static WJElement _WJECopy(_WJElement *parent, WJElement original, WJECopyCB copycb, void *data, const char *file, const int line)
{
	_WJElement	*l = NULL;
	_WJElement	*o;
	WJElement	c;
	char		*tmp;

	if (!(o = (_WJElement *) original)) {
		return(NULL);
	}

	if (copycb && !copycb((WJElement) parent, (WJElement) original, data, file, line)) {
		/* The consumer has rejected this item */
		return(NULL);
	}

	if ((l = _WJENew(parent, original->name, original->name ? strlen(original->name) : 0, file, line))) {
		switch ((l->pub.type = original->type)) {
			default:
			case WJR_TYPE_UNKNOWN:
			case WJR_TYPE_NULL:
				break;

			case WJR_TYPE_OBJECT:
			case WJR_TYPE_ARRAY:
				for (c = original->child; c; c = c->next) {
					_WJECopy(l, c, copycb, data, file, line);
				}
				break;

			case WJR_TYPE_STRING:
				if ((tmp = WJEString(original, NULL, WJE_GET, ""))) {
					l->value.string = MemStrdup(tmp);
					l->pub.length = original->length;
				} else {
					l->value.string = MemStrdup("");
					l->pub.length = 0;
				}
				break;

			case WJR_TYPE_NUMBER:
				l->value.number.negative		= o->value.number.negative;
				l->value.number.i				= o->value.number.i;
				l->value.number.d				= o->value.number.d;
				l->value.number.hasDecimalPoint	= o->value.number.hasDecimalPoint;
				break;

			case WJR_TYPE_TRUE:
			case WJR_TYPE_BOOL:
			case WJR_TYPE_FALSE:
				l->value.boolean = WJEBool(original, NULL, WJE_GET, FALSE);
				break;
		}
	}

	return((WJElement) l);
}
示例#2
0
文件: db.c 项目: bongo-project/bongo
/**
 * Open the Store database for this user. Updates the storedb handle of
 * the StoreClient to contain an SQLite handle to the Store.
 * \param	client	storeclient we're working for
 * \param	user	name of the store (usually, the username)
 * \return	0 on success, error codes otherwise
 */
int
StoreDBOpen(StoreClient *client, const char *user)
{
	char path[XPL_MAX_PATH +1];
	DBPoolEntry *entry = NULL;

	// try to find the handle in the pool - first, lock the pool
	XplMutexLock(StoreAgent.dbpool.lock); 
	{
		// find the entry
		entry = (DBPoolEntry *)
			BongoHashtableGet(StoreAgent.dbpool.entries, user);
		
		if (entry == NULL) {
			// no such entry, need to create one - we
			// do it here while locked
			entry = MemNew0(DBPoolEntry, 1);
			entry->user = MemStrdup(user);
			entry->clients = 0;
			
			if (BongoHashtablePutNoReplace(StoreAgent.dbpool.entries, MemStrdup(user), (void *)entry)) {
				// couldn't insert the new entry for whatever reason :(
				DBPoolEntryDelete(entry);
				goto db_fail;
			}
			
			// this bit is slow, but I can't think how we could
			// do this outside the dbpool.lock without being racy :(
			snprintf(path, XPL_MAX_PATH, "%s/%s/store.db", StoreAgent.store.rootDir, user);
			entry->handle = MsgSQLOpen(path, &client->memstack, 3000);
			if (entry->handle == NULL) {
				BongoHashtableRemove(StoreAgent.dbpool.entries, (void *)user);
				DBPoolEntryDelete(entry);
				goto db_fail;
			}
		}
		entry->clients++;
	}
	// unlock the pool on success
	XplMutexUnlock(StoreAgent.dbpool.lock);
	
	client->storedb = entry->handle;
	
	return 0;

db_fail:
	// unlock the pool on failure
	XplMutexUnlock(StoreAgent.dbpool.lock);
	return -1;
}
示例#3
0
static void
ReadEnvelope(ItipAgentClient *client, char *envelope, BongoSList **usersOut)
{
    char *ptr;
    char *delim;
    char *user;

    *usersOut = NULL;
    ptr = envelope;

    /* Queue-agent specific code.  This code just prints out the envelope */
    while (*ptr) {
        switch(*ptr++) {
        case QUEUE_RECIP_LOCAL :
        case QUEUE_RECIP_MBOX_LOCAL :
            delim = strchr(ptr, ' ');
            if (delim) {
                user = MemStrndup(ptr, delim - ptr);
            } else {
                user = MemStrdup(ptr);
            }
            *usersOut = BongoSListPrepend(*usersOut, user);
        }
        BONGO_ENVELOPE_NEXT(ptr);
    }
}
示例#4
0
EXPORT XplBool WJERename(WJElement document, const char *name)
{
	_WJElement	*current = (_WJElement *) document;
	WJElement	e;

	if (!document) {
		return(FALSE);
	}

	/* Look for any siblings with that name, and fail if found */
	if (name && document->parent) {
		for (e = document->parent->child; e; e = e->next) {
			if (e != document && !stricmp(e->name, name)) {
				return(FALSE);
			}
		}
	}

	/* Free the previous name if needed */
	if (document->name && current->_name != document->name) {
		MemRelease(&document->name);
	}

	/* Set the new name */
	if (name) {
		if (!(document->name = MemStrdup(name))) {
			return(FALSE);
		}
	} else {
		document->name = NULL;
	}

	return(TRUE);
}
示例#5
0
static char *
DisplaySubject(const char *subject)
{
    char *newSubject;
    const char *ptr;
    char *ptr2;
    int length;
    BOOL done;

    newSubject = MemStrdup(subject);
    length = strlen(newSubject);
    
    ptr = subject;

    do {
        done = TRUE;

        if ((*ptr == '[') && (newSubject[length - 1] == ']')) {
            /* Remove enclosing brackets */
            newSubject[--length] = '\0';
            ptr++;
            done = FALSE;
        } else if (*ptr == '[') {
            /* Remove bracketed prefixes (like mailing list names) */
            int depth = 0;
            do {
                if (*ptr == '[') {
                    depth++;
                } else if (*ptr == ']') {
                    depth--;
                }
                ptr++;
            } while (*ptr && (depth > 0));
            done = FALSE;
        } else if (isspace(*ptr)) {
            ptr++;
            done = FALSE;
        } else {
            unsigned int i;
            for (i = 0; i < (sizeof(stripPrefixes) / sizeof(char *)); i++) {
                char *prefix = stripPrefixes[i];
                int length = strlen(prefix);
                if (!XplStrNCaseCmp(ptr, prefix, length)) {
                    ptr += strlen(prefix);
                    done = FALSE;
                }
            }
        }
    } while (!done && *ptr != '\0');

    memmove(newSubject, ptr, strlen(ptr) + 1);
    for (ptr2 = newSubject; *ptr2 != '\0'; ptr2++) {
        if (*ptr2 == '\t') {
            *ptr2 = ' ';
        }
    }

    return newSubject;
}
示例#6
0
static void
ImportFile(const char *filename, char *destdir, FILE *tznames)
{
    BongoCalObject *cal;
    BongoJsonObject *obj = NULL;
    BongoHashtable *tzs = NULL;
    BongoHashtableIter iter;
    
    obj = ConvertFile(filename);
    if (!obj) {
        return;
    }

    cal = BongoCalObjectNew(obj);
    if (!cal) {
        return;
    }
    
    tzs = BongoCalObjectGetTimezones(cal);
    if (!tzs) {
        fprintf(stderr, "Couldn't get timezones from ical file %s\n", filename);        
        return;
    }
    
    for (BongoHashtableIterFirst(tzs, &iter);
         iter.key != NULL;
         BongoHashtableIterNext(tzs, &iter)) {
        const char *tzid = iter.key;
        char path[PATH_MAX];
        FILE *f;
        char *tz;
        char *name;
        char *p;

        snprintf(path, PATH_MAX, "%s/%s", destdir, GetFilename(tzid));

        f = fopen(path, "w");
        
        fprintf(f, "{ \"version\": { \"value\": \"2.0\" }, \"components\":[");
        
        tz = BongoJsonObjectToString(((BongoCalTimezone*)iter.value)->json);
        
        fwrite(tz, 1, strlen(tz), f);

        fprintf(f, "] }");

        name = MemStrdup(strrchr(tzid, '/') + 1);
        for (p = name; *p != '\0'; p++) {
            if (*p == '_') {
                *p = ' ';
            }
        }   

        fprintf(tznames, "%s|%s\n", name, tzid);
        
        fclose(f);
    }
}
示例#7
0
int main(int argc, char **argv)
{
	int			r		= 0;
	WJElement	doc		= NULL;
	WJReader	reader;
	int			i, a, line;
	char		*j, *x;

	MemoryManagerOpen("wjeunit");

	/* Begin tests */
	for (a = 1; a < argc; a++) {
		for (i = 0; tests[i].name && tests[i].cb; i++) {
			if (!stricmp(argv[a], tests[i].name)) {
				break;
			}
		}

		if (!tests[i].cb) {
			fprintf(stderr, "Ignoring unknown test \"%s\"\n", argv[a]);
		} else {
			/* Reopen the JSON for each test in case a test modified it */
			if ((j = MemStrdup(json))) {
				/* Correct the quotes */
				for (x = j; *x; x++) {
					if (*x == '\'') *x = '"';
				}

				// printf("JSON:\n%s\n", j);
				if ((reader = WJROpenMemDocument(j, NULL, 0))) {
					doc = WJEOpenDocument(reader, NULL, NULL, NULL);

					WJRCloseDocument(reader);
				}

				MemRelease(&j);
			}

			if (!doc) {
				fprintf(stderr, "error: Could not parse JSON document\n");
				MemoryManagerClose("wjeunit");
				return(1);
			}

			if ((line = tests[i].cb(doc))) {
				fprintf(stderr, "error: %s:%d: Failed test \"%s\"\n",
					__FILE__, line, tests[i].name);
				r = 1;
			}

			WJECloseDocument(doc);
		}
	}

	/* All done */
	MemoryManagerClose("wjeunit");
	return(r);
}
示例#8
0
static char *
NormalizeSubject(const char *subject)
{
    char *ret;
    char *p;
    
    ret = MemStrdup(subject);
    for (p = ret; *p != '\0'; p++) {
        if (isspace(*p)) {
            char *p1;
            for (p1 = p; *p1 != '\0'; p1++) {
                *p1 = *(p1 + 1);
            }
        }
    }

    return ret;
}
示例#9
0
/**
 * Start a new Query parser.
 * \param	state	New parser state - this gets initialised for future use
 * \param	query	String containing the query to parse
 * \param	max_expr	Maximum expression parts we will allow
 * \return	0 if successfully allocated, error codes otherwise
 */
int
QueryParserStart(struct parser_state *state, const char *query, int max_expr) {
	//size_t q_size;
	
	state->entries = max_expr;
	//q_size = sizeof(struct expression) * (max_expr + 1);
	
	state->start = MemNew0(struct expression, max_expr);
	if (state->start == NULL) return -1;
	state->last = state->start;
	
	state->query = MemStrdup(query);
	if (state->query == NULL) {
		MemFree(state->start);
		return -2;
	}
	
	state->query_ptr = state->query;
	
	return 0;
}
示例#10
0
BongoThreadPool *
BongoThreadPoolNew(const char *name, 
                  int stackSize,
                  int minimum,
                  int maximum,
                  int minSleep) 
{
    BongoThreadPool *pool;

    /* FIXME: do we want a private mempool for work items? */
    
    pool = MemMalloc(sizeof(BongoThreadPool));
    memset(pool, 0, sizeof(BongoThreadPool));
    
    XplMutexInit(pool->lock);
    XplOpenLocalSemaphore(pool->todo, 0);
    pool->minimum = minimum;
    pool->maximum = maximum;
    pool->minSleep = minSleep;
    pool->name = MemStrdup(name);
    pool->stackSize = stackSize;

    return pool;
}
示例#11
0
static BOOL 
ReadConfiguration(void) {
    unsigned int i;

    if (! ReadBongoConfiguration(GlobalConfig, "global")) {
        return FALSE;
    }

    if (! ReadBongoConfiguration(AVirusConfigSchema, "antivirus")) {
        return FALSE;
    }

    for (i=0; i < AVirus.clamd.hostlist->len; i++) {
        char *hostitem = g_array_index(AVirus.clamd.hostlist, char*, i);
        char *lHost = MemStrdup(hostitem);
        char *host;
        int port=AVIRUS_DEFAULT_PORT, weight=AVIRUS_DEFAULT_WEIGHT;
        ParseHost(lHost, &host, &port, &weight);
        ConnAddressPoolAddHost(&AVirus.clamd.hosts, host, port, weight);
        MemFree(lHost);
    }

    return TRUE;
}
示例#12
0
int main(int argc, char **argv)
{
	FILE		*in			= NULL;
	WJElement	doc			= NULL;
	WJElement	current		= NULL;
	int			r			= 0;
	WJReader	reader;
	char		*cmd;
	char		line[1024];

	/* Print pretty documents by default */
	wje.pretty = TRUE;

	/* Print base 10 by default */
	wje.base = 10;

	if (argc > 2) {
		/* Umm, we only allow one argument... a filename */
		usage(argv[0]);
		return(1);
	}

	MemoryManagerOpen("wje-cli");

	if (argc == 2) {
		if (!stricmp(argv[1], "--help") || !stricmp(argv[1], "-h")) {
			usage(argv[0]);
			MemoryManagerClose("wje-cli");
			return(0);
		}

		if (!(in = fopen(argv[1], "rb"))) {
			perror(NULL);
			MemoryManagerClose("wje-cli");
			return(2);
		}

		/*
			A filename was specified on the command line. Does this look
			like a script, or a JSON document?
		*/
		if (fgets(line, sizeof(line), in) &&
			!strncmp(line, "#!", 2)
		) {
			/* This looks like a script, read commands from this file */
			;
		} else {
			/* Assume it is a JSON document, rewind back to the start. */
			rewind(in);

			if ((reader = WJROpenFILEDocument(in, NULL, 0))) {
				doc = WJEOpenDocument(reader, NULL, NULL, NULL);
				WJRCloseDocument(reader);

				wje.filename = MemStrdup(argv[1]);
			}

			fclose(in);
			in = NULL;

			if (!doc) {
				fprintf(stderr, "Could not parse JSON document: %s\n", argv[1]);
			MemoryManagerClose("wje-cli");
				return(3);
			}
		}
	}

	if (!in) {
		/* Read commands from standard in */
		in = stdin;
	}

	if (!doc) {
		/* Start with an empty document if one wasn't specified */
		doc = WJEObject(NULL, NULL, WJE_SET);
	}
	current = doc;

	for (;;) {
		/* Read the next command */
		if (in == stdin && isatty(fileno(stdin))) {
			fprintf(stdout, "wje");

			if (r) {
				fprintf(stdout, " (%d)", r);
			}

			fprintf(stdout, "> ");
			fflush(stdout);
		}

		if (fgets(line, sizeof(line), in)) {
			cmd = skipspace(line);
		} else {
			cmd = NULL;
		}

		if (!cmd || !*cmd) {
			/* Ignore blank lines */
		} else {
			r = runcmd(&doc, &current, cmd);
		}
		cmd = NULL;

		if (feof(in) || wje.exiting) {
			break;
		}
	}

	if (doc) {
		WJECloseDocument(doc);
		doc = NULL;
	}

	if (in && in != stdin) {
		fclose(in);
		in = NULL;
	}

	if (wje.filename) {
		MemRelease(&wje.filename);
	}

	MemoryManagerClose("wje-cli");
	return(r);
}
示例#13
0
/**
 * Create the SQL query based on the information we've gathered so far and the queries we've parsed.
 * All queries are of the form:
 * SELECT <default rows>, <optional props> FROM storeobject, <other needed tables> WHERE <conditions> 
 *   ORDER BY <some column> [ASC|DESC] LIMIT <some amount>;
 * \param	builder	The builder instance we're using
 * \param	output	a char * where we should place the output. Caller frees? (can we do this in Finish?)
 * \return	0 on success, error codes otherwise.
 */
int
QueryBuilderCreateSQL(QueryBuilder *builder, char **output)
{
	BongoStringBuilder b;
	unsigned int i;
	int ccode;
	
	if (BongoStringBuilderInit(&b)) {
		// unable to start... ick
		return -1;
	}
	
	// basic start for all queries
	BongoStringBuilderAppend(&b, "SELECT so.guid, so.collection_guid, so.imap_uid, so.filename, so.type, so.flags, so.size, so.time_created, so.time_modified");
	
	// extra columns for additional properties
	for (i=0; i < builder->properties->len; i++) {
		StorePropInfo *prop = g_ptr_array_index(builder->properties, i);
		// only add those columns which we want returned.
		BongoStringBuilderAppend(&b, ", ");
		QueryBuilderPropertyToColumn(builder, &b, prop);
	}
	
	BongoStringBuilderAppend(&b, " FROM storeobject so");
	
	// add in the tables that we need
	if (builder->linkin_conversations) {
		BongoStringBuilderAppend(&b, " INNER JOIN conversation c ON so.guid=c.guid");
	}
	for (i=0; i < builder->links->len; i++) {
		ExtraLink *link = g_ptr_array_index(builder->links, i);
		BongoStringBuilderAppendF(&b, 
			" INNER JOIN links link_%d ON so.guid=link_%d.%s",
			i, i, link->join_column);
	}
	for (i=0; i < builder->properties->len; i++) {
		StorePropInfo *prop = g_ptr_array_index(builder->properties, i);
		// only add those columns which we want returned.
		if ((prop->table_name == NULL) && (prop->column == NULL)) {
			// FIXME. Both following queries should use bound parameters really.
			if (prop->type == STORE_PROP_EXTERNAL) {
				BongoStringBuilderAppendF(&b, 
					" LEFT JOIN properties prop_%d ON so.guid=prop_%d.guid AND prop_%d.name=\"%s\"",
					i, i, i, prop->name);
			} else {
				BongoStringBuilderAppendF(&b,
					" LEFT JOIN properties prop_%d ON so.guid=prop_%d.guid AND prop_%d.intprop=%d",
					i, i, i, (int)prop->type);
			}
		}
	}
	
	if (builder->int_query || builder->ext_query || (builder->properties->len > 0))
		BongoStringBuilderAppend(&b, " WHERE ");
	
	// add in any constraints specified on the various columns
	if (builder->int_query) {
		if (QueryExpressionToSQL(builder, builder->internal_parser.start, &b)) {
			ccode = -2;
			goto abort;
		}
	}
	if (builder->int_query && builder->ext_query) {
		BongoStringBuilderAppend(&b, " AND ");
	}
	if (builder->ext_query) {
		if (QueryExpressionToSQL(builder, builder->external_parser.start, &b)) {
			ccode = -3;
			goto abort;
		}
	}
	
	// set the order of the query if requested
	if (builder->order_prop != NULL) {
		StorePropInfo prop;
		
		char *direction = (builder->order_direction == ORDER_ASC)?
			"ASC" : "DESC";
		
		BongoStringBuilderAppendF(&b, " ORDER BY ");
		prop.name = (char *)builder->order_prop;
		StorePropertyFixup(&prop);
		
		// FIXME: do we also need the table alias here?
		BongoStringBuilderAppendF(&b, " %s %s", prop.column, direction);
	}
	
	// set any limit on the results.
	if (builder->limit_start > 0) {
		BongoStringBuilderAppendF(&b, " LIMIT %d, %d", 
			builder->limit_start, builder->limit_end);
	}
	
	BongoStringBuilderAppend(&b, ";");
	
	*output = MemStrdup(b.value);
	
	BongoStringBuilderDestroy(&b);
	
	return 0;

abort:
	BongoStringBuilderDestroy(&b);
	return ccode;
}
示例#14
0
BOOL VirusCheck(AVirusClient *client, const char *queueID, BOOL hasFlags, unsigned long msgFlags, unsigned long senderIp, char *senderUserName) {
    int ccode;
    long size;
    BOOL infected = FALSE;
    Connection *conn;

    conn = ConnAddressPoolConnect(&(AVirus.clamd.hosts), AVirus.clamd.connectionTimeout); 
    if (conn) {
	    Connection *data;
	    unsigned short port;
	    
	    ConnWrite(conn, "STREAM\r\n", strlen("STREAM\r\n"));
	    ConnFlush(conn);
	    ccode = ConnReadAnswer(conn, client->line, CONN_BUFSIZE);

	    if (!ccode || strncmp(client->line, "PORT ", strlen("PORT ")) != 0 || (port = atoi(client->line + strlen("PORT "))) == 0) {
		    ConnFree(conn);
		    return -1;
	    }
	    
	    data = ConnAlloc(TRUE);
	    if (!data) {
		    ConnFree(conn);
		    return -1;
	    }

	    memcpy(&data->socketAddress, &conn->socketAddress, sizeof(struct sockaddr_in));
	    data->socketAddress.sin_port = htons(port);
	    if (ConnConnectEx(data, NULL, 0, NULL, client->conn->trace.destination) < 0) {
		    ConnFree(conn);
		    ConnFree(data);
		    return -1;
	    }

	    size = 0;
	    if (((ccode = NMAPSendCommandF(client->conn, "QRETR %s MESSAGE\r\n", queueID)) != -1)
		&& ((ccode = NMAPReadAnswer(client->conn, client->line, CONN_BUFSIZE, TRUE)) != -1)
		&& ccode == 2023) {
		    char *ptr;

		    ptr = strchr (client->line, ' ');
		    if (ptr) {
		        *ptr = '\0';
		    }
		
		    size = atol(client->line);
	    }

	    if (size == 0) {
		    ConnFree(conn);
		    ConnFree(data);
		    return -1;
	    }

	    ccode = ConnReadToConn(client->conn, data, size);
	    
	    ConnFree(data);
	    
	    if ((ccode == -1) || ((ccode = NMAPReadAnswer(client->conn, client->line, CONN_BUFSIZE, TRUE)) != 1000)) {
            Log(LOG_DEBUG, "result: %d", ccode);
		    ConnFree(conn);
		    return -1;
	    }

	    while ((ccode = ConnReadAnswer(conn, client->line, CONN_BUFSIZE)) != -1) {
            char *ptr;

            ptr = strrchr(client->line, ' ');
            if (XplStrCaseCmp(ptr + 1, "FOUND") == 0) {
                *ptr = '\0';
                ptr = client->line + strlen("stream: ");

#if 0
                if(client->foundViruses.used == client->foundViruses.allocated) {
                    client->foundViruses.names = MemRealloc(client->foundViruses.names, sizeof(char*) * (client->foundViruses.allocated + MIME_REALLOC_SIZE));
                }
                client->foundViruses.names[client->foundViruses.used++] = MemStrdup(ptr);

                XplSafeIncrement(AVirus.stats.viruses);
#endif
                infected = TRUE;
            }
	    }
	    ConnFree(conn);
    }

    return infected;
}
示例#15
0
/** Callback function.  Whenever a new message arrives in the queue that
 * this agent has registered itself on, NMAP calls back to this function
 * and provides the agent with the unique hash of the new message.  The 
 * connection then remains open, and NMAP goes into slave state while
 * the agent does all necessary processing.
 * \param client The connection initiated by the NMAP store.
 */
static __inline int 
ProcessConnection(void *clientp, Connection *conn)
{
    AVirusClient *client = clientp;
    unsigned char *envelopeLine;
    BOOL hasFlags = FALSE;
    unsigned long msgFlags = 0;
    int ccode;

    unsigned long source = 0;
    unsigned char *ptr;
    char *ptr2;
    char *senderUserName = NULL;
    BOOL blocked = FALSE;

    client->conn = conn;
    ccode = BongoQueueAgentHandshake(client->conn, client->line, client->qID, &client->envelopeLength, &client->messageLength);
    client->envelope = BongoQueueAgentReadEnvelope(client->conn, client->line, client->envelopeLength, &client->envelopeLines);

    envelopeLine = client->envelope;
    while (*envelopeLine) {
        switch(*envelopeLine) {
        case QUEUE_FROM:
            if ((ptr = strchr(envelopeLine, ' '))) {
                ptr++;
                if ((ptr2 = strchr(ptr, ' '))) {
                    *ptr2 = '\0';
                    if (strcmp(ptr, "-") != 0) {
                        senderUserName = MemStrdup(ptr);
                    }
                    *ptr2 = ' ';
                }
            }
            break;
        case QUEUE_FLAGS:
            hasFlags = TRUE;
            msgFlags = atol(envelopeLine+1);
            break;
        case QUEUE_ADDRESS:
            source = atol(envelopeLine+1);
            break;
        }
        BONGO_ENVELOPE_NEXT(envelopeLine);
    }

    blocked = VirusCheck(client, client->qID, hasFlags, msgFlags, source, senderUserName);

    if (blocked == TRUE) {
        /* we found a virus of some kind.  drop the mail */
        Log(LOG_DEBUG, "Queue ID %s Infected and blocked", client->qID);
        ConnWriteF(client->conn, "QDELE %s\r\n", client->qID);
        ConnFlush(client->conn);

        /* the rules here are:
         * 1) notify the sender if they are auth'd
         * 2) notify postmaster if the flag for such is set */
        /* are we auth'd? */
        if (senderUserName) {
            /* create the bounce to the sender */
            BounceToSender(client, senderUserName);
        }

        if (AVirus.flags & AVIRUS_FLAG_NOTIFY_POSTMASTER) {
            /* create the bounce to the postmaster */
            BounceToPostmaster(client, senderUserName);
        }
    } else {
        Log(LOG_DEBUG, "Queue ID %s clean", client->qID);
        NMAPSendCommand(client->conn, "QDONE\r\n", 7);
        ccode = NMAPReadAnswer(client->conn, client->line, CONN_BUFSIZE, FALSE);
    }

    if (client->envelope) {
        MemFree(client->envelope);
        client->envelope = NULL;
    }

    if (senderUserName) {
        MemFree(senderUserName);
    }

    return(0);
}