/*
decode.* - uu/yenc/etc decoding (wrapper around uulib)
Copyright (C) 1999-2004 Matthew Mueller <donut AT dakotacom.net>
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "decode.h"
#include "strreps.h"
#include "texthandler.h"
#include "path.h"
#include "misc.h"
#include "myregex.h"
#include "status.h"
#include "_sstream.h"
#include <dirent.h>
#ifndef PROTOTYPES
#define PROTOTYPES //required for uudeview.h to prototype function args.
#endif
#ifdef HAVE_UUDEVIEW_H
#include <uudeview.h>
#else
#include "uudeview.h"
#endif
int uu_info_callback(void *v,char *i){
TextHandler *th = static_cast<TextHandler*>(v);
th->addinfo(i);
return 0;
};
struct uu_err_status {
int derr;
int last_t;
TextHandler *th;
// string last_m;
uu_err_status(void):derr(0),last_t(-1),th(NULL){}
};
void uu_msg_callback(void *v,char *m,int t){
if (t!=UUMSG_MESSAGE) {//######
uu_err_status *es = (uu_err_status *)v;
es->derr++;
es->last_t = t;
if (es->th && t!=UUMSG_NOTE) {
es->th->adddecodeinfo(m);
}
// es->last_m = m;
//++(*(int *)v);
}
if (quiet>=2 && (t==UUMSG_MESSAGE || t==UUMSG_NOTE))
return;
printf("uu_msg(%i):%s\n",t,m);
};
int uu_busy_callback(void *v,uuprogress *p){
// if (!quiet) printf("uu_busy(%i):%s %i%%\r",p->action,p->curfile,(100*p->partno-p->percent)/p->numparts);
if (!quiet) {printf(".");fflush(stdout);}
return 0;
};
string fname_filter(const char *path, const char *fn){
const char *fnp=fn;
int slen=strlen(fnp);
if (*fnp=='"') {
fnp++;
slen--;
}
if (fnp[slen-1]=='"')
slen--;
if (path)
return path_join(path,string(fnp,slen));
else
return string(fnp,slen);
}
char * uu_fname_filter(void *v,char *fn){
static string filtered;
const string *s=(const string *)v;
filtered = fname_filter(s->c_str(),fn);
PDEBUG(DEBUG_MED,"uu_fname_filter: filtered %s to %s",fn,filtered.c_str());
return const_cast<char*>(filtered.c_str()); //uulib isn't const-ified
}
const char *uutypetoa(int uudet) {
switch (uudet){
case YENC_ENCODED:return "yEnc";
case UU_ENCODED:return "UUdata";
case B64ENCODED:return "Base64";
case XX_ENCODED:return "XXdata";
case BH_ENCODED:return "BinHex";
case PT_ENCODED:return "plaintext";
case QP_ENCODED:return "qptext";
default:return "unknown";
}
}
string make_dupe_name(const string &path, const string &fn, c_nntp_file::ptr f) {
string s;
while (1) {
ostringstream ss;
ss << fn << '.' << f->badate() << '.' << rand();
s = ss.str();
if (!fexists(is_abspath(s.c_str())?s:path_join(path,s)))
return s;
}
}
int find_duplicate(const string &nfn, const string &orgfn) {
string path(orgfn), orgfntail;
path_split(path,orgfntail);
int found = 0;
DIR *dir=opendir(path.c_str());
struct dirent *de;
if (!dir)
throw PathExFatal(Ex_INIT,"opendir: %s(%i)",strerror(errno),errno);
while ((de=readdir(dir))) {
if (strcmp(de->d_name,"..")==0) continue;
if (strcmp(de->d_name,".")==0) continue;
string de_fnp = path_join(path,de->d_name);
if (de_fnp==nfn)
continue; //it's the new file itself
if (!strstartswith(de_fnp, orgfn))
continue; //not a matching file.
PDEBUG(DEBUG_MED,"find_duplicate: comparing %s and %s\n", nfn.c_str(), de_fnp.c_str());
if (filecompare(nfn.c_str(),de_fnp.c_str())){
found = 1;
break;
}
}
closedir(dir);
return found;
}
int remove_if_duplicate(const string &nfn, const string &orgfn) {
int found_dup = find_duplicate(nfn,orgfn);
if (found_dup){
// if identical to a previously decoded file, delete the one we just downloaded
unlink(nfn.c_str());
printf("Duplicate File Removed %s\n", nfn.c_str());
set_dupe_ok_status();
}
return found_dup;
}
void Decoder::addpart(int partno,char *fn) {
fnbuf.push_back(pair<int,char*>(partno,fn));
}
int Decoder::decode(const nget_options &options, const c_nntp_file_retr::ptr &fr, dupe_file_checker &flist) {
if (fnbuf.empty()) return 0;
int optionflags = options.gflags;
c_nntp_file::ptr f = fr->file;
uu_err_status uustatus;
c_regex_nosub uulib_textfn_re("^[0-9][0-9][0-9][0-9]+\\.txt$",REG_EXTENDED);
int r,un;
TextHandler texthandler(options.texthandling, options.save_text_for_binaries, options.mboxfname, fr, fnbuf.front().second);
uustatus.th = &texthandler;
if ((r=UUInitialize())!=UURET_OK)
throw ApplicationExFatal(Ex_INIT,"UUInitialize: %s",UUstrerror(r));
UUSetOption(UUOPT_DUMBNESS,1,NULL); // "smartness" barfs on some subjects
//UUSetOption(UUOPT_FAST,1,NULL);//we store each message in a seperate file
//actually, I guess that won't work, since some messages have multiple files in them anyway.
UUSetOption(UUOPT_OVERWRITE,0,NULL);//no thanks.
UUSetOption(UUOPT_USETEXT,1,NULL);//######hmmm...
UUSetOption(UUOPT_DESPERATE,1,NULL);
UUSetMsgCallback(&uustatus,uu_msg_callback);
UUSetBusyCallback(NULL,uu_busy_callback,1000);
UUSetFNameFilter((void*)&fr->path,uu_fname_filter);
for(t_fnbuf_list::const_iterator fncurb = fnbuf.begin();fncurb!=fnbuf.end();++fncurb){
UULoadFileWithPartNo((*fncurb).second,NULL,0,(*fncurb).first);
}
uulist * uul;
for (un=0;;un++){
if ((uul=UUGetFileListItem(un))==NULL)break;
if (uul->filename==NULL) {
printf("invalid uulist item, uul->filename==NULL\n");
uustatus.derr++;
//continue; // if not using UUOPT_DESPERATE.
UURenameFile(uul,"noname");
}
if (!(uul->state & UUFILE_OK)){
printf("%s not ok\n",uul->filename);
texthandler.adddecodeinfo(string(uul->filename) + " not ok");
uustatus.derr++;
//continue; // if not using UUOPT_DESPERATE.
}
// printf("\ns:%x d:%x\n",uul->state,uul->uudet);
r=UUInfoFile(uul,&texthandler,uu_info_callback);
if ((uul->uudet==PT_ENCODED || uul->uudet==QP_ENCODED) && uul->filename==uulib_textfn_re){
continue; //ignore, as this should be handled by the UUInfoFile already..
}
//check if dest file exists before attempting decode, avoids having to hack around the uu_error that occurs when the destfile exists and overwriting is disabled.
//also rename the file if it is not ok to avoid the impression that you have a correct file.
int pre_decode_derr = uustatus.derr;
string orig_fnp = fname_filter(fr->path.c_str(), uul->filename);
if ((uul->state & UUFILE_OK) && !fexists(orig_fnp)) {
r=UUDecodeFile(uul,NULL);
if ((r!=UURET_OK || uustatus.derr!=pre_decode_derr) && fexists(orig_fnp)) {
string nfnp;
nfnp = make_dupe_name(fr->path.c_str(), orig_fnp, f);
xxrename(orig_fnp.c_str(), nfnp.c_str());
remove_if_duplicate(nfnp, orig_fnp);
}
}
else {
//all the following ugliness with fname_filter is due to uulib forgetting that we already filtered the name and giving us the original name instead.
// Generate a new filename to use
string nfn(make_dupe_name(fr->path.c_str(), fname_filter(NULL,uul->filename), f));
UURenameFile(uul,const_cast<char*>(nfn.c_str())); //uulib isn't const-ified
r=UUDecodeFile(uul,NULL);
// did it decode something? (could still be incomplete or broken though)
if (r == UURET_OK){
string nfnp(path_join(fr->path,nfn));
int removed_dup = remove_if_duplicate(nfnp, orig_fnp);
if (fexists(orig_fnp) && !removed_dup){
set_dupe_warn_status();
}
}
}
if (r!=UURET_OK){
uustatus.derr++;
printf("decode(%s): %s\n",uul->filename,UUstrerror(r));
texthandler.adddecodeinfo(string("error decoding ")+uutypetoa(uul->uudet)+" "+uul->filename+": " + UUstrerror(r));
continue;
}
else if (pre_decode_derr!=uustatus.derr){
printf("decode(%s): %i derr(s)\n",uul->filename,uustatus.derr-pre_decode_derr);
texthandler.adddecodeinfo(string("error decoding ")+uutypetoa(uul->uudet)+" "+uul->filename+": " + tostr(uustatus.derr-pre_decode_derr) + "derr(s)");
continue;
}else{
texthandler.adddecodeinfo(string(uutypetoa(uul->uudet))+" "+uul->filename);
if (!(optionflags&GETFILES_NODUPEFILECHECK))
flist.addfile(fr->path, uul->filename); //#### is this the right place? what about dupes saved as different names??
switch (uul->uudet){
case YENC_ENCODED:set_yenc_ok_status();break;
case UU_ENCODED:set_uu_ok_status();break;
case B64ENCODED:set_base64_ok_status();break;
case XX_ENCODED:set_xx_ok_status();break;
case BH_ENCODED:set_binhex_ok_status();break;
case PT_ENCODED:set_plaintext_ok_status();break;
case QP_ENCODED:set_qp_ok_status();break;
default:set_unknown_ok_status();
}
}
}
UUCleanUp();
//handle posts that uulib says "no encoded data" for as text. (usually is posts with no body)
if (uustatus.derr==1 && uustatus.last_t==UUMSG_NOTE && un==0 && f->req<=0 && fnbuf.size()==1) {
uustatus.derr--; //HACK since this error will also cause a uu_note "No encoded data found", which will incr derr, but we don't want that.
texthandler.set_save_whole_tempfile(true);//sometimes uulib will get confused and think a text post is an incomplete binary and will say "no encoded data" for it, so tell the texthandler to save the body of the message, if there is one.
un++;
}
if (uustatus.derr==0)
texthandler.save();
if (uustatus.derr>0) {
set_decode_error_status();
printf(" %i decoding errors occured, keeping temp files.\n",uustatus.derr);
}
else if (un==0) {
uustatus.derr=1;
set_undecoded_warn_status();
printf("hm.. nothing decoded.. keeping temp files\n");
}
else {
assert(uustatus.derr==0);
set_total_ok_status();
if (optionflags&GETFILES_KEEPTEMP) {
if (quiet<2)
printf("decoded ok, keeping temp files.\n");
}
else {
if (quiet<2)
printf("decoded ok, deleting temp files.\n");
delete_tempfiles();
}
}
return uustatus.derr;
}
void Decoder::delete_tempfiles(void) {
for(t_fnbuf_list::iterator fncurb = fnbuf.begin();fncurb!=fnbuf.end();++fncurb)
unlink((*fncurb).second);
}
Decoder::~Decoder() {
for(t_fnbuf_list::iterator fncurb = fnbuf.begin();fncurb!=fnbuf.end();++fncurb)
free((*fncurb).second);
}
syntax highlighted by Code2HTML, v. 0.9.1