/****************************************************************************
**
** Copyright (C) 1999-2006 Frank Hemer <frank@hemer.org>,
**                         Tilo Riemer <riemer@crossvc.com>,
**                         Wim Delvaux <wim.delvaux@chello.be>,
**                         Jose Hernandez <joseh@tesco.net>,
**                         Helmut Koll <HelmutKoll@web.de>,
**                         Tom Mishima <tmishima@mail.at-m.or.jp>,
**                         Joerg Preiss <auba@auba.de>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt 
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt 
** CrossVC is released under the terms of the 
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification, 
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
**    different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
**    products derived from this software without specific prior written
**    permission.
** 4. The source code is the creative property of the authors.
**    Extensions and development under the terms of the Gnu Public License
**    are limited to the Unix platform. Any distribution or compilation of 
**    the source code against libraries licensed other than gpl requires 
**    the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** 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, version 2 of the License.
** 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 version 2 for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#define DEFINE_GLOBALS

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <time_ac.h>
#include <unistd.h>

//----------------------------------------------------------------------------

#include <qfile.h>
#include <qdir.h>
#include <qtextstream.h>
#include <qdialog.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qobject.h> 
#include <qbutton.h>
#include <qlineedit.h>
#include <qcheckbox.h>
#include <qapplication.h>
#include <qmessagebox.h>
#include <qregexp.h>

//----------------------------------------------------------------------------

#include "globals.h"
#include "wrapper.h"

#include "TextCodec.h"
#include "TextDecoder.h"
#include "TextEncoder.h"

#ifdef USE_FAM
#include "FamConnector.h"
#endif

#ifdef USE_WINNT_DIRMON
#include "WinNtConnector.h"
#endif

#ifdef USE_DNOTIFY
#include "DNotifyConnector.h"
#endif

//----------------------------------------------------------------------------

int sshPid = -1;

//----------------------------------------------------------------------------

void initGlobals() {
   WINVERSION = 0;

   determineTimeZoneDiff();
   projectSettings = new ProjectSettings();
   LookAndFeel::g_modalF = Qt::WType_Dialog|Qt::WStyle_DialogBorder|Qt::WStyle_SysMenu|Qt::WStyle_MinMax|Qt::WStyle_ContextHelp;
   LookAndFeel::g_nonModalF = Qt::WType_TopLevel|Qt::WStyle_SysMenu|Qt::WStyle_MinMax|Qt::WStyle_ContextHelp;
   Dialog::g_showWarning = true;
}

//----------------------------------------------------------------------------

const QString CvsOptions::cmprStr()
{
   if (CvsOptions::g_compression > 0) {
      QString compressionStr = "-z%1";
      return compressionStr.arg(CvsOptions::g_compression);
   } else return "";
}

//----------------------------------------------------------------------------

void determineTimeZoneDiff()
{
   timeZoneDiffInSecs = 0;
   struct tm *result = NULL;
   time_t t_loc = time(0);
   result = localtime(&t_loc);
   if (result) {//the difference to UTC without daylight diff
#ifdef USE_TM_GMTOFF
      timeZoneDiffInSecs = -(result->tm_gmtoff);
#else
      timeZoneDiffInSecs = timezone;
#endif
   }
   TIMEZERO.setTime_t(0);//ATTENTION: this is different from QDateTime()
}

//----------------------------------------------------------------------------

bool isInCvsPass(QString cvsRoot)
{
   QString method;
   QString user;
   QString passwd;
   QString host;
   int port;
   QString rootDir;
   extractCVSROOT( cvsRoot, method, user, passwd, host, port, rootDir);
   QString cvsRootNoPort = cvsRoot;
   if (port == 2401) {//only compare with portless root if default port
      cvsRootNoPort.replace( QRegExp(":"+QString::number(port)),":");//remove port to compare with entries
   } else {
      port = -1;
   }

   if (CVSVERSION == "cvs") {
      QFile f;
      QString line;

      //read .cvspass
      f.setName(CVSPASSPATH);
      if(f.open(IO_ReadOnly)) {//file is readable
	 QTextStream textStream(&f); 
    
	 while(!textStream.atEnd()) {
	    line = textStream.readLine();
	    line = line.replace( QRegExp(":"+QString::number(port)),":");//remove port to compare with entries
	    if(line.find(cvsRootNoPort) != -1) {
	       f.close();
	       return true;
	    }
	 }
	 f.close();
      }
   } else if (CVSVERSION == "cvsnt") {
      QFile f;
      QString line;

      //read ~/.cvs/cvspass
      f.setName(QDir::homeDirPath()+"/.cvs/cvspass");
      if(f.open(IO_ReadOnly)) {//file is readable
	 QTextStream textStream(&f); 
    
	 while(!textStream.atEnd()) {
	    line = textStream.readLine();
	    line = line.replace( QRegExp(":"+QString::number(port)+":"),":");//remove port to compare with entries
	    if(line.find(cvsRootNoPort) != -1) {
	       f.close();
	       return true;
	    }
	 }
	 f.close();
      }

   } else if (WINVERSION) {

      QString rootKey = "Software\\Cvsnt";
      QString subKey = "cvspass";

   }
   return false;
}

//----------------------------------------------------------------------------

bool writeToCvsPassFile(const QString& cvsRoot, const QString& pwd) {

   // restrictive umask
   mode_t oldMask = umask(0077);

   if (CVSVERSION == "cvs") {
      QFile f;
      QString line = cvsRoot+" "+pwd+"\n";
      f.setName(CVSPASSPATH);
      if(f.open(IO_WriteOnly | IO_Append)) {
	 QTextStream textStream(&f); 
	 textStream << line;
	 f.flush();
	 f.close();
	 chmod(CVSPASSPATH.ascii(),0600); // little paranoia always makes sense
	 umask(oldMask);
	 return true;
      }
   } else if (CVSVERSION == "cvsnt") {
      QFile f;
      QString line = cvsRoot+"="+pwd+"\n";
      f.setName(QDir::homeDirPath()+"/.cvs/cvspass");
      if(f.open(IO_WriteOnly | IO_Append)) {
	 QTextStream textStream(&f); 
	 textStream << line;
	 f.flush();
	 f.close();
	 chmod(CVSPASSPATH.ascii(),0600); // little paranoia always makes sense
	 umask(oldMask);
	 return true;
      }
   }
   umask(oldMask);
   return false;
}

//----------------------------------------------------------------------------

bool removeFromCvsPassFile( QString cvsRoot) {

   QString method;
   QString user;
   QString passwd;
   QString host;
   int port;
   QString rootDir;
   extractCVSROOT( cvsRoot, method, user, passwd, host, port, rootDir);
   QString cvsRootNoPort = cvsRoot;
   cvsRootNoPort.replace( QRegExp(":"+QString::number(port)),":");//remove port to compare with entries

   // restrictive umask
   mode_t oldMask = umask(0077);

   if (CVSVERSION == "cvs") {

      QStringList list;
      QString line;
      QFile f;
      bool found = false;

      f.setName(CVSPASSPATH);
      if(f.open(IO_ReadOnly)) {
	 QTextStream textStream(&f); 
	 while(!textStream.atEnd()) {
	    line = textStream.readLine();
	    line = line.replace( QRegExp(":"+QString::number(port)),":");//remove port to compare with entries
	    if (line.startsWith(cvsRootNoPort)) {
	       found = true;
	       continue;
	    }
	    list.append(line);
	 }
	 f.close();
	 if (!found) {
	    umask(oldMask);
	    return false;
	 }
	 f.remove();
	 if (f.open(IO_WriteOnly)) {
	    QTextStream newTextStream(&f);
	    for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
	       newTextStream << (*it);
	       newTextStream << "\n";
	    }
	    f.flush();
	    f.close();
	    chmod(CVSPASSPATH.ascii(),0600); // little paranoia always makes sense
	 }
      }
      umask(oldMask);
      return true;
   } else if (CVSVERSION == "cvsnt") {

      QStringList list;
      QString line;
      QFile f;
      bool found = false;

      f.setName(QDir::homeDirPath()+"/.cvs/cvspass");
      if(f.open(IO_ReadOnly)) {
	 QTextStream textStream(&f); 
	 while(!textStream.atEnd()) {
	    line = textStream.readLine();
	    line = line.replace( QRegExp(":"+QString::number(port)+":"),":");//remove port to compare with entries
	    if (line.startsWith(cvsRootNoPort)) {
	       found = true;
	       continue;
	    }
	    list.append(line);
	 }
	 f.close();
	 if (!found) {
	    umask(oldMask);
	    return false;
	 }
	 f.remove();
	 if (f.open(IO_WriteOnly)) {
	    QTextStream newTextStream(&f);
	    for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
	       newTextStream << (*it);
	       newTextStream << "\n";
	    }
	    f.flush();
	    f.close();
	    chmod(CVSPASSPATH.ascii(),0600); // little paranoia always makes sense
	 }
      }
      umask(oldMask);
      return true;
   }

   umask(oldMask);
   return false;
}

//----------------------------------------------------------------------------

bool extractCVSROOT( QString rootline,
      QString& method,
      QString& user,
      QString& passwd,
      QString& host,
      int& port,
      QString& path) {

   /* Access method specified, as in
    * "cvs -d :(gserver|kserver|pserver|sspi)[;params...]:[[[user][:password]@]host[:[port]]]/path",
    * "cvs -d [:(ext|server)[;params...]:][[[user]@]host[:]]/path",
    * "cvs -d :local[;params...]:e:\path",
    * "cvs -d :fork[;params...]:/path".
    *
    * Note that path for connecting to a cvs-winnt server might be something like:
    * "/d//somepath/cvs"
    */

   method = QString::null;
   user = QString::null;
   passwd = QString::null;
   host = QString::null;
   port = -1;
   path = QString::null;

   int from, to;
   int atpos;
   QString tmpStr;

   //method
   if (rootline.at(0) == ':') {
      from = 1;
      to = rootline.find(':',from);
      if (to == -1) return false;//syntax error
      method = rootline.mid((unsigned int)from,(unsigned int)(to-from));
      rootline = rootline.mid((unsigned int)(to+1));

      //extract additional options, separated by ';'
      QStringList optList = QStringList::split(";", method);
      method = optList[0];
      QStringList::Iterator it = optList.begin();
      while (++it != optList.end()) {
	 int pos = (*it).find('=');
	 if (pos > -1) {
	    QString keyword = (*it).left(pos);
	    QString value = (*it).mid(pos+1);
	    if (keyword == "username" || keyword == "user") {
	       user = value;
	    } else if (keyword == "password" || keyword == "pass") {
	       passwd = value;
	    } else if (keyword == "hostname" || keyword == "host") {
	       host = value;
	       if (port == -1) port = 2401;
	    } else if (keyword == "port") {
	       port = value.toInt();
	    } else if (keyword == "directory" || keyword == "path") {
	       path = value;
	    } else {
	       qDebug("ignoring CVSROOT option: '"+keyword+"="+value+"'");
	    }
	 } else {
	    qDebug("Invalid option in CVSROOT: '"+(*it));
	 }
      }

   } else if ( (rootline.at((unsigned int)0) != '/') && (rootline.contains('/')) ) {
      method = "ext";
   } else {
      method = "local";
   }

   if ( (method == "local") || (method == "fork")) {
      if ( rootline.find(':') > 1) return false;
      path = rootline;
      return true;
   }

   //path
   from = rootline.find('/');
   if (from >= 0) {//cvs server running on UNIX
      path = rootline.mid((unsigned int)from);
      rootline.truncate((unsigned int)from);
   } else {//cvsnt server running on winXX
      from = rootline.find('\\');
      if (from > 2) {//path found. it can contain a drive letter ... I love win ;-)
	 if (rootline[from-2].isLetter() &&
	       (rootline[from-1] == ':') &&
	       (rootline[from-3] == ':')) from -= 2;

	 path = rootline.mid((unsigned int)from);
	 rootline.truncate((unsigned int)from);
      } else return false;
   }


   //user:password@
   atpos = rootline.find('@');
   if (atpos >= 0) {
      to = rootline.findRev(':',atpos-1-rootline.length());
      if (to >= 0) {
	 user = rootline.mid((unsigned int)0,(unsigned int)to);
	 passwd = rootline.mid((unsigned int)(to+1),(unsigned int)(atpos-to-1));
	 if (passwd.isEmpty()) return false;
	 if (user.isEmpty()) user = getenv("USER");
      } else {
	 user = rootline.mid((unsigned int)0,(unsigned int)atpos);
      }
      if (user.isEmpty()) return false;//no user
   }
   rootline = rootline.mid((unsigned int)(atpos+1));


   //host:port
   if (host.isNull()) {
      from = rootline.find(':');
      to = rootline.find(':',from+1);
      if (from == -1) {
	 host = rootline;
	 port = 2401;
      } else if (to == -1) {
	 tmpStr = rootline.mid((unsigned int)(from+1));
	 if (tmpStr.isEmpty()) {
	    port = 2401;
	 } else {
	    port = tmpStr.toInt();
	 }
      } else {
	 port = rootline.mid((unsigned int)(from+1),(unsigned int)(to-from-1)).toInt();
      }
      host = rootline.mid((unsigned int)0,(unsigned int)from);
   }

   if (host.isEmpty()) return false;//no host
   if (port == 0) return false;//no number ie. syntax error

   return true;
}

