|
Packit |
8f70b4 |
/*
|
|
Packit |
8f70b4 |
* lftp - file transfer program
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8f70b4 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8f70b4 |
* the Free Software Foundation; either version 3 of the License, or
|
|
Packit |
8f70b4 |
* (at your option) any later version.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
8f70b4 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8f70b4 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8f70b4 |
* GNU General Public License for more details.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
8f70b4 |
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
8f70b4 |
*/
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#include <config.h>
|
|
Packit |
8f70b4 |
#include "HttpDir.h"
|
|
Packit |
8f70b4 |
#include "log.h"
|
|
Packit |
8f70b4 |
#include "url.h"
|
|
Packit |
8f70b4 |
#include "misc.h"
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#if USE_EXPAT
|
|
Packit |
8f70b4 |
#include <expat.h>
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
struct xml_context
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xarray_s<xstring_c> stack;
|
|
Packit |
8f70b4 |
Ref<FileSet> fs;
|
|
Packit |
8f70b4 |
Ref<FileInfo> fi;
|
|
Packit |
8f70b4 |
xstring base_dir;
|
|
Packit |
8f70b4 |
xstring chardata;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void push(const char *);
|
|
Packit |
8f70b4 |
void pop();
|
|
Packit |
8f70b4 |
void process_chardata();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void set_base_dir(const char *d) {
|
|
Packit |
8f70b4 |
base_dir.set(d);
|
|
Packit |
8f70b4 |
if(base_dir.length()>1)
|
|
Packit |
8f70b4 |
base_dir.chomp('/');
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const xstring_c& top(int i=0) const {
|
|
Packit |
8f70b4 |
return stack.count()>i ? stack[stack.count()-i-1] : xstring_c::null;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool in(const char *tag) const {
|
|
Packit |
8f70b4 |
return top().eq(tag);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
bool in(const char *tag0,const char *tag1) const {
|
|
Packit |
8f70b4 |
return top(0).eq(tag0) && top(1).eq(tag1);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
bool has_chardata() const {
|
|
Packit |
8f70b4 |
return chardata.length()>0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void log_tag(const char *end="") const {
|
|
Packit |
8f70b4 |
const char *tag=top();
|
|
Packit |
8f70b4 |
Log::global->Format(10,"XML: %*s<%s%s>\n",stack.length()*2,"",end,tag);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void log_tag_end() const { log_tag("/"); }
|
|
Packit |
8f70b4 |
void log_data() const {
|
|
Packit |
8f70b4 |
Log::global->Format(10,"XML: %*s`%s'\n",stack.length()*2+2,"",chardata.get());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
};
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void xml_context::push(const char *s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
stack.append(s);
|
|
Packit |
8f70b4 |
log_tag();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(in("DAV:response"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
delete fi;
|
|
Packit |
8f70b4 |
fi=new FileInfo;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(in("DAV:collection"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
fi->SetType(fi->DIRECTORY);
|
|
Packit |
8f70b4 |
fi->SetMode(0755);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
chardata.truncate();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void xml_context::pop()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(has_chardata())
|
|
Packit |
8f70b4 |
process_chardata();
|
|
Packit |
8f70b4 |
if(in("DAV:response"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(fi && fi->name)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!fs)
|
|
Packit |
8f70b4 |
fs=new FileSet;
|
|
Packit |
8f70b4 |
fs->Add(fi.borrow());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
log_tag_end();
|
|
Packit |
8f70b4 |
stack.chop();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void xml_context::process_chardata()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
log_data();
|
|
Packit |
8f70b4 |
if(in("DAV:href","DAV:response"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
ParsedURL u(chardata,true);
|
|
Packit |
8f70b4 |
xstring& s=u.path;
|
|
Packit |
8f70b4 |
bool is_directory=false;
|
|
Packit |
8f70b4 |
if(s.last_char()=='/')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
is_directory=true;
|
|
Packit |
8f70b4 |
s.chomp('/');
|
|
Packit |
8f70b4 |
fi->SetType(fi->DIRECTORY);
|
|
Packit |
8f70b4 |
fi->SetMode(0755);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
fi->SetType(fi->NORMAL);
|
|
Packit |
8f70b4 |
fi->SetMode(0644);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(s.begins_with("/~"))
|
|
Packit |
8f70b4 |
s.set_substr(0,1,0,0);
|
|
Packit |
8f70b4 |
fi->SetName(base_dir.eq(s) && is_directory ? "." : basename_ptr(s));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(in("DAV:getcontentlength"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
long long size_ll=0;
|
|
Packit |
8f70b4 |
if(sscanf(chardata,"%lld",&size_ll)==1)
|
|
Packit |
8f70b4 |
fi->SetSize(size_ll);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(in("DAV:getlastmodified"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
time_t tm=Http::atotm(chardata);
|
|
Packit |
8f70b4 |
if(tm!=Http::ATOTM_ERROR)
|
|
Packit |
8f70b4 |
fi->SetDate(tm,0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(in("DAV:creator-displayname"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
fi->SetUser(chardata);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(in("http://apache.org/dav/props/executable"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(chardata[0]=='T')
|
|
Packit |
8f70b4 |
fi->SetMode(0755);
|
|
Packit |
8f70b4 |
else if(chardata[0]=='F')
|
|
Packit |
8f70b4 |
fi->SetMode(0644);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static void start_handle(void *data, const char *el, const char **attr)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xml_context *ctx=(xml_context*)data;
|
|
Packit |
8f70b4 |
ctx->push(el);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
static void end_handle(void *data, const char *el)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xml_context *ctx=(xml_context*)data;
|
|
Packit |
8f70b4 |
ctx->pop();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
static void chardata_handle(void *data, const char *chardata, int len)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xml_context *ctx=(xml_context*)data;
|
|
Packit |
8f70b4 |
if(!ctx->fi)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
ctx->chardata.append(chardata,len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
FileSet *HttpListInfo::ParseProps(const char *b,int len,const char *base_dir)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
XML_Parser p = XML_ParserCreateNS(0,0);
|
|
Packit |
8f70b4 |
if(!p)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
xml_context ctx;
|
|
Packit |
8f70b4 |
ctx.set_base_dir(base_dir);
|
|
Packit |
8f70b4 |
XML_SetUserData(p,&ctx;;
|
|
Packit |
8f70b4 |
XML_SetElementHandler(p, start_handle, end_handle);
|
|
Packit |
8f70b4 |
XML_SetCharacterDataHandler(p, chardata_handle);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!XML_Parse(p, b, len, /*eof*/1))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Log::global->Format(0, "XML Parse error at line %d: %s\n",
|
|
Packit |
8f70b4 |
(int)XML_GetCurrentLineNumber(p),
|
|
Packit |
8f70b4 |
XML_ErrorString(XML_GetErrorCode(p)));
|
|
Packit |
8f70b4 |
XML_ParserFree(p);
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
XML_ParserFree(p);
|
|
Packit |
8f70b4 |
return ctx.fs.borrow();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void HttpDirList::ParsePropsFormat(const char *b,int len,bool eof)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(len==0)
|
|
Packit |
8f70b4 |
goto end;
|
|
Packit |
8f70b4 |
if(!xml_p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xml_p=XML_ParserCreateNS(0,0);
|
|
Packit |
8f70b4 |
xml_ctx=new xml_context;
|
|
Packit |
8f70b4 |
xml_ctx->set_base_dir(curr_url->path);
|
|
Packit |
8f70b4 |
XML_SetUserData(xml_p,xml_ctx);
|
|
Packit |
8f70b4 |
XML_SetElementHandler(xml_p, start_handle, end_handle);
|
|
Packit |
8f70b4 |
XML_SetCharacterDataHandler(xml_p, chardata_handle);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!XML_Parse(xml_p, b, len, eof))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Log::global->Format(0, "XML Parse error at line %d: %s\n",
|
|
Packit |
8f70b4 |
(int)XML_GetCurrentLineNumber(xml_p),
|
|
Packit |
8f70b4 |
XML_ErrorString(XML_GetErrorCode(xml_p)));
|
|
Packit |
8f70b4 |
parse_as_html=true;
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!xml_ctx->fs)
|
|
Packit |
8f70b4 |
goto end;
|
|
Packit |
8f70b4 |
xml_ctx->fs->rewind();
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
FileInfo *info=xml_ctx->fs->curr();
|
|
Packit |
8f70b4 |
if(!info)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
info->MakeLongName();
|
|
Packit |
8f70b4 |
buf->Put(info->longname);
|
|
Packit |
8f70b4 |
if(ls_options.append_type)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(info->filetype==info->DIRECTORY)
|
|
Packit |
8f70b4 |
buf->Put("/");
|
|
Packit |
8f70b4 |
else if(info->filetype==info->SYMLINK && !info->symlink)
|
|
Packit |
8f70b4 |
buf->Put("@");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
buf->Put("\n");
|
|
Packit |
8f70b4 |
xml_ctx->fs->next();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
xml_ctx->fs->Empty();
|
|
Packit |
8f70b4 |
end:
|
|
Packit |
8f70b4 |
if(eof && xml_p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
XML_ParserFree(xml_p);
|
|
Packit |
8f70b4 |
xml_p=0;
|
|
Packit |
8f70b4 |
delete xml_ctx;
|
|
Packit |
8f70b4 |
xml_ctx=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#else // !USE_EXPAT
|
|
Packit |
8f70b4 |
FileSet *HttpListInfo::ParseProps(const char *b,int len,const char *base_dir) { return 0; }
|
|
Packit |
8f70b4 |
void HttpDirList::ParsePropsFormat(const char *b,int len,bool eof) {}
|
|
Packit |
8f70b4 |
#endif // !USE_EXPAT
|