<commit_before>// @(#)root/dcache:$Name: v3-03-05 $:$Id: TDCacheFile.cxx,v 1.2 2002/03/25 16:43:16 rdm Exp $
// Author: Grzegorz Mazur   20/01/2002

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TDCacheFile                                                          //
//                                                                      //
// A TDCacheFile is like a normal TFile except that it may read and     //
// write its data via a dCache server (for more on the dCache daemon    //
// see http://www-dcache.desy.de/. Given a path which doesn't belong    //
// to the dCache managed filesystem, it falls back to the ordinary      //
// TFile behaviour.                                                     //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TDCacheFile.h"
#include "TError.h"
#include "TSystem.h"
#include "TROOT.h"

#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

#include <dcap.h>

//______________________________________________________________________________
static const char* const DCACHE_PREFIX = "dcache:";
static const size_t DCACHE_PREFIX_LEN = strlen(DCACHE_PREFIX);
static const char* const DCAP_PREFIX = "dcap:";
static const size_t DCAP_PREFIX_LEN = strlen(DCAP_PREFIX);

//______________________________________________________________________________
ClassImp(TDCacheFile)

//______________________________________________________________________________
TDCacheFile::TDCacheFile(const char *path, Option_t *option,
                         const char *ftitle, Int_t compress):
   TFile(path, "NET", ftitle, compress)
{
   // get rid of the optional URI
   if (!strncmp(path, DCACHE_PREFIX, DCACHE_PREFIX_LEN))
      path += 7;

   fOption = option;
   fOffset = 0;

   Bool_t create = kFALSE;
   if (!fOption.CompareTo("NEW", TString::kIgnoreCase) ||
       !fOption.CompareTo("CREATE", TString::kIgnoreCase))
      create = kTRUE;
   Bool_t recreate = fOption.CompareTo("RECREATE", TString::kIgnoreCase)
      ? kFALSE : kTRUE;
   Bool_t update   = fOption.CompareTo("UPDATE", TString::kIgnoreCase)
      ? kFALSE : kTRUE;
   Bool_t read     = fOption.CompareTo("READ", TString::kIgnoreCase)
      ? kFALSE : kTRUE;
   if (!create && !recreate && !update && !read) {
      read    = kTRUE;
      fOption = "READ";
   }

   const char *fname;

   if (!strncmp(path, DCAP_PREFIX, DCAP_PREFIX_LEN)) {
      // Ugh, no PNFS support
      if (create || recreate || update) {
         Error("TDCacheFile", "Without PNFS support only reading access is allowed.");
         goto zombie;
      }
      SetName(path);
      fname = GetName();
   } else {
      // Metadata provided by PNFS
      if ((fname = gSystem->ExpandPathName(path))) {
         SetName(fname);
         delete [] (char*)fname;
         fname = GetName();
      } else {
         Error("TDCacheFile", "error expanding path %s", path);
         goto zombie;
      }

      if (recreate) {
         if (!gSystem->AccessPathName(fname, kFileExists))
            gSystem->Unlink(fname);
         recreate = kFALSE;
         create   = kTRUE;
         fOption  = "CREATE";
      }
      if (create && !gSystem->AccessPathName(fname, kFileExists)) {
         Error("TDCacheFile", "file %s already exists", fname);
         goto zombie;
      }
      if (update) {
         if (gSystem->AccessPathName(fname, kFileExists)) {
            update = kFALSE;
            create = kTRUE;
         }
         if (update && gSystem->AccessPathName(fname, kWritePermission)) {
            Error("TDCacheFile", "no write permission, could not open file %s", fname);
            goto zombie;
         }
      }
      if (read) {
         if (gSystem->AccessPathName(fname, kFileExists)) {
            Error("TDCacheFile", "file %s does not exist", fname);
            goto zombie;
         }
         if (gSystem->AccessPathName(fname, kReadPermission)) {
            Error("TDCacheFile", "no read permission, could not open file %s", fname);
            goto zombie;
         }
      }
   }
   // Connect to file system stream
   if (create || update) {
#ifndef WIN32
      fD = SysOpen(fname, O_RDWR | O_CREAT, 0644);
#else
      fD = SysOpen(fname, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TDCacheFile", "file %s can not be opened", fname);
         goto zombie;
      }
      fWritable = kTRUE;
   } else {
#ifndef WIN32
      fD = SysOpen(fname, O_RDONLY, 0644);
#else
      fD = SysOpen(fname, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TFile", "file %s can not be opened for reading", fname);
         goto zombie;
      }
      fWritable = kFALSE;
   }

   // Disable dCache read-ahead buffer, as it doesn't cooperate well
   // with usually non-sequential file access pattern typical for
   // TFile. TCache performs much better, so UseCache() should be used
   // instead.
   dc_noBuffering(fD);

   Init(create);

   return;

zombie:
   // error in file opening occured, make this object a zombie
   MakeZombie();
   gDirectory = gROOT;
}

//______________________________________________________________________________
TDCacheFile::~TDCacheFile()
{
   // Close and cleanup dCache file.

   Close();
}

//______________________________________________________________________________
Bool_t TDCacheFile::ReadBuffer(char *buf, Int_t len)
{
   // Read specified byte range from remote file via dCache daemon.
   // Returns kTRUE in case of error.

   if (fCache) {
      Int_t st;
      Seek_t off = fOffset;
      if ((st = fCache->ReadBuffer(fOffset, buf, len)) < 0) {
         Error("ReadBuffer", "error reading from cache");
         return kTRUE;
      }
      if (st > 0) {
         // fOffset might have been changed via TCache::ReadBuffer(), reset it
         fOffset = off + len;
         return kFALSE;
      }
   }

   return TFile::ReadBuffer(buf, len);
}

//______________________________________________________________________________
Bool_t TDCacheFile::WriteBuffer(const char *buf, Int_t len)
{
   // Write specified byte range to remote file via dCache daemon.
   // Returns kTRUE in case of error.

   if (!IsOpen() || !fWritable) return kTRUE;

   if (fCache) {
      Int_t st;
      Seek_t off = fOffset;
      if ((st = fCache->WriteBuffer(fOffset, buf, len)) < 0) {
         Error("WriteBuffer", "error writing to cache");
         return kTRUE;
      }
      if (st > 0) {
         // fOffset might have been changed via TCache::WriteBuffer(), reset it
         fOffset = off + len;
         return kFALSE;
      }
   }

   return TFile::WriteBuffer(buf, len);
}

//______________________________________________________________________________
Bool_t TDCacheFile::Stage(const char *path, UInt_t after, const char *location)
{
   // Stage() returns kTRUE on success and kFALSE on failure.

   if (!strncmp(path, DCACHE_PREFIX, DCACHE_PREFIX_LEN))
      path += 7;

   dc_errno = 0;

   if (dc_stage(path, after, location) == 0)
      return kTRUE;

   if (dc_errno != 0)
      gSystem->SetErrorStr(dc_strerror(dc_errno));
   else
      gSystem->SetErrorStr(strerror(errno));

   return kFALSE;
}

//______________________________________________________________________________
Bool_t TDCacheFile::CheckFile(const char *path, const char *location)
{
   // Note: Name of the method was changed to avoid collision with Check
   // macro #defined in ROOT.
   // CheckFile() returns kTRUE on success and kFALSE on failure.  In
   // case the file exists but is not cached, CheckFile() returns
   // kFALSE and errno is set to EAGAIN.

   if (!strncmp(path, DCACHE_PREFIX, DCACHE_PREFIX_LEN))
      path += 7;

   dc_errno = 0;

   if (dc_check(path, location) == 0)
      return kTRUE;

   if (dc_errno != 0)
      gSystem->SetErrorStr(dc_strerror(dc_errno));
   else
      gSystem->SetErrorStr(strerror(errno));

   return kFALSE;
}

//______________________________________________________________________________
void TDCacheFile::SetOpenTimeout(UInt_t n)
{
   dc_setOpenTimeout(n);
}

//______________________________________________________________________________
void TDCacheFile::SetOnError(OnErrorAction a)
{
   dc_setOnError(a);
}

//______________________________________________________________________________
void TDCacheFile::SetReplyHostName(const char *host_name)
{
   dc_setReplyHostName((char*)host_name);
}

//______________________________________________________________________________
const char *TDCacheFile::GetDcapVersion()
{
   return getDcapVersion();
}

//______________________________________________________________________________
Bool_t TDCacheFile::EnableSSL()
{
#ifdef DCAP_USE_SSL
   dc_enableSSL();
   return kTRUE;
#else
   return kFALSE;
#endif
}

