Initial commit
[keepassx] / src / lib / tools.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2008 by Tarek Saidi                                *
3  *   tarek.saidi@arcor.de                                                  *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; version 2 of the License.               *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/ 
19
20 #include <QDesktopServices>
21 #include <QLibraryInfo>
22 #include <QProcess>
23 #include <QTranslator>
24
25 #if defined(Q_WS_X11) || defined(Q_WS_MAC)
26         #include <sys/mman.h>
27         #include <unistd.h>
28 #elif defined(Q_WS_WIN)
29         #include <QLibrary>
30         #include <windows.h>
31         #include <io.h>
32 #endif
33
34 void createBanner(QPixmap* Pixmap,const QPixmap* IconAlpha,const QString& Text,int Width){
35         createBanner(Pixmap,IconAlpha,Text,Width,config->bannerColor1(),config->bannerColor2(),config->bannerTextColor());
36 }
37
38 void createBanner(QPixmap* Pixmap,const QPixmap* IconAlpha,const QString& Text,int Width, QColor Color1, QColor Color2, QColor TextColor){
39         *Pixmap=QPixmap(Width,50);
40         QPainter painter(Pixmap);
41         QLinearGradient grad(0,0,Width,0);
42         grad.setColorAt(0,Color1);
43         grad.setColorAt(1,Color2);
44         painter.setPen(Qt::NoPen);
45         painter.setBrush(grad);
46         painter.drawRect(0,0,Width,50);
47         
48         QPixmap Icon(32,32);
49         if(IconAlpha){
50                 Icon.fill(TextColor);
51                 Icon.setAlphaChannel(*IconAlpha);
52                 painter.drawPixmap(10,10,Icon);
53         }
54         
55         painter.setPen(QPen(TextColor));
56         painter.setFont(QFont(QApplication::font().family(),16));
57         painter.drawText(50,35,Text);
58 }
59
60 QString decodeFileError(QFile::FileError Code){
61         switch(Code){
62                 case QFile::NoError: return QApplication::translate("FileErrors","No error occurred.");
63                 case QFile::ReadError: return QApplication::translate("FileErrors","An error occurred while reading from the file.");
64                 case QFile::WriteError: return QApplication::translate("FileErrors","An error occurred while writing to the file.");
65                 case QFile::FatalError: return QApplication::translate("FileErrors","A fatal error occurred.");
66                 case QFile::ResourceError: return QApplication::translate("FileErrors","An resource error occurred.");
67                 case QFile::OpenError: return QApplication::translate("FileErrors","The file could not be opened.");
68                 case QFile::AbortError: return QApplication::translate("FileErrors","The operation was aborted.");
69                 case QFile::TimeOutError: return QApplication::translate("FileErrors","A timeout occurred.");
70                 case QFile::UnspecifiedError: return QApplication::translate("FileErrors","An unspecified error occurred.");
71                 case QFile::RemoveError: return QApplication::translate("FileErrors","The file could not be removed.");
72                 case QFile::RenameError: return QApplication::translate("FileErrors","The file could not be renamed.");
73                 case QFile::PositionError: return QApplication::translate("FileErrors","The position in the file could not be changed.");
74                 case QFile::ResizeError: return QApplication::translate("FileErrors","The file could not be resized.");
75                 case QFile::PermissionsError: return QApplication::translate("FileErrors","The file could not be accessed.");
76                 case QFile::CopyError: return QApplication::translate("FileErrors","The file could not be copied.");
77         }
78         return QString();
79 }
80
81 void openBrowser(IEntryHandle* entry){
82         QString url = entry->url();
83         url.replace("{TITLE}", entry->title(), Qt::CaseInsensitive);
84         url.replace("{USERNAME}", entry->username(), Qt::CaseInsensitive);
85         
86         if (url.contains("{PASSWORD}",Qt::CaseInsensitive)){
87                 SecString password=entry->password();
88                 password.unlock();
89                 url.replace("{PASSWORD}", password, Qt::CaseInsensitive);
90         }
91         
92         openBrowser(url);
93 }
94
95 void openBrowser(const QString& UrlString){
96         if (UrlString.trimmed().isEmpty())
97                 return;
98         
99         if (UrlString.startsWith("cmd://") && UrlString.length()>6){
100                 QProcess::startDetached(UrlString.right(UrlString.length()-6));
101                 return;
102         }
103         
104         QUrl url(UrlString);
105         if(url.scheme().isEmpty())
106                 url=QUrl("http://"+UrlString);
107         if(config->urlCmdDef() || url.scheme()=="mailto"){
108                 QDesktopServices::openUrl(url);
109         }
110         else{
111                 QByteArray UrlEncoded = url.toEncoded();
112                 QString browser = config->urlCmd();
113                 if (browser.contains("%u", Qt::CaseInsensitive))
114                         browser.replace("%u", UrlEncoded, Qt::CaseInsensitive);
115                 else if (browser.contains("%1"))
116                         browser.replace("%1", UrlEncoded);
117                 else
118                         browser.append(" ").append(UrlEncoded);
119                 QProcess::startDetached(browser);
120         }
121 }
122
123
124 QString makePathRelative(const QString& AbsDir,const QString& CurDir){
125         QStringList abs=AbsDir.split('/');
126         QStringList cur=CurDir.split('/');
127         QString rel="./";
128         int common;
129         for(common=0; common < abs.size() && common < cur.size(); common++){
130                 if(abs[common]!=cur[common])break;
131         }
132         for(int i=0;i<cur.size()-common;i++)
133                 rel.append("../");
134         for(int i=common;i<abs.size();i++)
135                 rel.append(abs[i]+"/");
136         return rel;
137 }
138
139 void showErrMsg(const QString& msg,QWidget* parent){
140         QMessageBox::critical(parent, QApplication::translate("Main","Error"), msg);
141 }
142
143 QString getImageFile(const QString& name){
144         if (QFile::exists(DataDir+"/icons/"+name))
145                 return DataDir+"/icons/"+name;
146         else{
147                 QString errMsg = QApplication::translate("Main","File '%1' could not be found.").arg(name);
148                 showErrMsg(errMsg);
149                 qFatal("File '%s' could not be found.", CSTR(errMsg));
150                 return QString();
151         }
152 }
153
154 const QIcon& getIcon(const QString& name){
155         static QHash<QString,QIcon*>IconCache;
156         QIcon* CachedIcon=IconCache.value(name);
157         if(CachedIcon)
158                 return *CachedIcon;
159         QIcon* NewIcon=NULL;
160         if(IconLoader){
161                 NewIcon=new QIcon(IconLoader->getIcon(name));
162                 if(NewIcon->isNull()){
163                         delete NewIcon;
164                         NewIcon=NULL;
165                 }
166                 else
167                         IconCache.insert(name,NewIcon);
168         }
169         if(!NewIcon)
170         {
171                 NewIcon=new QIcon(getImageFile(name+".png"));
172                 IconCache.insert(name,NewIcon);
173         }
174         return *NewIcon;
175 }
176
177 const QPixmap* getPixmap(const QString& name){
178         static QHash<QString,QPixmap*>PixmapCache;
179         QPixmap* CachedPixmap=PixmapCache.value(name);
180         if(CachedPixmap)
181                 return CachedPixmap;
182         QImage img(getImageFile(name+".png"));
183         QPixmap* NewPixmap=new QPixmap(QPixmap::fromImage(img));
184         PixmapCache.insert(name,NewPixmap);
185         return NewPixmap;
186 }
187
188
189 bool createKeyFile(const QString& filename,QString* error,int length, bool Hex){
190         QFile file(filename);
191         if(!file.open(QIODevice::WriteOnly|QIODevice::Truncate|QIODevice::Unbuffered)){
192                 *error=decodeFileError(file.error());
193                 return false;
194         }
195         if(Hex)length*=2;
196         unsigned char* key=new unsigned char[length];
197         randomize(key,length);
198         if(Hex){
199                 // convert binary data to hex code (8 bit ==> 2 digits)
200                 for(int i=0; i<length; i+=2){
201                         unsigned char dig1,dig2;
202                         dig1=key[i]/16;
203                         key[i]-=(16*dig1);
204                         dig2=key[i];
205                         if(dig1>9)key[i]='A'+dig1-10;
206                         else key[i]='0'+dig1;
207                         if(dig2>9)key[i+1]='A'+dig2-10;
208                         else key[i+1]='0'+dig2;
209                 }
210         }
211         if(file.write((char*)key,length)==-1){
212                 delete [] key;
213                 *error=decodeFileError(file.error());
214                 file.close();
215                 return false;
216         }
217         file.close();
218         delete [] key;
219         return true;
220 }
221
222 bool lockPage(void* addr, int len){
223 #if defined(Q_WS_X11) || defined(Q_WS_MAC)
224         return (mlock(addr, len)==0);
225 #elif defined(Q_WS_WIN)
226         return VirtualLock(addr, len);
227 #else
228         return false;
229 #endif
230 }
231
232 bool unlockPage(void* addr, int len){
233 #if defined(Q_WS_X11) || defined(Q_WS_MAC)
234         return (munlock(addr, len)==0);
235 #elif defined(Q_WS_WIN)
236         return VirtualUnlock(addr, len);
237 #else
238         return false;
239 #endif
240 }
241
242 bool syncFile(QFile* file) {
243         if (!file->flush())
244                 return false;
245 #if defined(Q_WS_X11) || defined(Q_WS_MAC)
246         return (fsync(file->handle())==0);
247 #elif defined(Q_WS_WIN)
248         return (_commit(file->handle())==0);
249 #else
250         return false;
251 #endif
252 }
253
254 QTranslator* translator = new QTranslator();
255 QTranslator* qtTranslator = new QTranslator();
256 bool translatorActive = false;
257 bool qtTranslatorActive = false;
258
259 bool loadTranslation(QTranslator* tr,const QString& prefix,const QString& loc,const QStringList& paths){
260         for (int i=0;i<paths.size();i++){
261                 if(tr->load(prefix+loc+".qm",paths[i]))
262                         return true;
263         }
264         
265         for (int i=0;i<paths.size();i++){
266                 QDir dir(paths[i]);
267                 QStringList TrFiles=dir.entryList(QStringList()<<"*.qm",QDir::Files);
268                 for (int j=0;j<TrFiles.size();j++){
269                         if (TrFiles[j].left(prefix.length()+2)==prefix+loc.left(2)){
270                                 if (tr->load(TrFiles[j],paths[i]))
271                                         return true;
272                         }
273                 }
274         }
275         return false;
276 }
277
278 void deactivateTranslators(bool qtOnly=false){
279         if (translatorActive && !qtOnly){
280                 QApplication::removeTranslator(translator);
281                 translatorActive = false;
282         }
283         
284         if (qtTranslatorActive){
285                 QApplication::removeTranslator(qtTranslator);
286                 qtTranslatorActive = false;
287         }
288 }
289
290 void installTranslator(){
291         QString language = config->language();
292         if (language=="auto")
293                 language = QLocale::system().name();
294         
295         if (language.isEmpty() || language=="en_US"){
296                 deactivateTranslators();
297                 return;
298         }
299         
300         if (loadTranslation(translator,"keepassx-",language,QStringList()
301                 << HomeDir << DataDir+"/i18n/"))
302         {
303                 if (!translatorActive){
304                         QApplication::installTranslator(translator);
305                         translatorActive = true;
306                 }
307         }
308         else{
309                 deactivateTranslators();
310                 return;
311         }
312         
313         if (loadTranslation(qtTranslator,"qt_",language,QStringList()
314                 << HomeDir << DataDir+"/i18n/"
315                 << QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
316         {
317                 if (!qtTranslatorActive){
318                         QApplication::installTranslator(qtTranslator);
319                         qtTranslatorActive = true;
320                 }
321         }
322         else{
323                 deactivateTranslators(true);
324         }
325 }
326
327 bool isTranslationActive(){
328         return translatorActive;
329 }
330
331 bool operator<(const Translation& t1, const Translation& t2){
332         return t1.nameLong < t2.nameLong;
333 }
334
335 QList<Translation> getAllTranslations(){
336         QTranslator tmpTranslator;
337         QList<Translation> translations;
338         QSet<QString> names;
339         
340         QStringList paths = QStringList() << DataDir+"/i18n/" << HomeDir;
341         QRegExp filename("keepassx-([^_]{2}_[^\\.]{2}|[^\\.]{2})\\.qm");
342         for (int i=0;i<paths.size();i++){
343                 QDir dir(paths[i]);
344                 QStringList TrFiles=dir.entryList(QStringList()<<"*.qm",QDir::Files);
345                 for (int j=0;j<TrFiles.size();j++){
346                         if (filename.exactMatch(TrFiles[j]) && TrFiles[j]!="keepassx-xx_XX.qm" &&
347                                 tmpTranslator.load(TrFiles[j],paths[i]) && !names.contains(filename.cap(1)))
348                         {
349                                 Translation t;
350                                 t.nameCode = filename.cap(1);
351                                 t.nameLong = tmpTranslator.translate("Translation", "$LANGUAGE_NAME", "Insert your language name in the format: English (United States)");
352                                 t.author = tmpTranslator.translate("Translation", "$TRANSLATION_AUTHOR");
353                                 
354                                 QLocale l(t.nameCode);
355                                 t.nameEnglish = QLocale::languageToString(l.language());
356                                 if (t.nameCode.size()==5){
357                                         QString country = QLocale::countryToString(l.country());
358                                         int size = country.size();
359                                         for (int k=1; k<size; k++){
360                                                 if (country[k].isUpper()){
361                                                         country.insert(k, " ");
362                                                         k += 2;
363                                                         size++;
364                                                 }
365                                         }
366                                         t.nameEnglish.append(" (").append(country).append(")");
367                                 }
368                                 
369                                 if (t.nameLong.isEmpty())
370                                         t.nameLong = t.nameEnglish;
371                                 
372                                 translations << t;
373                                 names << t.nameCode;
374                         }
375                 }
376         }
377         
378         qSort(translations.begin(), translations.end());
379         return translations;
380 }
381
382 // from src/corelib/qsettings.cpp:
383 #ifdef Q_OS_WIN
384 QString qtWindowsConfigPath(int type)
385 {
386         QString result;
387
388         QLibrary library(QLatin1String("shell32"));
389         QT_WA( {
390                 typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPTSTR, int, BOOL);
391                 GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW");
392                 if (SHGetSpecialFolderPath) {
393                         TCHAR path[MAX_PATH];
394                         SHGetSpecialFolderPath(0, path, type, FALSE);
395                         result = QString::fromUtf16((ushort*)path);
396                 }
397         } , {
398                 typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, char*, int, BOOL);
399                 GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathA");
400                 if (SHGetSpecialFolderPath) {
401                         char path[MAX_PATH];
402                         SHGetSpecialFolderPath(0, path, type, FALSE);
403                         result = QString::fromLocal8Bit(path);
404                 }
405         } );
406
407         return result;
408 }
409 #endif // Q_OS_WIN