//----------------------------------------------------------------------------

int analyzeCVSROOT( QString& method,
      QString& user,
      QString& passwd,
      QString& host,
      int& port,
      QString& path) {

   QString line = getenv("CVSROOT");
   if (!line.isEmpty()) {
      bool retVal = extractCVSROOT(line, method, user, passwd, host, port, path);
      if (user.isNull()) user = getenv("USER");
      return retVal;
   } else {
      return false;
   }
}

//----------------------------------------------------------------------------

int validateCVSROOT( QString rootline) {
   QString method;
   QString user;
   QString passwd;
   QString host;
   int port;
   QString path;
   if (!extractCVSROOT(rootline,method,user,passwd,host,port,path)) return -1;
   /*TODO: add real implementation*/  
   return 0;
}

//----------------------------------------------------------------------------

void removeDoubleSlashes(QString& s)
{
   int pos;
   while ( (pos = s.find("//")) > -1) s.replace(pos, 2, "/");
}

//----------------------------------------------------------------------------

void startSshAgent() {

   bool useAgent = true;
   if (bUseSshAgentVars) {
      QString envValue;
      if ( envValue=getenv("SSH_AUTH_SOCK")) {
	 envSSH_AUTH_SOCK = "SSH_AUTH_SOCK=" + envValue;
      } else if ( envValue=getenv("SSH2_AUTH_SOCK")) {
	 envSSH_AUTH_SOCK = "SSH2_AUTH_SOCK=" + envValue;
      } else {
	 useAgent = false;
      }
      if ( envValue=getenv("SSH_AGENT_PID")) {
	 envSSH_AGENT_PID = "SSH_AGENT_PID=" + envValue;
      } else if ( envValue=getenv("SSH2_AGENT_PID")) {
	 envSSH_AGENT_PID = "SSH2_AGENT_PID=" + envValue;
      } else {
	 useAgent = false;
      }
      if (useAgent) {
	 sshAgentIsRunning = true;
	 return;
      } else if (sshPid > 0) {
	 return;
      }
   }

   QString sh = ExtApps::g_sshAgent.path;
   if (!sh.startsWith("/")) {
      sh = APPDIR + "/" + LC_TOOLS_DIR + "/" + sh;
   }
   QString options = ExtApps::g_sshAgent.options;
   masqedFilenamesForPlaceholders(options, ExtApps::g_sshKeyFiles);
   QStringList args = QStringList::split("\" \"",umasqWs(options));
   args.prepend(sh);

#ifdef Q_WS_MAC
   QString askpass = APPDIR + "/" + LC_TOOLS_DIR + "/ssh-askpass.app/Contents/MacOS/ssh-askpass";
#else
   QString askpass = APPDIR + "/" + LC_TOOLS_DIR + "/ssh-askpass.bin";
#endif

   QStringList * env = NULL;
   sshProc = new QProcess();
   sshProc->setArguments(args);

   if (QFileInfo(askpass).exists()) {
	   env = new QStringList();
	   QString tmpEnv;
	   QString tmpVar;
	   tmpVar = "DISPLAY";
	   tmpEnv = getenv(tmpVar);
	   if (tmpEnv.length() > 0) {
		   env->append(tmpVar+"="+tmpEnv);
#ifdef Q_WS_MAC
	   } else {
		   env->append(tmpVar+"=:0.0");
#endif
	   }
	   tmpVar = "PATH";
	   tmpEnv = getenv(tmpVar);
	   if (tmpEnv.length() != 0) {
		   env->append(tmpVar+"="+tmpEnv);
	   }
	   tmpVar = "HOME";
	   tmpEnv = getenv(tmpVar);
	   if (tmpEnv.length() != 0) {
		   env->append(tmpVar+"="+tmpEnv);
	   }
	   env->append("SSH_ASKPASS="+askpass);
   }
   
   if (sshProc->start(env)) {
      while (sshProc->isRunning()) {
	 wait(100);
	 qApp->processEvents();
	 if (globalStopAction) sshProc->kill();
      }
      if ( (sshProc->normalExit()) && (sshProc->exitStatus() == 0) ) {
	 sshAgentStarted = true;
      } else {
	 checkSshAgent();
	 killSshAgent();
      }
   } else {
      qDebug("couldn't start proc: ->"+sh+"<-");
   }
   delete sshProc;
   sshProc = 0;
}

