All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gridscale.webdav.package.scala Maven / Gradle / Ivy

The newest version!
package gridscale

import gridscale.webdav.WebDAV._
import java.io.{ ByteArrayInputStream, IOException, InputStream }
import java.time.{ LocalDate, LocalDateTime, ZoneOffset }

import gridscale.effectaside._
import org.apache.http.{ HttpRequest, HttpResponse, HttpStatus }
import org.apache.http.client.protocol.HttpClientContext
import org.apache.http.entity.InputStreamEntity
import org.apache.http.impl.client.DefaultRedirectStrategy

import scala.collection.mutable.ListBuffer
import scala.language.{ higherKinds, postfixOps }

package object webdav {

  type Server = http.Server
  lazy val WebDAVServer = http.HTTPServer
  lazy val WebDAVSServer = http.HTTPSServer

  def listProperties(server: http.Server, path: String)(implicit httpEffect: Effect[http.HTTP]) = {
    http.readStream(server, path, parsePropsResponse, http.PropFind())
  }

  def list(server: http.Server, path: String)(implicit httpEffect: Effect[http.HTTP]) = {
    val properties = listProperties(server, path)
    properties.drop(1).map { p: Prop ⇒ ListEntry(p.displayName, if (p.isCollection) FileType.Directory else FileType.File, Some(p.modified.toEpochSecond(ZoneOffset.UTC) * 1000)) }
  }

  def exists(server: http.Server, path: String)(implicit httpEffect: Effect[http.HTTP]): Boolean = {
    def readResponse(response: HttpResponse) =
      response.getStatusLine.getStatusCode match {
        case x if x < HttpStatus.SC_MULTIPLE_CHOICES ⇒ true
        case HttpStatus.SC_NOT_FOUND                 ⇒ false
        case _                                       ⇒ throw new IOException(s"Server responded with an unexpected response: ${response.getStatusLine.getStatusCode} ${response.getStatusLine.getReasonPhrase}")
      }

    val response = httpEffect().request(server, path, (_, resp) ⇒ resp, http.Head(), testResponse = false)
    readResponse(response)
  }

  def rmFile(server: http.Server, path: String)(implicit httpEffect: Effect[http.HTTP]): Unit = http.read(server, path, http.Delete())
  def rmDirectory(server: Server, path: String)(implicit httpEffect: Effect[http.HTTP]): Unit = http.read(server, path, http.Delete(headers = Seq("Depth" -> "infinity")))
  def mkDirectory(server: Server, path: String)(implicit httpEffect: Effect[http.HTTP]): Unit = http.read(server, path, http.MkCol())
  def mv(server: Server, from: String, to: String)(implicit httpEffect: Effect[http.HTTP]): Unit = http.read(server, from, http.Move(to))

  def writeStream(server: Server, is: () ⇒ InputStream, path: String, redirect: Boolean = true)(implicit httpEffect: Effect[http.HTTP]): Unit = {
    def redirectedServer =
      if (!redirect) server
      else {
        val diskURI = httpEffect().request(server, path, WebDAV.getRedirectURI, http.Put(() ⇒ WebDAV.emptyStream(), headers = Seq(http.Headers.expectContinue)))
        http.Server.copy(server)(url = diskURI)
      }

    val s = redirectedServer
    http.read(s, "", http.Put(is))
  }

  def read(server: Server, path: String, method: http.HTTPMethod = http.Get())(implicit httpEffect: Effect[http.HTTP]) = http.read(server, path, method)
  def readStream[T](server: Server, path: String, f: InputStream ⇒ T, method: http.HTTPMethod = http.Get())(implicit httpEffect: Effect[http.HTTP]): T =
    http.readStream[T](server, path, f, method)

  import java.time.ZoneId

  import scala.util.Try
  import scala.xml.{ Node, XML }

  object WebDAV {

    def gmt = ZoneId.of("GMT")

    private def dateFormats = {
      import java.time.format._
      def createFormat(f: String) = DateTimeFormatter.ofPattern(f).withLocale(java.util.Locale.US).withZone(gmt)

      Vector(
        "yyyy-MM-dd'T'HH:mm:ss'Z'",
        "EEE, dd MMM yyyy HH:mm:ss zzz",
        //"yyyy-MM-dd'T'HH:mm:ss.sss'Z'",
        "yyyy-MM-dd'T'HH:mm:ssZ",
        "EEE MMM dd HH:mm:ss zzz yyyy",
        //      "EEEEEE, dd-MMM-yy HH:mm:ss zzz",
        "EEE MMMM d HH:mm:ss yyyy").map(createFormat)
    }

    private def parseDate(s: String) = {
      import java.time._
      dateFormats.view.flatMap { format ⇒ Try { LocalDateTime.parse(s, format) }.toOption }.headOption
    }

    case class Prop(
      displayName: String,
      isCollection: Boolean,
      modified: java.time.LocalDateTime)

    def parsePropsResponse(r: InputStream) = {
      import scala.xml.pull._
      import scala.io._
      val er = new XMLEventReader(Source.fromInputStream(r))
      var props = ListBuffer[Prop]()

      while (er.hasNext) {
        er.next() match {
          case EvElemStart(_, "prop", _, _) ⇒
            def end(x: XMLEvent) = x match {
              case EvElemEnd(_, "prop") ⇒ true
              case _                    ⇒ false
            }

            var x = er.next()
            var displayName: Option[String] = None
            var isCollection: Option[Boolean] = None
            var lastModified: Option[LocalDateTime] = None

            while (!end(x)) {
              x match {
                case EvElemStart(_, "displayname", _, _)     ⇒ displayName = Some(er.next().asInstanceOf[EvText].text)
                case EvElemStart(_, "iscollection", _, _)    ⇒ isCollection = Some(er.next().asInstanceOf[EvText].text == "1")
                case EvElemStart(_, "getlastmodified", _, _) ⇒ lastModified = parseDate(er.next().asInstanceOf[EvText].text)
                case _                                       ⇒
              }
              x = er.next()
            }

            props += Prop(displayName.get, isCollection.get, lastModified.get)
          case _ ⇒
        }
      }

      props.toVector
    }

    def emptyStream() = new java.io.ByteArrayInputStream(Array())
    def getRedirectURI(put: HttpRequest, redirect: HttpResponse) =
      new DefaultRedirectStrategy().getLocationURI(put, redirect, new HttpClientContext())

    def doNothing(put: HttpRequest, redirect: HttpResponse) = {}

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy