760 lines
18 KiB
C++
760 lines
18 KiB
C++
#define Use_TagList
|
||
#define Use_LinkedList
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <ctype.h>
|
||
#include <string.h>
|
||
#include <dos.h>
|
||
#include "proboard.hpp"
|
||
|
||
struct InputFile
|
||
{
|
||
int area;
|
||
String name;
|
||
|
||
InputFile();
|
||
InputFile(char *s);
|
||
InputFile(char *s , int area);
|
||
};
|
||
|
||
const int MAX_DOWNLOADS = 100;
|
||
|
||
void create_dirlist(int area,char *firstdir,LinkedList<String>& dirlist);
|
||
bool is_freefile(char *fname);
|
||
bool send_files( protocol& p , LinkedList<DownloadFile>& files ,LinkedList<String>& downloads , LinkedList<String>& uploads);
|
||
void read_personal_files( LinkedList<InputFile>& files );
|
||
void delete_personal_file( char *name );
|
||
void copy_cdrom( LinkedList<DownloadFile> &files , LinkedList<String> &copied);
|
||
void delete_copied ( LinkedList<String> &copied);
|
||
|
||
void process_uploads(protocol& p , char *dir , LinkedList<String>& uploads , bool pvt , bool ask_desc , bool no_log , bool quiet_dl , String extra_log );
|
||
|
||
bool check_dszlog(protocol& p , LinkedList<String>& downloads , LinkedList<String>& uploads);
|
||
bool create_dszctl(protocol& p , LinkedList<DownloadFile>& files);
|
||
|
||
static void
|
||
read_from_file(LinkedList<InputFile>& input_files , char *fn)
|
||
{
|
||
TextFile f(fn);
|
||
|
||
if(f.opened())
|
||
{
|
||
while(!f.eof())
|
||
{
|
||
String s = f.readLine();
|
||
|
||
s.delLast('\n');
|
||
s.trim();
|
||
|
||
if(!s.len()) continue;
|
||
|
||
input_files.add(InputFile(s));
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
download(char *data)
|
||
{
|
||
bool no_input = FALSE;
|
||
bool any_file = FALSE;
|
||
bool private_download = FALSE;
|
||
bool free_time = FALSE;
|
||
char protocol_key = '\0';
|
||
bool quiet_download = FALSE;
|
||
bool goodbye = FALSE;
|
||
bool ignore_kbytes = FALSE;
|
||
bool no_log = FALSE;
|
||
bool ask_description = TRUE;
|
||
int npara;
|
||
int i;
|
||
long total_bytes;
|
||
long free_bytes;
|
||
String param[20];
|
||
BitArray arealist(MAX_FILE_AREAS,1);
|
||
LinkedList<DownloadFile> files;
|
||
LinkedList<InputFile> input_files;
|
||
LinkedList<String> copied_to_cd;
|
||
protocol prot;
|
||
FileName extra_log;
|
||
|
||
npara = parse_data(data,param); // Parse command line
|
||
|
||
create_arealist(param,npara,arealist); // Create list of selected file areas
|
||
|
||
adjust_limits();
|
||
|
||
for(i=0 ; i<npara ; i++)
|
||
{
|
||
if(param[i][0]=='/')
|
||
switch(toupper(param[i][1]))
|
||
{
|
||
case 'F': if(param[i][2] == '=')
|
||
{
|
||
if(param[i][3] == '@')
|
||
read_from_file(input_files,¶m[i][4]);
|
||
else
|
||
input_files.add(InputFile(¶m[i][3]));
|
||
}
|
||
else
|
||
{
|
||
input_files.add(InputFile(¶m[i][2]));
|
||
}
|
||
no_input = TRUE;
|
||
case 'A': any_file = TRUE;
|
||
break;
|
||
case 'P': private_download = TRUE;
|
||
no_input = TRUE;
|
||
break;
|
||
case 'T': free_time = TRUE;
|
||
break;
|
||
case 'K': protocol_key = param[i][3];
|
||
break;
|
||
case 'Q': quiet_download = TRUE;
|
||
break;
|
||
case 'I': ignore_kbytes = TRUE;
|
||
break;
|
||
case 'N': no_log = TRUE;
|
||
break;
|
||
case 'L': extra_log = ¶m[i][3];
|
||
break;
|
||
case 'D': ask_description = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(user.uFlags & UFLAG_IGNORE)
|
||
ignore_kbytes = TRUE;
|
||
|
||
if(!quiet_download)
|
||
{
|
||
io << "\n\f" << S_DOWNLOAD_TITLE << "\n\n";
|
||
|
||
if(timer.online()<download_delay && !(user.uFlags & UFLAG_IGNORE) && !private_download)
|
||
{
|
||
if(showansascrip("DLDELAY") == ANS_NOFILE)
|
||
{
|
||
io << S_DOWNLOAD_DELAY(form("%d",download_delay)) << "\n\n";
|
||
}
|
||
|
||
io << S_PRESS_ENTER_TO_CONTINUE;
|
||
|
||
return;
|
||
}
|
||
|
||
if(!cfg.downloadHours.enabled() && !(user.uFlags & UFLAG_IGNORE) && !private_download)
|
||
{
|
||
io << S_DOWNLOAD_NOT_NOW;
|
||
return;
|
||
}
|
||
}
|
||
|
||
if(!user.defaultProtocol || !select_protocol(prot,user.defaultProtocol))
|
||
{
|
||
if(!select_protocol(prot,protocol_key))
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
io << S_PROTOCOL_SELECTED(prot.name);
|
||
}
|
||
|
||
if(!any_file && !no_input)
|
||
for(taglist.rewind() ; !taglist.eol() ; taglist++)
|
||
{
|
||
input_files.add( InputFile(taglist.get().name , taglist.get().area) );
|
||
}
|
||
|
||
for(input_files.rewind() ; !input_files.eol() ; input_files++)
|
||
{
|
||
if(input_files.get().area)
|
||
arealist.set(input_files.get().area);
|
||
}
|
||
|
||
if(!quiet_download && !no_input)
|
||
{
|
||
char s[80];
|
||
|
||
io << "\n\n" << S_ENTER_FILES_TO_DOWNLOAD << "\n\n";
|
||
|
||
int file_count = 1;
|
||
|
||
for( input_files.rewind() ; !input_files.eol() ; input_files++ , file_count++)
|
||
{
|
||
io << S_ENTER_DOWNLOAD_FILENAME(form("%d",file_count));
|
||
io << input_files.get().name << '\n';
|
||
}
|
||
|
||
for(;;file_count++)
|
||
{
|
||
io << S_ENTER_DOWNLOAD_FILENAME(form("%d",file_count));
|
||
|
||
io.read(s,(any_file ? 60:13),READMODE_UPALL);
|
||
|
||
if(!s[0]) break;
|
||
|
||
char *tmp = strtok(s," ");
|
||
|
||
for(i=0 ; tmp != NULL ; tmp = strtok(NULL," "))
|
||
{
|
||
if(strchr(tmp,'\\') && !any_file) continue;
|
||
|
||
input_files.add(InputFile(tmp));
|
||
}
|
||
|
||
io << "\n";
|
||
}
|
||
|
||
if(!input_files.count())
|
||
return;
|
||
|
||
io << '\n';
|
||
}
|
||
|
||
if(!quiet_download)
|
||
io << '\n' << S_SEARCHING_FILE_DATABASE << '\xFF';
|
||
|
||
if(private_download)
|
||
{
|
||
read_personal_files(input_files);
|
||
|
||
if(!input_files.count())
|
||
{
|
||
io << S_NO_PERSONAL_FILES_FOUND << "\n\n" << S_PRESS_ENTER_TO_CONTINUE;
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
// At this point we have a list of files that have to be download in the
|
||
// linked list called 'input_files'. If a path name is specified, it
|
||
// should not be looked for in the file areas
|
||
|
||
int files_found = 0;
|
||
|
||
for( input_files.rewind() ; !input_files.eol() && files_found < MAX_DOWNLOADS; )
|
||
{
|
||
DownloadFile cur_file;
|
||
|
||
if(strchr(input_files.get().name , '\\')) // Explicit path found?
|
||
{
|
||
DirScan scan(input_files.get().name);
|
||
|
||
while(int(scan) && files_found < MAX_DOWNLOADS)
|
||
{
|
||
FileName fn(input_files.get().name);
|
||
|
||
fn.stripName();
|
||
fn.appendBS();
|
||
|
||
cur_file.area = 0;
|
||
cur_file.free = FALSE;
|
||
cur_file.copy = FALSE;
|
||
cur_file.size = scan.size();
|
||
cur_file.name = fn + scan.name();
|
||
|
||
files.add(cur_file);
|
||
files_found++;
|
||
|
||
scan++;
|
||
}
|
||
|
||
input_files.remove();
|
||
}
|
||
else
|
||
{
|
||
input_files++;
|
||
}
|
||
}
|
||
|
||
if(input_files.count())
|
||
{
|
||
File fidx;
|
||
FilesIdx idx;
|
||
FileArea fa;
|
||
|
||
if(fidx.open(FileName(syspath,"FILESIDX.PB") , fmode_read , cfg.fastmode ? 8192:2048))
|
||
{
|
||
long idx_size = fidx.len() / sizeof(idx);
|
||
long step = idx_size / 50;
|
||
long count = 0;
|
||
|
||
if(step <= 1)
|
||
step = 1;
|
||
|
||
fidx.seek(0);
|
||
|
||
if(!quiet_download)
|
||
{
|
||
if(!ansi_mode && !avatar)
|
||
{
|
||
io << "\n\n";
|
||
}
|
||
|
||
io << "\6<EFBFBD> <20> <20> <20> <20> <20> <20> <20> <20> <20> <20>";
|
||
|
||
if(ansi_mode || avatar)
|
||
{
|
||
io << "\x1b[51D\3";
|
||
}
|
||
else
|
||
{
|
||
io << '\r';
|
||
}
|
||
}
|
||
|
||
String firstchars;
|
||
bool wildcard_found = FALSE;
|
||
|
||
for( input_files.rewind() ; !input_files.eol() ; input_files++)
|
||
{
|
||
firstchars << char(toupper(input_files.get().name[0]));
|
||
}
|
||
|
||
if(strpbrk(firstchars,"*?")) wildcard_found = TRUE;
|
||
|
||
|
||
while(files_found < MAX_DOWNLOADS)
|
||
{
|
||
if(fidx.read(&idx,sizeof(idx)) != sizeof(idx))
|
||
break;
|
||
|
||
if(!((count++) % step) && !quiet_download) io << "<EFBFBD>\xFF";
|
||
|
||
if(!wildcard_found && !strchr(firstchars,toupper(idx.name[0]))) continue;
|
||
|
||
if(!arealist[idx.area]) continue;
|
||
|
||
for( input_files.rewind() ; !input_files.eol() && files_found < MAX_DOWNLOADS ; input_files++)
|
||
{
|
||
if(input_files.get().area && idx.area != input_files.get().area) continue;
|
||
|
||
if(!matchfile(input_files.get().name,idx.name)) continue;
|
||
|
||
if(!fa.read(idx.area) || !check_access(fa.level,fa.flags,fa.flagsNot)) continue;
|
||
|
||
LinkedList<String> dirlist;
|
||
|
||
create_dirlist(idx.area,fa.filepath,dirlist);
|
||
|
||
for( dirlist.rewind() ; !dirlist.eol() && files_found < MAX_DOWNLOADS ; dirlist++)
|
||
{
|
||
String wildcard = dirlist.get();
|
||
|
||
wildcard << idx.name;
|
||
|
||
DirScan scan(wildcard);
|
||
|
||
while(int(scan) && files_found < MAX_DOWNLOADS)
|
||
{
|
||
DownloadFile tmpfile;
|
||
|
||
tmpfile.area = idx.area;
|
||
tmpfile.size = scan.size();
|
||
tmpfile.free = FALSE;
|
||
tmpfile.copy = !!fa.cdrom;
|
||
tmpfile.name = dirlist.get() + scan.name();
|
||
|
||
files.add(tmpfile);
|
||
files_found++;
|
||
|
||
scan++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LOG("Error opening FILESIDX.PB");
|
||
}
|
||
}
|
||
|
||
total_bytes = 0;
|
||
free_bytes = 0;
|
||
|
||
if(!quiet_download)
|
||
{
|
||
io << '\n';
|
||
|
||
if(files.count())
|
||
{
|
||
io << '\n' << S_DOWNLOAD_FILES_FOUND_HEADER << '\n';
|
||
}
|
||
|
||
for(files.rewind() ; !files.eol() ; )
|
||
{
|
||
FileArea fa;
|
||
FileName fn;
|
||
String area_name;
|
||
|
||
fn = files.get().name;
|
||
fn.stripPath();
|
||
|
||
if(files.get().area)
|
||
{
|
||
fa.read(files.get().area);
|
||
area_name = fa.name;
|
||
}
|
||
else
|
||
{
|
||
if(private_download)
|
||
area_name = "Personal Files";
|
||
else
|
||
area_name = "Global";
|
||
}
|
||
|
||
io << S_DOWNLOAD_FILE_FOUND_ENTRY((char *)fn,form("%ld",(files.get().size+512L) / 1024L),(char *)area_name);
|
||
|
||
files.get().free = (fa.free || is_freefile((char *)fn));
|
||
|
||
if((total_bytes - free_bytes + files.get().size + user.kbToday*1024L) > (1024L*download_limit) && !files.get().free)
|
||
{
|
||
io << S_FILE_EXCEEDS_LIMIT(form("%u",download_limit)) << '\n';
|
||
|
||
files.remove();
|
||
}
|
||
else
|
||
{
|
||
if(upload_needed > 0 && !files.get().free)
|
||
{
|
||
io << S_UPLOAD_REQUIRED(form("%u",upload_needed)) << '\n';
|
||
|
||
files.remove();
|
||
}
|
||
else
|
||
{
|
||
io << S_FILE_FOUND_ACTION_PROMPT;
|
||
|
||
char action = wait_language_hotkeys(K_FILE_FOUND_ACTION_PROMPT);
|
||
|
||
int prompt_len = language_string_length(S_FILE_FOUND_ACTION_PROMPT);
|
||
|
||
io << String('\b',prompt_len) << String(' ',prompt_len) << String('\b',prompt_len);
|
||
|
||
switch(action)
|
||
{
|
||
case '\r':
|
||
case 0:
|
||
{
|
||
io << S_ACTION_DOWNLOAD << ' ';
|
||
|
||
if(files.get().free)
|
||
{
|
||
io << S_FREE_FILE;
|
||
|
||
free_bytes += files.get().size;
|
||
}
|
||
|
||
total_bytes += files.get().size;
|
||
|
||
io << '\n';
|
||
files++;
|
||
}
|
||
break;
|
||
case 1 :
|
||
{
|
||
io << S_ACTION_FILE_SKIPPED << '\n';
|
||
|
||
files.remove();
|
||
}
|
||
break;
|
||
case 2 :
|
||
{
|
||
io << S_ACTION_FILE_QUIT << '\n';
|
||
|
||
while(!files.eol()) files.remove();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if(!files.count())
|
||
{
|
||
io << '\n' << S_NO_FILES_FOUND << "\n\n" << S_PRESS_ENTER_TO_CONTINUE;
|
||
|
||
return;
|
||
}
|
||
|
||
word estimated = word(total_bytes / ((io.baud ? io.baud:115200L) * (prot.efficiency?prot.efficiency:100)/1000L));
|
||
|
||
if(prot.flags & PROT_LOCAL)
|
||
estimated = 0;
|
||
|
||
io << '\n' << S_X_FILES_SELECTED(form("%d",files.count()),form("%d",int(total_bytes/1024)),form("%02u:%02u",estimated/60,estimated%60)) << '\n';
|
||
|
||
if(!free_time && estimated/60>timer.left())
|
||
{
|
||
io << '\n' << S_NOT_ENOUGH_TIME_LEFT_FOR_DOWNLOAD << "\n\n" << S_PRESS_ENTER_TO_CONTINUE;
|
||
|
||
return;
|
||
}
|
||
|
||
io << '\n' << S_START_DOWNLOAD_PROMPT;
|
||
|
||
char k = wait_language_hotkeys(K_START_DOWNLOAD_PROMPT);
|
||
|
||
if(k==2)
|
||
return;
|
||
|
||
if(k==1)
|
||
{
|
||
io << S_DOWNLOAD_ACTION_GOODBYE;
|
||
|
||
goodbye = TRUE;
|
||
}
|
||
|
||
io << "\n\n";
|
||
|
||
copy_cdrom( files , copied_to_cd);
|
||
|
||
io << "\n\n" << S_ACTIVATING_PROTOCOL << '\xFF';
|
||
}
|
||
|
||
LinkedList<String> downloads,uploads;
|
||
|
||
if(free_time) timer.suspend();
|
||
|
||
if(!send_files(prot , files , downloads , uploads))
|
||
{
|
||
delete_copied(copied_to_cd);
|
||
|
||
return;
|
||
}
|
||
|
||
delete_copied(copied_to_cd);
|
||
|
||
if(free_time) timer.restart();
|
||
|
||
total_bytes = 0;
|
||
free_bytes = 0;
|
||
files_found = 0;
|
||
|
||
for(downloads.rewind() ; !downloads.eol() ; downloads++)
|
||
{
|
||
for(files.rewind() ; !files.eol() ; )
|
||
{
|
||
FileName fn(files.get().name);
|
||
|
||
fn.stripPath();
|
||
|
||
if(downloads.get() == fn)
|
||
{
|
||
total_bytes += files.get().size;
|
||
|
||
if(files.get().free)
|
||
free_bytes += files.get().size;
|
||
|
||
remove_tag(fn,files.get().area);
|
||
|
||
if(private_download)
|
||
delete_personal_file(files.get().name);
|
||
|
||
files_found++;
|
||
|
||
File f;
|
||
|
||
if(extra_log.len())
|
||
{
|
||
f.open(extra_log,fmode_copen | fmode_text | fmode_append | fmode_write);
|
||
|
||
f.printf("D %d %s %ld %s\n",files.get().area,(char *)files.get().name,files.get().size,files.get().free ? "YES":"NO");
|
||
f.close();
|
||
}
|
||
|
||
if(!no_log)
|
||
{
|
||
f.open(FileName(syspath,"DOWNLOAD.LOG"),fmode_write | fmode_append |
|
||
fmode_copen | fmode_text);
|
||
|
||
f.printf("%05d %s\n",files.get().area,(char *)files.get().name);
|
||
f.close();
|
||
|
||
LOG(1,"Download-%c %s",prot.key,(char *)fn);
|
||
}
|
||
|
||
files.remove();
|
||
}
|
||
else
|
||
{
|
||
files++;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(!ignore_kbytes)
|
||
{
|
||
user.numDownloads += files_found;
|
||
user.kbDownloaded += int((total_bytes-free_bytes) / 1024L);
|
||
user.kbToday += int((total_bytes-free_bytes) / 1024L);
|
||
}
|
||
|
||
if(!quiet_download)
|
||
{
|
||
io << "\n\f\n" << S_X_FILES_DOWNLOADED_SUCCESSFULLY(form("%d",files_found),form("%ld",total_bytes/1024L)) << "\n\n";
|
||
|
||
if(files.count())
|
||
io << S_X_FILES_NOT_DOWNLOADED(form("%d",files.count())) << "\n\n";
|
||
|
||
if(goodbye && !files.count() && !uploads.count())
|
||
{
|
||
unlink(prot.logfile);
|
||
unlink(prot.ctlfile);
|
||
|
||
io << "\n\n" << S_HANGUP_AFTER_DOWNLOAD_PROMPT << "10";
|
||
|
||
for(int i=10;i>0;i--)
|
||
{
|
||
io << form("\b\b%2d\xFF",i);
|
||
|
||
char c = toupper(io.readkey());
|
||
|
||
if(c==K_HANGUP_AFTER_DOWNLOAD_PROMPT[0]) return;
|
||
if(c==K_HANGUP_AFTER_DOWNLOAD_PROMPT[1]) break;
|
||
|
||
sleep(1);
|
||
}
|
||
|
||
LOG("Hanging up after download.");
|
||
|
||
showansascrip("DLHANGUP");
|
||
|
||
exit_proboard();
|
||
}
|
||
|
||
if(uploads.count())
|
||
process_uploads(prot,cfg.uploadpath,uploads,private_download , ask_description , no_log , quiet_download , extra_log );
|
||
|
||
io << S_PRESS_ENTER_TO_CONTINUE;
|
||
}
|
||
}
|
||
|
||
void
|
||
read_personal_files( LinkedList<InputFile>& files )
|
||
{
|
||
File f(FN_PVTFILES_PB);
|
||
|
||
if(!f.opened()) return;
|
||
|
||
f.rewind();
|
||
|
||
for(;;)
|
||
{
|
||
_PrivateFile pvt;
|
||
|
||
if(f.read(&pvt,sizeof(pvt)) != sizeof(pvt))
|
||
break;
|
||
|
||
if(strcmpl(pvt.to,user.name) || !pvt.fname[0]) continue;
|
||
|
||
String fname;
|
||
|
||
if(!strchr(pvt.fname,'\\'))
|
||
fname = String(cfg.pvtuploadpath) + pvt.fname;
|
||
else
|
||
fname = pvt.fname;
|
||
|
||
DirScan scan(fname);
|
||
|
||
if(int(scan))
|
||
files.add( InputFile(fname) );
|
||
}
|
||
}
|
||
|
||
void
|
||
delete_personal_file( char *name )
|
||
{
|
||
File f(FN_PVTFILES_PB,fmode_rw | fmode_excl);
|
||
|
||
if(!f.opened())
|
||
return;
|
||
|
||
FileName fn;
|
||
FileName fn_full( name );
|
||
|
||
_PrivateFile pvt;
|
||
|
||
int num_found = 0;
|
||
|
||
for(;;)
|
||
{
|
||
if(f.read(&pvt,sizeof(pvt))!=sizeof(pvt)) break;
|
||
|
||
if(!pvt.fname[0])
|
||
continue;
|
||
|
||
if(!strchr(pvt.fname,'\\'))
|
||
fn = String(cfg.pvtuploadpath) + pvt.fname;
|
||
else
|
||
fn = pvt.fname;
|
||
|
||
if(fn == fn_full)
|
||
num_found++;
|
||
}
|
||
|
||
f.rewind();
|
||
|
||
for(;;)
|
||
{
|
||
if(f.read(&pvt,sizeof(pvt))!=sizeof(pvt)) break;
|
||
|
||
if(!pvt.fname[0] || stricmp(pvt.to,user.name))
|
||
continue;
|
||
|
||
if(!strchr(pvt.fname,'\\'))
|
||
fn = String(cfg.pvtuploadpath) + pvt.fname;
|
||
else
|
||
fn = pvt.fname;
|
||
|
||
if(fn_full == fn)
|
||
{
|
||
pvt.fname[0]=0;
|
||
|
||
f.seek(-long(sizeof(pvt)),seek_cur);
|
||
f.write(&pvt,sizeof(pvt));
|
||
|
||
if(pvt.attr & PVTFILE_KEEP)
|
||
continue;
|
||
|
||
if(num_found < 2) // Only kill file if not waiting for oher user
|
||
unlink(fn_full);
|
||
}
|
||
}
|
||
}
|
||
|
||
InputFile::InputFile()
|
||
{
|
||
area = 0;
|
||
}
|
||
|
||
InputFile::InputFile(char *s)
|
||
{
|
||
area = 0;
|
||
|
||
if(s[0] == '[')
|
||
{
|
||
area = atoi(&s[1]);
|
||
|
||
for(int i=0; s[i] ; i++)
|
||
{
|
||
if(s[i] == ']')
|
||
{
|
||
name = &s[i+1];
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
name = s;
|
||
}
|
||
|
||
InputFile::InputFile(char *s,int a)
|
||
{
|
||
name = s;
|
||
area = a;
|
||
}
|