//----------------------------------------------------------------------------

void checkSshAgent() {

   QFile f;
   QString line;

   //now read the environment file and ascertain the PID of ssh-agent

   f.setName(QDir::homeDirPath() + "/.ssh/lincvs-environment");
   
   int tPid = -1;
   sshAuthSock = "";
  
   if(f.open(IO_ReadOnly)) {//file is readable
      QTextStream textStream(&f); 
    
      while(!textStream.atEnd()) {
	 line = textStream.readLine();
	 if (line.find("setenv SSH_AUTH_SOCK ") == 0) {
	    QString tmp = line.mid(21);
	    envSSH_AUTH_SOCK = "SSH_AUTH_SOCK=" + tmp.left(tmp.find(";"));
	 } else if(line.contains("_AUTH_SOCK=") > 0) {//SSH or SSH2
	    envSSH_AUTH_SOCK = line.left(line.find(";"));
	 }
	 if (line.find("setenv SSH_AGENT_PID ") == 0) {
	    QString tmp = line.mid(21, 20);
	    QChar ch;
	    ch = tmp[0];
	    unsigned int ch_pos = 0;
	    while (ch.isDigit() && (tmp.length() > ch_pos)) {
	       ch_pos++;
	       ch = tmp.at(ch_pos);
	    }
	    QString pid = tmp.left(ch_pos);
	    envSSH_AGENT_PID = "SSH_AGENT_PID="+pid;
	    tPid = pid.toInt();
	 } else if(line.contains("_AGENT_PID=") > 0) {//SSH or SSH2
	    envSSH_AGENT_PID = line.left(line.find(";"));
	    QString pid = envSSH_AGENT_PID.mid(envSSH_AGENT_PID.find("=")+1);
	    tPid = pid.toInt();
	 }
      }
   }   
  
   if (tPid > 0 && (!envSSH_AUTH_SOCK.isEmpty())) {
      if (sshPid == -1) sshPid = tPid;
      sshAgentIsRunning = true;
   } else {
      killSshAgent();
      sshAgentIsRunning = false;
   }
}

