source: psi/trunk/src/common.cpp@ 189

Last change on this file since 189 was 84, checked in by dmik, 19 years ago

Psi: Added installation of the OS/2 System Exception handler (QtOS2SysXcptHandler) on the main thread.

File size: 47.4 KB
Line 
1/*
2 * common.cpp - contains all the common variables and functions for Psi
3 * Copyright (C) 2001-2003 Justin Karneges
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include"common.h"
22
23#if defined(Q_OS_OS2) && !defined(QT_OS2_NO_SYSEXCEPTIONS)
24#include <qt_os2.h>
25#endif
26
27#include"profiles.h"
28#include"rtparse.h"
29
30#include"psievent.h"
31
32QString PROG_NAME = "Psi/2";
33//QString PROG_VERSION = "0.10.2";
34QString PROG_VERSION = "0.10.2-dev (" __DATE__ ")";
35
36#ifdef HAVE_CONFIG
37#include "config.h"
38#endif
39
40#ifndef PSI_DATADIR
41#define PSI_DATADIR "/usr/local/share/psi"
42#endif
43
44#include<qregexp.h>
45#include<qdir.h>
46#include<qfile.h>
47#include<qapplication.h>
48#include<qsound.h>
49#include<qobjectlist.h>
50#include<qlibrary.h>
51#include<qstylesheet.h>
52
53#include<qthread.h>
54#include<qmutex.h>
55#include<qsemaphore.h>
56#include<qvaluelist.h>
57
58
59#include<stdio.h>
60
61#ifdef Q_WS_X11
62#include<sys/types.h>
63#include<sys/stat.h>
64#include<sys/wait.h>
65#endif
66
67#ifdef Q_WS_WIN
68#include<windows.h>
69#endif
70
71#ifdef Q_WS_PM
72#include<sys/types.h>
73#include<sys/stat.h>
74#endif
75
76#ifdef Q_WS_MAC
77#include<sys/types.h>
78#include<sys/stat.h>
79#include <Carbon/Carbon.h> // for HIToolbox/InternetConfig
80#include <CoreServices/CoreServices.h>
81#endif
82
83QString activeProfile;
84
85//QStringList dtcp_hostList;
86//int dtcp_port;
87//QString dtcp_proxy;
88//bool link_test = false;
89bool use_gpg = true;
90bool no_gpg_agent = false;
91uint psi_dialog_flags = (Qt::WStyle_SysMenu | Qt::WStyle_MinMax);
92//uint psi_dialog_flags = (Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WStyle_MinMax);
93
94char *pixdat_ft_back, *pixdat_ft_file, *pixdat_ft_folder;
95int pixlen_ft_back, pixlen_ft_file, pixlen_ft_folder;
96
97
98XMPP::Client *debug_jabber = 0;
99Options option;
100PsiGlobal g;
101PsiIconset *is;
102bool useSound;
103
104
105QString qstrlower(QString str)
106{
107 for(unsigned int n = 0; n < str.length(); ++n) {
108 str.at(n) = str.at(n).lower();
109 }
110
111 return str;
112}
113
114int qstrcasecmp(const QString &str1, const QString &str2)
115{
116 if(str1.length() != str2.length())
117 return 1;
118
119 for(unsigned int n = 0; n < str1.length(); ++n) {
120 if(str1.at(n).lower() != str2.at(n).lower())
121 return 1;
122 }
123
124 return 0;
125}
126
127int qstringlistmatch(QStringList &list, const QString &str)
128{
129 int n = 0;
130
131 for(QStringList::Iterator i = list.begin(); i != list.end(); ++i, ++n) {
132 if(*i == str)
133 return n;
134 }
135
136 return -1;
137}
138
139QString qstringlistlookup(QStringList &list, int x)
140{
141 int n = 0;
142 QStringList::Iterator i = list.begin();
143 for(;i != list.end() && n < x; ++i, ++n);
144 if(n != x)
145 return "";
146
147 return *i;
148}
149
150QString CAP(const QString &str)
151{
152 return QString("%1: %2").arg(PROG_NAME).arg(str);
153}
154
155QString eatptag(QString txt)
156{
157 if(txt.left(3) == "<p>")
158 txt = txt.mid(3, txt.length() - 7); // strlen("<p>") + strlen("</p>") = 7
159
160 return txt;
161}
162
163QString qstrquote(const QString &toquote, int width, bool quoteEmpty)
164{
165 int ql = 0, col = 0, atstart = 1, ls=0;
166
167 QString quoted = "> "+toquote; // quote first line
168 QString rxs = quoteEmpty ? "\n" : "\n(?!\\s*\n)";
169 QRegExp rx(rxs); // quote following lines
170 quoted.replace(rx, "\n> ");
171 rx.setPattern("> +>"); // compress > > > > quotes to >>>>
172 quoted.replace(rx, ">>");
173 quoted.replace(rx, ">>");
174 quoted.replace(QRegExp(" +\n"), "\n"); // remove trailing spaces
175
176 if (!quoteEmpty)
177 {
178 quoted.replace(QRegExp("^>+\n"), "\n\n"); // unquote empty lines
179 quoted.replace(QRegExp("\n>+\n"), "\n\n");
180 }
181
182
183 for (int i=0;i<(int) quoted.length();i++)
184 {
185 col++;
186 if (atstart && quoted[i] == '>') ql++; else atstart=0;
187
188 switch(quoted[i].latin1())
189 {
190 case '\n': ql = col = 0; atstart = 1; break;
191 case ' ':
192 case '\t': ls = i; break;
193
194 }
195 if (quoted[i]=='\n') { ql=0; atstart = 1; }
196
197 if (col > width)
198 {
199 if ((ls+width) < i)
200 {
201 ls = i; i = quoted.length();
202 while ((ls<i) && !quoted[ls].isSpace()) ls++;
203 i = ls;
204 }
205 if ((i<(int)quoted.length()) && (quoted[ls] != '\n'))
206 {
207 quoted.insert(ls, '\n');
208 ++ls;
209 quoted.insert(ls, QString().fill('>', ql));
210 i += ql+1;
211 col = 0;
212 }
213 }
214 }
215 quoted += "\n\n";// add two empty lines to quoted text - the cursor
216 // will be positioned at the end of those.
217 return quoted;
218}
219
220QString plain2rich(const QString &plain)
221{
222 QString rich;
223 int col = 0;
224
225 for(int i = 0; i < (int)plain.length(); ++i) {
226 if(plain[i] == '\n') {
227 rich += "<br>";
228 col = 0;
229 }
230 else if(plain[i] == '\t') {
231 rich += QChar::nbsp;
232 while(col % 4) {
233 rich += QChar::nbsp;
234 ++col;
235 }
236 }
237 else if(plain[i].isSpace()) {
238 if(i > 0 && plain[i-1] == ' ')
239 rich += QChar::nbsp;
240 else
241 rich += ' ';
242 }
243 else if(plain[i] == '<')
244 rich += "&lt;";
245 else if(plain[i] == '>')
246 rich += "&gt;";
247 else if(plain[i] == '\"')
248 rich += "&quot;";
249 else if(plain[i] == '\'')
250 rich += "&apos;";
251 else if(plain[i] == '&')
252 rich += "&amp;";
253 else
254 rich += plain[i];
255 ++col;
256 }
257
258 return rich;
259}
260
261QString rich2plain(const QString &in)
262{
263 QString out;
264
265 for(int i = 0; i < (int)in.length(); ++i) {
266 // tag?
267 if(in[i] == '<') {
268 // find end of tag
269 ++i;
270 int n = in.find('>', i);
271 if(n == -1)
272 break;
273 QString str = in.mid(i, (n-i));
274 i = n;
275
276 QString tagName;
277 n = str.find(' ');
278 if(n != -1)
279 tagName = str.mid(0, n);
280 else
281 tagName = str;
282
283 if(tagName == "br")
284 out += '\n';
285 }
286 // entity?
287 else if(in[i] == '&') {
288 // find a semicolon
289 ++i;
290 int n = in.find(';', i);
291 if(n == -1)
292 break;
293 QString type = in.mid(i, (n-i));
294 i = n; // should be n+1, but we'll let the loop increment do it
295
296 if(type == "amp")
297 out += '&';
298 else if(type == "lt")
299 out += '<';
300 else if(type == "gt")
301 out += '>';
302 else if(type == "quot")
303 out += '\"';
304 else if(type == "apos")
305 out += '\'';
306 }
307 else if(in[i].isSpace()) {
308 if(in[i] == QChar::nbsp)
309 out += ' ';
310 else if(in[i] != '\n') {
311 if(i == 0)
312 out += ' ';
313 else {
314 QChar last = out.at(out.length()-1);
315 bool ok = TRUE;
316 if(last.isSpace() && last != '\n')
317 ok = FALSE;
318 if(ok)
319 out += ' ';
320 }
321 }
322 }
323 else {
324 out += in[i];
325 }
326 }
327
328 return out;
329}
330
331
332// clips plain text
333QString clipStatus(const QString &str, int width, int height)
334{
335 QString out = "";
336 int at = 0;
337 int len = str.length();
338 if(len == 0)
339 return out;
340
341 // only take the first "height" lines
342 for(int n2 = 0; n2 < height; ++n2) {
343 // only take the first "width" chars
344 QString line;
345 bool hasNewline = false;
346 for(int n = 0; at < len; ++n, ++at) {
347 if(str.at(at) == '\n') {
348 hasNewline = true;
349 break;
350 }
351 line += str.at(at);
352 }
353 ++at;
354 if((int)line.length() > width) {
355 line.truncate(width-3);
356 line += "...";
357 }
358 out += line;
359 if(hasNewline)
360 out += '\n';
361
362 if(at >= len)
363 break;
364 }
365
366 return out;
367}
368
369QString expandEntities(const QString &in)
370{
371 return QStyleSheet::escape(in);
372}
373
374QString resolveEntities(const QString &in)
375{
376 QString out;
377
378 for(int i = 0; i < (int)in.length(); ++i) {
379 if(in[i] == '&') {
380 // find a semicolon
381 ++i;
382 int n = in.find(';', i);
383 if(n == -1)
384 break;
385 QString type = in.mid(i, (n-i));
386 i = n; // should be n+1, but we'll let the loop increment do it
387
388 if(type == "amp")
389 out += '&';
390 else if(type == "lt")
391 out += '<';
392 else if(type == "gt")
393 out += '>';
394 else if(type == "quot")
395 out += '\"';
396 else if(type == "apos")
397 out += '\'';
398 }
399 else {
400 out += in[i];
401 }
402 }
403
404 return out;
405}
406
407
408static bool linkify_pmatch(const QString &str1, int at, const QString &str2)
409{
410 if(str2.length() > (str1.length()-at))
411 return FALSE;
412
413 for(int n = 0; n < (int)str2.length(); ++n) {
414 if(str1.at(n+at).lower() != str2.at(n).lower())
415 return FALSE;
416 }
417
418 return TRUE;
419}
420
421static bool linkify_isOneOf(const QChar &c, const QString &charlist)
422{
423 for(int i = 0; i < (int)charlist.length(); ++i) {
424 if(c == charlist.at(i))
425 return TRUE;
426 }
427
428 return FALSE;
429}
430
431// encodes a few dangerous html characters
432static QString linkify_htmlsafe(const QString &in)
433{
434 QString out;
435
436 for(unsigned int n = 0; n < in.length(); ++n) {
437 if(linkify_isOneOf(in.at(n), "\"\'`<>")) {
438 // hex encode
439 QString hex;
440 hex.sprintf("%%%02X", in.at(n).latin1());
441 out.append(hex);
442 }
443 else {
444 out.append(in.at(n));
445 }
446 }
447
448 return out;
449}
450
451static bool linkify_okUrl(const QString &url)
452{
453 if(url.at(url.length()-1) == '.')
454 return FALSE;
455
456 return TRUE;
457}
458
459static bool linkify_okEmail(const QString &addy)
460{
461 // this makes sure that there is an '@' and a '.' after it, and that there is
462 // at least one char for each of the three sections
463 int n = addy.find('@');
464 if(n == -1 || n == 0)
465 return FALSE;
466 int d = addy.find('.', n+1);
467 if(d == -1 || d == 0)
468 return FALSE;
469 if((addy.length()-1) - d <= 0)
470 return FALSE;
471 if(addy.find("..") != -1)
472 return false;
473
474 return TRUE;
475}
476
477QString linkify(const QString &in)
478{
479 QString out = in;
480 int x1, x2;
481 bool isUrl, isEmail;
482 QString linked, link, href;
483
484 for(int n = 0; n < (int)out.length(); ++n) {
485 isUrl = FALSE;
486 isEmail = FALSE;
487 x1 = n;
488
489 if(linkify_pmatch(out, n, "http://")) {
490 n += 7;
491 isUrl = TRUE;
492 href = "";
493 }
494 else if(linkify_pmatch(out, n, "https://")) {
495 n += 8;
496 isUrl = TRUE;
497 href = "";
498 }
499 else if(linkify_pmatch(out, n, "ftp://")) {
500 n += 6;
501 isUrl = TRUE;
502 href = "";
503 }
504 else if(linkify_pmatch(out, n, "news://")) {
505 n += 7;
506 isUrl = TRUE;
507 href = "";
508 }
509 else if (linkify_pmatch(out, n, "ed2k://")) {
510 n += 7;
511 isUrl = TRUE;
512 href = "";
513 }
514 else if(linkify_pmatch(out, n, "www.")) {
515 isUrl = TRUE;
516 href = "http://";
517 }
518 else if(linkify_pmatch(out, n, "ftp.")) {
519 isUrl = TRUE;
520 href = "ftp://";
521 }
522 else if(linkify_pmatch(out, n, "@")) {
523 isEmail = TRUE;
524 href = "mailto:";
525 }
526
527 if(isUrl) {
528 // make sure the previous char is not alphanumeric
529 if(x1 > 0 && out.at(x1-1).isLetterOrNumber())
530 continue;
531
532 // find whitespace (or end)
533 for(x2 = n; x2 < (int)out.length(); ++x2) {
534 if(out.at(x2).isSpace() || out.at(x2) == '<')
535 break;
536 }
537 int len = x2-x1;
538 QString pre = resolveEntities(out.mid(x1, x2-x1));
539
540 // go backward hacking off unwanted punctuation
541 int cutoff;
542 for(cutoff = pre.length()-1; cutoff >= 0; --cutoff) {
543 if(!linkify_isOneOf(pre.at(cutoff), "!?,.()[]{}<>\""))
544 break;
545 }
546 ++cutoff;
547 //++x2;
548
549 link = pre.mid(0, cutoff);
550 if(!linkify_okUrl(link)) {
551 n = x1 + link.length();
552 continue;
553 }
554 href += link;
555 href = linkify_htmlsafe(href);
556 //printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
557 linked = QString("<a href=\"%1\">").arg(href) + expandEntities(link) + "</a>" + expandEntities(pre.mid(cutoff));
558 out.replace(x1, len, linked);
559 n = x1 + linked.length() - 1;
560 }
561 else if(isEmail) {
562 // go backward till we find the beginning
563 if(x1 == 0)
564 continue;
565 --x1;
566 for(; x1 >= 0; --x1) {
567 if(!linkify_isOneOf(out.at(x1), "_.-") && !out.at(x1).isLetterOrNumber())
568 break;
569 }
570 ++x1;
571
572 // go forward till we find the end
573 x2 = n + 1;
574 for(; x2 < (int)out.length(); ++x2) {
575 if(!linkify_isOneOf(out.at(x2), "_.-") && !out.at(x2).isLetterOrNumber())
576 break;
577 }
578
579 int len = x2-x1;
580 link = out.mid(x1, len);
581 //link = resolveEntities(link);
582
583 if(!linkify_okEmail(link)) {
584 n = x1 + link.length();
585 continue;
586 }
587
588 href += link;
589 //printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
590 linked = QString("<a href=\"%1\">").arg(href) + link + "</a>";
591 out.replace(x1, len, linked);
592 n = x1 + linked.length() - 1;
593 }
594 }
595
596 return out;
597}
598
599// sickening
600QString emoticonify(const QString &in)
601{
602 RTParse p(in);
603 while ( !p.atEnd() ) {
604 // returns us the first chunk as a plaintext string
605 QString str = p.next();
606
607 int i = 0;
608 while ( i >= 0 ) {
609 // find closest emoticon
610 int ePos = -1;
611 Icon *closest = 0;
612
613 int foundPos = -1, foundLen = -1;
614
615 QPtrListIterator<Iconset> iconsets(is->emoticons);
616 Iconset *iconset;
617 while ( (iconset = iconsets.current()) != 0 ) {
618 QPtrListIterator<Icon> it = iconset->iterator();
619 for ( ; it.current(); ++it) {
620 Icon *icon = it.current();
621 if ( icon->regExp().isEmpty() )
622 continue;
623
624 // some hackery
625 int iii = i;
626 bool searchAgain;
627
628 do {
629 searchAgain = false;
630
631 // find the closest match
632 const QRegExp &rx = icon->regExp();
633 int n = rx.search(str, iii);
634 if ( n == -1 )
635 continue;
636
637 if(ePos == -1 || n < ePos || (rx.matchedLength() > foundLen && n < ePos + foundLen)) {
638 // there must be whitespace at least on one side of the emoticon
639 if ( ( n == 0 ) ||
640 ( n+rx.matchedLength() == (int)str.length() ) ||
641 ( n > 0 && str[n-1].isSpace() ) ||
642 ( n+rx.matchedLength() < (int)str.length() && str[n+rx.matchedLength()].isSpace() ) )
643 {
644 ePos = n;
645 closest = icon;
646
647 foundPos = n;
648 foundLen = rx.matchedLength();
649 break;
650 }
651
652 searchAgain = true;
653 }
654
655 iii = n + rx.matchedLength();
656 } while ( searchAgain );
657 }
658
659 ++iconsets;
660 }
661
662 QString s;
663 if(ePos == -1)
664 s = str.mid(i);
665 else
666 s = str.mid(i, ePos-i);
667 p.putPlain(s);
668
669 if ( !closest )
670 break;
671
672 p.putRich( QString("<icon name=\"%1\" text=\"%2\">").arg(closest->name()).arg(str.mid(foundPos, foundLen)) );
673 i = foundPos + foundLen;
674 }
675 }
676
677 QString out = p.output();
678
679 //enable *bold* stuff
680 // //old code
681 //out=out.replace(QRegExp("(^[^<>\\s]*|\\s[^<>\\s]*)\\*(\\S+)\\*([^<>\\s]*\\s|[^<>\\s]*$)"),"\\1<b>*\\2*</b>\\3");
682 //out=out.replace(QRegExp("(^[^<>\\s\\/]*|\\s[^<>\\s\\/]*)\\/([^\\/\\s]+)\\/([^<>\\s\\/]*\\s|[^<>\\s\\/]*$)"),"\\1<i>/\\2/</i>\\3");
683 //out=out.replace(QRegExp("(^[^<>\\s]*|\\s[^<>\\s]*)_(\\S+)_([^<>\\s]*\\s|[^<>\\s]*$)"),"\\1<u>_\\2_</u>\\3");
684
685 out=out.replace(QRegExp("(^_|\\s_)(\\S+)(_\\s|_$)"),"\\1<u>\\2</u>\\3");
686 out=out.replace(QRegExp("(^\\*|\\s\\*)(\\S+)(\\*\\s|\\*$)"),"\\1<b>\\2</b>\\3");
687 out=out.replace(QRegExp("(^\\/|\\s\\/)(\\S+)(\\/\\s|\\/$)"),"\\1<i>\\2</i>\\3");
688
689 return out;
690}
691
692QString encodePassword(const QString &pass, const QString &key)
693{
694 QString result;
695 unsigned int n1, n2;
696
697 if(key.length() == 0)
698 return pass;
699
700 for(n1 = 0, n2 = 0; n1 < pass.length(); ++n1) {
701 ushort x = pass.at(n1).unicode() ^ key.at(n2++).unicode();
702 QString hex;
703 hex.sprintf("%04x", x);
704 result += hex;
705 if(n2 >= key.length())
706 n2 = 0;
707 }
708 return result;
709}
710
711QString decodePassword(const QString &pass, const QString &key)
712{
713 QString result;
714 unsigned int n1, n2;
715
716 if(key.length() == 0)
717 return pass;
718
719 for(n1 = 0, n2 = 0; n1 < pass.length(); n1 += 4) {
720 ushort x = 0;
721 if(n1 + 4 > pass.length())
722 break;
723 x += hexChar2int(pass.at(n1))*4096;
724 x += hexChar2int(pass.at(n1+1))*256;
725 x += hexChar2int(pass.at(n1+2))*16;
726 x += hexChar2int(pass.at(n1+3));
727 QChar c(x ^ key.at(n2++).unicode());
728 result += c;
729 if(n2 >= key.length())
730 n2 = 0;
731 }
732 return result;
733}
734
735QString status2txt(int status)
736{
737 switch(status) {
738 case STATUS_OFFLINE: return QObject::tr("Offline");
739 case STATUS_AWAY: return QObject::tr("Away");
740 case STATUS_XA: return QObject::tr("Not Available");
741 case STATUS_DND: return QObject::tr("Do not Disturb");
742 case STATUS_CHAT: return QObject::tr("Free for Chat");
743 case STATUS_INVISIBLE: return QObject::tr("Invisible");
744
745 case STATUS_ONLINE:
746 default: return QObject::tr("Online");
747 }
748}
749
750Icon category2icon(const QString &category, const QString &type)
751{
752 // TODO: update this to http://www.jabber.org/registrar/disco-categories.html#gateway
753
754 // still have to add more options...
755 if ( category == "service" || category == "gateway" ) {
756 QString trans;
757
758 if (type == "aim")
759 trans = "aim";
760 else if (type == "icq")
761 trans = "icq";
762 else if (type == "msn")
763 trans = "msn";
764 else if (type == "yahoo")
765 trans = "yahoo";
766 else if (type == "gadu-gadu" || type == "x-gadugadu")
767 trans = "gadugadu";
768 else if (type == "sms")
769 trans = "sms";
770 else
771 trans = "transport";
772
773 return is->transportStatus(trans, STATUS_ONLINE);
774
775 // irc
776 // jud
777 // pager
778 // jabber
779 // serverlist
780 // smtp
781 }
782 else if ( category == "conference" ) {
783 if (type == "public" || type == "private" || type == "text" || type == "irc")
784 return IconsetFactory::icon("psi/groupChat");
785 else if (type == "url")
786 return IconsetFactory::icon("psi/www");
787 // irc
788 // list
789 // topic
790 }
791 else if ( category == "validate" ) {
792 if (type == "xml")
793 return IconsetFactory::icon("psi/xml");
794 // grammar
795 // spell
796 }
797 else if ( category == "user" || category == "client" ) {
798 // client
799 // forward
800 // inbox
801 // portable
802 // voice
803 return is->status(STATUS_ONLINE);
804 }
805 // application
806 // bot
807 // calendar
808 // editor
809 // fileserver
810 // game
811 // whiteboard
812 // headline
813 // logger
814 // notice
815 // rss
816 // stock
817 // keyword
818 // dictionary
819 // dns
820 // software
821 // thesaurus
822 // web
823 // whois
824 // render
825 // en2ru
826 // ??2??
827 // tts
828 return Icon();
829}
830
831int hexChar2int(char c)
832{
833 if(c >= 'A' && c <= 'F')
834 return c - 'A' + 10;
835 else if(c >= 'a' && c <= 'f')
836 return c - 'a' + 10;
837 else if(c >= '0' && c <= '9')
838 return c - '0';
839
840 return 0;
841}
842
843char int2hexChar(int x)
844{
845 if(x < 10)
846 return (char)x + '0';
847 else
848 return (char)x - 10 + 'a';
849}
850
851QString jidEncode(const QString &jid)
852{
853 QString jid2;
854
855 for(unsigned int n = 0; n < jid.length(); ++n) {
856 if(jid.at(n) == '@') {
857 jid2.append("_at_");
858 }
859 else if(jid.at(n) == '.') {
860 jid2.append('.');
861 }
862 else if(!jid.at(n).isLetterOrNumber()) {
863 // hex encode
864 QString hex;
865 hex.sprintf("%%%02X", jid.at(n).latin1());
866 jid2.append(hex);
867 }
868 else {
869 jid2.append(jid.at(n));
870 }
871 }
872
873 return jid2;
874}
875
876QString jidDecode(const QString &jid)
877{
878 QString jid2;
879 int n;
880
881 for(n = 0; n < (int)jid.length(); ++n) {
882 if(jid.at(n) == '%' && (jid.length() - n - 1) >= 2) {
883 QString str = jid.mid(n+1,2);
884 bool ok;
885 char c = str.toInt(&ok, 16);
886 if(!ok)
887 continue;
888
889 QChar a(c);
890 jid2.append(a);
891 n += 2;
892 }
893 else {
894 jid2.append(jid.at(n));
895 }
896 }
897
898 // search for the _at_ backwards, just in case
899 for(n = (int)jid2.length(); n >= 3; --n) {
900 if(jid2.mid(n, 4) == "_at_") {
901 jid2.replace(n, 4, "@");
902 break;
903 }
904 }
905
906 return jid2;
907}
908
909QString jidnick(const QString &jid, const QString &nick)
910{
911 if(nick.isEmpty())
912 return jid;
913 else
914 return nick;
915}
916
917QString logencode(QString str)
918{
919 str.replace(QRegExp("\\\\"), "\\\\"); // backslash to double-backslash
920 str.replace(QRegExp("\\|"), "\\p"); // pipe to \p
921 str.replace(QRegExp("\n"), "\\n"); // newline to \n
922 return str;
923}
924
925QString logdecode(const QString &str)
926{
927 QString ret;
928
929 for(unsigned int n = 0; n < str.length(); ++n) {
930 if(str.at(n) == '\\') {
931 ++n;
932 if(n >= str.length())
933 break;
934
935 if(str.at(n) == 'n')
936 ret.append('\n');
937 if(str.at(n) == 'p')
938 ret.append('|');
939 if(str.at(n) == '\\')
940 ret.append('\\');
941 }
942 else {
943 ret.append(str.at(n));
944 }
945 }
946
947 return ret;
948}
949
950void qstringlistisort(QStringList &c)
951{
952 if ( c.count() <= 1 )
953 return;
954
955 QStringList::Iterator it;
956 uint size = c.count();
957
958 // first, make array that is easy (and quick) to manipulate
959 QString *heap = new QString[ size ];
960
961 int i = 0;
962 for (it = c.begin(); it != c.end(); ++it)
963 heap[i++] = *it;
964
965 // Insertion sort
966 for (uint tmp = 0; tmp < c.count(); tmp++) {
967 heap[tmp] = c[tmp];
968 size = tmp + 1;
969
970 for (uint j = 1; j < size; j++) {
971 QString k = heap[j].lower();
972 QString r = heap[j];
973
974 for (i = j - 1; i >= 0; i--) {
975 if ( QString::compare(k, heap[i].lower()) > 0 )
976 break;
977
978 heap[i+1] = heap[i];
979 }
980
981 heap[i+1] = r;
982 }
983 }
984
985 // now, copy sorted data back to QStringList
986 it = c.begin();
987 for (i = 0; i < (int)size; i++)
988 *it++ = heap[i];
989
990 delete[] heap;
991}
992
993#ifdef Q_WS_PM
994static QString getBrowserSetting (PCSZ key)
995{
996 static const PSZ app = "WPURLDEFAULTSETTINGS";
997
998 QString value;
999 ULONG keyLen = 0;
1000
1001 if ( PrfQueryProfileSize( HINI_USERPROFILE, app, key, &keyLen ) && keyLen ) {
1002 char *buf = new char [keyLen];
1003 ULONG realLen =
1004 PrfQueryProfileString( HINI_USERPROFILE, app, key, NULL, buf, keyLen );
1005 if (realLen) {
1006 realLen --; // excude zero terminator
1007 buf[realLen] = 0;
1008 value = QString::fromLocal8Bit(buf);
1009 }
1010 delete[] buf;
1011 }
1012
1013 return value;
1014}
1015#endif
1016
1017void openURL(const QString &url)
1018{
1019 //fprintf(stderr, "openURL: [%s]\n", url.latin1());
1020 bool useCustom = TRUE;
1021
1022 int colon = url.find(':');
1023 if ( colon == -1 )
1024 colon = 0;
1025 QString service = url.left( colon );
1026 if ( service == "jabber" || service == "jid" ) {
1027 // TODO
1028 return;
1029 }
1030
1031#ifdef Q_WS_WIN
1032 if(option.browser == 0)
1033 useCustom = FALSE;
1034#endif
1035#ifdef Q_WS_PM
1036 if(option.browser == 0)
1037 useCustom = FALSE;
1038#endif
1039#ifdef Q_WS_X11
1040 if(option.browser == 0 || option.browser == 2)
1041 useCustom = FALSE;
1042#endif
1043#ifdef Q_WS_MAC
1044 useCustom = FALSE;
1045#endif
1046
1047 if(useCustom) {
1048 bool isMail = FALSE;
1049 QString s = url;
1050 if(url.left(7).lower() == "mailto:") {
1051#ifdef Q_WS_PM
1052 // mozilla/thunderbird doesn't understand e-mails w/o 'mailto:'
1053#else
1054 s.remove(0, 7);
1055#endif
1056 isMail = TRUE;
1057 }
1058
1059 QStringList args;
1060
1061 if(isMail) {
1062 if(option.customMailer.isEmpty()) {
1063 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a mailer (see Options)."));
1064 return;
1065 }
1066 args += QStringList::split(' ', option.customMailer);
1067 }
1068 else {
1069 if(option.customBrowser.isEmpty()) {
1070 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a browser (see Options)."));
1071 return;
1072 }
1073 args += QStringList::split(' ', option.customBrowser);
1074 }
1075
1076 args += s;
1077 QProcess cmd(args);
1078 if(!cmd.start()) {
1079 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that your custom browser/mailer exists (see Options)."));
1080 }
1081 }
1082 else {
1083#ifdef Q_WS_WIN
1084 QCString cs = url.local8Bit();
1085 if ((unsigned int)::ShellExecuteA(NULL,NULL,cs.data(),NULL,NULL,SW_SHOW) <= 32) {
1086 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have a web browser installed."));
1087 }
1088#endif
1089#ifdef Q_WS_PM
1090 QString exe, params, workDir;
1091
1092 if (url.left(7).lower() == "mailto:") {
1093 exe = getBrowserSetting("DefaultMailExe");
1094 params = getBrowserSetting("DefaultMailParameters");
1095 workDir = getBrowserSetting("DefaultMailWorkingDir");
1096 } else if (url.left(5).lower() == "news:") {
1097 exe = getBrowserSetting("DefaultNewsExe");
1098 params = getBrowserSetting("DefaultNewsParameters");
1099 workDir = getBrowserSetting("DefaultNewsWorkingDir");
1100 } else if (url.left(4).lower() == "ftp:") {
1101 exe = getBrowserSetting("DefaultFTPExe");
1102 params = getBrowserSetting("DefaultFTPParameters");
1103 workDir = getBrowserSetting("DefaultFTPWorkingDir");
1104 } else if (url.left(4).lower() == "irc:") {
1105 exe = getBrowserSetting("DefaultIRCExe");
1106 params = getBrowserSetting("DefaultIRCParameters");
1107 workDir = getBrowserSetting("DefaultIRCWorkingDir");
1108 }
1109 // if the specific app is not found, fallback to the general broswer
1110 if (exe.isEmpty()) {
1111 exe = getBrowserSetting("DefaultBrowserExe");
1112 params = getBrowserSetting("DefaultParameters");
1113 workDir = getBrowserSetting("DefaultWorkingDir");
1114 }
1115
1116 bool success = false;
1117
1118 if (!exe.isEmpty()) {
1119 QStringList args;
1120 args += exe;
1121 if (!params.isEmpty()) args += params;
1122 args += url;
1123 QProcess cmd(args);
1124 if (!workDir.isEmpty()) cmd.setWorkingDirectory(workDir);
1125 success = cmd.start();
1126 }
1127
1128 if (!success)
1129 QMessageBox::critical(0, CAP(QObject::tr("URL error")),
1130 QObject::tr("Unable to open the URL. Ensure that you have a web "
1131 "browser installed."));
1132#endif
1133#ifdef Q_WS_X11
1134 // KDE
1135 if(option.browser == 0) {
1136 QStringList args;
1137 args += "kfmclient";
1138 args += "exec";
1139 args += url;
1140 QProcess cmd(args);
1141 if(!cmd.start()) {
1142 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have KDE installed."));
1143 }
1144 }
1145 // GNOME 2
1146 else if(option.browser == 2) {
1147 QStringList args;
1148 args += "gnome-open";
1149 args += url;
1150 QProcess cmd(args);
1151 if(!cmd.start()) {
1152 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have GNOME 2 installed."));
1153 }
1154 }
1155#endif
1156#ifdef Q_WS_MAC
1157 // Use Internet Config to hand the URL to the appropriate application, as
1158 // set by the user in the Internet Preferences pane.
1159 // NOTE: ICStart could be called once at Psi startup, saving the
1160 // ICInstance in a global variable, as a minor optimization.
1161 // ICStop should then be called at Psi shutdown if ICStart succeeded.
1162 ICInstance icInstance;
1163 OSType psiSignature = 'psi ';
1164 OSStatus error = ::ICStart( &icInstance, psiSignature );
1165 if ( error == noErr ) {
1166 ConstStr255Param hint( 0x0 );
1167 QCString cs = url.local8Bit();
1168 const char* data = cs.data();
1169 long length = cs.length();
1170 long start( 0 );
1171 long end( length );
1172 // Don't bother testing return value (error); launched application will report problems.
1173 ::ICLaunchURL( icInstance, hint, data, length, &start, &end );
1174 ICStop( icInstance );
1175 }
1176#endif
1177 }
1178}
1179
1180static bool sysinfo_done = FALSE;
1181static int timezone_offset = 0;
1182static QString timezone_str = "N/A";
1183static QString os_str = "Unknown";
1184
1185#if defined(Q_WS_X11) || defined(Q_WS_MAC)
1186#include<time.h>
1187#include<stdlib.h>
1188#include<string.h>
1189#include<sys/utsname.h>
1190#endif
1191
1192static void getSysInfo()
1193{
1194#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_PM)
1195 time_t x;
1196 time(&x);
1197 char str[256];
1198 char fmt[32];
1199 strcpy(fmt, "%z");
1200 strftime(str, 256, fmt, localtime(&x));
1201 if(strcmp(fmt, str)) {
1202 QString s = str;
1203 if(s.at(0) == '+')
1204 s.remove(0,1);
1205 s.truncate(s.length()-2);
1206 timezone_offset = s.toInt();
1207 }
1208 strcpy(fmt, "%Z");
1209 strftime(str, 256, fmt, localtime(&x));
1210 if(strcmp(fmt, str))
1211 timezone_str = str;
1212#endif
1213#if defined(Q_WS_X11)
1214 struct utsname u;
1215 uname(&u);
1216 os_str.sprintf("%s", u.sysname);
1217
1218 // get description about os
1219 enum LinuxName {
1220 LinuxNone = 0,
1221
1222 LinuxMandrake,
1223 LinuxDebian,
1224 LinuxRedHat,
1225 LinuxGentoo,
1226 LinuxSlackware,
1227 LinuxSuSE,
1228 LinuxConectiva,
1229 LinuxCaldera,
1230 LinuxLFS,
1231
1232 LinuxASP, // Russian Linux distros
1233 LinuxALT,
1234
1235 LinuxPLD, // Polish Linux distros
1236 LinuxAurox,
1237 LinuxArch
1238 };
1239
1240 enum OsFlags {
1241 OsUseName = 0,
1242 OsUseFile,
1243 OsAppendFile
1244 };
1245
1246 struct OsInfo {
1247 LinuxName id;
1248 OsFlags flags;
1249 QString file;
1250 QString name;
1251 } osInfo[] = {
1252 { LinuxMandrake, OsUseFile, "/etc/mandrake-release", "Mandrake Linux" },
1253 { LinuxDebian, OsAppendFile, "/etc/debian_version", "Debian GNU/Linux" },
1254 { LinuxGentoo, OsUseFile, "/etc/gentoo-release", "Gentoo Linux" },
1255 { LinuxSlackware, OsAppendFile, "/etc/slackware-version", "Slackware Linux" },
1256 { LinuxPLD, OsUseFile, "/etc/pld-release", "PLD Linux" },
1257 { LinuxAurox, OsUseName, "/etc/aurox-release", "Aurox Linux" },
1258 { LinuxArch, OsUseFile, "/etc/arch-release", "Arch Linux" },
1259 { LinuxLFS, OsAppendFile, "/etc/lfs-release", "LFS Linux" },
1260
1261 // untested
1262 { LinuxSuSE, OsUseFile, "/etc/SuSE-release", "SuSE Linux" },
1263 { LinuxConectiva, OsUseFile, "/etc/conectiva-release", "Conectiva Linux" },
1264 { LinuxCaldera, OsUseFile, "/etc/.installed", "Caldera Linux" },
1265
1266 // many distros use the /etc/redhat-release for compatibility, so RedHat will be the last :)
1267 { LinuxRedHat, OsUseFile, "/etc/redhat-release", "RedHat Linux" },
1268
1269 { LinuxNone, OsUseName, "", "" }
1270 };
1271
1272 for (int i = 0; osInfo[i].id != LinuxNone; i++) {
1273 QFileInfo fi( osInfo[i].file );
1274 if ( fi.exists() ) {
1275 QString desc;
1276
1277 QFile f( osInfo[i].file );
1278 f.open( IO_ReadOnly );
1279 f.readLine( desc, 128 );
1280
1281 desc = desc.stripWhiteSpace ();
1282
1283 switch ( osInfo[i].flags ) {
1284 case OsUseFile:
1285 os_str = desc;
1286 break;
1287 case OsUseName:
1288 os_str = osInfo[i].name;
1289 break;
1290 case OsAppendFile:
1291 os_str = osInfo[i].name + " (" + desc + ")";
1292 break;
1293 }
1294
1295 break;
1296 }
1297 }
1298#elif defined(Q_WS_PM)
1299 os_str = "OS/2";
1300#elif defined(Q_WS_MAC)
1301 os_str = "Mac OS X";
1302#endif
1303
1304#if defined(Q_WS_WIN)
1305 TIME_ZONE_INFORMATION i;
1306 //GetTimeZoneInformation(&i);
1307 //timezone_offset = (-i.Bias) / 60;
1308 memset(&i, 0, sizeof(i));
1309 bool inDST = (GetTimeZoneInformation(&i) == TIME_ZONE_ID_DAYLIGHT);
1310 int bias = i.Bias;
1311 if(inDST)
1312 bias += i.DaylightBias;
1313 timezone_offset = (-bias) / 60;
1314 timezone_str = "";
1315 for(int n = 0; n < 32; ++n) {
1316 uint w = inDST ? i.DaylightName[n] : i.StandardName[n];
1317 if(w == 0)
1318 break;
1319 timezone_str += QChar(w);
1320 }
1321
1322 Qt::WindowsVersion v = QApplication::winVersion();
1323 if(v == Qt::WV_95)
1324 os_str = "Windows 95";
1325 else if(v == Qt::WV_98)
1326 os_str = "Windows 98";
1327 else if(v == Qt::WV_Me)
1328 os_str = "Windows Me";
1329 else if(v == Qt::WV_DOS_based)
1330 os_str = "Windows 9x/Me";
1331 else if(v == Qt::WV_NT)
1332 os_str = "Windows NT 4.x";
1333 else if(v == Qt::WV_2000)
1334 os_str = "Windows 2000";
1335 else if(v == Qt::WV_XP)
1336 os_str = "Windows XP";
1337#if QT_VERSION >= 0x030300
1338 else if(v == Qt::WV_2003)
1339 os_str = "Windows Server 2003";
1340#endif
1341 else if(v == Qt::WV_NT_based)
1342 os_str = "Windows NT";
1343#endif
1344 sysinfo_done = TRUE;
1345}
1346
1347QString getOSName()
1348{
1349 if(!sysinfo_done)
1350 getSysInfo();
1351
1352 return os_str;
1353}
1354
1355int getTZOffset()
1356{
1357 if(!sysinfo_done)
1358 getSysInfo();
1359
1360 return timezone_offset;
1361}
1362
1363QString getTZString()
1364{
1365 if(!sysinfo_done)
1366 getSysInfo();
1367
1368 return timezone_str;
1369}
1370
1371
1372#ifdef Q_WS_X11
1373QString getResourcesDir()
1374{
1375 return PSI_DATADIR;
1376}
1377
1378QString getHomeDir()
1379{
1380 QDir proghome(QDir::homeDirPath() + "/.psi");
1381 if(!proghome.exists()) {
1382 QDir home = QDir::home();
1383 home.mkdir(".psi");
1384 chmod(QFile::encodeName(proghome.path()), 0700);
1385 }
1386
1387 return proghome.path();
1388}
1389#endif
1390
1391#ifdef Q_WS_WIN
1392QString getResourcesDir()
1393{
1394#if QT_VERSION >= 0x030200
1395 return qApp->applicationDirPath();
1396#else
1397 char baseName[MAX_PATH];
1398 GetModuleFileNameA(GetModuleHandle(NULL), baseName, MAX_PATH);
1399 QString base(baseName);
1400 int idx = base.findRev('\\');
1401
1402 if (-1 == idx)
1403 return ".";
1404
1405 base.truncate(idx);
1406 QDir baseDir(base);
1407
1408 return baseDir.absPath();
1409#endif
1410}
1411
1412QString getHomeDir()
1413{
1414 QString base;
1415
1416 // Windows 9x
1417 if(QDir::homeDirPath() == QDir::rootDirPath())
1418 base = ".";
1419 // Windows NT/2K/XP variant
1420 else
1421 base = QDir::homeDirPath();
1422
1423 // no trailing slash
1424 if(base.at(base.length()-1) == '/')
1425 base.truncate(base.length()-1);
1426
1427 QDir proghome(base + "/PsiData");
1428 if(!proghome.exists()) {
1429 QDir home(base);
1430 home.mkdir("PsiData");
1431 }
1432
1433 return proghome.path();
1434}
1435#endif
1436
1437#ifdef Q_WS_PM
1438QString getResourcesDir()
1439{
1440#if QT_VERSION >= 0x030200
1441 return qApp->applicationDirPath();
1442#else
1443# error Not implemented!
1444#endif
1445}
1446
1447QString getHomeDir()
1448{
1449 QString base = QDir::homeDirPath();
1450
1451 // no trailing slash
1452 if(base.at(base.length()-1) == '/')
1453 base.truncate(base.length()-1);
1454
1455 QDir proghome(base + "/PsiData");
1456 if(!proghome.exists()) {
1457 QDir home(base);
1458 home.mkdir("PsiData");
1459 }
1460
1461 return proghome.path();
1462}
1463#endif
1464
1465#ifdef Q_WS_MAC
1466/******************************************************************************/
1467/* Get path to Resources directory as a string. */
1468/* Return an empty string if can't find it. */
1469/******************************************************************************/
1470QString getResourcesDir()
1471{
1472 // System routine locates resource files. We "know" that Psi.icns is
1473 // in the Resources directory.
1474 QString resourcePath;
1475 CFBundleRef mainBundle = CFBundleGetMainBundle();
1476 CFStringRef resourceCFStringRef
1477 = CFStringCreateWithCString( NULL, "application.icns",
1478 kCFStringEncodingASCII );
1479 CFURLRef resourceURLRef = CFBundleCopyResourceURL( mainBundle,
1480 resourceCFStringRef,
1481 NULL,
1482 NULL );
1483 if ( resourceURLRef ) {
1484 CFStringRef resourcePathStringRef =
1485 CFURLCopyFileSystemPath( resourceURLRef, kCFURLPOSIXPathStyle );
1486 const char* resourcePathCString =
1487 CFStringGetCStringPtr( resourcePathStringRef, kCFStringEncodingASCII );
1488 if ( resourcePathCString ) {
1489 resourcePath.setLatin1( resourcePathCString );
1490 } else { // CFStringGetCStringPtr failed; use fallback conversion
1491 CFIndex bufferLength = CFStringGetLength( resourcePathStringRef ) + 1;
1492 char* resourcePathCString = new char[ bufferLength ];
1493 Boolean conversionSuccess =
1494 CFStringGetCString( resourcePathStringRef,
1495 resourcePathCString, bufferLength,
1496 kCFStringEncodingASCII );
1497 if ( conversionSuccess ) {
1498 resourcePath = resourcePathCString;
1499 }
1500 delete [] resourcePathCString; // I own this
1501 }
1502 CFRelease( resourcePathStringRef ); // I own this
1503 }
1504 // Remove the tail component of the path
1505 if ( ! resourcePath.isNull() ) {
1506 QFileInfo fileInfo( resourcePath );
1507 resourcePath = fileInfo.dirPath( true );
1508 }
1509 return resourcePath;
1510}
1511
1512QString getHomeDir()
1513{
1514 QDir proghome(QDir::homeDirPath() + "/.psi");
1515 if(!proghome.exists()) {
1516 QDir home = QDir::home();
1517 home.mkdir(".psi");
1518 chmod(QFile::encodeName(proghome.path()), 0700);
1519 }
1520
1521 return proghome.path();
1522}
1523#endif
1524
1525
1526QString getHistoryDir()
1527{
1528 QDir history(pathToProfile(activeProfile) + "/history");
1529 if (!history.exists()) {
1530 QDir profile(pathToProfile(activeProfile));
1531 profile.mkdir("history");
1532 }
1533
1534 return history.path();
1535}
1536
1537QString getVCardDir()
1538{
1539 QDir vcard(pathToProfile(activeProfile) + "/vcard");
1540 if (!vcard.exists()) {
1541 QDir profile(pathToProfile(activeProfile));
1542 profile.mkdir("vcard");
1543 }
1544
1545 return vcard.path();
1546}
1547
1548bool fileCopy(const QString &src, const QString &dest)
1549{
1550 QFile in(src);
1551 QFile out(dest);
1552
1553 if(!in.open(IO_ReadOnly))
1554 return FALSE;
1555 if(!out.open(IO_WriteOnly))
1556 return FALSE;
1557
1558 char *dat = new char[16384];
1559 int n = 0;
1560 while(!in.atEnd()) {
1561 n = in.readBlock(dat, 16384);
1562 if(n == -1) {
1563 delete dat;
1564 return FALSE;
1565 }
1566 out.writeBlock(dat, n);
1567 }
1568 delete dat;
1569
1570 out.close();
1571 in.close();
1572
1573 return TRUE;
1574}
1575
1576class BeepThread : public QThread
1577{
1578public:
1579 BeepThread() : sem(1), terminate(false) {}
1580 ~BeepThread() { while (sem.available() != sem.total()) sem--; }
1581 void run() {
1582 lock.lock();
1583 while (!terminate) {
1584 while (!terminate && queue.count()) {
1585 Beep beep = queue.front();
1586 queue.pop_front();
1587 lock.unlock();
1588#if defined(Q_WS_WIN)
1589 if (beep.freq && beep.dur)
1590 ::Beep(beep.freq, beep.dur);
1591 else
1592#elif defined(Q_WS_PM)
1593 if (beep.freq && beep.dur)
1594 DosBeep(beep.freq, beep.dur);
1595 else
1596#endif
1597 QApplication::beep();
1598 lock.lock();
1599 }
1600 lock.unlock();
1601 sem++;
1602 lock.lock();
1603 }
1604 lock.unlock();
1605 }
1606 void beginSequence() {
1607 lock.lock();
1608 queue.clear(); // stop the previous sequence
1609 }
1610 void addNote(ulong freq, ulong dur) {
1611 queue.push_back(Beep(freq, dur));
1612 }
1613 void endSequence() {
1614 lock.unlock();
1615 if (!sem.available())
1616 sem--;
1617 }
1618 void stop() {
1619 lock.lock();
1620 terminate = true;
1621 lock.unlock();
1622 if (!sem.available())
1623 sem--;
1624 wait();
1625 }
1626private:
1627 struct Beep {
1628 Beep() : freq(0), dur(0) {}
1629 Beep(ulong f, ulong d) : freq(f), dur(d) {}
1630 const ulong freq;
1631 const ulong dur;
1632 };
1633 QMutex lock;
1634 QSemaphore sem; // not QWaitCond cause we need level-triggered semantics
1635 QValueList<Beep> queue;
1636 bool terminate;
1637};
1638
1639static BeepThread *beepThread = 0;
1640
1641static void cleanupBeepThread()
1642{
1643 if (beepThread) {
1644 beepThread->stop();
1645 delete beepThread;
1646 beepThread = 0;
1647 }
1648}
1649
1650void soundPlay(const QString &str)
1651{
1652 if (str.startsWith("!beep")) {
1653 QStringList seq = QStringList::split(':',str);
1654 if (seq[0].length() == 5 /* strlen("!beep") */) {
1655 if (beepThread == 0) {
1656 beepThread = new BeepThread();
1657 if (beepThread) {
1658 qAddPostRoutine(cleanupBeepThread);
1659 beepThread->start();
1660 }
1661 }
1662 Q_ASSERT(beepThread);
1663 if (beepThread) {
1664 beepThread->beginSequence();
1665#if defined(Q_WS_WIN) || defined(Q_WS_PM)
1666 ulong lastDur = 100;
1667 size_t i = 1;
1668 for (; i < seq.count(); ++i) {
1669 QStringList note = QStringList::split(',',seq[i]);
1670 if (note.count() < 1) break;
1671 ulong freq = note[0].toULong();
1672 if (freq < 0x25 || freq > 0x7FFF) break;
1673 ulong dur = lastDur;
1674 if (note.count() > 2) break;
1675 if (note.count() == 2) {
1676 dur = note[1].toULong();
1677 // 3s is the reasonable maximum for the duration
1678 if (dur == 0 || dur > 3000) break;
1679 lastDur = dur;
1680 }
1681 beepThread->addNote(freq, dur);
1682 }
1683 if (i == 1) // didn't find a single note
1684#endif
1685 beepThread->addNote(0, 0);
1686 beepThread->endSequence();
1687 }
1688 return;
1689 }
1690 }
1691
1692 if(!QFile::exists(str))
1693 return;
1694
1695#if defined(Q_WS_WIN) || defined(Q_WS_PM) || defined(Q_WS_MAC)
1696 QSound::play(str);
1697#else
1698 if(!option.player.isEmpty()) {
1699 QStringList args;
1700 args = QStringList::split(' ', option.player);
1701 args += str;
1702 QProcess cmd(args);
1703 if(!cmd.start())
1704 wait3(NULL,WNOHANG,NULL);
1705 }
1706#endif
1707}
1708
1709XMPP::Status makeStatus(int x, const QString &str)
1710{
1711 XMPP::Status s;
1712 s.setStatus(str);
1713 if(x == STATUS_OFFLINE)
1714 s.setIsAvailable(false);
1715 else if(x == STATUS_INVISIBLE)
1716 s.setIsInvisible(true);
1717 else {
1718 if(x == STATUS_AWAY)
1719 s.setShow("away");
1720 else if(x == STATUS_XA)
1721 s.setShow("xa");
1722 else if(x == STATUS_DND)
1723 s.setShow("dnd");
1724 else if(x == STATUS_CHAT)
1725 s.setShow("chat");
1726 }
1727
1728 return s;
1729}
1730
1731int makeSTATUS(const XMPP::Status &s)
1732{
1733 int type = STATUS_ONLINE;
1734 if(!s.isAvailable())
1735 type = STATUS_OFFLINE;
1736 else if(s.isInvisible())
1737 type= STATUS_INVISIBLE;
1738 else {
1739 if(s.show() == "away")
1740 type = STATUS_AWAY;
1741 else if(s.show() == "xa")
1742 type = STATUS_XA;
1743 else if(s.show() == "dnd")
1744 type = STATUS_DND;
1745 else if(s.show() == "chat")
1746 type = STATUS_CHAT;
1747 }
1748 return type;
1749}
1750
1751#include<qlayout.h>
1752QLayout *rw_recurseFindLayout(QLayout *lo, QWidget *w)
1753{
1754 //printf("scanning layout: %p\n", lo);
1755 QLayoutIterator it = lo->iterator();
1756 for(QLayoutItem *i; (i = it.current()); ++it) {
1757 //printf("found: %p,%p\n", i->layout(), i->widget());
1758 QLayout *slo = i->layout();
1759 if(slo) {
1760 QLayout *tlo = rw_recurseFindLayout(slo, w);
1761 if(tlo)
1762 return tlo;
1763 }
1764 else if(i->widget() == w)
1765 return lo;
1766 }
1767 return 0;
1768}
1769
1770QLayout *rw_findLayoutOf(QWidget *w)
1771{
1772 return rw_recurseFindLayout(w->parentWidget()->layout(), w);
1773}
1774
1775void replaceWidget(QWidget *a, QWidget *b)
1776{
1777 if(!a)
1778 return;
1779
1780 QLayout *lo = rw_findLayoutOf(a);
1781 if(!lo)
1782 return;
1783 //printf("decided on this: %p\n", lo);
1784
1785 if(lo->inherits("QBoxLayout")) {
1786 QBoxLayout *bo = (QBoxLayout *)lo;
1787 int n = bo->findWidget(a);
1788 bo->insertWidget(n+1, b);
1789 delete a;
1790 }
1791}
1792
1793void closeDialogs(QWidget *w)
1794{
1795 // close qmessagebox?
1796 const QObjectList *list = w->children();
1797 if(list) {
1798 QObjectListIt it(*list);
1799 QPtrList<QDialog> dialogs;
1800 for(QObject *o; (o = it.current()); ++it) {
1801 if(o->inherits("QDialog"))
1802 dialogs.append((QDialog *)o);
1803 }
1804 QPtrListIterator<QDialog> dit(dialogs);
1805 for(QDialog *w; (w = dit.current()); ++dit)
1806 w->close();
1807 }
1808}
1809
1810QString enc822jid(const QString &s)
1811{
1812 QString out;
1813 for(int n = 0; n < (int)s.length(); ++n) {
1814 if(s[n] == '\\' || s[n] == '<' || s[n] == '>') {
1815 QString hex;
1816 hex.sprintf("\\x%02X", (unsigned char )s[n]);
1817 out.append(hex);
1818 }
1819 else
1820 out += s[n];
1821 }
1822 return out;
1823}
1824
1825QString dec822jid(const QString &s)
1826{
1827 QString out;
1828 for(int n = 0; n < (int)s.length(); ++n) {
1829 if(s[n] == '\\' && n + 3 < (int)s.length()) {
1830 int x = n + 1;
1831 n += 3;
1832 if(s[x] != 'x')
1833 continue;
1834 ushort val = 0;
1835 val += hexChar2int(s[x+1])*16;
1836 val += hexChar2int(s[x+2]);
1837 QChar c(val);
1838 out += c;
1839 }
1840 else
1841 out += s[n];
1842 }
1843 return out;
1844}
1845
1846#ifdef Q_WS_X11
1847#include<X11/Xlib.h>
1848#include<X11/Xutil.h> // needed for WM_CLASS hinting
1849
1850void x11wmClass(Display *dsp, WId wid, QString resName)
1851{
1852 char app_name[] = "psi";
1853
1854 //Display *dsp = x11Display(); // get the display
1855 //WId win = winId(); // get the window
1856 XClassHint classhint; // class hints
1857 classhint.res_name = (char *)resName.latin1(); // res_name
1858 classhint.res_class = app_name; // res_class
1859 XSetClassHint(dsp, wid, &classhint); // set the class hints
1860}
1861
1862//>>>-- Nathaniel Gray -- Caltech Computer Science ------>
1863//>>>-- Mojave Project -- http://mojave.cs.caltech.edu -->
1864// Copied from http://www.nedit.org/archives/discuss/2002-Aug/0386.html
1865
1866// Helper function
1867bool getCardinal32Prop(Display *display, Window win, char *propName, long *value)
1868{
1869 Atom nameAtom, typeAtom, actual_type_return;
1870 int actual_format_return, result;
1871 unsigned long nitems_return, bytes_after_return;
1872 long *result_array=NULL;
1873
1874 nameAtom = XInternAtom(display, propName, False);
1875 typeAtom = XInternAtom(display, "CARDINAL", False);
1876 if (nameAtom == None || typeAtom == None) {
1877 //qDebug("Atoms not interned!");
1878 return false;
1879 }
1880
1881
1882 // Try to get the property
1883 result = XGetWindowProperty(display, win, nameAtom, 0, 1, False,
1884 typeAtom, &actual_type_return, &actual_format_return,
1885 &nitems_return, &bytes_after_return,
1886 (unsigned char **)&result_array);
1887
1888 if( result != Success ) {
1889 //qDebug("not Success");
1890 return false;
1891 }
1892 if( actual_type_return == None || actual_format_return == 0 ) {
1893 //qDebug("Prop not found");
1894 return false;
1895 }
1896 if( actual_type_return != typeAtom ) {
1897 //qDebug("Wrong type atom");
1898 }
1899 *value = result_array[0];
1900 XFree(result_array);
1901 return true;
1902}
1903
1904
1905// Get the desktop number that a window is on
1906bool desktopOfWindow(Window *window, long *desktop)
1907{
1908 Display *display = qt_xdisplay();
1909 bool result = getCardinal32Prop(display, *window, (char *)"_NET_WM_DESKTOP", desktop);
1910 //if( result )
1911 // qDebug("Desktop: " + QString::number(*desktop));
1912 return result;
1913}
1914
1915
1916// Get the current desktop the WM is displaying
1917bool currentDesktop(long *desktop)
1918{
1919 Window rootWin;
1920 Display *display = qt_xdisplay();;
1921 bool result;
1922
1923 rootWin = RootWindow(qt_xdisplay(), XDefaultScreen(qt_xdisplay()));
1924 result = getCardinal32Prop(display, rootWin, (char *)"_NET_CURRENT_DESKTOP", desktop);
1925 //if( result )
1926 // qDebug("Current Desktop: " + QString::number(*desktop));
1927 return result;
1928}
1929#endif
1930
1931void bringToFront(QWidget *w, bool grabFocus /* = true */)
1932{
1933#if defined(Q_WS_X11)
1934 // If we're not on the current desktop, do the hide/show trick
1935 long dsk, curr_dsk;
1936 Window win = w->winId();
1937 if(desktopOfWindow(&win, &dsk) && currentDesktop(&curr_dsk)) {
1938 if(dsk != curr_dsk) {
1939 w->hide();
1940 //qApp->processEvents();
1941 }
1942 }
1943#else
1944 // FIXME: multi-desktop hacks for Win and Mac required
1945#endif
1946
1947 w->show();
1948 if(w->isMinimized()) {
1949 //w->hide();
1950 if(w->isMaximized())
1951 w->showMaximized();
1952 else
1953 w->showNormal();
1954 }
1955
1956#ifdef Q_WS_WIN
1957 w->raise();
1958 if (grabFocus) {
1959 w->setActiveWindow();
1960 } else {
1961 if (GetTopWindow(NULL) != w->winId()) {
1962 // On Win32, raise() cannot move a window on top of the currently
1963 // active window w/o making the former active. To overcome this,
1964 // we use a trick by first making the window floating on top and
1965 // then restoring the normal behavior.
1966 SetWindowPos(w->winId(), HWND_TOPMOST, 0, 0, 0, 0,
1967 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1968 SetWindowPos(w->winId(), HWND_NOTOPMOST, 0, 0, 0, 0,
1969 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1970 }
1971 }
1972#else
1973 if(grabFocus)
1974 w->setActiveWindow();
1975 else
1976 w->raise();
1977#endif
1978
1979#if defined(Q_WS_PM)
1980 // When the widget is totally outside the screen bounds after making it shown,
1981 // we assume it's on a different (invisible) XPager's page. The assumption is
1982 // not so safe, but is there any other way to determine XPager is in action?..
1983 QRect geo = w->frameGeometry();
1984 QRect scrGeo = qApp->desktop()->screenGeometry();
1985 if (!w->isMinimized() && !scrGeo.contains(geo)) {
1986 // We assume that XPager stores invisible pages in the coordinate space
1987 // beyond (0,0)x(screen_width,screen_height) wiht a 8 px gap between
1988 // pages. Move it to the visible page.
1989 int sw = scrGeo.width() + 8;
1990 int sh = scrGeo.height() + 8;
1991 int x = geo.x() % sw;
1992 if (x < 0) x = sw + x;
1993 int y = geo.y() % sh;
1994 if (y < 0) y = sh + y;
1995 w->move (x, y);
1996 }
1997#endif
1998}
1999
2000bool operator!=(const QMap<QString, QString> &m1, const QMap<QString, QString> &m2)
2001{
2002 if ( m1.size() != m2.size() )
2003 return true;
2004
2005 QMap<QString, QString>::ConstIterator it = m1.begin(), it2;
2006 for ( ; it != m1.end(); ++it) {
2007 it2 = m2.find( it.key() );
2008 if ( it2 == m2.end() )
2009 return true;
2010 if ( it.data() != it2.data() )
2011 return true;
2012 }
2013
2014 return false;
2015}
2016
2017#if defined(Q_OS_OS2) && !defined(QT_OS2_NO_SYSEXCEPTIONS)
2018int psiOS2SysXcptCallback( QtOS2SysXcptReq req, QtOS2SysXcptWriter writer,
2019 int /* reserved */ )
2020{
2021 switch( req )
2022 {
2023 case QtOS2SysXcptReq_AppName:
2024 if ( writer ) writer( PROG_NAME.latin1() );
2025 return TRUE;
2026 case QtOS2SysXcptReq_AppVer:
2027 if ( writer ) writer( PROG_VERSION.latin1() );
2028 return TRUE;
2029 case QtOS2SysXcptReq_ReportTo:
2030 if ( writer ) writer( "qt-traps@hugaida.com" );
2031 return TRUE;
2032 default:
2033 break;
2034 }
2035 return FALSE;
2036}
2037#endif
Note: See TracBrowser for help on using the repository browser.