Blame src/HttpDirXML.cc

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