//----------------------------------------------------------------------------

void killSshAgent() {
   if (sshPid > 0) {
      QString pid;
      pid.setNum(sshPid);
      if (system("kill " + pid)) qDebug("kill ssh agent pid: "+QString::number(sshPid)+" failed");
      QString fileName = QDir::homeDirPath() + "/.ssh/lincvs-environment";
      if (!QFile::remove(fileName)) {
	 qDebug("Kill ssh agent: couldn't remove environment:\n"+fileName);
      }
      sshAgentIsRunning = false;
      sshAgentStarted = false;
      sshPid = -1;
   }
}

//----------------------------------------------------------------------------

QString writeTmpFile(QString text)
{
   QString fname = createTempFile();
   if (fname.length()) {
      QFile file(fname);
      if (file.open(IO_WriteOnly)) {
	 QTextStream s(&file);                        // serialize using f
	 s.setCodec(I18n::g_pTextEncoder->pTextCodecPool()->pCodecForName(I18n::g_nameOfEncoder));
	 s << text;     // serialize string
      }
      file.close();
   }
   
   return fname;
}

//----------------------------------------------------------------------------

void writeUserHstFile(QString text)
{
   QString fname = createHistoryFile();

   if (fname.length()) {
      QFile file(fname);
      if (file.open(IO_WriteOnly)) {                  // open file for writing
	 QTextStream s(&file);                        // serialize using f
	 s.setCodec(I18n::g_pTextEncoder->pTextCodecPool()->pCodecForName(I18n::g_nameOfEncoder));
	 s << text;     // serialize string
      }
      file.close();
   }
}

