Skip to content
Snippets Groups Projects
  • Rob Swindell's avatar
    7cb684c3
    Make the fully-supported (lib)archive file types/formats configurable · 7cb684c3
    Rob Swindell authored
    Before now, the archive formats/types (e.g. for creating QWK/REP packets or
    temp file download archives) supported by libarchive have been *hard-coded* in
    Synchronet to "zip, 7z, tgz", but if you really want to support the creation
    of more archive formats using the internal (libarchive) support in Synchronet,
    and your system supports it (e.g. confirmed using archive.js), you can add
    those types to this list or remove any that are problematic.
    
    This list does not impact the archive types that can be viewed or extracted
    using libarchive.
    7cb684c3
    History
    Make the fully-supported (lib)archive file types/formats configurable
    Rob Swindell authored
    Before now, the archive formats/types (e.g. for creating QWK/REP packets or
    temp file download archives) supported by libarchive have been *hard-coded* in
    Synchronet to "zip, 7z, tgz", but if you really want to support the creation
    of more archive formats using the internal (libarchive) support in Synchronet,
    and your system supports it (e.g. confirmed using archive.js), you can add
    those types to this list or remove any that are problematic.
    
    This list does not impact the archive types that can be viewed or extracted
    using libarchive.
pack_rep.cpp 12.50 KiB
/* Synchronet QWK reply (REP) packet creation routine */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "qwk.h"
#include "filedat.h"

