summaryrefslogtreecommitdiffabout
path: root/microkde/kurl.cpp
Unidiff
Diffstat (limited to 'microkde/kurl.cpp') (more/less context) (show whitespace changes)
-rw-r--r--microkde/kurl.cpp1942
1 files changed, 1942 insertions, 0 deletions
diff --git a/microkde/kurl.cpp b/microkde/kurl.cpp
new file mode 100644
index 0000000..2574e25
--- a/dev/null
+++ b/microkde/kurl.cpp
@@ -0,0 +1,1942 @@
1/*
2 Copyright (C) 1999 Torben Weis <weis@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library 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 GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
18*/
19
20#include "kurl.h"
21
22#ifndef KDE_QT_ONLY
23#include <kdebug.h>
24#include <kglobal.h>
25//US#include <kidna.h>
26#endif
27
28#include <stdio.h>
29#include <assert.h>
30#include <ctype.h>
31#include <stdlib.h>
32#ifdef _WIN32_
33
34#else
35#include <unistd.h>
36#endif
37#include <qurl.h>
38#include <qdir.h>
39#include <qstringlist.h>
40#include <qregexp.h>
41//US#include <qstylesheet.h>
42#include <qmap.h>
43#include <qtextcodec.h>
44
45static const QString fileProt = "file";
46
47static QTextCodec * codecForHint( int encoding_hint /* not 0 ! */ )
48{
49 return QTextCodec::codecForMib( encoding_hint );
50}
51
52static QString encode( const QString& segment, bool encode_slash, int encoding_hint )
53{
54 const char *encode_string;
55 if (encode_slash)
56 encode_string = "<>#@\"&%?={}|^~[]\'`\\:+/";
57 else
58 encode_string = "<>#@\"&%?={}|^~[]\'`\\:+";
59
60 QCString local;
61 if (encoding_hint==0)
62 local = segment.local8Bit();
63 else
64 {
65 QTextCodec * textCodec = codecForHint( encoding_hint );
66 if (!textCodec)
67 local = segment.local8Bit();
68 else
69 local = textCodec->fromUnicode( segment );
70 }
71
72 int old_length = local.length();
73
74 if ( !old_length )
75 return segment.isNull() ? QString::null : QString(""); // differenciate null and empty
76
77 // a worst case approximation
78 QChar *new_segment = new QChar[ old_length * 3 + 1 ];
79 int new_length = 0;
80
81 for ( int i = 0; i < old_length; i++ )
82 {
83 // 'unsave' and 'reserved' characters
84 // according to RFC 1738,
85 // 2.2. URL Character Encoding Issues (pp. 3-4)
86 // WABA: Added non-ascii
87 unsigned char character = local[i];
88 if ( (character <= 32) || (character >= 127) ||
89 strchr(encode_string, character) )
90 {
91 new_segment[ new_length++ ] = '%';
92
93 unsigned int c = character / 16;
94 c += (c > 9) ? ('A' - 10) : '0';
95 new_segment[ new_length++ ] = c;
96
97 c = character % 16;
98 c += (c > 9) ? ('A' - 10) : '0';
99 new_segment[ new_length++ ] = c;
100
101 }
102 else
103 new_segment[ new_length++ ] = local[i];
104 }
105
106 QString result = QString(new_segment, new_length);
107 delete [] new_segment;
108 return result;
109}
110
111static QString encodeHost( const QString& segment, bool encode_slash, int encoding_hint )
112{
113 // Hostnames are encoded differently
114 // we use the IDNA transformation instead
115
116 // Note: when merging qt-addon, use QResolver::domainToAscii here
117#ifndef KDE_QT_ONLY
118 Q_UNUSED( encode_slash );
119 Q_UNUSED( encoding_hint );
120 return KIDNA::toAscii(segment);
121#else
122 return encode(segment, encode_slash, encoding_hint);
123#endif
124}
125
126static int hex2int( unsigned int _char )
127{
128 if ( _char >= 'A' && _char <='F')
129 return _char - 'A' + 10;
130 if ( _char >= 'a' && _char <='f')
131 return _char - 'a' + 10;
132 if ( _char >= '0' && _char <='9')
133 return _char - '0';
134 return -1;
135}
136
137// WABA: The result of lazy_encode isn't usable for a URL which
138// needs to satisfies RFC requirements. However, the following
139// operation will make it usable again:
140// encode(decode(...))
141//
142// As a result one can see that url.prettyURL() does not result in
143// a RFC compliant URL but that the following sequence does:
144// KURL(url.prettyURL()).url()
145
146
147static QString lazy_encode( const QString& segment )
148{
149 int old_length = segment.length();
150
151 if ( !old_length )
152 return QString::null;
153
154 // a worst case approximation
155 QChar *new_segment = new QChar[ old_length * 3 + 1 ];
156 int new_length = 0;
157
158 for ( int i = 0; i < old_length; i++ )
159 {
160 unsigned int character = segment[i].unicode(); // Don't use latin1()
161 // It returns 0 for non-latin1 values
162 // Small set of really ambiguous chars
163 if ((character < 32) || // Low ASCII
164 ((character == '%') && // The escape character itself
165 (i+2 < old_length) && // But only if part of a valid escape sequence!
166 (hex2int(segment[i+1].unicode())!= -1) &&
167 (hex2int(segment[i+2].unicode())!= -1)) ||
168 (character == '?') || // Start of query delimiter
169 (character == '@') || // Username delimiter
170 (character == '#') || // Start of reference delimiter
171 ((character == 32) && (i+1 == old_length))) // A trailing space
172 {
173 new_segment[ new_length++ ] = '%';
174
175 unsigned int c = character / 16;
176 c += (c > 9) ? ('A' - 10) : '0';
177 new_segment[ new_length++ ] = c;
178
179 c = character % 16;
180 c += (c > 9) ? ('A' - 10) : '0';
181 new_segment[ new_length++ ] = c;
182 }
183 else
184 new_segment[ new_length++ ] = segment[i];
185 }
186
187 QString result = QString(new_segment, new_length);
188 delete [] new_segment;
189 return result;
190}
191
192static void decode( const QString& segment, QString &decoded, QString &encoded, int encoding_hint=0, bool updateDecoded = true )
193{
194 decoded = QString::null;
195 encoded = segment;
196
197 int old_length = segment.length();
198 if ( !old_length )
199 return;
200
201 QTextCodec *textCodec = 0;
202 if (encoding_hint)
203 textCodec = codecForHint( encoding_hint );
204
205 if (!textCodec)
206 textCodec = QTextCodec::codecForLocale();
207
208 QCString csegment = textCodec->fromUnicode(segment);
209 // Check if everything went ok
210 if (textCodec->toUnicode(csegment) != segment)
211 {
212 // Uh oh
213 textCodec = codecForHint( 106 ); // Fall back to utf-8
214 csegment = textCodec->fromUnicode(segment);
215 }
216 old_length = csegment.length();
217
218 int new_length = 0;
219 int new_length2 = 0;
220
221 // make a copy of the old one
222 char *new_segment = new char[ old_length + 1 ];
223 QChar *new_usegment = new QChar[ old_length * 3 + 1 ];
224
225 int i = 0;
226 while( i < old_length )
227 {
228 bool bReencode = false;
229 unsigned char character = csegment[ i++ ];
230 if ((character <= ' ') || (character > 127))
231 bReencode = true;
232
233 new_usegment [ new_length2++ ] = character;
234 if (character == '%' )
235 {
236 int a = i+1 < old_length ? hex2int( csegment[i] ) : -1;
237 int b = i+1 < old_length ? hex2int( csegment[i+1] ) : -1;
238 if ((a == -1) || (b == -1)) // Only replace if sequence is valid
239 {
240 // Contains stray %, make sure to re-encode!
241 bReencode = true;
242 }
243 else
244 {
245 // Valid %xx sequence
246 character = a * 16 + b; // Replace with value of %dd
247 if (!character && updateDecoded)
248 break; // Stop at %00
249
250 new_usegment [ new_length2++ ] = (unsigned char) csegment[i++];
251 new_usegment [ new_length2++ ] = (unsigned char) csegment[i++];
252 }
253 }
254 if (bReencode)
255 {
256 new_length2--;
257 new_usegment [ new_length2++ ] = '%';
258
259 unsigned int c = character / 16;
260 c += (c > 9) ? ('A' - 10) : '0';
261 new_usegment[ new_length2++ ] = c;
262
263 c = character % 16;
264 c += (c > 9) ? ('A' - 10) : '0';
265 new_usegment[ new_length2++ ] = c;
266 }
267
268 new_segment [ new_length++ ] = character;
269 }
270 new_segment [ new_length ] = 0;
271
272 encoded = QString( new_usegment, new_length2);
273
274 // Encoding specified
275 if (updateDecoded)
276 {
277 QByteArray array;
278 array.setRawData(new_segment, new_length);
279 decoded = textCodec->toUnicode( array, new_length );
280 array.resetRawData(new_segment, new_length);
281 QCString validate = textCodec->fromUnicode(decoded);
282
283 if (strcmp(validate.data(), new_segment) != 0)
284 {
285 decoded = QString::fromLocal8Bit(new_segment, new_length);
286 }
287 }
288
289 delete [] new_segment;
290 delete [] new_usegment;
291}
292
293static QString decode(const QString &segment, int encoding_hint = 0)
294{
295 QString result;
296 QString tmp;
297 decode(segment, result, tmp, encoding_hint);
298 return result;
299}
300
301static QString cleanpath(const QString &path, bool cleanDirSeparator=true)
302{
303 if (path.isEmpty()) return QString::null;
304 int len = path.length();
305 bool slash = (len && path[len-1] == '/') ||
306 (len > 1 && path[len-2] == '/' && path[len-1] == '.');
307
308 // The following code cleans up directory path much like
309 // QDir::cleanDirPath() except it can be made to ignore multiple
310 // directory separators by setting the flag to false. That fixes
311 // bug# 15044, mail.altavista.com and other similar brain-dead server
312 // implementations that do not follow what has been specified in
313 // RFC 2396!! (dA)
314 QString result;
315 int cdUp, orig_pos, pos;
316
317 cdUp = 0;
318 pos = orig_pos = len;
319 while ( pos && (pos = path.findRev('/',--pos)) != -1 )
320 {
321 len = orig_pos - pos - 1;
322 if ( len == 2 && path[pos+1] == '.' && path[pos+2] == '.' )
323 cdUp++;
324 else
325 {
326 // Ignore any occurances of '.'
327 // This includes entries that simply do not make sense like /..../
328 if ( (len || !cleanDirSeparator) &&
329 (len != 1 || path[pos+1] != '.' ) )
330 {
331 if ( !cdUp )
332 result.prepend(path.mid(pos, len+1));
333 else
334 cdUp--;
335 }
336 }
337 orig_pos = pos;
338 }
339
340 if ( result.isEmpty() )
341 result = "/";
342 else if ( slash && result.at(result.length()-1) != '/' )
343 result.append('/');
344
345 return result;
346}
347
348bool KURL::isRelativeURL(const QString &_url)
349{
350 int len = _url.length();
351 if (!len) return true; // Very short relative URL.
352 const QChar *str = _url.unicode();
353
354 // Absolute URL must start with alpha-character
355 if (!isalpha(str[0].latin1()))
356 return true; // Relative URL
357
358 for(int i = 1; i < len; i++)
359 {
360 char c = str[i].latin1(); // Note: non-latin1 chars return 0!
361 if (c == ':')
362 return false; // Absolute URL
363
364 // Protocol part may only contain alpha, digit, + or -
365 if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-'))
366 return true; // Relative URL
367 }
368 // URL did not contain ':'
369 return true; // Relative URL
370}
371
372KURL::List::List(const KURL &url)
373{
374 append( url );
375}
376
377KURL::List::List(const QStringList &list)
378{
379 for (QStringList::ConstIterator it = list.begin();
380 it != list.end();
381 it++)
382 {
383 append( KURL(*it) );
384 }
385}
386
387QStringList KURL::List::toStringList() const
388{
389 QStringList lst;
390 for( KURL::List::ConstIterator it = begin();
391 it != end();
392 it++)
393 {
394 lst.append( (*it).url() );
395 }
396 return lst;
397}
398
399
400KURL::KURL()
401{
402 reset();
403}
404
405KURL::~KURL()
406{
407}
408
409
410KURL::KURL( const QString &url, int encoding_hint )
411{
412 reset();
413 parse( url, encoding_hint );
414}
415
416KURL::KURL( const char * url, int encoding_hint )
417{
418 reset();
419 parse( QString::fromLatin1(url), encoding_hint );
420}
421
422KURL::KURL( const QCString& url, int encoding_hint )
423{
424 reset();
425 parse( QString::fromLatin1(url), encoding_hint );
426}
427
428KURL::KURL( const KURL& _u )
429{
430 *this = _u;
431}
432
433QDataStream & operator<< (QDataStream & s, const KURL & a)
434{
435 QString QueryForWire=a.m_strQuery_encoded;
436 if (!a.m_strQuery_encoded.isNull())
437 QueryForWire.prepend("?");
438
439 s << a.m_strProtocol << a.m_strUser << a.m_strPass << a.m_strHost
440 << a.m_strPath << a.m_strPath_encoded << QueryForWire << a.m_strRef_encoded
441 << Q_INT8(a.m_bIsMalformed ? 1 : 0) << a.m_iPort;
442 return s;
443}
444
445QDataStream & operator>> (QDataStream & s, KURL & a)
446{
447 Q_INT8 malf;
448 QString QueryFromWire;
449 s >> a.m_strProtocol >> a.m_strUser >> a.m_strPass >> a.m_strHost
450 >> a.m_strPath >> a.m_strPath_encoded >> QueryFromWire >> a.m_strRef_encoded
451 >> malf >> a.m_iPort;
452 a.m_bIsMalformed = (malf != 0);
453
454 if ( QueryFromWire.isEmpty() )
455 a.m_strQuery_encoded = QString::null;
456 else
457 a.m_strQuery_encoded = QueryFromWire.mid(1);
458
459 return s;
460}
461
462#ifndef QT_NO_NETWORKPROTOCOL
463KURL::KURL( const QUrl &u )
464{
465 *this = u;
466}
467#endif
468
469KURL::KURL( const KURL& _u, const QString& _rel_url, int encoding_hint )
470{
471 // WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS
472 // http:/index.html AS A VALID SYNTAX FOR RELATIVE
473 // URLS. ( RFC 2396 section 5.2 item # 3 )
474 QString rUrl = _rel_url;
475 int len = _u.m_strProtocol.length();
476 if ( !_u.m_strHost.isEmpty() && !rUrl.isEmpty() &&
477 rUrl.find( _u.m_strProtocol, 0, false ) == 0 &&
478 rUrl[len] == ':' && (rUrl[len+1] != '/' ||
479 (rUrl[len+1] == '/' && rUrl[len+2] != '/')) )
480 {
481 rUrl.remove( 0, rUrl.find( ':' ) + 1 );
482 }
483
484 if ( rUrl.isEmpty() )
485 {
486 *this = _u;
487 }
488 else if ( rUrl[0] == '#' )
489 {
490 *this = _u;
491 QString ref = decode(rUrl.mid(1), encoding_hint);
492 if ( ref.isNull() )
493 ref = ""; // we know there was an (empty) html ref, we saw the '#'
494 setHTMLRef( ref );
495 }
496 else if ( isRelativeURL( rUrl) )
497 {
498 *this = _u;
499 m_strQuery_encoded = QString::null;
500 m_strRef_encoded = QString::null;
501 if ( rUrl[0] == '/')
502 {
503 if ((rUrl.length() > 1) && (rUrl[1] == '/'))
504 {
505 m_strHost = QString::null;
506 }
507 m_strPath = QString::null;
508 m_strPath_encoded = QString::null;
509 }
510 else if ( rUrl[0] != '?' )
511 {
512 int pos = m_strPath.findRev( '/' );
513 if (pos >= 0)
514 m_strPath.truncate(pos);
515 m_strPath += '/';
516 if (!m_strPath_encoded.isEmpty())
517 {
518 pos = m_strPath_encoded.findRev( '/' );
519 if (pos >= 0)
520 m_strPath_encoded.truncate(pos);
521 m_strPath_encoded += '/';
522 }
523 }
524 else
525 {
526 if ( m_strPath.isEmpty() )
527 m_strPath = '/';
528 }
529 KURL tmp( url() + rUrl, encoding_hint);
530 *this = tmp;
531 cleanPath(false);
532 }
533 else
534 {
535 KURL tmp( rUrl, encoding_hint);
536 *this = tmp;
537 // Preserve userinfo if applicable.
538 if (!_u.m_strUser.isEmpty() && m_strUser.isEmpty() && (_u.m_strHost == m_strHost) && (_u.m_strProtocol == m_strProtocol))
539 {
540 m_strUser = _u.m_strUser;
541 m_strPass = _u.m_strPass;
542 }
543 }
544}
545
546void KURL::reset()
547{
548 m_strProtocol = QString::null;
549 m_strUser = QString::null;
550 m_strPass = QString::null;
551 m_strHost = QString::null;
552 m_strPath = QString::null;
553 m_strPath_encoded = QString::null;
554 m_strQuery_encoded = QString::null;
555 m_strRef_encoded = QString::null;
556 m_bIsMalformed = true;
557 m_iPort = 0;
558}
559
560bool KURL::isEmpty() const
561{
562 return (m_strPath.isEmpty() && m_strProtocol.isEmpty());
563}
564
565void KURL::parse( const QString& _url, int encoding_hint )
566{
567 //kdDebug(126) << "parse " << _url << endl;
568 // Return immediately whenever the given url
569 // is empty or null.
570 if ( _url.isEmpty() )
571 {
572 m_strProtocol = _url;
573 return;
574 }
575
576 QString port;
577 bool badHostName = false;
578 int start = 0;
579 uint len = _url.length();
580 const QChar* buf = _url.unicode();
581 const QChar* orig = buf;
582
583 QChar delim;
584 QString tmp;
585
586 uint pos = 0;
587
588 // Node 1: Accept alpha or slash
589 QChar x = buf[pos++];
590 if ( x == '/' )
591 goto Node9;
592 if ( !isalpha( (int)x ) )
593 goto NodeErr;
594
595 // Node 2: Accept any amount of (alpha|digit|'+'|'-')
596 // '.' is not currently accepted, because current KURL may be confused.
597 // Proceed with :// :/ or :
598 while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) ||
599 buf[pos] == '+' || buf[pos] == '-')) pos++;
600
601 if ( pos+2 < len && buf[pos] == ':' && buf[pos+1] == '/' && buf[pos+2] == '/' )
602 {
603 m_strProtocol = QString( orig, pos ).lower();
604 pos += 3;
605 }
606 else if (pos+1 < len && buf[pos] == ':' ) // Need to always compare length()-1 otherwise KURL passes "http:" as legal!!
607 {
608 m_strProtocol = QString( orig, pos ).lower();
609 //kdDebug(126)<<"setting protocol to "<<m_strProtocol<<endl;
610 pos++;
611 start = pos;
612 goto Node9;
613 }
614 else
615 goto NodeErr;
616
617 //Node 3: We need at least one character here
618 if ( pos == len )
619 goto NodeErr;
620 start = pos;
621
622 // Node 4: Accept any amount of characters.
623 if (buf[pos] == '[') // An IPv6 host follows.
624 goto Node8;
625 // Terminate on / or @ or ? or # or " or ; or <
626 x = buf[pos];
627 while( (x != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') )
628 {
629 if ((x == '\"') || (x == ';') || (x == '<'))
630 badHostName = true;
631 if (++pos == len)
632 break;
633 x = buf[pos];
634 }
635 if ( pos == len )
636 {
637 if (badHostName)
638 goto NodeErr;
639
640 setHost(decode(QString( buf + start, pos - start ), encoding_hint));
641 goto NodeOk;
642 }
643 if ( x == '@' )
644 {
645 m_strUser = decode(QString( buf + start, pos - start ), encoding_hint);
646 pos++;
647 goto Node7;
648 }
649 else if ( (x == '/') || (x == '?') || (x == '#'))
650 {
651 if (badHostName)
652 goto NodeErr;
653
654 setHost(decode(QString( buf + start, pos - start ), encoding_hint));
655 start = pos;
656 goto Node9;
657 }
658 else if ( x != ':' )
659 goto NodeErr;
660 m_strUser = decode(QString( buf + start, pos - start ), encoding_hint);
661 pos++;
662
663 // Node 5: We need at least one character
664 if ( pos == len )
665 goto NodeErr;
666 start = pos++;
667
668 // Node 6: Read everything until @, /, ? or #
669 while( (pos < len) &&
670 (buf[pos] != '@') &&
671 (buf[pos] != '/') &&
672 (buf[pos] != '?') &&
673 (buf[pos] != '#')) pos++;
674 // If we now have a '@' the ':' seperates user and password.
675 // Otherwise it seperates host and port.
676 if ( (pos == len) || (buf[pos] != '@') )
677 {
678 // Ok the : was used to separate host and port
679 if (badHostName)
680 goto NodeErr;
681 setHost(m_strUser);
682 m_strUser = QString::null;
683 QString tmp( buf + start, pos - start );
684 char *endptr;
685 m_iPort = (unsigned short int)strtol(tmp.ascii(), &endptr, 10);
686 if ((pos == len) && (strlen(endptr) == 0))
687 goto NodeOk;
688 // there is more after the digits
689 pos -= strlen(endptr);
690 start = pos++;
691 goto Node9;
692 }
693 m_strPass = decode(QString( buf + start, pos - start), encoding_hint);
694 pos++;
695
696 // Node 7: We need at least one character
697 Node7:
698 if ( pos == len )
699 goto NodeErr;
700
701 Node8:
702 if (buf[pos] == '[')
703 {
704 // IPv6 address
705 start = ++pos; // Skip '['
706
707 if (pos == len)
708 {
709 badHostName = true;
710 goto NodeErr;
711 }
712 // Node 8b: Read everything until ] or terminate
713 badHostName = false;
714 x = buf[pos];
715 while( (x != ']') )
716 {
717 if ((x == '\"') || (x == ';') || (x == '<'))
718 badHostName = true;
719 if (++pos == len)
720 {
721 badHostName = true;
722 break;
723 }
724 x = buf[pos];
725 }
726 if (badHostName)
727 goto NodeErr;
728 setHost(decode(QString( buf + start, pos - start ), encoding_hint));
729 if (pos < len) pos++; // Skip ']'
730 if (pos == len)
731 goto NodeOk;
732 }
733 else
734 {
735 // Non IPv6 address
736 start = pos;
737
738 // Node 8b: Read everything until / : or terminate
739 badHostName = false;
740 x = buf[pos];
741 while( (x != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') )
742 {
743 if ((x == '\"') || (x == ';') || (x == '<'))
744 badHostName = true;
745 if (++pos == len)
746 break;
747 x = buf[pos];
748 }
749 if (badHostName)
750 goto NodeErr;
751 if ( pos == len )
752 {
753 setHost(decode(QString( buf + start, pos - start ), encoding_hint));
754 goto NodeOk;
755 }
756 setHost(decode(QString( buf + start, pos - start ), encoding_hint));
757 }
758 x = buf[pos];
759 if ( x == '/' )
760 {
761 start = pos++;
762 goto Node9;
763 }
764 else if ( x != ':' )
765 goto NodeErr;
766 pos++;
767
768 // Node 8a: Accept at least one digit
769 if ( pos == len )
770 goto NodeErr;
771 start = pos;
772 if ( !isdigit( buf[pos++] ) )
773 goto NodeErr;
774
775 // Node 8b: Accept any amount of digits
776 while( pos < len && isdigit( buf[pos] ) ) pos++;
777 port = QString( buf + start, pos - start );
778 m_iPort = port.toUShort();
779 if ( pos == len )
780 goto NodeOk;
781 start = pos++;
782
783 Node9: // parse path until query or reference reached
784
785 while( pos < len && buf[pos] != '#' && buf[pos]!='?' ) pos++;
786
787 tmp = QString( buf + start, pos - start );
788 //kdDebug(126)<<" setting encoded path&query to:"<<tmp<<endl;
789 setEncodedPath( tmp, encoding_hint );
790
791 if ( pos == len )
792 goto NodeOk;
793
794 //Node10: // parse query or reference depending on what comes first
795 delim = (buf[pos++]=='#'?'?':'#');
796
797 start = pos;
798
799 while(pos < len && buf[pos]!=delim ) pos++;
800
801 tmp = QString(buf + start, pos - start);
802 if (delim=='#')
803 setQuery(tmp, encoding_hint);
804 else
805 m_strRef_encoded = tmp;
806
807 if (pos == len)
808 goto NodeOk;
809
810 //Node11: // feed the rest into the remaining variable
811 tmp = QString( buf + pos + 1, len - pos - 1);
812 if (delim == '#')
813 m_strRef_encoded = tmp;
814 else
815 setQuery(tmp, encoding_hint);
816
817 NodeOk:
818 //kdDebug(126)<<"parsing finished. m_strProtocol="<<m_strProtocol<<" m_strHost="<<m_strHost<<" m_strPath="<<m_strPath<<endl;
819 m_bIsMalformed = false; // Valid URL
820
821 //kdDebug()<<"Prot="<<m_strProtocol<<"\nUser="<<m_strUser<<"\nPass="<<m_strPass<<"\nHost="<<m_strHost<<"\nPath="<<m_strPath<<"\nQuery="<<m_strQuery_encoded<<"\nRef="<<m_strRef_encoded<<"\nPort="<<m_iPort<<endl;
822 if (m_strProtocol.isEmpty())
823 {
824 m_strProtocol = fileProt;
825 }
826 return;
827
828 NodeErr:
829// kdDebug(126) << "KURL couldn't parse URL \"" << _url << "\"" << endl;
830 reset();
831 m_strProtocol = _url;
832}
833
834KURL& KURL::operator=( const QString& _url )
835{
836 reset();
837 parse( _url );
838
839 return *this;
840}
841
842KURL& KURL::operator=( const char * _url )
843{
844 reset();
845 parse( QString::fromLatin1(_url) );
846
847 return *this;
848}
849
850#ifndef QT_NO_NETWORKPROTOCOL
851KURL& KURL::operator=( const QUrl & u )
852{
853 m_strProtocol = u.protocol();
854 m_strUser = u.user();
855 m_strPass = u.password();
856 m_strHost = u.host();
857 m_strPath = u.path( FALSE );
858 m_strPath_encoded = QString::null;
859 m_strQuery_encoded = u.query();
860 m_strRef_encoded = u.ref();
861 m_bIsMalformed = !u.isValid();
862 m_iPort = u.port();
863
864 return *this;
865}
866#endif
867
868KURL& KURL::operator=( const KURL& _u )
869{
870 m_strProtocol = _u.m_strProtocol;
871 m_strUser = _u.m_strUser;
872 m_strPass = _u.m_strPass;
873 m_strHost = _u.m_strHost;
874 m_strPath = _u.m_strPath;
875 m_strPath_encoded = _u.m_strPath_encoded;
876 m_strQuery_encoded = _u.m_strQuery_encoded;
877 m_strRef_encoded = _u.m_strRef_encoded;
878 m_bIsMalformed = _u.m_bIsMalformed;
879 m_iPort = _u.m_iPort;
880
881 return *this;
882}
883
884bool KURL::operator==( const KURL& _u ) const
885{
886 if ( isMalformed() || _u.isMalformed() )
887 return false;
888
889 if ( m_strProtocol == _u.m_strProtocol &&
890 m_strUser == _u.m_strUser &&
891 m_strPass == _u.m_strPass &&
892 m_strHost == _u.m_strHost &&
893 m_strPath == _u.m_strPath &&
894 // The encoded path may be null, but the URLs are still equal (David)
895 ( m_strPath_encoded.isNull() || _u.m_strPath_encoded.isNull() ||
896 m_strPath_encoded == _u.m_strPath_encoded ) &&
897 m_strQuery_encoded == _u.m_strQuery_encoded &&
898 m_strRef_encoded == _u.m_strRef_encoded &&
899 m_iPort == _u.m_iPort )
900 {
901 return true;
902 }
903
904 return false;
905}
906
907bool KURL::operator==( const QString& _u ) const
908{
909 KURL u( _u );
910 return ( *this == u );
911}
912
913bool KURL::cmp( const KURL &u, bool ignore_trailing ) const
914{
915 return equals( u, ignore_trailing );
916}
917
918bool KURL::equals( const KURL &_u, bool ignore_trailing ) const
919{
920 if ( isMalformed() || _u.isMalformed() )
921 return false;
922
923 if ( ignore_trailing )
924 {
925 QString path1 = path(1);
926 QString path2 = _u.path(1);
927 if ( path1 != path2 )
928 return false;
929
930 if ( m_strProtocol == _u.m_strProtocol &&
931 m_strUser == _u.m_strUser &&
932 m_strPass == _u.m_strPass &&
933 m_strHost == _u.m_strHost &&
934 m_strQuery_encoded == _u.m_strQuery_encoded &&
935 m_strRef_encoded == _u.m_strRef_encoded &&
936 m_iPort == _u.m_iPort )
937 return true;
938
939 return false;
940 }
941
942 return ( *this == _u );
943}
944
945bool KURL::isParentOf( const KURL& _u ) const
946{
947 if ( isMalformed() || _u.isMalformed() )
948 return false;
949
950 if ( m_strProtocol == _u.m_strProtocol &&
951 m_strUser == _u.m_strUser &&
952 m_strPass == _u.m_strPass &&
953 m_strHost == _u.m_strHost &&
954 m_strQuery_encoded == _u.m_strQuery_encoded &&
955 m_strRef_encoded == _u.m_strRef_encoded &&
956 m_iPort == _u.m_iPort )
957 {
958 if ( path().isEmpty() || _u.path().isEmpty() )
959 return false; // can't work with implicit paths
960
961 QString p1( cleanpath( path() ) );
962 if ( p1.at(p1.length()-1) != '/' )
963 p1 += '/';
964 QString p2( cleanpath( _u.path() ) );
965 if ( p2.at(p2.length()-1) != '/' )
966 p2 += '/';
967
968 //kdDebug(126) << "p1=" << p1 << endl;
969 //kdDebug(126) << "p2=" << p2 << endl;
970 //kdDebug(126) << "p1.length()=" << p1.length() << endl;
971 //kdDebug(126) << "p2.left(!$)=" << p2.left( p1.length() ) << endl;
972 return p2.startsWith( p1 );
973 }
974 return false;
975}
976
977void KURL::setFileName( const QString& _txt )
978{
979 m_strRef_encoded = QString::null;
980 int i = 0;
981 while( _txt[i] == '/' ) ++i;
982 QString tmp;
983 if ( i )
984 tmp = _txt.mid( i );
985 else
986 tmp = _txt;
987
988 QString path = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
989 if ( path.isEmpty() )
990 path = "/";
991 else
992 {
993 int lastSlash = path.findRev( '/' );
994 if ( lastSlash == -1)
995 {
996 // The first character is not a '/' ???
997 // This looks strange ...
998 path = "/";
999 }
1000 else if ( path.right(1) != "/" )
1001 path.truncate( lastSlash+1 ); // keep the "/"
1002 }
1003 if (m_strPath_encoded.isEmpty())
1004 {
1005 path += tmp;
1006 setPath( path );
1007 }
1008 else
1009 {
1010 path += encode_string(tmp);
1011 setEncodedPath( path );
1012 }
1013 cleanPath();
1014}
1015
1016void KURL::cleanPath( bool cleanDirSeparator ) // taken from the old KURL
1017{
1018 m_strPath = cleanpath(m_strPath, cleanDirSeparator);
1019 // WABA: Is this safe when "/../" is encoded with %?
1020 m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator);
1021}
1022
1023static QString trailingSlash( int _trailing, const QString &path )
1024{
1025 QString result = path;
1026
1027 if ( _trailing == 0 )
1028 return result;
1029 else if ( _trailing == 1 )
1030 {
1031 int len = result.length();
1032 if ( len == 0 )
1033 result = QString::null;
1034 else if ( result[ len - 1 ] != '/' )
1035 result += "/";
1036 return result;
1037 }
1038 else if ( _trailing == -1 )
1039 {
1040 if ( result == "/" )
1041 return result;
1042 int len = result.length();
1043 if ( len != 0 && result[ len - 1 ] == '/' )
1044 result.truncate( len - 1 );
1045 return result;
1046 }
1047 else {
1048 assert( 0 );
1049 return QString::null;
1050 }
1051}
1052
1053void KURL::adjustPath( int _trailing )
1054{
1055 if (!m_strPath_encoded.isEmpty())
1056 {
1057 m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded );
1058 }
1059 m_strPath = trailingSlash( _trailing, m_strPath );
1060}
1061
1062
1063QString KURL::encodedPathAndQuery( int _trailing, bool _no_empty_path, int encoding_hint ) const
1064{
1065 QString tmp;
1066 if (!m_strPath_encoded.isEmpty() && encoding_hint == 0)
1067 {
1068 tmp = trailingSlash( _trailing, m_strPath_encoded );
1069 }
1070 else
1071 {
1072 tmp = path( _trailing );
1073 if ( _no_empty_path && tmp.isEmpty() )
1074 tmp = "/";
1075 tmp = encode( tmp, false, encoding_hint );
1076 }
1077
1078 // TODO apply encoding_hint to the query
1079 if (!m_strQuery_encoded.isNull())
1080 tmp += '?' + m_strQuery_encoded;
1081 return tmp;
1082}
1083
1084void KURL::setEncodedPath( const QString& _txt, int encoding_hint )
1085{
1086 m_strPath_encoded = _txt;
1087
1088 decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint );
1089 // Throw away encoding for local files, makes file-operations faster.
1090 if (m_strProtocol == fileProt)
1091 m_strPath_encoded = QString::null;
1092}
1093
1094
1095void KURL::setEncodedPathAndQuery( const QString& _txt, int encoding_hint )
1096{
1097 int pos = _txt.find( '?' );
1098 if ( pos == -1 )
1099 {
1100 setEncodedPath(_txt, encoding_hint);
1101 m_strQuery_encoded = QString::null;
1102 }
1103 else
1104 {
1105 setEncodedPath(_txt.left( pos ), encoding_hint);
1106 setQuery(_txt.right(_txt.length() - pos - 1), encoding_hint);
1107 }
1108}
1109
1110QString KURL::path( int _trailing ) const
1111{
1112 return trailingSlash( _trailing, path() );
1113}
1114
1115bool KURL::isLocalFile() const
1116{
1117 if ( (m_strProtocol != fileProt ) || hasSubURL() )
1118 return false;
1119
1120 if (m_strHost.isEmpty() || (m_strHost == "localhost"))
1121 return true;
1122
1123 char hostname[ 256 ];
1124 hostname[ 0 ] = '\0';
1125#ifdef _WIN32_
1126 // pending LR fixme
1127 //hostname = "localhost";
1128#else
1129 if (!gethostname( hostname, 255 ))
1130 hostname[sizeof(hostname)-1] = '\0';
1131#endif
1132 for(char *p = hostname; *p; p++)
1133 *p = tolower(*p);
1134
1135 return (m_strHost == hostname);
1136}
1137
1138void KURL::setFileEncoding(const QString &encoding)
1139{
1140 if (!isLocalFile())
1141 return;
1142
1143 QString q = query();
1144
1145 if (!q.isEmpty() && (q[0] == '?'))
1146 q = q.mid(1);
1147
1148 QStringList args = QStringList::split('&', q);
1149 for(QStringList::Iterator it = args.begin();
1150 it != args.end();)
1151 {
1152 QString s = decode_string(*it);
1153 if (s.startsWith("charset="))
1154//US changed erase into remove ???
1155 it = args.remove(it);
1156 else
1157 ++it;
1158 }
1159 if (!encoding.isEmpty())
1160 args.append("charset="+encode_string(encoding));
1161
1162 if (args.isEmpty())
1163 setQuery(QString::null);
1164 else
1165 setQuery(args.join("&"));
1166}
1167
1168QString KURL::fileEncoding() const
1169{
1170 if (!isLocalFile())
1171 return QString::null;
1172
1173 QString q = query();
1174
1175 if (q.isEmpty())
1176 return QString::null;
1177
1178 if (q[0] == '?')
1179 q = q.mid(1);
1180
1181 QStringList args = QStringList::split('&', q);
1182 for(QStringList::ConstIterator it = args.begin();
1183 it != args.end();
1184 ++it)
1185 {
1186 QString s = decode_string(*it);
1187 if (s.startsWith("charset="))
1188 return s.mid(8);
1189 }
1190 return QString::null;
1191}
1192
1193bool KURL::hasSubURL() const
1194{
1195 if ( m_strProtocol.isEmpty() || m_bIsMalformed )
1196 return false;
1197 if (m_strRef_encoded.isEmpty())
1198 return false;
1199 if (m_strRef_encoded.startsWith("gzip:"))
1200 return true;
1201 if (m_strRef_encoded.startsWith("bzip:"))
1202 return true;
1203 if (m_strRef_encoded.startsWith("bzip2:"))
1204 return true;
1205 if (m_strRef_encoded.startsWith("tar:"))
1206 return true;
1207 if ( m_strProtocol == "error" ) // anything that starts with error: has suburls
1208 return true;
1209 return false;
1210}
1211
1212QString KURL::url( int _trailing, int encoding_hint ) const
1213{
1214 if( m_bIsMalformed )
1215 {
1216 // Return the whole url even when the url is
1217 // malformed. Under such conditions the url
1218 // is stored in m_strProtocol.
1219 return m_strProtocol;
1220 }
1221
1222 QString u = m_strProtocol;
1223 if (!u.isEmpty())
1224 u += ":";
1225
1226 if ( hasHost() )
1227 {
1228 u += "//";
1229 if ( hasUser() )
1230 {
1231 u += encode(m_strUser, true, encoding_hint);
1232 if ( hasPass() )
1233 {
1234 u += ":";
1235 u += encode(m_strPass, true, encoding_hint);
1236 }
1237 u += "@";
1238 }
1239 bool IPv6 = (m_strHost.find(':') != -1);
1240 if (IPv6)
1241 u += '[' + m_strHost + ']';
1242 else
1243 u += encodeHost(m_strHost, true, encoding_hint);
1244 if ( m_iPort != 0 ) {
1245 QString buffer;
1246 buffer.sprintf( ":%u", m_iPort );
1247 u += buffer;
1248 }
1249 }
1250
1251 u += encodedPathAndQuery( _trailing, false, encoding_hint );
1252
1253 if ( hasRef() )
1254 {
1255 u += "#";
1256 u += m_strRef_encoded;
1257 }
1258
1259 return u;
1260}
1261
1262QString KURL::prettyURL( int _trailing ) const
1263{
1264 if( m_bIsMalformed )
1265 {
1266 // Return the whole url even when the url is
1267 // malformed. Under such conditions the url
1268 // is stored in m_strProtocol.
1269 return m_strProtocol;
1270 }
1271
1272 QString u = m_strProtocol;
1273 if (!u.isEmpty())
1274 u += ":";
1275
1276 if ( hasHost() )
1277 {
1278 u += "//";
1279 if ( hasUser() )
1280 {
1281 u += lazy_encode(m_strUser);
1282 // Don't show password!
1283 u += "@";
1284 }
1285 bool IPv6 = (m_strHost.find(':') != -1);
1286 if (IPv6)
1287 {
1288 u += '[' + m_strHost + ']';
1289 }
1290 else
1291 {
1292 u += lazy_encode(m_strHost);
1293 }
1294 if ( m_iPort != 0 ) {
1295 QString buffer;
1296 buffer.sprintf( ":%u", m_iPort );
1297 u += buffer;
1298 }
1299 }
1300
1301 u += trailingSlash( _trailing, lazy_encode( m_strPath ) );
1302 if (!m_strQuery_encoded.isNull())
1303 u += '?' + m_strQuery_encoded;
1304
1305 if ( hasRef() )
1306 {
1307 u += "#";
1308 u += m_strRef_encoded;
1309 }
1310
1311 return u;
1312}
1313
1314QString KURL::prettyURL( int _trailing, AdjustementFlags _flags) const
1315{
1316 QString u = prettyURL(_trailing);
1317 if (_flags & StripFileProtocol && u.startsWith("file:"))
1318 u.remove(0, 5);
1319 return u;
1320}
1321
1322QString KURL::htmlURL() const
1323{
1324//US QStyleSheet::escape was not in my Qt distribution. Why ???
1325//US return QStyleSheet::escape(prettyURL());
1326 return prettyURL();
1327}
1328
1329KURL::List KURL::split( const KURL& _url )
1330{
1331 QString ref;
1332 KURL::List lst;
1333 KURL url = _url;
1334
1335 while(true)
1336 {
1337 KURL u = url;
1338 u.m_strRef_encoded = QString::null;
1339 lst.append(u);
1340 if (url.hasSubURL())
1341 {
1342 url = KURL(url.m_strRef_encoded);
1343 }
1344 else
1345 {
1346 ref = url.m_strRef_encoded;
1347 break;
1348 }
1349 }
1350
1351 // Set HTML ref in all URLs.
1352 KURL::List::Iterator it;
1353 for( it = lst.begin() ; it != lst.end(); ++it )
1354 {
1355 (*it).m_strRef_encoded = ref;
1356 }
1357
1358 return lst;
1359}
1360
1361KURL::List KURL::split( const QString& _url )
1362{
1363 return split(KURL(_url));
1364}
1365
1366KURL KURL::join( const KURL::List & lst )
1367{
1368 if (lst.isEmpty()) return KURL();
1369 KURL tmp;
1370
1371 KURL::List::ConstIterator first = lst.fromLast();
1372 for( KURL::List::ConstIterator it = first; it != lst.end(); --it )
1373 {
1374 KURL u(*it);
1375 if (it != first)
1376 {
1377 if (!u.m_strRef_encoded) u.m_strRef_encoded = tmp.url();
1378 else u.m_strRef_encoded += "#" + tmp.url(); // Support more than one suburl thingy
1379 }
1380 tmp = u;
1381 }
1382
1383 return tmp;
1384}
1385
1386QString KURL::fileName( bool _strip_trailing_slash ) const
1387{
1388 QString fname;
1389 if (hasSubURL()) { // If we have a suburl, then return the filename from there
1390 KURL::List list = KURL::split(*this);
1391 KURL::List::Iterator it = list.fromLast();
1392 return (*it).fileName(_strip_trailing_slash);
1393 }
1394 const QString &path = m_strPath;
1395
1396 int len = path.length();
1397 if ( len == 0 )
1398 return fname;
1399
1400 if ( _strip_trailing_slash )
1401 {
1402 while ( len >= 1 && path[ len - 1 ] == '/' )
1403 len--;
1404 }
1405 else if ( path[ len - 1 ] == '/' )
1406 return fname;
1407
1408 // Does the path only consist of '/' characters ?
1409 if ( len == 1 && path[ 0 ] == '/' )
1410 return fname;
1411
1412 // Skip last n slashes
1413 int n = 1;
1414 if (!m_strPath_encoded.isEmpty())
1415 {
1416 // This is hairy, we need the last unencoded slash.
1417 // Count in the encoded string how many encoded slashes follow the last
1418 // unencoded one.
1419 int i = m_strPath_encoded.findRev( '/', len - 1 );
1420 QString fileName_encoded = m_strPath_encoded.mid(i+1);
1421 n += fileName_encoded.contains("%2f", false);
1422 }
1423 int i = len;
1424 do {
1425 i = path.findRev( '/', i - 1 );
1426 }
1427 while (--n && (i > 0));
1428
1429 // If ( i == -1 ) => the first character is not a '/'
1430 // So it's some URL like file:blah.tgz, return the whole path
1431 if ( i == -1 ) {
1432 if ( len == (int)path.length() )
1433 fname = path;
1434 else
1435 // Might get here if _strip_trailing_slash is true
1436 fname = path.left( len );
1437 }
1438 else
1439 {
1440 fname = path.mid( i + 1, len - i - 1 ); // TO CHECK
1441 }
1442 return fname;
1443}
1444
1445void KURL::addPath( const QString& _txt )
1446{
1447 if (hasSubURL())
1448 {
1449 KURL::List lst = split( *this );
1450 KURL &u = lst.last();
1451 u.addPath(_txt);
1452 *this = join( lst );
1453 return;
1454 }
1455
1456 m_strPath_encoded = QString::null;
1457
1458 if ( _txt.isEmpty() )
1459 return;
1460
1461 int i = 0;
1462 int len = m_strPath.length();
1463 // NB: avoid three '/' when building a new path from nothing
1464 if ( len == 0 ) {
1465 while( _txt[i] == '/' ) ++i;
1466 }
1467 // Add the trailing '/' if it is missing
1468 else if ( _txt[0] != '/' && ( len == 0 || m_strPath[ len - 1 ] != '/' ) )
1469 m_strPath += "/";
1470
1471 // No double '/' characters
1472 i = 0;
1473 if ( len != 0 && m_strPath[ len - 1 ] == '/' )
1474 {
1475 while( _txt[i] == '/' )
1476 ++i;
1477 }
1478
1479 m_strPath += _txt.mid( i );
1480}
1481
1482QString KURL::directory( bool _strip_trailing_slash_from_result,
1483 bool _ignore_trailing_slash_in_path ) const
1484{
1485 QString result = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
1486 if ( _ignore_trailing_slash_in_path )
1487 result = trailingSlash( -1, result );
1488
1489 if ( result.isEmpty() || result == "/" )
1490 return result;
1491
1492 int i = result.findRev( "/" );
1493 // If ( i == -1 ) => the first character is not a '/'
1494 // So it's some URL like file:blah.tgz, with no path
1495 if ( i == -1 )
1496 return QString::null;
1497
1498 if ( i == 0 )
1499 {
1500 result = "/";
1501 return result;
1502 }
1503
1504 if ( _strip_trailing_slash_from_result )
1505 result = result.left( i );
1506 else
1507 result = result.left( i + 1 );
1508
1509 if (!m_strPath_encoded.isEmpty())
1510 result = decode(result);
1511
1512 return result;
1513}
1514
1515
1516bool KURL::cd( const QString& _dir )
1517{
1518 if ( _dir.isEmpty() || m_bIsMalformed )
1519 return false;
1520
1521 if (hasSubURL())
1522 {
1523 KURL::List lst = split( *this );
1524 KURL &u = lst.last();
1525 u.cd(_dir);
1526 *this = join( lst );
1527 return true;
1528 }
1529
1530 // absolute path ?
1531 if ( _dir[0] == '/' )
1532 {
1533 m_strPath_encoded = QString::null;
1534 m_strPath = _dir;
1535 setHTMLRef( QString::null );
1536 m_strQuery_encoded = QString::null;
1537 return true;
1538 }
1539
1540 // Users home directory on the local disk ?
1541 if ( ( _dir[0] == '~' ) && ( m_strProtocol == fileProt ))
1542 {
1543 m_strPath_encoded = QString::null;
1544 m_strPath = QDir::homeDirPath();
1545 m_strPath += "/";
1546 m_strPath += _dir.right(m_strPath.length() - 1);
1547 setHTMLRef( QString::null );
1548 m_strQuery_encoded = QString::null;
1549 return true;
1550 }
1551
1552 // relative path
1553 // we always work on the past of the first url.
1554 // Sub URLs are not touched.
1555
1556 // append '/' if necessary
1557 QString p = path(1);
1558 p += _dir;
1559 p = cleanpath( p );
1560 setPath( p );
1561
1562 setHTMLRef( QString::null );
1563 m_strQuery_encoded = QString::null;
1564
1565 return true;
1566}
1567
1568KURL KURL::upURL( ) const
1569{
1570 if (!query().isEmpty())
1571 {
1572 KURL u(*this);
1573 u.setQuery(QString::null);
1574 return u;
1575 };
1576
1577 if (!hasSubURL())
1578 {
1579 KURL u(*this);
1580 u.cd("../");
1581 return u;
1582 }
1583
1584 // We have a subURL.
1585 KURL::List lst = split( *this );
1586 if (lst.isEmpty())
1587 return KURL(); // Huh?
1588 while (true)
1589 {
1590 KURL &u = lst.last();
1591 QString old = u.path();
1592 u.cd("../");
1593 if (u.path() != old)
1594 break; // Finshed.
1595 if (lst.count() == 1)
1596 break; // Finished.
1597 lst.remove(lst.fromLast());
1598 }
1599 return join( lst );
1600}
1601
1602QString KURL::htmlRef() const
1603{
1604 if ( !hasSubURL() )
1605 {
1606 return decode( ref() );
1607 }
1608
1609 List lst = split( *this );
1610 return decode( (*lst.begin()).ref() );
1611}
1612
1613QString KURL::encodedHtmlRef() const
1614{
1615 if ( !hasSubURL() )
1616 {
1617 return ref();
1618 }
1619
1620 List lst = split( *this );
1621 return (*lst.begin()).ref();
1622}
1623
1624void KURL::setHTMLRef( const QString& _ref )
1625{
1626 if ( !hasSubURL() )
1627 {
1628 m_strRef_encoded = encode( _ref, true, 0 /*?*/);
1629 return;
1630 }
1631
1632 List lst = split( *this );
1633
1634 (*lst.begin()).setRef( encode( _ref, true, 0 /*?*/) );
1635
1636 *this = join( lst );
1637}
1638
1639bool KURL::hasHTMLRef() const
1640{
1641 if ( !hasSubURL() )
1642 {
1643 return hasRef();
1644 }
1645
1646 List lst = split( *this );
1647 return (*lst.begin()).hasRef();
1648}
1649
1650void
1651KURL::setProtocol( const QString& _txt )
1652{
1653 m_strProtocol = _txt;
1654 m_bIsMalformed = false;
1655}
1656
1657void
1658KURL::setUser( const QString& _txt )
1659{
1660 m_strUser = _txt;
1661}
1662
1663void
1664KURL::setPass( const QString& _txt )
1665{
1666 m_strPass = _txt;
1667}
1668
1669void
1670KURL::setHost( const QString& _txt )
1671{
1672#ifndef KDE_QT_ONLY
1673 m_strHost = KIDNA::toUnicode(_txt);
1674 if (m_strHost.isEmpty())
1675 m_strHost = _txt.lower(); // Probably an invalid hostname, but...
1676#else
1677 m_strHost = _txt.lower();
1678#endif
1679}
1680
1681void
1682KURL::setPort( unsigned short int _p )
1683{
1684 m_iPort = _p;
1685}
1686
1687void KURL::setPath( const QString & path )
1688{
1689 if (isEmpty())
1690 m_bIsMalformed = false;
1691 if (m_strProtocol.isEmpty())
1692 {
1693 m_strProtocol = fileProt;
1694 }
1695 m_strPath = path;
1696 m_strPath_encoded = QString::null;
1697}
1698
1699void KURL::setDirectory( const QString &dir)
1700{
1701//US this has to be fixed. endsWith is not available in my distribution
1702//US if ( dir.endsWith("/"))
1703//US setPath(dir);
1704//US else
1705 setPath(dir+"/");
1706}
1707
1708void KURL::setQuery( const QString &_txt, int encoding_hint)
1709{
1710 if (!_txt.length())
1711 {
1712 m_strQuery_encoded = _txt;
1713 return;
1714 }
1715 if (_txt[0] =='?')
1716 m_strQuery_encoded = _txt.mid(1);
1717 else
1718 m_strQuery_encoded = _txt;
1719
1720 int l = m_strQuery_encoded.length();
1721 int i = 0;
1722 QString result;
1723 while (i < l)
1724 {
1725 int s = i;
1726 // Re-encode. Break encoded string up according to the reserved
1727 // characters '&:;=/?' and re-encode part by part.
1728 while(i < l)
1729 {
1730 char c = m_strQuery_encoded[i].latin1();
1731 if ((c == '&') || (c == ':') || (c == ';') ||
1732 (c == '=') || (c == '/') || (c == '?'))
1733 break;
1734 i++;
1735 }
1736 if (i > s)
1737 {
1738 QString tmp = m_strQuery_encoded.mid(s, i-s);
1739 QString newTmp;
1740 decode( tmp, newTmp, tmp, encoding_hint, false );
1741 result += tmp;
1742 }
1743 if (i < l)
1744 {
1745 result += m_strQuery_encoded[i];
1746 i++;
1747 }
1748 }
1749 m_strQuery_encoded = result;
1750}
1751
1752QString KURL::query() const
1753{
1754 if (m_strQuery_encoded.isNull())
1755 return QString::null;
1756 return '?'+m_strQuery_encoded;
1757}
1758
1759QString KURL::decode_string(const QString &str, int encoding_hint)
1760{
1761 return decode(str, encoding_hint);
1762}
1763
1764QString KURL::encode_string(const QString &str, int encoding_hint)
1765{
1766 return encode(str, false, encoding_hint);
1767}
1768
1769QString KURL::encode_string_no_slash(const QString &str, int encoding_hint)
1770{
1771 return encode(str, true, encoding_hint);
1772}
1773
1774bool urlcmp( const QString& _url1, const QString& _url2 )
1775{
1776 // Both empty ?
1777 if ( _url1.isEmpty() && _url2.isEmpty() )
1778 return true;
1779 // Only one empty ?
1780 if ( _url1.isEmpty() || _url2.isEmpty() )
1781 return false;
1782
1783 KURL::List list1 = KURL::split( _url1 );
1784 KURL::List list2 = KURL::split( _url2 );
1785
1786 // Malformed ?
1787 if ( list1.isEmpty() || list2.isEmpty() )
1788 return false;
1789
1790 return ( list1 == list2 );
1791}
1792
1793bool urlcmp( const QString& _url1, const QString& _url2, bool _ignore_trailing, bool _ignore_ref )
1794{
1795 // Both empty ?
1796 if ( _url1.isEmpty() && _url2.isEmpty() )
1797 return true;
1798 // Only one empty ?
1799 if ( _url1.isEmpty() || _url2.isEmpty() )
1800 return false;
1801
1802 KURL::List list1 = KURL::split( _url1 );
1803 KURL::List list2 = KURL::split( _url2 );
1804
1805 // Malformed ?
1806 if ( list1.isEmpty() || list2.isEmpty() )
1807 return false;
1808
1809 unsigned int size = list1.count();
1810 if ( list2.count() != size )
1811 return false;
1812
1813 if ( _ignore_ref )
1814 {
1815 (*list1.begin()).setRef(QString::null);
1816 (*list2.begin()).setRef(QString::null);
1817 }
1818
1819 KURL::List::Iterator it1 = list1.begin();
1820 KURL::List::Iterator it2 = list2.begin();
1821 for( ; it1 != list1.end() ; ++it1, ++it2 )
1822 if ( !(*it1).equals( *it2, _ignore_trailing ) )
1823 return false;
1824
1825 return true;
1826}
1827/*US we do not need this functions
1828
1829QMap< QString, QString > KURL::queryItems( int options ) const {
1830 return queryItems(options, 0);
1831}
1832
1833QMap< QString, QString > KURL::queryItems( int options, int encoding_hint ) const {
1834 if ( m_strQuery_encoded.isEmpty() )
1835 return QMap<QString,QString>();
1836
1837 QMap< QString, QString > result;
1838 QStringList items = QStringList::split( '&', m_strQuery_encoded );
1839 for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
1840 int equal_pos = (*it).find( '=' );
1841 if ( equal_pos > 0 ) { // = is not the first char...
1842 QString name = (*it).left( equal_pos );
1843 if ( options & CaseInsensitiveKeys )
1844 name = name.lower();
1845 QString value = (*it).mid( equal_pos + 1 );
1846 if ( value.isEmpty() )
1847 result.insert( name, QString::fromLatin1("") );
1848 else {
1849 // ### why is decoding name not neccessary?
1850 value.replace( '+', ' ' ); // + in queries means space
1851 result.insert( name, decode_string( value, encoding_hint ) );
1852 }
1853 } else if ( equal_pos < 0 ) { // no =
1854 QString name = (*it);
1855 if ( options & CaseInsensitiveKeys )
1856 name = name.lower();
1857 result.insert( name, QString::null );
1858 }
1859 }
1860
1861 return result;
1862}
1863
1864QString KURL::queryItem( const QString& _item ) const
1865{
1866 return queryItem( _item, 0 );
1867}
1868
1869QString KURL::queryItem( const QString& _item, int encoding_hint ) const
1870{
1871 QString item = _item + '=';
1872 if ( m_strQuery_encoded.length() <= 1 )
1873 return QString::null;
1874
1875 QStringList items = QStringList::split( '&', m_strQuery_encoded );
1876 unsigned int _len = item.length();
1877 for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it )
1878 {
1879 if ( (*it).startsWith( item ) )
1880 {
1881 if ( (*it).length() > _len )
1882 {
1883 QString str = (*it).mid( _len );
1884 str.replace( '+', ' ' ); // + in queries means space.
1885 return decode_string( str, encoding_hint );
1886 }
1887 else // empty value
1888 return QString::fromLatin1("");
1889 }
1890 }
1891
1892 return QString::null;
1893}
1894US we do not need this functions*/
1895
1896void KURL::removeQueryItem( const QString& _item )
1897{
1898 QString item = _item + '=';
1899 if ( m_strQuery_encoded.length() <= 1 )
1900 return;
1901
1902 QStringList items = QStringList::split( '&', m_strQuery_encoded );
1903 for ( QStringList::Iterator it = items.begin(); it != items.end(); )
1904 {
1905 if ( (*it).startsWith( item ) || (*it == _item) )
1906 {
1907 QStringList::Iterator deleteIt = it;
1908 ++it;
1909 items.remove(deleteIt);
1910 }
1911 else
1912 {
1913 ++it;
1914 }
1915 }
1916 m_strQuery_encoded = items.join( "&" );
1917}
1918
1919void KURL::addQueryItem( const QString& _item, const QString& _value, int encoding_hint )
1920{
1921 QString item = _item + '=';
1922 QString value = encode( _value, true, encoding_hint );
1923
1924 if (!m_strQuery_encoded.isEmpty())
1925 m_strQuery_encoded += '&';
1926 m_strQuery_encoded += item + value;
1927}
1928
1929// static
1930KURL KURL::fromPathOrURL( const QString& text )
1931{
1932 if ( text.isEmpty() )
1933 return KURL();
1934
1935 KURL url;
1936 if ( text[0] == '/' )
1937 url.setPath( text );
1938 else
1939 url = text;
1940
1941 return url;
1942}