//----------------------------------------------------------------------------

QString readTextFile(QString fn)
{
   QFile f(fn);
   QString str = "";

   if (f.open(IO_ReadOnly)) {
      QTextStream ts(&f);
      ts.setCodec(I18n::g_pTextDecoder->pTextCodecPool()->pCodecForName(I18n::g_nameOfDecoder));
      QString line;
      while(!ts.eof()) {
	 line = ts.readLine();
	 str += line + "\n";
      }
      f.close();
   }

   return str;
}

//----------------------------------------------------------------------------

QString readDefaultTemplateFile()
{
   return readTextFile(tmpDir + "/Template/rcsinfo");
}

//----------------------------------------------------------------------------

void writeDefaultTemplateFile(QString text)
{
   QFile rcsFile(tmpDir + "/Template/rcsinfo");

   if (text.isEmpty()) {//no text for template --> remove rcsinfo
      if (rcsFile.exists()) rcsFile.remove();
   } else {//we have some text --> write it into rcsinfo
      createTemplateDir();  //create template dir if necessary

      if (rcsFile.open(IO_WriteOnly)) {
	 QTextStream s(&rcsFile);                     // serialize using f
	 s.setCodec(I18n::g_pTextEncoder->pTextCodecPool()->pCodecForName(I18n::g_nameOfEncoder));
	 s << text;     // serialize string
      }
      rcsFile.close();
   }

}
 
//----------------------------------------------------------------------------

void createTmpDir()
{
   // restrictive umask
   mode_t oldMask = umask(0077);

   QDir home = QDir::home();
   QDir tempDir;

   Misc::g_bVeryFirstStart = false;

   QFileInfo tmp(home, ".crossvc");
   if (tmp.exists()) {
      if (!tmp.isDir()) {
         QFile f(tmp.absFilePath());
         f.remove();  //we must check the result of remove!!!
         tempDir.mkdir(tmp.absFilePath());
         Misc::g_bVeryFirstStart = true;
      }
   } else {
      tempDir.mkdir(tmp.absFilePath());
      Misc::g_bVeryFirstStart = true;
   }
 
   chmod(tmp.absFilePath(), 0700);

   tmpDir = tmp.absFilePath();
   umask(oldMask);
}

//----------------------------------------------------------------------------

QString createTempFile(QString appendix /*= QString::null*/)
{
   if (!appendix.isNull()) appendix.replace(QRegExp("[\\/<>&\"]"),"_");
    
   QString tmp = tmpDir + "/tmp.XXXXXX";

   char* fname = new char[tmp.length() + 1];
   strcpy(fname, tmp);

   int res = mkstemp(fname);

   if (res == -1) {
      qDebug("Can't create tempfile: "+QString(fname));
      delete [] fname;
      return "";
   } else if (remove(fname) == -1) {//remove the file because: 1. we only need its name 2. this should be handled as on the win platform
      qDebug("Can't remove tempfile: "+QString(fname));
      delete [] fname;
      return "";
   }
  
   QString tmpFile = fname + appendix;
   delete [] fname;

   if (QFileInfo(tmpFile).exists()) {
      qDebug("tempfile exists: "+tmpFile);
      return "";
   }
   return tmpFile;
}