/****************************************************************************/
/* Creates an REP packet for upload to QWK hub 'hubnum'.                    */
/* Returns 1 if successful, 0 if not.										*/
/****************************************************************************/
bool sbbs_t::pack_rep(uint hubnum)
{
	char        str[MAX_PATH + 1];
	char        tmp[MAX_PATH + 1], tmp2[MAX_PATH + 1];
	char        hubid_upper[LEN_QWKID + 1];
	char        hubid_lower[LEN_QWKID + 1];
	char        error[256];
	int         mode;
	const char* fmode;
	int         i, j, k;
	long        msgcnt, submsgs, packedmail, netfiles = 0, deleted;
	uint32_t    u;
	uint32_t    posts;
	uint32_t    mailmsgs;
	ulong       msgs;
	uint32_t    last;
	post_t*     post;
	mail_t*     mail;
	FILE*       rep;
	FILE*       hdrs = NULL;
	FILE*       voting = NULL;
	DIR*        dir;
	DIRENT*     dirent;
	smbmsg_t    msg;

	msgcnt = 0L;
	delfiles(cfg.temp_dir, ALLFILES);

	SAFECOPY(hubid_upper, cfg.qhub[hubnum]->id);
	strupr(hubid_upper);
	SAFECOPY(hubid_lower, cfg.qhub[hubnum]->id);
	strlwr(hubid_lower);

	SAFEPRINTF2(str, "%s%s.REP", cfg.data_dir, hubid_upper);
	if (fexistcase(str)) {
		lprintf(LOG_INFO, "Updating %s", str);
		long file_count = extract_files_from_archive(str
		                                             , /* outdir: */ cfg.temp_dir
		                                             , /* allowed_filename_chars: */ NULL /* any */
		                                             , /* with_path: */ false
		                                             , /* overwrite: */ true
		                                             , /* max_files: */ 0 /* unlimited */
		                                             , /* file_list: */ NULL /* all files */
		                                             , /* recurse: */ false
		                                             , error, sizeof(error));
		if (file_count > 0) {
			lprintf(LOG_DEBUG, "libarchive extracted %lu files from %s", file_count, str);
		} else {
			if (*error)
				lprintf(LOG_NOTICE, "libarchive error (%s) extracting %s", error, str);
			if (*cfg.qhub[hubnum]->unpack)
				external(cmdstr(cfg.qhub[hubnum]->unpack, str, ALLFILES, NULL), EX_OFFLINE);
		}
	} else
		lprintf(LOG_INFO, "Creating %s", str);
	/*************************************************/
	/* Create SYSID.MSG, write header and leave open */
	/*************************************************/
	SAFEPRINTF2(str, "%s%s.MSG", cfg.temp_dir, hubid_upper);
	if (fexistcase(str))
		fmode = "r+b";
	else
		fmode = "w+b";
	if ((rep = fopen(str, fmode)) == NULL) {
		errormsg(WHERE, ERR_CREATE, str, 0, fmode);
		return false;
	}
	fseek(rep, 0, SEEK_END);
	if (ftell(rep) < 1) {                        /* New REP packet */
		fprintf(rep, "%-*s"
		        , QWK_BLOCK_LEN, hubid_upper);  /* So write header */
	}
	if (!(cfg.qhub[hubnum]->misc & QHUB_NOHEADERS)) {
		SAFEPRINTF(str, "%sHEADERS.DAT", cfg.temp_dir);
		fexistcase(str);
		if ((hdrs = fopen(str, "a")) == NULL)
			errormsg(WHERE, ERR_CREATE, str, 0);
	}
	if (!(cfg.qhub[hubnum]->misc & QHUB_NOVOTING)) {
		SAFEPRINTF(str, "%sVOTING.DAT", cfg.temp_dir);
		fexistcase(str);
		if ((voting = fopen(str, "a")) == NULL)
			errormsg(WHERE, ERR_CREATE, str, 0);
	}
	/*********************/
	/* Pack new messages */
	/*********************/
	SAFEPRINTF(smb.file, "%smail", cfg.data_dir);
	smb.retry_time = cfg.smb_retry_time;
	smb.subnum = INVALID_SUB;
	if ((i = smb_open(&smb)) != 0) {
		fclose(rep);
		if (hdrs != NULL)
			fclose(hdrs);
		if (voting != NULL)
			fclose(voting);
		errormsg(WHERE, ERR_OPEN, smb.file, i, smb.last_error);
		return false;
	}

	/***********************/
	/* Pack E-mail, if any */
	/***********************/
	qwkmail_last = 0;
	mail = loadmail(&smb, &mailmsgs, 0, MAIL_YOUR, 0);
	packedmail = 0;
	if (mailmsgs) {
		lprintf(LOG_INFO, "Packing NetMail for %s", cfg.qhub[hubnum]->id);
		for (u = 0; u < mailmsgs; u++) {
			//		bprintf("\b\b\b\b\b%-5lu",u+1);
			memset(&msg, 0, sizeof(msg));
			msg.idx = mail[u];
			if (msg.idx.number > qwkmail_last)
				qwkmail_last = msg.idx.number;
			if (loadmsg(&msg, mail[u].number) < 1)
				continue;

			SAFEPRINTF(str, "%s/", cfg.qhub[hubnum]->id);
			if (msg.to_net.type != NET_QWK
			    || (strcmp((char *)msg.to_net.addr, cfg.qhub[hubnum]->id)
			        && strncmp((char *)msg.to_net.addr, str, strlen(str)))) {
				smb_unlockmsghdr(&smb, &msg);
				smb_freemsgmem(&msg);
				continue;
			}

			mode = QM_TO_QNET | QM_REP;
			mode |= (cfg.qhub[hubnum]->misc & (QHUB_EXT | QHUB_CTRL_A | QHUB_UTF8));
			/* For an unclear reason, kludge lines (including @VIA and @TZ) were not included in NetMail previously */
			if (!(cfg.qhub[hubnum]->misc & QHUB_NOHEADERS))
				mode |= (QM_VIA | QM_TZ | QM_MSGID | QM_REPLYTO);
			if (msgtoqwk(&msg, rep, mode, &smb, /* confnum: */ 0, hdrs) > 0)
				packedmail++;
			smb_unlockmsghdr(&smb, &msg);
			smb_freemsgmem(&msg);
			YIELD();    /* yield */
		}
		lprintf(LOG_INFO, "Packed %ld NetMail messages", packedmail);
	}
	smb_close(&smb);                    /* Close the e-mail */
	if (mailmsgs)
		free(mail);

	for (i = 0; i < cfg.qhub[hubnum]->subs; i++) {
		j = cfg.qhub[hubnum]->sub[i]->subnum;             /* j now equals the real sub num */
		msgs = getlastmsg(j, &last, 0);
		lncntr = 0;                       /* defeat pause */
		if (!msgs || last <= subscan[j].ptr) {
			if (subscan[j].ptr > last) {
				subscan[j].ptr = last;
				subscan[j].last = last;
			}
			lprintf(LOG_INFO, remove_ctrl_a(text[NScanStatusFmt], tmp)
			        , cfg.grp[cfg.sub[j]->grp]->sname
			        , cfg.sub[j]->lname, 0L, msgs);
			continue;
		}

		SAFEPRINTF2(smb.file, "%s%s"
		            , cfg.sub[j]->data_dir, cfg.sub[j]->code);
		smb.retry_time = cfg.smb_retry_time;
		smb.subnum = j;
		if ((k = smb_open(&smb)) != 0) {
			errormsg(WHERE, ERR_OPEN, smb.file, k, smb.last_error);
			continue;
		}

		post = loadposts(&posts, j, subscan[j].ptr, LP_BYSELF | LP_OTHERS | LP_PRIVATE | LP_REP | LP_VOTES | LP_POLLS, NULL);
		lprintf(LOG_INFO, remove_ctrl_a(text[NScanStatusFmt], tmp)
		        , cfg.grp[cfg.sub[j]->grp]->sname
		        , cfg.sub[j]->lname, posts, msgs);
		if (!posts)  { /* no new messages */
			smb_close(&smb);
			continue;
		}

		subscan[j].ptr = last;                   /* set pointer */
		lprintf(LOG_INFO, "%s", remove_ctrl_a(text[QWKPackingSubboard], tmp)); /* ptr to last msg	*/
		submsgs = 0;
		for (u = 0; u < posts; u++) {
			//		bprintf("\b\b\b\b\b%-5lu",u+1);

			memset(&msg, 0, sizeof(msg));
			msg.idx = post[u].idx;
			if (loadmsg(&msg, post[u].idx.number) < 1)
				continue;

			if (msg.from_net.type && msg.from_net.type != NET_QWK &&
			    !(cfg.sub[j]->misc & SUB_GATE)) {
				smb_freemsgmem(&msg);
				smb_unlockmsghdr(&smb, &msg);
				continue;
			}

			if (!strnicmp(msg.subj, "NE:", 3) || (msg.from_net.type == NET_QWK &&
			                                      route_circ((char *)msg.from_net.addr, cfg.qhub[hubnum]->id))) {
				smb_freemsgmem(&msg);
				smb_unlockmsghdr(&smb, &msg);
				continue;
			}

			mode = cfg.qhub[hubnum]->mode[i] | QM_TO_QNET | QM_REP;
			mode |= (cfg.qhub[hubnum]->misc & (QHUB_EXT | QHUB_CTRL_A | QHUB_UTF8));
			if (!(cfg.qhub[hubnum]->misc & QHUB_NOHEADERS))
				mode |= (QM_VIA | QM_TZ | QM_MSGID | QM_REPLYTO);
			if (msg.from_net.type != NET_QWK)
				mode |= QM_TAGLINE;

			if (msgtoqwk(&msg, rep, mode, &smb, cfg.qhub[hubnum]->conf[i], hdrs, voting) > 0) {
				msgcnt++;
				submsgs++;
			}

			smb_freemsgmem(&msg);
			smb_unlockmsghdr(&smb, &msg);
			if (!(u % 50))
				YIELD(); /* yield */
		}
		lprintf(LOG_INFO, remove_ctrl_a(text[QWKPackedSubboard], tmp), submsgs, msgcnt);
		free(post);
		smb_close(&smb);
		YIELD();    /* yield */
	}

	BOOL voting_data = FALSE;
	if (hdrs != NULL)
		fclose(hdrs);
	if (voting != NULL) {
		voting_data = ftell(voting);
		fclose(voting);
	}
	fclose(rep);            /* close HUB_ID.MSG */
	CRLF;
	/* Look for extra files to send out */
	SAFEPRINTF2(str, "%sqnet/%s.out", cfg.data_dir, hubid_lower);
	dir = opendir(str);
	while (dir != NULL && (dirent = readdir(dir)) != NULL) {
		SAFEPRINTF3(str, "%sqnet/%s.out/%s", cfg.data_dir, hubid_lower, dirent->d_name);
		if (isdir(str))
			continue;
		SAFEPRINTF2(tmp2, "%s%s", cfg.temp_dir, dirent->d_name);
		lprintf(LOG_INFO, remove_ctrl_a(text[RetrievingFile], tmp), str);
		if (!mv(str, tmp2, /* copy: */ TRUE))
			netfiles++;
	}
	if (dir != NULL)
		closedir(dir);
	if (netfiles)
		CRLF;
	if (!msgcnt && !netfiles && !packedmail && !voting_data) {
		lprintf(LOG_INFO, "%s", remove_ctrl_a(text[QWKNoNewMessages], tmp));
		return true;   // Changed from false Mar-11-2005 (needs to be true to save updated ptrs)
	}

	/*******************/
	/* Compress Packet */
	/*******************/
	SAFEPRINTF2(str, "%s%s.REP", cfg.data_dir, hubid_upper);
	SAFEPRINTF2(tmp2, "%s%s", cfg.temp_dir, ALLFILES);
	if (strListFind(cfg.supported_archive_formats, cfg.qhub[hubnum]->fmt, /* case_sensitive */ FALSE) >= 0) {
		str_list_t file_list = directory(tmp2);
		long       file_count = create_archive(str, cfg.qhub[hubnum]->fmt, /* with_path: */ false, file_list, error, sizeof(error));
		strListFree(&file_list);
		if (file_count < 0)
			lprintf(LOG_ERR, "libarchive error %ld (%s) creating %s", file_count, error, str);
		else
			lprintf(LOG_INFO, "libarchive created %s from %ld files", str, file_count);
	} else {
		i = external(cmdstr(cfg.qhub[hubnum]->pack, str, tmp2, NULL)
		             , EX_OFFLINE | EX_WILDCARD);
		if (i)
			errormsg(WHERE, ERR_EXEC, cmdstr(cfg.qhub[hubnum]->pack, str, tmp2, NULL), i);
	}
	if (!fexistcase(str)) {
		lprintf(LOG_WARNING, "%s", remove_ctrl_a(text[QWKCompressionFailed], tmp));
		lprintf(LOG_ERR, "Couldn't compress REP packet");
		return false;
	}
	SAFEPRINTF2(str, "%sqnet/%s.out/", cfg.data_dir, hubid_lower);
	delfiles(str, ALLFILES);

	if (packedmail) {                        /* Delete NetMail */
		SAFEPRINTF(smb.file, "%smail", cfg.data_dir);
		smb.retry_time = cfg.smb_retry_time;
		smb.subnum = INVALID_SUB;
		if ((i = smb_open(&smb)) != 0) {
			errormsg(WHERE, ERR_OPEN, smb.file, i, smb.last_error);
			return true;
		}

		mail = loadmail(&smb, &mailmsgs, 0, MAIL_YOUR, 0);

		if ((i = smb_locksmbhdr(&smb)) != 0) {             /* Lock the base, so nobody */
			if (mailmsgs)
				free(mail);
			smb_close(&smb);
			errormsg(WHERE, ERR_LOCK, smb.file, i, smb.last_error); /* messes with the index */
			return true;
		}

		if ((i = smb_getstatus(&smb)) != 0) {
			if (mailmsgs)
				free(mail);
			smb_close(&smb);
			errormsg(WHERE, ERR_READ, smb.file, i, smb.last_error);
			return true;
		}

		deleted = 0;
		/* Mark as READ and DELETE */
		for (u = 0; u < mailmsgs; u++) {
			if (mail[u].number > qwkmail_last)
				continue;
			memset(&msg, 0, sizeof(msg));
			/* !IMPORTANT: search by number (do not initialize msg.idx.offset) */
			if (loadmsg(&msg, mail[u].number) < 1)
				continue;

			SAFEPRINTF(str, "%s/", cfg.qhub[hubnum]->id);
			if (msg.to_net.type != NET_QWK
			    || (strcmp((char *)msg.to_net.addr, cfg.qhub[hubnum]->id)
			        && strncmp((char *)msg.to_net.addr, str, strlen(str)))) {
				smb_unlockmsghdr(&smb, &msg);
				smb_freemsgmem(&msg);
				continue;
			}

			msg.hdr.attr |= MSG_DELETE;
			msg.idx.attr = msg.hdr.attr;
			if ((i = smb_putmsg(&smb, &msg)) != 0)
				errormsg(WHERE, ERR_WRITE, smb.file, i, smb.last_error);
			else
				deleted++;
			smb_unlockmsghdr(&smb, &msg);
			smb_freemsgmem(&msg);
		}

		if (deleted && cfg.sys_misc & SM_DELEMAIL)
			delmail(0, MAIL_YOUR);
		smb_close(&smb);
		if (mailmsgs)
			free(mail);
		lprintf(LOG_INFO, "Deleted %ld sent NetMail messages", deleted);
	}

	return true;
}