//______________________________________________________________________________
Int_t TDCacheFile::SysOpen(const char *pathname, Int_t flags, UInt_t mode)
{
   dc_errno = 0;

   Int_t rc = dc_open(pathname, flags, (Int_t) mode);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysClose(Int_t fd)
{
   dc_errno = 0;

   Int_t rc = dc_close(fd);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysRead(Int_t fd, void *buf, Int_t len)
{
   fOffset += len;

   dc_errno = 0;

   Int_t rc = dc_read(fd, buf, len);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysWrite(Int_t fd, const void *buf, Int_t len)
{
   fOffset += len;

   dc_errno = 0;

   Int_t rc =  dc_write(fd, (char *)buf, len);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Seek_t TDCacheFile::SysSeek(Int_t fd, Seek_t offset, Int_t whence)
{
   switch (whence) {
   case SEEK_SET:
      if (offset == fOffset)
         return offset;
      else
         fOffset = offset;
      break;
   case SEEK_CUR:
      fOffset += offset;
      break;
   case SEEK_END:
      fOffset = fEND + offset;
      break;
   }

   dc_errno = 0;

   Int_t rc = dc_lseek(fd, offset, whence);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysSync(Int_t fd)
{
   // dCache always keep it's files sync'ed, so there's no need to
   // sync() them manually.

   return 0;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysStat(Int_t fd, Long_t *id, Long_t *size,
                           Long_t *flags, Long_t *modtime)
{
   // FIXME: dcap library doesn't (yet) provide any stat()
   // capabilities, relying on PNFS to provide all meta-level data. In
   // spite of this, it is possible to open a file which is not
   // accessible via PNFS. In such case we do some dirty tricks to
   // provide as much information as possible, but not everything can
   // really be done correctly.

   if (!strncmp(GetName(), DCAP_PREFIX, DCAP_PREFIX_LEN)) {
      // Ugh, no PNFS support.

      // Try to provide a unique id
      *id = ::Hash(GetName());

      // Funny way of checking the file size, isn't it?
      Seek_t offset = fOffset;
      *size = SysSeek(fd, 0, SEEK_END);
      SysSeek(fd, offset, SEEK_SET);

      *flags = 0;          // This one is easy, only ordinary files are allowed here
      *modtime = 0;        // FIXME: no way to get it right without dc_stat()
      return 0;
   } else {
      return TFile::SysStat(fd, id, size, flags, modtime);
   }
}

//______________________________________________________________________________
void TDCacheFile::ResetErrno() const
{
   // Method resetting the dc_errno and errno.

   dc_errno = 0;
   TSystem::ResetErrno();
}
<commit_msg>small mod in option handling.<commit_after>// @(#)root/dcache:$Name:  $:$Id: TDCacheFile.cxx,v 1.3 2002/07/19 11:41:41 rdm Exp $
// Author: Grzegorz Mazur   20/01/2002

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TDCacheFile                                                          //
//                                                                      //
// A TDCacheFile is like a normal TFile except that it may read and     //
// write its data via a dCache server (for more on the dCache daemon    //
// see http://www-dcache.desy.de/. Given a path which doesn't belong    //
// to the dCache managed filesystem, it falls back to the ordinary      //
// TFile behaviour.                                                     //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TDCacheFile.h"
#include "TError.h"
#include "TSystem.h"
#include "TROOT.h"

#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

#include <dcap.h>

//______________________________________________________________________________
static const char* const DCACHE_PREFIX = "dcache:";
static const size_t DCACHE_PREFIX_LEN = strlen(DCACHE_PREFIX);
static const char* const DCAP_PREFIX = "dcap:";
static const size_t DCAP_PREFIX_LEN = strlen(DCAP_PREFIX);

//______________________________________________________________________________
ClassImp(TDCacheFile)

//______________________________________________________________________________
TDCacheFile::TDCacheFile(const char *path, Option_t *option,
                         const char *ftitle, Int_t compress):
   TFile(path, "NET", ftitle, compress)
{
   // get rid of the optional URI
   if (!strncmp(path, DCACHE_PREFIX, DCACHE_PREFIX_LEN))
      path += 7;

   fOffset = 0;
   fOption = option;
   fOption.ToUpper();

   if (fOption == "NEW")
      fOption = "CREATE";

   Bool_t create   = (fOption == "CREATE") ? kTRUE : kFALSE;
   Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
   Bool_t update   = (fOption == "UPDATE") ? kTRUE : kFALSE;
   Bool_t read     = (fOption == "READ") ? kTRUE : kFALSE;
   if (!create && !recreate && !update && !read) {
      read    = kTRUE;
      fOption = "READ";
   }

   const char *fname;

   if (!strncmp(path, DCAP_PREFIX, DCAP_PREFIX_LEN)) {
      // Ugh, no PNFS support
      if (create || recreate || update) {
         Error("TDCacheFile", "Without PNFS support only reading access is allowed.");
         goto zombie;
      }
      SetName(path);
      fname = GetName();
   } else {
      // Metadata provided by PNFS
      if ((fname = gSystem->ExpandPathName(path))) {
         SetName(fname);
         delete [] (char*)fname;
         fname = GetName();
      } else {
         Error("TDCacheFile", "error expanding path %s", path);
         goto zombie;
      }

      if (recreate) {
         if (!gSystem->AccessPathName(fname, kFileExists))
            gSystem->Unlink(fname);
         recreate = kFALSE;
         create   = kTRUE;
         fOption  = "CREATE";
      }
      if (create && !gSystem->AccessPathName(fname, kFileExists)) {
         Error("TDCacheFile", "file %s already exists", fname);
         goto zombie;
      }
      if (update) {
         if (gSystem->AccessPathName(fname, kFileExists)) {
            update = kFALSE;
            create = kTRUE;
         }
         if (update && gSystem->AccessPathName(fname, kWritePermission)) {
            Error("TDCacheFile", "no write permission, could not open file %s", fname);
            goto zombie;
         }
      }
      if (read) {
         if (gSystem->AccessPathName(fname, kFileExists)) {
            Error("TDCacheFile", "file %s does not exist", fname);
            goto zombie;
         }
         if (gSystem->AccessPathName(fname, kReadPermission)) {
            Error("TDCacheFile", "no read permission, could not open file %s", fname);
            goto zombie;
         }
      }
   }
   // Connect to file system stream
   if (create || update) {
#ifndef WIN32
      fD = SysOpen(fname, O_RDWR | O_CREAT, 0644);
#else
      fD = SysOpen(fname, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TDCacheFile", "file %s can not be opened", fname);
         goto zombie;
      }
      fWritable = kTRUE;
   } else {
#ifndef WIN32
      fD = SysOpen(fname, O_RDONLY, 0644);
#else
      fD = SysOpen(fname, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TFile", "file %s can not be opened for reading", fname);
         goto zombie;
      }
      fWritable = kFALSE;
   }

   // Disable dCache read-ahead buffer, as it doesn't cooperate well
   // with usually non-sequential file access pattern typical for
   // TFile. TCache performs much better, so UseCache() should be used
   // instead.
   dc_noBuffering(fD);

   Init(create);

   return;

zombie:
   // error in file opening occured, make this object a zombie
   MakeZombie();
   gDirectory = gROOT;
}

//______________________________________________________________________________
TDCacheFile::~TDCacheFile()
{
   // Close and cleanup dCache file.

   Close();
}

//______________________________________________________________________________
Bool_t TDCacheFile::ReadBuffer(char *buf, Int_t len)
{
   // Read specified byte range from remote file via dCache daemon.
   // Returns kTRUE in case of error.

   if (fCache) {
      Int_t st;
      Seek_t off = fOffset;
      if ((st = fCache->ReadBuffer(fOffset, buf, len)) < 0) {
         Error("ReadBuffer", "error reading from cache");
         return kTRUE;
      }
      if (st > 0) {
         // fOffset might have been changed via TCache::ReadBuffer(), reset it
         fOffset = off + len;
         return kFALSE;
      }
   }

   return TFile::ReadBuffer(buf, len);
}

//______________________________________________________________________________
Bool_t TDCacheFile::WriteBuffer(const char *buf, Int_t len)
{
   // Write specified byte range to remote file via dCache daemon.
   // Returns kTRUE in case of error.

   if (!IsOpen() || !fWritable) return kTRUE;

   if (fCache) {
      Int_t st;
      Seek_t off = fOffset;
      if ((st = fCache->WriteBuffer(fOffset, buf, len)) < 0) {
         Error("WriteBuffer", "error writing to cache");
         return kTRUE;
      }
      if (st > 0) {
         // fOffset might have been changed via TCache::WriteBuffer(), reset it
         fOffset = off + len;
         return kFALSE;
      }
   }

   return TFile::WriteBuffer(buf, len);
}

//______________________________________________________________________________
Bool_t TDCacheFile::Stage(const char *path, UInt_t after, const char *location)
{
   // Stage() returns kTRUE on success and kFALSE on failure.

   if (!strncmp(path, DCACHE_PREFIX, DCACHE_PREFIX_LEN))
      path += 7;

   dc_errno = 0;

   if (dc_stage(path, after, location) == 0)
      return kTRUE;

   if (dc_errno != 0)
      gSystem->SetErrorStr(dc_strerror(dc_errno));
   else
      gSystem->SetErrorStr(strerror(errno));

   return kFALSE;
}

//______________________________________________________________________________
Bool_t TDCacheFile::CheckFile(const char *path, const char *location)
{
   // Note: Name of the method was changed to avoid collision with Check
   // macro #defined in ROOT.
   // CheckFile() returns kTRUE on success and kFALSE on failure.  In
   // case the file exists but is not cached, CheckFile() returns
   // kFALSE and errno is set to EAGAIN.

   if (!strncmp(path, DCACHE_PREFIX, DCACHE_PREFIX_LEN))
      path += 7;

   dc_errno = 0;

   if (dc_check(path, location) == 0)
      return kTRUE;

   if (dc_errno != 0)
      gSystem->SetErrorStr(dc_strerror(dc_errno));
   else
      gSystem->SetErrorStr(strerror(errno));

   return kFALSE;
}

//______________________________________________________________________________
void TDCacheFile::SetOpenTimeout(UInt_t n)
{
   dc_setOpenTimeout(n);
}

//______________________________________________________________________________
void TDCacheFile::SetOnError(OnErrorAction a)
{
   dc_setOnError(a);
}

//______________________________________________________________________________
void TDCacheFile::SetReplyHostName(const char *host_name)
{
   dc_setReplyHostName((char*)host_name);
}

//______________________________________________________________________________
const char *TDCacheFile::GetDcapVersion()
{
   return getDcapVersion();
}

//______________________________________________________________________________
Bool_t TDCacheFile::EnableSSL()
{
#ifdef DCAP_USE_SSL
   dc_enableSSL();
   return kTRUE;
#else
   return kFALSE;
#endif
}

//______________________________________________________________________________
Int_t TDCacheFile::SysOpen(const char *pathname, Int_t flags, UInt_t mode)
{
   dc_errno = 0;

   Int_t rc = dc_open(pathname, flags, (Int_t) mode);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysClose(Int_t fd)
{
   dc_errno = 0;

   Int_t rc = dc_close(fd);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysRead(Int_t fd, void *buf, Int_t len)
{
   fOffset += len;

   dc_errno = 0;

   Int_t rc = dc_read(fd, buf, len);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysWrite(Int_t fd, const void *buf, Int_t len)
{
   fOffset += len;

   dc_errno = 0;

   Int_t rc =  dc_write(fd, (char *)buf, len);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Seek_t TDCacheFile::SysSeek(Int_t fd, Seek_t offset, Int_t whence)
{
   switch (whence) {
   case SEEK_SET:
      if (offset == fOffset)
         return offset;
      else
         fOffset = offset;
      break;
   case SEEK_CUR:
      fOffset += offset;
      break;
   case SEEK_END:
      fOffset = fEND + offset;
      break;
   }

   dc_errno = 0;

   Int_t rc = dc_lseek(fd, offset, whence);

   if (rc < 0) {
      if (dc_errno != 0)
         gSystem->SetErrorStr(dc_strerror(dc_errno));
      else
         gSystem->SetErrorStr(strerror(errno));
   }

   return rc;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysSync(Int_t fd)
{
   // dCache always keep it's files sync'ed, so there's no need to
   // sync() them manually.

   return 0;
}

//______________________________________________________________________________
Int_t TDCacheFile::SysStat(Int_t fd, Long_t *id, Long_t *size,
                           Long_t *flags, Long_t *modtime)
{
   // FIXME: dcap library doesn't (yet) provide any stat()
   // capabilities, relying on PNFS to provide all meta-level data. In
   // spite of this, it is possible to open a file which is not
   // accessible via PNFS. In such case we do some dirty tricks to
   // provide as much information as possible, but not everything can
   // really be done correctly.

   if (!strncmp(GetName(), DCAP_PREFIX, DCAP_PREFIX_LEN)) {
      // Ugh, no PNFS support.

      // Try to provide a unique id
      *id = ::Hash(GetName());

      // Funny way of checking the file size, isn't it?
      Seek_t offset = fOffset;
      *size = SysSeek(fd, 0, SEEK_END);
      SysSeek(fd, offset, SEEK_SET);

      *flags = 0;          // This one is easy, only ordinary files are allowed here
      *modtime = 0;        // FIXME: no way to get it right without dc_stat()
      return 0;
   } else {
      return TFile::SysStat(fd, id, size, flags, modtime);
   }
}

//______________________________________________________________________________
void TDCacheFile::ResetErrno() const
{
   // Method resetting the dc_errno and errno.

   dc_errno = 0;
   TSystem::ResetErrno();
}
<|endoftext|>


<commit_before>
#include "microsoftpush.hh"
#include "log/logmanager.hh"
#include "sofia-sip/base64.h"
#include <string.h>
#include <iostream>
#include <vector>

using namespace std;

WindowsPhonePushNotificationRequest::WindowsPhonePushNotificationRequest(const PushInfo &pinfo)
: PushNotificationRequest(pinfo.mAppId, pinfo.mType), mPushInfo(pinfo) {

    if(pinfo.mType == "wp"){
        createHTTPRequest("");
    }
}

void WindowsPhonePushNotificationRequest::createHTTPRequest(const std::string &access_token) {
    const string &host = mPushInfo.mAppId;
    char decodeUri[512] = {0};

    bool is_message = mPushInfo.mEvent == PushInfo::Message;
    const std::string &message = mPushInfo.mText;
    const std::string &sender_name = mPushInfo.mFromName;
    const std::string &sender_uri = mPushInfo.mFromUri;
    ostringstream httpBody;
    ostringstream httpHeader;

    if(mPushInfo.mType == "w10") {
        char unescapedUrl[512];
        url_unescape(unescapedUrl, mPushInfo.mDeviceToken.c_str());
        base64_d(decodeUri, sizeof(decodeUri), unescapedUrl);
        string query(decodeUri);
        if (is_message) {
            // We have to send the content of the message and the name of the sender.
            // We also need the sender address to be able to display the full chat view in case the receiver click the
            // toast.

            httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                << "<toast launch=\"" << "chat?sip=" << sender_uri  << "\">"
                << "<visual>"
                << "<binding template =\"ToastGeneric\">"
                << "<text>"	<< sender_uri << "</text>"
                << "<text>" << message << "</text>"
                << "</binding>"
                <<  "</visual>"
                << "</toast>";
        } else {
            // No need to specify name or number, this PN will only wake up linphone.
            httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
        		<< "<toast launch=\"" << sender_uri << "\" >"
                << "<visual>"
                << "<binding template=\"ToastGeneric\" >"
                << "<text>Incoming Call</text>"
                << "<text>" << sender_uri << "</text>"
                << "</binding>"
        		<< "</visual>"
                << "</toast>";
        }

        httpHeader << "POST " << query << " HTTP/1.1\r\n"
            << "Authorization: Bearer " << access_token << "\r\n"
            << "X-WNS-RequestForStatus: true\r\n"
            << "X-WNS-Type: wns/toast\r\n"
            << "Content-Type: text/xml\r\n"
            << "Host: " << host << "\r\n"
            << "Content-Length: " << httpBody.str().size() << "\r\n\r\n";

    } else if(mPushInfo.mType == "wp"){
        if(is_message){
            httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?><wp:Notification "
                "xmlns:wp=\"WPNotification\"><wp:Toast><wp:Text1>"
                << sender_name << "</wp:Text1><wp:Text2>" << message << "</wp:Text2><wp:Param>"
            	<< "/Views/Chat.xaml?sip=" << sender_uri << "</wp:Param></wp:Toast></wp:Notification>";

            // Notification class 2 is the type for toast notifitcation.
            httpHeader
                << "POST " << mPushInfo.mDeviceToken << " HTTP/1.1\r\nHost:" << host
                << "\r\nX-WindowsPhone-Target:toast\r\nX-NotificationClass:2\r\nContent-Type:text/xml\r\nContent-Length:"
                << httpBody.str().size() << "\r\n\r\n";
        } else {
            httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                << "<IncomingCall><Name></Name><Number></Number></IncomingCall>";

            // Notification class 4 is the type for VoIP incoming call.
            httpHeader << "POST " << mPushInfo.mDeviceToken << " HTTP/1.1\r\nHost:" << host
            	<< "\r\nX-NotificationClass:4\r\nContent-Type:text/xml\r\nContent-Length:" << httpBody.str().size()
            	<< "\r\n\r\n";
        }
    }

    mHttpHeader = httpHeader.str();
    mHttpBody = httpBody.str();

    SLOGD << "PNR " << this << " POST header is " << mHttpHeader;
    SLOGD << "PNR " << this << " POST body is " << mHttpBody;
}

void WindowsPhonePushNotificationRequest::createPushNotification() {
	int headerLength = mHttpHeader.length();
	int bodyLength = mHttpBody.length();

	mBuffer.clear();
	mBuffer.resize(headerLength + bodyLength);

	char *binaryMessageBuff = &mBuffer[0];
	char *binaryMessagePt = binaryMessageBuff;

	memcpy(binaryMessagePt, &mHttpHeader[0], headerLength);
	binaryMessagePt += headerLength;

	memcpy(binaryMessagePt, &mHttpBody[0], bodyLength);
	binaryMessagePt += bodyLength;
}

const vector<char> &WindowsPhonePushNotificationRequest::getData() {
	createPushNotification();
	return mBuffer;
}

std::string WindowsPhonePushNotificationRequest::isValidResponse(const string &str) {
	string line;
	istringstream iss(str);
	bool valid = false, connect = false, notif = false;
	while (getline(iss, line)) {
        if(mPushInfo.mType == "w10"){
            valid |= (line.find("HTTP/1.1 200 OK") != string::npos);
            connect |= (line.find("X-WNS-DEVICECONNECTIONSTATUS: connected") != string::npos);
            notif |= (line.find("X-WNS-STATUS: received") != string::npos);
        } else {
            connect |= (line.find("X-DeviceConnectionStatus: connected") != string::npos);
            notif |= (line.find("X-NotificationStatus: Received") != string::npos);
            valid |= (line.find("X-SubscriptionStatus: Active") != string::npos);

        }

		auto it = line.find("X-WNS-ERROR-DESCRIPTION");
		if (it != string::npos) {
			return line.substr(line.find(' '));
		}
	}
	if (!valid) return "Unexpected HTTP response value (not 200 OK)";
	if (!connect) return "Device connection status not set to connected";
	if (!notif) return "Notification not received by server";
	return "";
}
<commit_msg>fix potential buffer overflow and reformat source code<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "microsoftpush.hh"
#include "log/logmanager.hh"
#include "sofia-sip/base64.h"
#include <string.h>
#include <iostream>
#include <vector>

using namespace std;

WindowsPhonePushNotificationRequest::WindowsPhonePushNotificationRequest ( const PushInfo &pinfo )
	: PushNotificationRequest ( pinfo.mAppId, pinfo.mType ), mPushInfo ( pinfo ) {

	if ( pinfo.mType == "wp" ) {
		createHTTPRequest ( "" );
	}
}

void WindowsPhonePushNotificationRequest::createHTTPRequest ( const std::string &access_token ) {
	const string &host = mPushInfo.mAppId;
	char decodeUri[512] = {0};

	bool is_message = mPushInfo.mEvent == PushInfo::Message;
	const std::string &message = mPushInfo.mText;
	const std::string &sender_name = mPushInfo.mFromName;
	const std::string &sender_uri = mPushInfo.mFromUri;
	ostringstream httpBody;
	ostringstream httpHeader;

	if ( mPushInfo.mType == "w10" ) {
		string unescapedUrl;

		unescapedUrl.resize ( mPushInfo.mDeviceToken.size() );
		url_unescape ( &unescapedUrl[0], mPushInfo.mDeviceToken.c_str() );
		base64_d ( decodeUri, sizeof ( decodeUri ), unescapedUrl.c_str() );
		string query ( decodeUri );
		if ( is_message ) {
			// We have to send the content of the message and the name of the sender.
			// We also need the sender address to be able to display the full chat view in case the receiver click the
			// toast.

			httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
					 << "<toast launch=\"" << "chat?sip=" << sender_uri  << "\">"
					 << "<visual>"
					 << "<binding template =\"ToastGeneric\">"
					 << "<text>"	<< sender_uri << "</text>"
					 << "<text>" << message << "</text>"
					 << "</binding>"
					 <<  "</visual>"
					 << "</toast>";
		} else {
			// No need to specify name or number, this PN will only wake up linphone.
			httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
					 << "<toast launch=\"" << sender_uri << "\" >"
					 << "<visual>"
					 << "<binding template=\"ToastGeneric\" >"
					 << "<text>Incoming Call</text>"
					 << "<text>" << sender_uri << "</text>"
					 << "</binding>"
					 << "</visual>"
					 << "</toast>";
		}

		httpHeader << "POST " << query << " HTTP/1.1\r\n"
				   << "Authorization: Bearer " << access_token << "\r\n"
				   << "X-WNS-RequestForStatus: true\r\n"
				   << "X-WNS-Type: wns/toast\r\n"
				   << "Content-Type: text/xml\r\n"
				   << "Host: " << host << "\r\n"
				   << "Content-Length: " << httpBody.str().size() << "\r\n\r\n";

	} else if ( mPushInfo.mType == "wp" ) {
		if ( is_message ) {
			httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?><wp:Notification "
					 "xmlns:wp=\"WPNotification\"><wp:Toast><wp:Text1>"
					 << sender_name << "</wp:Text1><wp:Text2>" << message << "</wp:Text2><wp:Param>"
					 << "/Views/Chat.xaml?sip=" << sender_uri << "</wp:Param></wp:Toast></wp:Notification>";

			// Notification class 2 is the type for toast notifitcation.
			httpHeader
					<< "POST " << mPushInfo.mDeviceToken << " HTTP/1.1\r\nHost:" << host
					<< "\r\nX-WindowsPhone-Target:toast\r\nX-NotificationClass:2\r\nContent-Type:text/xml\r\nContent-Length:"
					<< httpBody.str().size() << "\r\n\r\n";
		} else {
			httpBody << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
					 << "<IncomingCall><Name></Name><Number></Number></IncomingCall>";

			// Notification class 4 is the type for VoIP incoming call.
			httpHeader << "POST " << mPushInfo.mDeviceToken << " HTTP/1.1\r\nHost:" << host
					   << "\r\nX-NotificationClass:4\r\nContent-Type:text/xml\r\nContent-Length:" << httpBody.str().size()
					   << "\r\n\r\n";
		}
	}

	mHttpHeader = httpHeader.str();
	mHttpBody = httpBody.str();

	SLOGD << "PNR " << this << " POST header is " << mHttpHeader;
	SLOGD << "PNR " << this << " POST body is " << mHttpBody;
}

void WindowsPhonePushNotificationRequest::createPushNotification() {
	int headerLength = mHttpHeader.length();
	int bodyLength = mHttpBody.length();

	mBuffer.clear();
	mBuffer.resize ( headerLength + bodyLength );

	char *binaryMessageBuff = &mBuffer[0];
	char *binaryMessagePt = binaryMessageBuff;

	memcpy ( binaryMessagePt, &mHttpHeader[0], headerLength );
	binaryMessagePt += headerLength;

	memcpy ( binaryMessagePt, &mHttpBody[0], bodyLength );
	binaryMessagePt += bodyLength;
}

const vector<char> &WindowsPhonePushNotificationRequest::getData() {
	createPushNotification();
	return mBuffer;
}

std::string WindowsPhonePushNotificationRequest::isValidResponse ( const string &str ) {
	string line;
	istringstream iss ( str );
	bool valid = false, connect = false, notif = false;
	while ( getline ( iss, line ) ) {
		if ( mPushInfo.mType == "w10" ) {
			valid |= ( line.find ( "HTTP/1.1 200 OK" ) != string::npos );
			connect |= ( line.find ( "X-WNS-DEVICECONNECTIONSTATUS: connected" ) != string::npos );
			notif |= ( line.find ( "X-WNS-STATUS: received" ) != string::npos );
		} else {
			connect |= ( line.find ( "X-DeviceConnectionStatus: connected" ) != string::npos );
			notif |= ( line.find ( "X-NotificationStatus: Received" ) != string::npos );
			valid |= ( line.find ( "X-SubscriptionStatus: Active" ) != string::npos );

		}

		auto it = line.find ( "X-WNS-ERROR-DESCRIPTION" );
		if ( it != string::npos ) {
			return line.substr ( line.find ( ' ' ) );
		}
	}
	if ( !valid ) return "Unexpected HTTP response value (not 200 OK)";
	if ( !connect ) return "Device connection status not set to connected";
	if ( !notif ) return "Notification not received by server";
	return "";
}
<|endoftext|>


<commit_before>/*
 * PrimeSieveGUI_menu.cpp -- This file is part of primesieve
 *
 * Copyright (C) 2012 Kim Walisch, <kim.walisch@gmail.com>
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>
 */

#include "PrimeSieveGUI.h"
#include "ui_PrimeSieveGUI.h"
#include "../soe/ParallelPrimeSieve.h"

#include <QMessageBox>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QFileDialog>

/**
 * Initialize the menu items.
 */
void PrimeSieveGUI::createMenuActions(QVector<QString>& primeText) {
  // file actions
  saveAct_ = new QAction("&Save", this);
  saveAct_->setShortcut(tr("Ctrl+S"));
  quitAct_ = new QAction("&Quit", this);
  quitAct_->setShortcut(tr("Ctrl+Q"));

  // count actions
  countAct_.push_back(new QAction(primeText[0], this));
  countAct_.back()->setCheckable(true);
  countAct_.push_back(new QAction("Prime k-tuplets", this));
  countAct_.back()->setCheckable(true);
  // default count prime numbers
  countAct_.front()->setChecked(true);

  // radio button like behaviour for print actions
  alignmentGroup_ = new QActionGroup(this);
  alignmentGroup_->setExclusive(false);

  // print actions
  for (int i = 0; i < primeText.size(); i++) {
    printAct_.push_back(new QAction(primeText[i], this));
    printAct_.back()->setCheckable(true);
    alignmentGroup_->addAction(printAct_.back());
  }
  // about action
  aboutAct_ = new QAction("About", this);
}

/**
 * Create the menu bar with 'File', 'Count', 'Print' and 'Help'
 * menu options.
 */
void PrimeSieveGUI::createMenu(QVector<QString>& primeText) {
  this->createMenuActions(primeText);

  fileMenu_ = menuBar()->addMenu("&File");
  fileMenu_->addAction(saveAct_);
  fileMenu_->addAction(quitAct_);
  countMenu_ = menuBar()->addMenu("&Count");
  for (int i = 0; i < countAct_.size(); i++)
    countMenu_->addAction(countAct_[i]);
  printMenu_ = menuBar()->addMenu("&Print");
  for (int i = 0; i < printAct_.size(); i++)
    printMenu_->addAction(printAct_[i]);
  helpMenu_ = menuBar()->addMenu("&Help");
  helpMenu_->addAction(aboutAct_);
}

/**
 * Return the count and print menu settings as bit flags.
 */
int PrimeSieveGUI::getMenuSettings() {
  int flags = 0;
  // get count settings
  if (countAct_[0]->isChecked())
    flags |= COUNT_PRIMES;
  if (countAct_[1]->isChecked())
    flags |= COUNT_KTUPLETS;
  // get print settings
  for (int i = 0; i < printAct_.size(); i++)
    if (printAct_[i]->isChecked())
      flags |= PRINT_PRIMES << i;
  return flags;
}

/**
 * Disable the "Threads" ComboBox and the "Auto set" CheckBox and
 * set to 1 Threads for printing (else invert).
 */
void PrimeSieveGUI::printMenuClicked(QAction* qAct) {
  // disable other print options
  for (int i = 0; i < printAct_.size(); i++) {
    if (printAct_[i] != qAct)
      printAct_[i]->setChecked(false);
  }
  ui->autoSetCheckBox->setDisabled(qAct->isChecked());
  if (qAct->isChecked()) {
    ui->autoSetCheckBox->setChecked(true);
    ui->threadsComboBox->setCurrentIndex(0);
  }
  ui->threadsComboBox->setDisabled(qAct->isChecked());
  this->autoSetThreads();
}

/**
 * Save the content of the textEdit to a file.
 */
void PrimeSieveGUI::saveToFile() {
  // Qt uses '/' internally, also for Windows
  QString currentPath = QDir::currentPath() + "/Unsaved Document 1";
  QString fileName = QFileDialog::getSaveFileName(this, "Save As...", currentPath, "All Files (*)");
  QFile file(fileName);
  if (file.open(QFile::WriteOnly | QFile::Text)) {
    QTextStream textStream(&file);
    textStream << ui->textEdit->toPlainText();
  }
}

void PrimeSieveGUI::showAboutDialog() {
  QString title = "About " + APPLICATION_NAME;
  QString message = "
" + APPLICATION_NAME + " " + PRIMESIEVE_VERSION + "
"
      + "
Copyright © " + PRIMESIEVE_YEAR + " Kim Walisch
"
      + "
" + APPLICATION_ABOUT + "
"
      + "" + APPLICATION_HOMEPAGE + "";
  QMessageBox::about(this, title, message);
}
<commit_msg>fixed typing error<commit_after>/*
 * PrimeSieveGUI_menu.cpp -- This file is part of primesieve
 *
 * Copyright (C) 2012 Kim Walisch, <kim.walisch@gmail.com>
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>
 */

#include "PrimeSieveGUI.h"
#include "ui_PrimeSieveGUI.h"
#include "../soe/ParallelPrimeSieve.h"

#include <QMessageBox>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QFileDialog>

/**
 * Initialize the menu items.
 */
void PrimeSieveGUI::createMenuActions(QVector<QString>& primeText) {
  // file actions
  saveAct_ = new QAction("&Save", this);
  saveAct_->setShortcut(tr("Ctrl+S"));
  quitAct_ = new QAction("&Quit", this);
  quitAct_->setShortcut(tr("Ctrl+Q"));

  // count actions
  countAct_.push_back(new QAction(primeText[0], this));
  countAct_.back()->setCheckable(true);
  countAct_.push_back(new QAction("Prime k-tuplets", this));
  countAct_.back()->setCheckable(true);
  // default count prime numbers
  countAct_.front()->setChecked(true);

  // radio button like behaviour for print actions
  alignmentGroup_ = new QActionGroup(this);
  alignmentGroup_->setExclusive(false);

  // print actions
  for (int i = 0; i < primeText.size(); i++) {
    printAct_.push_back(new QAction(primeText[i], this));
    printAct_.back()->setCheckable(true);
    alignmentGroup_->addAction(printAct_.back());
  }
  // about action
  aboutAct_ = new QAction("About", this);
}

/**
 * Create the menu bar with 'File', 'Count', 'Print' and 'Help'
 * menu options.
 */
void PrimeSieveGUI::createMenu(QVector<QString>& primeText) {
  this->createMenuActions(primeText);

  fileMenu_ = menuBar()->addMenu("&File");
  fileMenu_->addAction(saveAct_);
  fileMenu_->addAction(quitAct_);
  countMenu_ = menuBar()->addMenu("&Count");
  for (int i = 0; i < countAct_.size(); i++)
    countMenu_->addAction(countAct_[i]);
  printMenu_ = menuBar()->addMenu("&Print");
  for (int i = 0; i < printAct_.size(); i++)
    printMenu_->addAction(printAct_[i]);
  helpMenu_ = menuBar()->addMenu("&Help");
  helpMenu_->addAction(aboutAct_);
}

/**
 * Return the count and print menu settings as bit flags.
 */
int PrimeSieveGUI::getMenuSettings() {
  int flags = 0;
  // get count settings
  if (countAct_[0]->isChecked())
    flags |= COUNT_PRIMES;
  if (countAct_[1]->isChecked())
    flags |= COUNT_KTUPLETS;
  // get print settings
  for (int i = 0; i < printAct_.size(); i++)
    if (printAct_[i]->isChecked())
      flags |= PRINT_PRIMES << i;
  return flags;
}

/**
 * Disable the "Threads" ComboBox and the "Auto set" CheckBox and
 * set to 1 Threads for printing (else invert).
 */
void PrimeSieveGUI::printMenuClicked(QAction* qAct) {
  // disable other print options
  for (int i = 0; i < printAct_.size(); i++) {
    if (printAct_[i] != qAct)
      printAct_[i]->setChecked(false);
  }
  ui->autoSetCheckBox->setDisabled(qAct->isChecked());
  if (qAct->isChecked()) {
    ui->autoSetCheckBox->setChecked(true);
    ui->threadsComboBox->setCurrentIndex(0);
  }
  ui->threadsComboBox->setDisabled(qAct->isChecked());
  this->autoSetThreads();
}

/**
 * Save the content of the textEdit to a file.
 */
void PrimeSieveGUI::saveToFile() {
  // Qt uses '/' internally, also for Windows
  QString currentPath = QDir::currentPath() + "/Unsaved Document 1";
  QString fileName = QFileDialog::getSaveFileName(this, "Save As...", currentPath, "All Files (*)");
  QFile file(fileName);
  if (file.open(QFile::WriteOnly | QFile::Text)) {
    QTextStream textStream(&file);
    textStream << ui->textEdit->toPlainText();
  }
}

void PrimeSieveGUI::showAboutDialog() {
  QString year = QString::number(PRIMESIEVE_YEAR);
  QString title = "About " + APPLICATION_NAME;
  QString message = "
" + APPLICATION_NAME + " " + PRIMESIEVE_VERSION + "
"
      + "
Copyright © " + year + " Kim Walisch
"
      + "
" + APPLICATION_ABOUT + "
"
      + "" + APPLICATION_HOMEPAGE + "";
  QMessageBox::about(this, title, message);
}
<|endoftext|>


<commit_before>#include "offerwhitelisttablemodel.h"

#include "guiutil.h"
#include "walletmodel.h"

#include "wallet/wallet.h"
#include "base58.h"

#include <QFont>
using namespace std;

struct MyOfferWhitelistTableEntry
{
	QString offer;
    QString alias;
	QString expires;
	QString discount;

    MyOfferWhitelistTableEntry() {}
    MyOfferWhitelistTableEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount):
        offer(offer), alias(alias), expires(expires),discount(discount) {}
};

// Private implementation
class MyOfferWhitelistTablePriv
{
public:
    QList<MyOfferWhitelistTableEntry> cachedEntryTable;
    MyOfferWhitelistTableModel *parent;

    OfferWhitelistTablePriv(MyOfferWhitelistTableModel *parent):
        parent(parent) {}

    void updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
    {
		if(!parent)
		{
			return;
		}

        switch(status)
        {
        case CT_NEW:

            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedEntryTable.insert(lowerIndex, MyOfferWhitelistTableEntry(offer, alias, expires, discount));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            lower->offer = offer;
			lower->alias = alias;
			lower->expires = expires;
			lower->discount = discount;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:

            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedEntryTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedEntryTable.size();
    }

    OfferWhitelistTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedEntryTable.size())
        {
            return &cachedEntryTable[idx];
        }
        else
        {
            return 0;
        }
    }
};

MyOfferWhitelistTableModel::MyOfferWhitelistTableModel(WalletModel *parent) :
    QAbstractTableModel(parent)
{
    columns << tr("Offer") << tr("Alias") << tr("Discount") << tr("Expires In");
    priv = new OfferWhitelistTablePriv(this);

}

MyOfferWhitelistTableModel::~MyOfferWhitelistTableModel()
{
    delete priv;
}
int MyOfferWhitelistTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MyOfferWhitelistTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MyOfferWhitelistTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    OfferWhitelistTableEntry *rec = static_cast<OfferWhitelistTableEntry*>(index.internalPointer());

    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            return rec->offer;
        case Alias:
            return rec->alias;
        case Discount:
            return rec->discount;
        case Expires:
            return rec->expires;
        }
    }
    else if (role == AliasRole)
    {
        return rec->alias;
    }
    return QVariant();
}

bool MyOfferWhitelistTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    OfferWhitelistTableEntry *rec = static_cast<OfferWhitelistTableEntry*>(index.internalPointer());

    editStatus = OK;

    if(role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            // Do nothing, if old value == new value
            if(rec->offer == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Alias:
            // Do nothing, if old value == new value
            if(rec->alias == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Discount:
            // Do nothing, if old value == new value
            if(rec->discount == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
       case Expires:
            // Do nothing, if old value == new value
            if(rec->expires == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            break;
        return true;
		}
    }
    return false;
}

QVariant MyOfferWhitelistTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags MyOfferWhitelistTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;
    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    return retval;
}

QModelIndex MyOfferWhitelistTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    OfferWhitelistTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MyOfferWhitelistTableModel::updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
{
    priv->updateEntry(offer, alias, expires, discount, status);
}

QString MyOfferWhitelistTableModel::addRow(const QString &offer, const QString &alias, const QString &expires,const QString &discount)
{
    std::string strAlias = alias.toStdString();
    editStatus = OK;
    return QString::fromStdString(strAlias);
}
void MyOfferWhitelistTableModel::clear()
{
	beginResetModel();
    priv->cachedEntryTable.clear();
	endResetModel();
}

void MyOfferWhitelistTableModel::emitDataChanged(int idx)
{
    Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}
<commit_msg>typo<commit_after>#include "myofferwhitelisttablemodel.h"

#include "guiutil.h"
#include "walletmodel.h"

#include "wallet/wallet.h"
#include "base58.h"

#include <QFont>
using namespace std;

struct MyOfferWhitelistTableEntry
{
	QString offer;
    QString alias;
	QString expires;
	QString discount;

    MyOfferWhitelistTableEntry() {}
    MyOfferWhitelistTableEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount):
        offer(offer), alias(alias), expires(expires),discount(discount) {}
};

// Private implementation
class MyOfferWhitelistTablePriv
{
public:
    QList<MyOfferWhitelistTableEntry> cachedEntryTable;
    MyOfferWhitelistTableModel *parent;

    OfferWhitelistTablePriv(MyOfferWhitelistTableModel *parent):
        parent(parent) {}

    void updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
    {
		if(!parent)
		{
			return;
		}

        switch(status)
        {
        case CT_NEW:

            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedEntryTable.insert(lowerIndex, MyOfferWhitelistTableEntry(offer, alias, expires, discount));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            lower->offer = offer;
			lower->alias = alias;
			lower->expires = expires;
			lower->discount = discount;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:

            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedEntryTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedEntryTable.size();
    }

    OfferWhitelistTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedEntryTable.size())
        {
            return &cachedEntryTable[idx];
        }
        else
        {
            return 0;
        }
    }
};

MyOfferWhitelistTableModel::MyOfferWhitelistTableModel(WalletModel *parent) :
    QAbstractTableModel(parent)
{
    columns << tr("Offer") << tr("Alias") << tr("Discount") << tr("Expires In");
    priv = new OfferWhitelistTablePriv(this);

}

MyOfferWhitelistTableModel::~MyOfferWhitelistTableModel()
{
    delete priv;
}
int MyOfferWhitelistTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MyOfferWhitelistTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MyOfferWhitelistTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    OfferWhitelistTableEntry *rec = static_cast<OfferWhitelistTableEntry*>(index.internalPointer());

    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            return rec->offer;
        case Alias:
            return rec->alias;
        case Discount:
            return rec->discount;
        case Expires:
            return rec->expires;
        }
    }
    else if (role == AliasRole)
    {
        return rec->alias;
    }
    return QVariant();
}

bool MyOfferWhitelistTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    OfferWhitelistTableEntry *rec = static_cast<OfferWhitelistTableEntry*>(index.internalPointer());

    editStatus = OK;

    if(role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            // Do nothing, if old value == new value
            if(rec->offer == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Alias:
            // Do nothing, if old value == new value
            if(rec->alias == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Discount:
            // Do nothing, if old value == new value
            if(rec->discount == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
       case Expires:
            // Do nothing, if old value == new value
            if(rec->expires == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            break;
        return true;
		}
    }
    return false;
}

QVariant MyOfferWhitelistTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags MyOfferWhitelistTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;
    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    return retval;
}

QModelIndex MyOfferWhitelistTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    OfferWhitelistTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MyOfferWhitelistTableModel::updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
{
    priv->updateEntry(offer, alias, expires, discount, status);
}

QString MyOfferWhitelistTableModel::addRow(const QString &offer, const QString &alias, const QString &expires,const QString &discount)
{
    std::string strAlias = alias.toStdString();
    editStatus = OK;
    return QString::fromStdString(strAlias);
}
void MyOfferWhitelistTableModel::clear()
{
	beginResetModel();
    priv->cachedEntryTable.clear();
	endResetModel();
}

void MyOfferWhitelistTableModel::emitDataChanged(int idx)
{
    Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Raine Makelainen <raine.makelainen@jollamobile.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "declarativewebpage.h"
#include "declarativewebcontainer.h"
#include "dbmanager.h"

#include <QtConcurrent>
#include <QStandardPaths>

static const QString gFullScreenMessage("embed:fullscreenchanged");
static const QString gDomContentLoadedMessage("embed:domcontentloaded");

static const QString gLinkAddedMessage("chrome:linkadded");
static const QString gAlertMessage("embed:alert");
static const QString gConfirmMessage("embed:confirm");
static const QString gPromptMessage("embed:prompt");
static const QString gAuthMessage("embed:auth");
static const QString gLoginMessage("embed:login");
static const QString gFindMessage("embed:find");
static const QString gPermissionsMessage("embed:permissions");
static const QString gContextMenuMessage("Content:ContextMenu");
static const QString gSelectionRangeMessage("Content:SelectionRange");
static const QString gSelectionCopiedMessage("Content:SelectionCopied");
static const QString gSelectAsyncMessage("embed:selectasync");
static const QString gFilePickerMessage("embed:filepicker");

bool isBlack(QRgb rgb)
{
    return qRed(rgb) == 0 && qGreen(rgb) == 0 && qBlue(rgb) == 0;
}

bool allBlack(const QImage &image)
{
    int h = image.height();
    int w = image.width();

    for (int j = 0; j < h; ++j) {
        const QRgb *b = (const QRgb *)image.constScanLine(j);
        for (int i = 0; i < w; ++i) {
            if (!isBlack(b[i]))
                return false;
        }
    }
    return true;
}

DeclarativeWebPage::DeclarativeWebPage(QObject *parent)
    : QOpenGLWebPage(parent)
    , m_container(0)
    , m_userHasDraggedWhileLoading(false)
    , m_fullscreen(false)
    , m_forcedChrome(false)
    , m_domContentLoaded(false)
    , m_initialLoadHasHappened(false)
    , m_tabHistoryReady(false)
    , m_urlReady(false)
{
    addMessageListener(gFullScreenMessage);
    addMessageListener(gDomContentLoadedMessage);

    addMessageListener(gLinkAddedMessage);
    addMessageListener(gAlertMessage);
    addMessageListener(gConfirmMessage);
    addMessageListener(gPromptMessage);
    addMessageListener(gAuthMessage);
    addMessageListener(gLoginMessage);
    addMessageListener(gFindMessage);
    addMessageListener(gPermissionsMessage);
    addMessageListener(gContextMenuMessage);
    addMessageListener(gSelectionRangeMessage);
    addMessageListener(gSelectionCopiedMessage);
    addMessageListener(gSelectAsyncMessage);
    addMessageListener(gFilePickerMessage);

    loadFrameScript("chrome://embedlite/content/SelectAsyncHelper.js");
    loadFrameScript("chrome://embedlite/content/embedhelper.js");

    connect(this, SIGNAL(recvAsyncMessage(const QString, const QVariant)),
            this, SLOT(onRecvAsyncMessage(const QString&, const QVariant&)));
    connect(&m_grabWritter, SIGNAL(finished()), this, SLOT(grabWritten()));
    connect(this, SIGNAL(contentHeightChanged()), this, SLOT(resetHeight()));
    connect(this, SIGNAL(scrollableOffsetChanged()), this, SLOT(resetHeight()));
    connect(this, SIGNAL(urlChanged()), this, SLOT(onUrlChanged()));
}

DeclarativeWebPage::~DeclarativeWebPage()
{
    m_grabWritter.cancel();
    m_grabWritter.waitForFinished();
    m_grabResult.clear();
    m_thumbnailResult.clear();
}

DeclarativeWebContainer *DeclarativeWebPage::container() const
{
    return m_container;
}

void DeclarativeWebPage::setContainer(DeclarativeWebContainer *container)
{
    if (m_container != container) {
        m_container = container;
        emit containerChanged();
    }
}

int DeclarativeWebPage::tabId() const
{
    return m_initialTab.tabId();
}

void DeclarativeWebPage::setInitialTab(const Tab& tab)
{
    Q_ASSERT(m_initialTab.tabId() == 0);

    m_initialTab = tab;
    emit tabIdChanged();
    connect(DBManager::instance(), SIGNAL(tabHistoryAvailable(int, QList<Link>)),
            this, SLOT(onTabHistoryAvailable(int, QList<Link>)));
    DBManager::instance()->getTabHistory(tabId());
}

void DeclarativeWebPage::onUrlChanged()
{
    disconnect(this, SIGNAL(urlChanged()), this, SLOT(onUrlChanged()));
    m_urlReady = true;
    restoreHistory();
}

void DeclarativeWebPage::onTabHistoryAvailable(const int& historyTabId, const QList<Link>& links)
{
    if (historyTabId == tabId()) {
        m_restoredTabHistory = links;

        std::reverse(m_restoredTabHistory.begin(), m_restoredTabHistory.end());
        DBManager::instance()->disconnect(this);
        m_tabHistoryReady = true;
        restoreHistory();
    }
}

void DeclarativeWebPage::restoreHistory() {
    if (!m_urlReady || !m_tabHistoryReady || m_restoredTabHistory.count() == 0) {
        return;
    }

    QList<QString> urls;
    int index(-1);
    int i(0);
    foreach (Link link, m_restoredTabHistory) {
        urls << link.url();
        if (link.linkId() == m_initialTab.currentLink()) {
            index = i;
            if (link.url() != m_initialTab.url()) {
                // The browser was started with an initial URL as a cmdline parameter -> reset tab history
                urls << m_initialTab.url();
                index++;
                DBManager::instance()->navigateTo(tabId(), m_initialTab.url(), "", "");
                break;
            }
        }
        i++;
    }

    if (index < 0) {
        urls << url().toString();
        index = urls.count() - 1;
    }

    QVariantMap data;
    data.insert(QString("links"), QVariant(urls));
    data.insert(QString("index"), QVariant(index));
    sendAsyncMessage("embedui:addhistory", QVariant(data));
}

bool DeclarativeWebPage::domContentLoaded() const
{
    return m_domContentLoaded;
}

bool DeclarativeWebPage::initialLoadHasHappened() const
{
    return m_initialLoadHasHappened;
}

void DeclarativeWebPage::setInitialLoadHasHappened()
{
    m_initialLoadHasHappened = true;
}

QVariant DeclarativeWebPage::resurrectedContentRect() const
{
    return m_resurrectedContentRect;
}

void DeclarativeWebPage::setResurrectedContentRect(QVariant resurrectedContentRect)
{
    if (m_resurrectedContentRect != resurrectedContentRect) {
        m_resurrectedContentRect = resurrectedContentRect;
        emit resurrectedContentRectChanged();
    }
}

void DeclarativeWebPage::loadTab(QString newUrl, bool force)
{
    // Always enable chrome when load is called.
    setChrome(true);
    QString oldUrl = url().toString();
    if ((!newUrl.isEmpty() && oldUrl != newUrl) || force) {
        m_domContentLoaded = false;
        emit domContentLoadedChanged();
        load(newUrl);
    }
}

void DeclarativeWebPage::grabToFile(const QSize &size)
{
    emit clearGrabResult();
    // grabToImage handles invalid geometry.
    m_grabResult = grabToImage(size);
    if (m_grabResult) {
        if (!m_grabResult->isReady()) {
            connect(m_grabResult.data(), SIGNAL(ready()), this, SLOT(grabResultReady()));
        } else {
            grabResultReady();
        }
    }
}

void DeclarativeWebPage::grabThumbnail(const QSize &size)
{
    m_thumbnailResult = grabToImage(size);
    if (m_thumbnailResult) {
        connect(m_thumbnailResult.data(), SIGNAL(ready()), this, SLOT(thumbnailReady()));
    }
}

/**
 * Use this to lock to chrome mode. This disables the gesture
 * that normally enables fullscreen mode. The chromeGestureEnabled property
 * is bound to this so that contentHeight changes do not re-enable the
 * gesture.
 *
 * When gesture is allowed to be used again, unlock call by forceChrome(false).
 *
 * Used for instance when find-in-page view is active that is part of
 * the new browser user interface.
 */
void DeclarativeWebPage::forceChrome(bool forcedChrome)
{
    // This way we don't break chromeGestureEnabled and chrome bindings.
    setChromeGestureEnabled(!forcedChrome);
    if (forcedChrome) {
        setChrome(forcedChrome);
    }
    // Without chrome respect content height.
    resetHeight(!forcedChrome);
    if (m_forcedChrome != forcedChrome) {
        m_forcedChrome = forcedChrome;
        emit forcedChromeChanged();
    }
}

void DeclarativeWebPage::resetHeight(bool respectContentHeight)
{
    // Input panel is fully open.
    if (m_container->imOpened()) {
        return;
    }

    // fullscreen() below in the fullscreen request coming from the web content.
    if (respectContentHeight && (!m_forcedChrome || fullscreen())) {
        // Handle webPage height over here, BrowserPage.qml loading
        // reset might be redundant as we have also loaded trigger
        // reset. However, I'd leave it there for safety reasons.
        // We need to reset height always back to short height when loading starts
        // so that after tab change there is always initial short composited height.
        // Height may expand when content is moved.
        if (contentHeight() > (m_fullScreenHeight + m_toolbarHeight) || fullscreen()) {
            setHeight(m_fullScreenHeight);
        } else {
            setHeight(m_fullScreenHeight - m_toolbarHeight);
        }
    } else {
        setHeight(m_fullScreenHeight - m_toolbarHeight);
    }
}

void DeclarativeWebPage::grabResultReady()
{
    QImage image = m_grabResult->image();
    m_grabResult.clear();
    m_grabWritter.setFuture(QtConcurrent::run(this, &DeclarativeWebPage::saveToFile, image));
}

void DeclarativeWebPage::grabWritten()
{
    QString path = m_grabWritter.result();
    emit grabResult(path);
}

void DeclarativeWebPage::thumbnailReady()
{
    QImage image = m_thumbnailResult->image();
    m_thumbnailResult.clear();
    QByteArray iconData;
    QBuffer buffer(&iconData);
    buffer.open(QIODevice::WriteOnly);
    if (image.save(&buffer, "jpg", 75)) {
        buffer.close();
        emit thumbnailResult(QString(BASE64_IMAGE).arg(QString(iconData.toBase64())));
    } else {
        emit thumbnailResult(DEFAULT_DESKTOP_BOOKMARK_ICON);
    }
}

QString DeclarativeWebPage::saveToFile(QImage image)
{
    if (image.isNull()) {
        return "";
    }

    // 75% quality jpg produces small and good enough capture.
    QString path = QString("%1/tab-%2-thumb.jpg").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).arg(tabId());
    return !allBlack(image) && image.save(path, "jpg", 75) ? path : "";
}

void DeclarativeWebPage::onRecvAsyncMessage(const QString& message, const QVariant& data)
{
    if (message == gFullScreenMessage) {
        setFullscreen(data.toMap().value(QString("fullscreen")).toBool());
    } else if (message == gDomContentLoadedMessage && data.toMap().value("rootFrame").toBool()) {
        m_domContentLoaded = true;
        emit domContentLoadedChanged();
    }
}

bool DeclarativeWebPage::fullscreen() const
{
    return m_fullscreen;
}

bool DeclarativeWebPage::forcedChrome() const
{
    return m_forcedChrome;
}

void DeclarativeWebPage::setFullscreen(const bool fullscreen)
{
    if (m_fullscreen != fullscreen) {
        m_fullscreen = fullscreen;
        resetHeight();
        emit fullscreenChanged();
    }
}

QDebug operator<<(QDebug dbg, const DeclarativeWebPage *page)
{
    if (!page) {
        return dbg << "DeclarativeWebPage (this = 0x0)";
    }

    dbg.nospace() << "DeclarativeWebPage(url = " << page->url() << ", title = " << page->title() << ", width = " << page->width()
                  << ", height = " << page->height() << ", completed = " << page->completed()
                  << ", active = " << page->active() << ", enabled = " << page->enabled() << ")";
    return dbg.space();
}
<commit_msg>[history] Use actual current URL when modifying session history.<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Raine Makelainen <raine.makelainen@jollamobile.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "declarativewebpage.h"
#include "declarativewebcontainer.h"
#include "dbmanager.h"

#include <QtConcurrent>
#include <QStandardPaths>

static const QString gFullScreenMessage("embed:fullscreenchanged");
static const QString gDomContentLoadedMessage("embed:domcontentloaded");

static const QString gLinkAddedMessage("chrome:linkadded");
static const QString gAlertMessage("embed:alert");
static const QString gConfirmMessage("embed:confirm");
static const QString gPromptMessage("embed:prompt");
static const QString gAuthMessage("embed:auth");
static const QString gLoginMessage("embed:login");
static const QString gFindMessage("embed:find");
static const QString gPermissionsMessage("embed:permissions");
static const QString gContextMenuMessage("Content:ContextMenu");
static const QString gSelectionRangeMessage("Content:SelectionRange");
static const QString gSelectionCopiedMessage("Content:SelectionCopied");
static const QString gSelectAsyncMessage("embed:selectasync");
static const QString gFilePickerMessage("embed:filepicker");

bool isBlack(QRgb rgb)
{
    return qRed(rgb) == 0 && qGreen(rgb) == 0 && qBlue(rgb) == 0;
}

bool allBlack(const QImage &image)
{
    int h = image.height();
    int w = image.width();

    for (int j = 0; j < h; ++j) {
        const QRgb *b = (const QRgb *)image.constScanLine(j);
        for (int i = 0; i < w; ++i) {
            if (!isBlack(b[i]))
                return false;
        }
    }
    return true;
}

DeclarativeWebPage::DeclarativeWebPage(QObject *parent)
    : QOpenGLWebPage(parent)
    , m_container(0)
    , m_userHasDraggedWhileLoading(false)
    , m_fullscreen(false)
    , m_forcedChrome(false)
    , m_domContentLoaded(false)
    , m_initialLoadHasHappened(false)
    , m_tabHistoryReady(false)
    , m_urlReady(false)
{
    addMessageListener(gFullScreenMessage);
    addMessageListener(gDomContentLoadedMessage);

    addMessageListener(gLinkAddedMessage);
    addMessageListener(gAlertMessage);
    addMessageListener(gConfirmMessage);
    addMessageListener(gPromptMessage);
    addMessageListener(gAuthMessage);
    addMessageListener(gLoginMessage);
    addMessageListener(gFindMessage);
    addMessageListener(gPermissionsMessage);
    addMessageListener(gContextMenuMessage);
    addMessageListener(gSelectionRangeMessage);
    addMessageListener(gSelectionCopiedMessage);
    addMessageListener(gSelectAsyncMessage);
    addMessageListener(gFilePickerMessage);

    loadFrameScript("chrome://embedlite/content/SelectAsyncHelper.js");
    loadFrameScript("chrome://embedlite/content/embedhelper.js");

    connect(this, SIGNAL(recvAsyncMessage(const QString, const QVariant)),
            this, SLOT(onRecvAsyncMessage(const QString&, const QVariant&)));
    connect(&m_grabWritter, SIGNAL(finished()), this, SLOT(grabWritten()));
    connect(this, SIGNAL(contentHeightChanged()), this, SLOT(resetHeight()));
    connect(this, SIGNAL(scrollableOffsetChanged()), this, SLOT(resetHeight()));
    connect(this, SIGNAL(urlChanged()), this, SLOT(onUrlChanged()));
}

DeclarativeWebPage::~DeclarativeWebPage()
{
    m_grabWritter.cancel();
    m_grabWritter.waitForFinished();
    m_grabResult.clear();
    m_thumbnailResult.clear();
}

DeclarativeWebContainer *DeclarativeWebPage::container() const
{
    return m_container;
}

void DeclarativeWebPage::setContainer(DeclarativeWebContainer *container)
{
    if (m_container != container) {
        m_container = container;
        emit containerChanged();
    }
}

int DeclarativeWebPage::tabId() const
{
    return m_initialTab.tabId();
}

void DeclarativeWebPage::setInitialTab(const Tab& tab)
{
    Q_ASSERT(m_initialTab.tabId() == 0);

    m_initialTab = tab;
    emit tabIdChanged();
    connect(DBManager::instance(), SIGNAL(tabHistoryAvailable(int, QList<Link>)),
            this, SLOT(onTabHistoryAvailable(int, QList<Link>)));
    DBManager::instance()->getTabHistory(tabId());
}

void DeclarativeWebPage::onUrlChanged()
{
    disconnect(this, SIGNAL(urlChanged()), this, SLOT(onUrlChanged()));
    m_urlReady = true;
    restoreHistory();
}

void DeclarativeWebPage::onTabHistoryAvailable(const int& historyTabId, const QList<Link>& links)
{
    if (historyTabId == tabId()) {
        m_restoredTabHistory = links;

        std::reverse(m_restoredTabHistory.begin(), m_restoredTabHistory.end());
        DBManager::instance()->disconnect(this);
        m_tabHistoryReady = true;
        restoreHistory();
    }
}

void DeclarativeWebPage::restoreHistory() {
    if (!m_urlReady || !m_tabHistoryReady || m_restoredTabHistory.count() == 0) {
        return;
    }

    QList<QString> urls;
    int index(-1);
    int i(0);
    foreach (Link link, m_restoredTabHistory) {
        urls << link.url();
        if (link.linkId() == m_initialTab.currentLink()) {
            index = i;
            QString currentUrl(url().toString());
            if (link.url() != currentUrl) {
                // The browser was started with an initial URL as a cmdline parameter -> reset tab history
                urls << currentUrl;
                index++;
                DBManager::instance()->navigateTo(tabId(), currentUrl, "", "");
                break;
            }
        }
        i++;
    }

    if (index < 0) {
        urls << url().toString();
        index = urls.count() - 1;
    }

    QVariantMap data;
    data.insert(QString("links"), QVariant(urls));
    data.insert(QString("index"), QVariant(index));
    sendAsyncMessage("embedui:addhistory", QVariant(data));
}

bool DeclarativeWebPage::domContentLoaded() const
{
    return m_domContentLoaded;
}

bool DeclarativeWebPage::initialLoadHasHappened() const
{
    return m_initialLoadHasHappened;
}

void DeclarativeWebPage::setInitialLoadHasHappened()
{
    m_initialLoadHasHappened = true;
}

QVariant DeclarativeWebPage::resurrectedContentRect() const
{
    return m_resurrectedContentRect;
}

void DeclarativeWebPage::setResurrectedContentRect(QVariant resurrectedContentRect)
{
    if (m_resurrectedContentRect != resurrectedContentRect) {
        m_resurrectedContentRect = resurrectedContentRect;
        emit resurrectedContentRectChanged();
    }
}

void DeclarativeWebPage::loadTab(QString newUrl, bool force)
{
    // Always enable chrome when load is called.
    setChrome(true);
    QString oldUrl = url().toString();
    if ((!newUrl.isEmpty() && oldUrl != newUrl) || force) {
        m_domContentLoaded = false;
        emit domContentLoadedChanged();
        load(newUrl);
    }
}

void DeclarativeWebPage::grabToFile(const QSize &size)
{
    emit clearGrabResult();
    // grabToImage handles invalid geometry.
    m_grabResult = grabToImage(size);
    if (m_grabResult) {
        if (!m_grabResult->isReady()) {
            connect(m_grabResult.data(), SIGNAL(ready()), this, SLOT(grabResultReady()));
        } else {
            grabResultReady();
        }
    }
}

void DeclarativeWebPage::grabThumbnail(const QSize &size)
{
    m_thumbnailResult = grabToImage(size);
    if (m_thumbnailResult) {
        connect(m_thumbnailResult.data(), SIGNAL(ready()), this, SLOT(thumbnailReady()));
    }
}

/**
 * Use this to lock to chrome mode. This disables the gesture
 * that normally enables fullscreen mode. The chromeGestureEnabled property
 * is bound to this so that contentHeight changes do not re-enable the
 * gesture.
 *
 * When gesture is allowed to be used again, unlock call by forceChrome(false).
 *
 * Used for instance when find-in-page view is active that is part of
 * the new browser user interface.
 */
void DeclarativeWebPage::forceChrome(bool forcedChrome)
{
    // This way we don't break chromeGestureEnabled and chrome bindings.
    setChromeGestureEnabled(!forcedChrome);
    if (forcedChrome) {
        setChrome(forcedChrome);
    }
    // Without chrome respect content height.
    resetHeight(!forcedChrome);
    if (m_forcedChrome != forcedChrome) {
        m_forcedChrome = forcedChrome;
        emit forcedChromeChanged();
    }
}

void DeclarativeWebPage::resetHeight(bool respectContentHeight)
{
    // Input panel is fully open.
    if (m_container->imOpened()) {
        return;
    }

    // fullscreen() below in the fullscreen request coming from the web content.
    if (respectContentHeight && (!m_forcedChrome || fullscreen())) {
        // Handle webPage height over here, BrowserPage.qml loading
        // reset might be redundant as we have also loaded trigger
        // reset. However, I'd leave it there for safety reasons.
        // We need to reset height always back to short height when loading starts
        // so that after tab change there is always initial short composited height.
        // Height may expand when content is moved.
        if (contentHeight() > (m_fullScreenHeight + m_toolbarHeight) || fullscreen()) {
            setHeight(m_fullScreenHeight);
        } else {
            setHeight(m_fullScreenHeight - m_toolbarHeight);
        }
    } else {
        setHeight(m_fullScreenHeight - m_toolbarHeight);
    }
}

void DeclarativeWebPage::grabResultReady()
{
    QImage image = m_grabResult->image();
    m_grabResult.clear();
    m_grabWritter.setFuture(QtConcurrent::run(this, &DeclarativeWebPage::saveToFile, image));
}

void DeclarativeWebPage::grabWritten()
{
    QString path = m_grabWritter.result();
    emit grabResult(path);
}

void DeclarativeWebPage::thumbnailReady()
{
    QImage image = m_thumbnailResult->image();
    m_thumbnailResult.clear();
    QByteArray iconData;
    QBuffer buffer(&iconData);
    buffer.open(QIODevice::WriteOnly);
    if (image.save(&buffer, "jpg", 75)) {
        buffer.close();
        emit thumbnailResult(QString(BASE64_IMAGE).arg(QString(iconData.toBase64())));
    } else {
        emit thumbnailResult(DEFAULT_DESKTOP_BOOKMARK_ICON);
    }
}

QString DeclarativeWebPage::saveToFile(QImage image)
{
    if (image.isNull()) {
        return "";
    }

    // 75% quality jpg produces small and good enough capture.
    QString path = QString("%1/tab-%2-thumb.jpg").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).arg(tabId());
    return !allBlack(image) && image.save(path, "jpg", 75) ? path : "";
}

void DeclarativeWebPage::onRecvAsyncMessage(const QString& message, const QVariant& data)
{
    if (message == gFullScreenMessage) {
        setFullscreen(data.toMap().value(QString("fullscreen")).toBool());
    } else if (message == gDomContentLoadedMessage && data.toMap().value("rootFrame").toBool()) {
        m_domContentLoaded = true;
        emit domContentLoadedChanged();
    }
}

bool DeclarativeWebPage::fullscreen() const
{
    return m_fullscreen;
}

bool DeclarativeWebPage::forcedChrome() const
{
    return m_forcedChrome;
}

void DeclarativeWebPage::setFullscreen(const bool fullscreen)
{
    if (m_fullscreen != fullscreen) {
        m_fullscreen = fullscreen;
        resetHeight();
        emit fullscreenChanged();
    }
}

QDebug operator<<(QDebug dbg, const DeclarativeWebPage *page)
{
    if (!page) {
        return dbg << "DeclarativeWebPage (this = 0x0)";
    }

    dbg.nospace() << "DeclarativeWebPage(url = " << page->url() << ", title = " << page->title() << ", width = " << page->width()
                  << ", height = " << page->height() << ", completed = " << page->completed()
                  << ", active = " << page->active() << ", enabled = " << page->enabled() << ")";
    return dbg.space();
}
<|endoftext|>