//----------------------------------------------------------------------------

void createTemplateDir()
{
   // restrictive umask
   mode_t oldMask = umask(0077);

   QDir tempDir;
   QFileInfo tmp(tmpDir, "Template");
   if (tmp.exists()) {
      if (!tmp.isDir()) {
	 QFile f(tmp.absFilePath());
	 f.remove();  //we must check the result of remove!!!
	 tempDir.mkdir(tmp.absFilePath());
      }
   } else tempDir.mkdir(tmp.absFilePath());

   chmod(tmp.absFilePath(), 0700);
   umask(oldMask);
}

//----------------------------------------------------------------------------

QString createHistoryFile()
{
   createTemplateDir();

   // restrictive umask
   mode_t oldMask = umask(0077);

   QString tmpl = tmpDir + "/Template/XXXXXX";

   char* fname = new char[tmpl.length() + 1];
   strcpy(fname, tmpl);
   
   int file = mkstemp(fname);
  
   QString tmpFile = fname;
   delete [] fname;      

   if (file > -1) {
      fchmod(file, 0600);
      close(file);

      umask(oldMask);
      return tmpFile;
   }

   umask(oldMask);  
   return "";
}

//----------------------------------------------------------------------------

void runExternal( QString arg, QDir *dir) {

   arg = LW::validateEchse(arg);
   QString debugStr = "running: " + arg + " in";
   if (dir) debugStr += ": " + dir->absPath();
   else debugStr += " default dir: "+QDir::currentDirPath();
   qDebug(debugStr);
   arg += " ";

   int S = 1;
   int K = 2;
   int blocked = 0;
   unsigned int i;
   unsigned int count = 0;
   unsigned int lastPos = 0;
   QStringList args;
   
   for (i=0; i<arg.length(); i++) {
      if ( arg.at(i) == '\"') {
         if (blocked != S) {
            count++;
            blocked = S;
         } else {
            count--;
            blocked = K;
         }
      }
      if ( arg.mid(i,1) == "'" ) {
         if (blocked != K) {
            count++;
            blocked = K;
         } else {
            count--;
            blocked = S;
         }
      }
      if ( (count == 0) && (arg.at(i) == ' ')) {
         QString tmp = arg.mid(lastPos,i-lastPos);
         if ( (tmp.startsWith("\"")) || (tmp.startsWith("'")) ) {
            tmp = tmp.mid(1);
         }
         if ( (tmp.endsWith("\"")) || (tmp.endsWith("'")) ) {
            tmp = tmp.left(tmp.length()-1);
         }
         if (!tmp.isEmpty()) {
            tmp.replace(PLACEHOLDER_FOR_QUOTATION_MARKS, "\"");
            args.append (tmp);
         }
         lastPos = i+1;
      }
   }
   //   for ( QStringList::Iterator it = args.begin(); it != args.end(); ++it ) {
      //     qDebug("cmd: ->"+(*it)+"<-");
   //   }
   runExternal( args, dir);
}

//----------------------------------------------------------------------------

void runExternal( QStringList args, QDir *dir) {

   QProcess proc;
   if (dir) {
      proc.setWorkingDirectory(*dir);
   }
   proc.setArguments(args);
   if (!proc.start()) {
      QString msg;
      if (dir) msg += "in dir: ->"+dir->absPath()+"<-, ";
      msg += "couldn't start proc: ->"+args.join(" ")+"<-";
      qDebug(msg);
   }
}

//----------------------------------------------------------------------------

void adaptQStringList(const QString &s, QStringList *list, int size) {
   if (s.isEmpty()) {
      return;
   }
   if (list->findIndex(s) == -1) {
      //not in list
      while ( (list->count() >= (unsigned int)size) && (list->count() > 0)) {
	 list->remove(--(list->end()));//remove the last element
      }
      list->prepend(s);
   } else if (list->findIndex(s) > 0) {
      // text isn't the first item in list
      list->remove(s);
      list->prepend(s);
   }
}

//----------------------------------------------------------------------------

void simplifyStringList(QStringList* pList)
{
   QStringList::Iterator it, head;
   bool bAllDone = false;

   head = pList->begin();

   while(!bAllDone) {
      it = head;
      if (it == pList->end()) {
	 bAllDone = true;
	 //             qDebug("DONE");
      } else {
	 //qDebug(*head);   //the lovely win doesn't like the star before head
	 QString cur = *it;
	 it++;   //we need the second item

	 while(it != pList->end()) {
	    if (*it == cur) {
	       //                     qDebug("remove: " + *it);
	       //remove the multiple entry
	       it = pList->remove(it);
	    } else it++;
	 }

	 head++;
      }
   }

   // remove empty entries
   it = pList->begin();
   while(it != pList->end()) {
      if ((*it).isEmpty()) it = pList->remove(it);
      else it++;
   }
    
#ifdef ANY_DEBUG_STUFF
   qDebug("StringList: Start");
   for (it = pList->begin(); it != pList->end(); it++) {
      qDebug(*it);
   }
   qDebug("simplify StringList: End");
#endif
}

//----------------------------------------------------------------------------

void cutList(QStringList* pList, int allowedSize)
{
   int size = pList->count();
   int diff = size - allowedSize;
    
   if (diff < 1) {
      return;
   }

   int i;
   for (i = 0; i < diff; i++) {
      pList->remove(pList->at(size - 1 - i));
   }
}

//----------------------------------------------------------------------------

QString masqWs(const QString &str) {
   return "\""+str+"\"";
}

//----------------------------------------------------------------------------

QString umasqWs(QString str) {
   str = str.stripWhiteSpace();
   if (str.startsWith("\"")) {
      str.remove(0,1);
   }
   if (str.endsWith("\"")) {
      str.truncate(str.length()-1);
   }
   return str;
}

//----------------------------------------------------------------------------

void masqQuoteMarks(QStringList *pList)
{
   QStringList::Iterator it = pList->begin();
   while (it != pList->end()) {
      (*it).replace("\"", PLACEHOLDER_FOR_QUOTATION_MARKS);
      it++;
   }
}

//----------------------------------------------------------------------------

Mapping::FileAppItem getMatchForWildcard(const QStringList& nameList, const Mapping::FileAppList& appList) {

   QRegExp rx;
   Mapping::FileAppList::ConstIterator appIt;
   for ( appIt = appList.begin(); appIt != appList.end(); ++appIt ) {
      rx.setWildcard(!(*appIt).isRegExp);
      rx.setPattern( (*appIt).wildcard);
      QStringList::ConstIterator nameIt;
      bool match = TRUE;
      for ( nameIt = nameList.begin(); nameIt != nameList.end(); ++nameIt ) {
	 if (!rx.exactMatch( (*nameIt))) {
	    match = FALSE;
	    break;
	 }
      }
      if (match) {
	 return Mapping::FileAppItem((*appIt));
      }
   }
   Mapping::FileAppItem item;
   item.wildcard = "";
   item.app = "";
   item.params = "";
   item.isRegExp = FALSE;
   return item;
}

//----------------------------------------------------------------------------

QString validateOptions(const QString options)
{
   QString newOps = options;
   if (options.find(QRegExp("%([0-9]|n)")) < 0) newOps += " %n";
   //printf("%d\n", options.find(QRegExp("%([0-9]|n)")));
   return newOps;
}

//----------------------------------------------------------------------------

void openUrlWithRemoteBrowser(const QString url)
{
   QStringList urlList;
   urlList.append(url);
   openUrlsWithRemoteBrowser(urlList);
}

//----------------------------------------------------------------------------

void openUrlsWithRemoteBrowser(const QStringList urlList)
{
   QString cmd = "\"" + ExtApps::g_remoteBrowser.path +"\" ";
   QString options = ExtApps::g_remoteBrowser.options;
   options = validateOptions(options);
   masqedFilenamesForPlaceholders(options, urlList);
   cmd += options;
   runExternal(cmd);
}

//----------------------------------------------------------------------------

bool masqedFilenamesForPlaceholders(QString& params, const QStringList& nameList) {

   unsigned int oldSize = params.length();
   int pos = 0;
   while ( (pos = params.find("%",pos)) > -1) {
      ++pos;
      unsigned int i = 0;
      while ( (params.length() > ((unsigned)pos)+i) && (params.at(((unsigned)pos)+i).isDigit()) ) i++;
      if (i>0) {
	 QString exp = params.mid(pos,i);
	 int namePos = exp.toInt()-1;
	 if ( (namePos > -1) && (nameList.count() > (unsigned)namePos) ) {
	    params.replace(QRegExp("%"+params.mid(pos,i)),"\""+nameList[namePos]+"\"");
	 }
      }
   }
   QString allFiles = nameList.join("\" \"");
   params.replace(QRegExp("%n"),"\""+allFiles+"\"");

   if (oldSize<params.length()) return TRUE;
   else return FALSE;
}

//----------------------------------------------------------------------------

bool setPermission(const QFile& file, int perm) {

   if (perm <= 7) {
      perm = perm << 6;
      struct stat buf;
      if (!stat(file.name().latin1(),&buf)) {//only change owner permissions
	 mode_t mode = buf.st_mode & (S_IRWXG | S_IRWXO);
	 perm += mode;
      } else {
	 return false;
      }
   }
   if (!chmod( file.name().latin1(),perm)) {
      return true;
   }
   return false;
}

//----------------------------------------------------------------------------

bool rename(const QString& src, const QString& dst) {
   QFile f(src);
   bool readonly = false;
   if (!QFileInfo(f).isWritable()) {
      if (!setPermission(f,READABLE | WRITEABLE)) {
         return false;
      }
      readonly = true;
   }
   if (rename(src.latin1(),dst.latin1())) {
      return false;
   }
   if (readonly) {
      f.setName(dst);
      if (!setPermission(f,READABLE)) {
         return false;
      }
   }
   return true;
}

//----------------------------------------------------------------------------

//return proxystring if available
//settings should be entered like:
//user@my.remote.server::otheruser@my.proxy.server
//where user and otheruser are optional
QString getProxy(const QString& userAtHost) {

   QString host = userAtHost.mid(userAtHost.find("@")+1);

   QString proxy;
   
   for (QStringList::Iterator proxyit = proxyList.begin(); proxyit != proxyList.end(); proxyit++) {
      if ( (*proxyit).startsWith(userAtHost)) {
         proxy = (*proxyit);
         break;
      }
   }
   if (proxy.isEmpty()) {//no proxy found, try again without user spec
      for (QStringList::Iterator proxyit = proxyList.begin(); proxyit != proxyList.end(); proxyit++) {
         if ( (*proxyit).startsWith(host)) {
            proxy = (*proxyit);
            break;
         }
      }
   }
   if (!proxy.isEmpty()) {
      int splitPos;
      if ( (splitPos=proxy.find("::")) >= 0) {
         proxy = proxy.mid(splitPos+2);
         int etpos;
         if ( (etpos=proxy.find("@"))>=0) {
            proxy = proxy.mid(etpos+1);
         }
         return proxy.stripWhiteSpace();
      }
   }
   return "";
}

//----------------------------------------------------------------------------

void wait(int usecs) {

#ifdef NANOSLEEP
   struct timespec req;
   req.tv_sec  =  usecs / 1000000;
   req.tv_nsec = (usecs % 1000000) * 1000;
   nanosleep(&req,NULL);
#else
   usleep(usecs);
#endif
}

//----------------------------------------------------------------------------

QString getSystemLF() {
   return "\n";
}

//----------------------------------------------------------------------------

void I18n::init()
{
   CTextCodecPool* pTextCodecPool = new CTextCodecPool();
  
   I18n::g_pTextDecoder = new CMetaTextDecoder(pTextCodecPool);
   I18n::g_pTextDecoder->setGeneralDecoder(I18n::g_nameOfDecoder);

   I18n::g_pTextEncoder = new CTextEncoder(pTextCodecPool);
   I18n::g_pTextEncoder->setEncoder(I18n::g_nameOfEncoder);
}

//----------------------------------------------------------------------------

void I18n::destroy()
{
   CTextCodecPool* pTextCodecPool = I18n::g_pTextDecoder->pTextCodecPool();
   delete I18n::g_pTextDecoder;
   I18n::g_pTextDecoder = 0;
   delete I18n::g_pTextEncoder;
   I18n::g_pTextEncoder = 0;
   delete pTextCodecPool;
}

//----------------------------------------------------------------------------

bool initMonitoring() {
   fc = NULL;
   m_hasDirWatch = FALSE;
   DirWatch::b_isActive = FALSE;

#ifdef USE_FAM
   m_hasDirWatch = true;
   if ( DirWatch::b_useDirWatch) {
      if ((fc = new FamConnector()) && fc->isRunning()) {
         DirWatch::b_isActive = true;
      } else {
         qDebug("init fam failed");
      }
   }
#endif
  
#ifdef USE_DNOTIFY
   m_hasDirWatch = true;
   if ( DirWatch::b_useDirWatch) {
      if ((fc = new DNotifyConnector()) && fc->isRunning()) {
         DirWatch::b_isActive = true;
      } else {
         qDebug("init dnotify failed");
      }
   }
#endif

   return DirWatch::b_isActive;
}

DirConnector* getDirConnector() {
   return fc;
}

bool hasDirWatch() {
   return m_hasDirWatch;
}

void releaseMonitoring() {
   if (fc) {
      DirWatch::b_isActive = false;
      delete fc;
      fc = NULL;
   }
}

//----------------------------------------------------------------------------

bool addMonitoredDir(DirBase * dir) {
   if (!DirWatch::b_isActive) return FALSE;
   bool added = fc->addMonitoredDir( dir);
   if (!added) {
      releaseMonitoring();   //hope it works here
   }
   return added;
}

//----------------------------------------------------------------------------

void releaseMonitoredDir(DirBase * dir) {
   if (!DirWatch::b_isActive) return;
   fc->releaseMonitoredDir( dir);
   return;
}

//----------------------------------------------------------------------------

DirBase* checkMonitoredEvent(QString& fileName,bool& deleted) {
   if (!DirWatch::b_isActive) return NULL;
   return fc->checkEvent(fileName,deleted);
}

void suspendDirWatch() {
   if (!DirWatch::b_isActive) return;
   fc->suspend();
}

void resumeDirWatch() {
   if (!DirWatch::b_isActive) return;
   fc->resume();
}

void lockDirWatch() {
   if (!DirWatch::b_isActive) return;
   fc->lock();
}

void unlockDirWatch() {
   if (!DirWatch::b_isActive) return;
   fc->unlock();
}

