~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/qt3support/network/q3url.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the Qt 3 compatibility classes of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "q3url.h"
 
30
 
 
31
#ifndef QT_NO_URL
 
32
 
 
33
#include "q3cstring.h"
 
34
#include "qdir.h"
 
35
 
 
36
#include <stdlib.h>
 
37
 
 
38
// used by q3filedialog.cpp
 
39
bool qt_resolve_symlinks = true;
 
40
 
 
41
class Q3UrlPrivate
 
42
{
 
43
public:
 
44
    QString protocol;
 
45
    QString user;
 
46
    QString pass;
 
47
    QString host;
 
48
    QString path, cleanPath;
 
49
    QString refEncoded;
 
50
    QString queryEncoded;
 
51
    bool isValid;
 
52
    int port;
 
53
    bool cleanPathDirty;
 
54
};
 
55
 
 
56
/*!
 
57
    Replaces backslashes with slashes and removes multiple occurrences
 
58
    of slashes or backslashes if \c allowMultiple is false.
 
59
*/
 
60
 
 
61
static void slashify( QString& s, bool allowMultiple = true )
 
62
{
 
63
    bool justHadSlash = false;
 
64
    for ( int i = 0; i < (int)s.length(); i++ ) {
 
65
        if ( !allowMultiple && justHadSlash &&
 
66
             ( s[ i ] == '/' || s[ i ] == '\\' ) ) {
 
67
            s.remove( i, 1 );
 
68
            --i;
 
69
            continue;
 
70
        }
 
71
        if ( s[ i ] == '\\' )
 
72
            s[ i ] = '/';
 
73
#if defined (Q_WS_MAC9)
 
74
        if ( s[ i ] == ':' && (i == (int)s.length()-1 || s[ i + 1 ] != '/' ) ) //mac colon's go away, unless after a protocol
 
75
                s[ i ] = '/';
 
76
#endif
 
77
        if ( s[ i ] == '/' )
 
78
            justHadSlash = true;
 
79
        else
 
80
            justHadSlash = false;
 
81
    }
 
82
}
 
83
 
 
84
 
 
85
 
 
86
/*!
 
87
    \class Q3Url q3url.h
 
88
    \brief The Q3Url class provides a URL parser and simplifies working with URLs.
 
89
 
 
90
    \compat
 
91
 
 
92
    The Q3Url class is provided for simple work with URLs. It can
 
93
    parse, decode, encode, etc.
 
94
 
 
95
    Q3Url works with the decoded path and encoded query in turn.
 
96
 
 
97
    Example:
 
98
 
 
99
    <tt>http://www.trolltech.com:80/cgi-bin/test%20me.pl?cmd=Hello%20you</tt>
 
100
 
 
101
    \table
 
102
    \header \i Function     \i Returns
 
103
    \row \i \l protocol()   \i "http"
 
104
    \row \i \l host()       \i "www.trolltech.com"
 
105
    \row \i \l port()       \i 80
 
106
    \row \i \l path()       \i "/cgi-bin/test&nbsp;me.pl"
 
107
    \row \i \l fileName()   \i "test&nbsp;me.pl"
 
108
    \row \i \l query()      \i "cmd=Hello%20you"
 
109
    \endtable
 
110
 
 
111
    Example:
 
112
 
 
113
    <tt>http://doc.trolltech.com/qdockarea.html#lines</tt>
 
114
 
 
115
    \table
 
116
    \header \i Function     \i Returns
 
117
    \row \i \l protocol()   \i "http"
 
118
    \row \i \l host()       \i "doc.trolltech.com"
 
119
    \row \i \l fileName()   \i "qdockarea.html"
 
120
    \row \i \l ref()        \i "lines"
 
121
    \endtable
 
122
 
 
123
    The individual parts of a URL can be set with setProtocol(),
 
124
    setHost(), setPort(), setPath(), setFileName(), setRef() and
 
125
    setQuery(). A URL could contain, for example, an ftp address which
 
126
    requires a user name and password; these can be set with setUser()
 
127
    and setPassword().
 
128
 
 
129
    Because path is always encoded internally you must not use "%00"
 
130
    in the path, although this is okay (but not recommended) for the
 
131
    query.
 
132
 
 
133
    Q3Url is normally used like this:
 
134
 
 
135
    \code
 
136
    Q3Url url( "http://www.trolltech.com" );
 
137
    // or
 
138
    Q3Url url( "file:/home/myself/Mail", "Inbox" );
 
139
    \endcode
 
140
 
 
141
    You can then access and manipulate the various parts of the URL.
 
142
 
 
143
    To make it easy to work with Q3Urls and QStrings, Q3Url implements
 
144
    the necessary cast and assignment operators so you can do
 
145
    following:
 
146
 
 
147
    \code
 
148
    Q3Url url( "http://www.trolltech.com" );
 
149
    QString s = url;
 
150
    // or
 
151
    QString s( "http://www.trolltech.com" );
 
152
    Q3Url url( s );
 
153
    \endcode
 
154
 
 
155
    Use the static functions, encode() and decode() to encode or
 
156
    decode a URL in a string. (They operate on the string in-place.)
 
157
    The isRelativeUrl() static function returns true if the given
 
158
    string is a relative URL.
 
159
 
 
160
    If you want to use a URL to work on a hierarchical structure (e.g.
 
161
    a local or remote filesystem), you might want to use the subclass
 
162
    Q3UrlOperator.
 
163
 
 
164
    \sa Q3UrlOperator
 
165
*/
 
166
 
 
167
 
 
168
/*!
 
169
    Constructs an empty URL that is invalid.
 
170
*/
 
171
 
 
172
Q3Url::Q3Url()
 
173
{
 
174
    d = new Q3UrlPrivate;
 
175
    d->isValid = false;
 
176
    d->port = -1;
 
177
    d->cleanPathDirty = true;
 
178
}
 
179
 
 
180
/*!
 
181
    Constructs a URL by parsing the string \a url.
 
182
 
 
183
    If you pass a string like "/home/qt", the "file" protocol is
 
184
    assumed.
 
185
*/
 
186
 
 
187
Q3Url::Q3Url( const QString& url )
 
188
{
 
189
    d = new Q3UrlPrivate;
 
190
    d->protocol = "file";
 
191
    d->port = -1;
 
192
    parse( url );
 
193
}
 
194
 
 
195
/*!
 
196
    Copy constructor. Copies the data of \a url.
 
197
*/
 
198
 
 
199
Q3Url::Q3Url( const Q3Url& url )
 
200
{
 
201
    d = new Q3UrlPrivate;
 
202
    *d = *url.d;
 
203
}
 
204
 
 
205
/*!
 
206
    Returns true if \a url is relative; otherwise returns false.
 
207
*/
 
208
 
 
209
bool Q3Url::isRelativeUrl( const QString &url )
 
210
{
 
211
    int colon = url.find( ":" );
 
212
    int slash = url.find( "/" );
 
213
 
 
214
    return ( slash != 0 && ( colon == -1 || ( slash != -1 && colon > slash ) ) );
 
215
}
 
216
 
 
217
/*!
 
218
    Constructs an URL taking \a url as the base (context) and
 
219
    \a relUrl as a relative URL to \a url. If \a relUrl is not relative,
 
220
    \a relUrl is taken as the new URL.
 
221
 
 
222
    For example, the path of
 
223
    \code
 
224
    Q3Url url( "ftp://ftp.trolltech.com/qt/source", "qt-2.1.0.tar.gz" );
 
225
    \endcode
 
226
    will be "/qt/srource/qt-2.1.0.tar.gz".
 
227
 
 
228
    On the other hand,
 
229
    \code
 
230
    Q3Url url( "ftp://ftp.trolltech.com/qt/source", "/usr/local" );
 
231
    \endcode
 
232
    will result in a new URL, "ftp://ftp.trolltech.com/usr/local",
 
233
    because "/usr/local" isn't relative.
 
234
 
 
235
    Similarly,
 
236
    \code
 
237
    Q3Url url( "ftp://ftp.trolltech.com/qt/source", "file:/usr/local" );
 
238
    \endcode
 
239
    will result in a new URL, with "/usr/local" as the path
 
240
    and "file" as the protocol.
 
241
 
 
242
    Normally it is expected that the path of \a url points to a
 
243
    directory, even if the path has no slash at the end. But if you
 
244
    want the constructor to handle the last part of the path as a file
 
245
    name if there is no slash at the end, and to let it be replaced by
 
246
    the file name of \a relUrl (if it contains one), set \a checkSlash
 
247
    to true.
 
248
*/
 
249
 
 
250
Q3Url::Q3Url( const Q3Url& url, const QString& relUrl, bool checkSlash )
 
251
{
 
252
    d = new Q3UrlPrivate;
 
253
    QString rel = relUrl;
 
254
    slashify( rel );
 
255
 
 
256
    Q3Url urlTmp( url );
 
257
    if ( !urlTmp.isValid() ) {
 
258
        urlTmp.reset();
 
259
    }
 
260
    if ( isRelativeUrl( rel ) ) {
 
261
        if ( rel[ 0 ] == '#' ) {
 
262
            *this = urlTmp;
 
263
            rel.remove( (uint)0, 1 );
 
264
            decode( rel );
 
265
            setRef( rel );
 
266
        } else if ( rel[ 0 ] == '?' ) {
 
267
            *this = urlTmp;
 
268
            rel.remove( (uint)0, 1 );
 
269
            setQuery( rel );
 
270
        } else {
 
271
            decode( rel );
 
272
            *this = urlTmp;
 
273
            setRef( QString() );
 
274
            if ( checkSlash && d->cleanPath[(int)path().length()-1] != '/' ) {
 
275
                if ( isRelativeUrl( path() ) )
 
276
                    setEncodedPathAndQuery( rel );
 
277
                else
 
278
                    setFileName( rel );
 
279
            } else {
 
280
                QString p = urlTmp.path();
 
281
                if ( p.isEmpty() ) {
 
282
                    // allow URLs like "file:foo"
 
283
                    if ( !d->host.isEmpty() && !d->user.isEmpty() && !d->pass.isEmpty() )
 
284
                        p = "/";
 
285
                }
 
286
                if ( !p.isEmpty() && p.right(1)!="/" )
 
287
                    p += "/";
 
288
                p += rel;
 
289
                d->path = p;
 
290
                d->cleanPathDirty = true;
 
291
            }
 
292
        }
 
293
    } else {
 
294
        if ( rel[ 0 ] == QChar( '/' ) ) {
 
295
            *this = urlTmp;
 
296
            setEncodedPathAndQuery( rel );
 
297
        } else {
 
298
            *this = rel;
 
299
        }
 
300
    }
 
301
}
 
302
 
 
303
/*!
 
304
    Destructor.
 
305
*/
 
306
 
 
307
Q3Url::~Q3Url()
 
308
{
 
309
    delete d;
 
310
    d = 0;
 
311
}
 
312
 
 
313
/*!
 
314
    Returns the protocol of the URL. Typically, "file", "http", "ftp",
 
315
    etc.
 
316
 
 
317
    \sa setProtocol()
 
318
*/
 
319
 
 
320
QString Q3Url::protocol() const
 
321
{
 
322
    return d->protocol;
 
323
}
 
324
 
 
325
/*!
 
326
    Sets the protocol of the URL to \a protocol. Typically, "file",
 
327
    "http", "ftp", etc.
 
328
 
 
329
    \sa protocol()
 
330
*/
 
331
 
 
332
void Q3Url::setProtocol( const QString& protocol )
 
333
{
 
334
    d->protocol = protocol;
 
335
    if ( hasHost() )
 
336
        d->isValid = true;
 
337
}
 
338
 
 
339
/*!
 
340
    Returns the username of the URL.
 
341
 
 
342
    \sa setUser() setPassword()
 
343
*/
 
344
 
 
345
QString Q3Url::user() const
 
346
{
 
347
    return  d->user;
 
348
}
 
349
 
 
350
/*!
 
351
    Sets the username of the URL to \a user.
 
352
 
 
353
    \sa user() setPassword()
 
354
*/
 
355
 
 
356
void Q3Url::setUser( const QString& user )
 
357
{
 
358
    d->user = user;
 
359
}
 
360
 
 
361
/*!
 
362
    Returns true if the URL contains a username; otherwise returns
 
363
    false.
 
364
 
 
365
    \sa setUser() setPassword()
 
366
*/
 
367
 
 
368
bool Q3Url::hasUser() const
 
369
{
 
370
    return !d->user.isEmpty();
 
371
}
 
372
 
 
373
/*!
 
374
    Returns the password of the URL.
 
375
 
 
376
    \warning Passwords passed in URLs are normally \e insecure; this
 
377
    is due to the mechanism, not because of Qt.
 
378
 
 
379
    \sa setPassword() setUser()
 
380
*/
 
381
 
 
382
QString Q3Url::password() const
 
383
{
 
384
    return d->pass;
 
385
}
 
386
 
 
387
/*!
 
388
    Sets the password of the URL to \a pass.
 
389
 
 
390
    \warning Passwords passed in URLs are normally \e insecure; this
 
391
    is due to the mechanism, not because of Qt.
 
392
 
 
393
    \sa password() setUser()
 
394
*/
 
395
 
 
396
void Q3Url::setPassword( const QString& pass )
 
397
{
 
398
    d->pass = pass;
 
399
}
 
400
 
 
401
/*!
 
402
    Returns true if the URL contains a password; otherwise returns
 
403
    false.
 
404
 
 
405
    \warning Passwords passed in URLs are normally \e insecure; this
 
406
    is due to the mechanism, not because of Qt.
 
407
 
 
408
    \sa setPassword() setUser()
 
409
*/
 
410
 
 
411
bool Q3Url::hasPassword() const
 
412
{
 
413
    return !d->pass.isEmpty();
 
414
}
 
415
 
 
416
/*!
 
417
    Returns the hostname of the URL.
 
418
 
 
419
    \sa setHost() hasHost()
 
420
*/
 
421
 
 
422
QString Q3Url::host() const
 
423
{
 
424
    return d->host;
 
425
}
 
426
 
 
427
/*!
 
428
    Sets the hostname of the URL to \a host.
 
429
 
 
430
    \sa host() hasHost()
 
431
*/
 
432
 
 
433
void Q3Url::setHost( const QString& host )
 
434
{
 
435
    d->host = host;
 
436
    if ( !d->protocol.isNull() && d->protocol != "file" )
 
437
        d->isValid = true;
 
438
}
 
439
 
 
440
/*!
 
441
    Returns true if the URL contains a hostname; otherwise returns
 
442
    false.
 
443
 
 
444
    \sa setHost()
 
445
*/
 
446
 
 
447
bool Q3Url::hasHost() const
 
448
{
 
449
    return !d->host.isEmpty();
 
450
}
 
451
 
 
452
/*!
 
453
    Returns the port of the URL or -1 if no port has been set.
 
454
 
 
455
    \sa setPort()
 
456
*/
 
457
 
 
458
int Q3Url::port() const
 
459
{
 
460
    return d->port;
 
461
}
 
462
 
 
463
/*!
 
464
    Sets the port of the URL to \a port.
 
465
 
 
466
    \sa port()
 
467
*/
 
468
 
 
469
void Q3Url::setPort( int port )
 
470
{
 
471
    d->port = port;
 
472
}
 
473
 
 
474
/*!
 
475
    Returns true if the URL contains a port; otherwise returns false.
 
476
 
 
477
    \sa setPort()
 
478
*/
 
479
 
 
480
bool Q3Url::hasPort() const
 
481
{
 
482
    return d->port >= 0;
 
483
}
 
484
 
 
485
/*!
 
486
    Sets the path of the URL to \a path.
 
487
 
 
488
    \sa path() hasPath()
 
489
*/
 
490
 
 
491
void Q3Url::setPath( const QString& path )
 
492
{
 
493
    d->path = path;
 
494
    slashify( d->path );
 
495
    d->cleanPathDirty = true;
 
496
    d->isValid = true;
 
497
}
 
498
 
 
499
/*!
 
500
    Returns true if the URL contains a path; otherwise returns false.
 
501
 
 
502
    \sa path() setPath()
 
503
*/
 
504
 
 
505
bool Q3Url::hasPath() const
 
506
{
 
507
    return !d->path.isEmpty();
 
508
}
 
509
 
 
510
/*!
 
511
    Sets the query of the URL to \a txt. \a txt must be encoded.
 
512
 
 
513
    \sa query() encode()
 
514
*/
 
515
 
 
516
void Q3Url::setQuery( const QString& txt )
 
517
{
 
518
    d->queryEncoded = txt;
 
519
}
 
520
 
 
521
/*!
 
522
    Returns the (encoded) query of the URL.
 
523
 
 
524
    \sa setQuery() decode()
 
525
*/
 
526
 
 
527
QString Q3Url::query() const
 
528
{
 
529
    return d->queryEncoded;
 
530
}
 
531
 
 
532
/*!
 
533
    Returns the (encoded) reference of the URL.
 
534
 
 
535
    \sa setRef() hasRef() decode()
 
536
*/
 
537
 
 
538
QString Q3Url::ref() const
 
539
{
 
540
    return d->refEncoded;
 
541
}
 
542
 
 
543
/*!
 
544
    Sets the reference of the URL to \a txt. \a txt must be encoded.
 
545
 
 
546
    \sa ref() hasRef() encode()
 
547
*/
 
548
 
 
549
void Q3Url::setRef( const QString& txt )
 
550
{
 
551
    d->refEncoded = txt;
 
552
}
 
553
 
 
554
/*!
 
555
    Returns true if the URL has a reference; otherwise returns false.
 
556
 
 
557
    \sa setRef()
 
558
*/
 
559
 
 
560
bool Q3Url::hasRef() const
 
561
{
 
562
    return !d->refEncoded.isEmpty();
 
563
}
 
564
 
 
565
/*!
 
566
    Returns true if the URL is valid; otherwise returns false. A URL
 
567
    is invalid if it cannot be parsed, for example.
 
568
*/
 
569
 
 
570
bool Q3Url::isValid() const
 
571
{
 
572
    return d->isValid;
 
573
}
 
574
 
 
575
/*!
 
576
    Resets all parts of the URL to their default values and
 
577
    invalidates it.
 
578
*/
 
579
 
 
580
void Q3Url::reset()
 
581
{
 
582
    d->protocol = "file";
 
583
    d->user = "";
 
584
    d->pass = "";
 
585
    d->host = "";
 
586
    d->path = "";
 
587
    d->queryEncoded = "";
 
588
    d->refEncoded = "";
 
589
    d->isValid = true;
 
590
    d->port = -1;
 
591
    d->cleanPathDirty = true;
 
592
}
 
593
 
 
594
/*!
 
595
    Parses the \a url.
 
596
*/
 
597
 
 
598
bool Q3Url::parse( const QString& url )
 
599
{
 
600
    QString url_( url );
 
601
    slashify( url_ );
 
602
 
 
603
    if ( url_.isEmpty() ) {
 
604
        d->isValid = false;
 
605
        return false;
 
606
    }
 
607
 
 
608
    d->cleanPathDirty = true;
 
609
    d->isValid = true;
 
610
    QString oldProtocol = d->protocol;
 
611
    d->protocol.clear();
 
612
 
 
613
    const int Init      = 0;
 
614
    const int Protocol  = 1;
 
615
    const int Separator1= 2; // :
 
616
    const int Separator2= 3; // :/
 
617
    const int Separator3= 4; // :// or more slashes
 
618
    const int User      = 5;
 
619
    const int Pass      = 6;
 
620
    const int Host      = 7;
 
621
    const int Path      = 8;
 
622
    const int Ref       = 9;
 
623
    const int Query     = 10;
 
624
    const int Port      = 11;
 
625
    const int Done      = 12;
 
626
 
 
627
    const int InputAlpha= 1;
 
628
    const int InputDigit= 2;
 
629
    const int InputSlash= 3;
 
630
    const int InputColon= 4;
 
631
    const int InputAt   = 5;
 
632
    const int InputHash = 6;
 
633
    const int InputQuery= 7;
 
634
 
 
635
    static uchar table[ 12 ][ 8 ] = {
 
636
     /* None       InputAlpha  InputDigit  InputSlash  InputColon  InputAt     InputHash   InputQuery */
 
637
        { 0,       Protocol,   0,          Path,       0,          0,          0,          0,         }, // Init
 
638
        { 0,       Protocol,   Protocol,   0,          Separator1, 0,          0,          0,         }, // Protocol
 
639
        { 0,       Path,       Path,       Separator2, 0,          0,          0,          0,         }, // Separator1
 
640
        { 0,       Path,       Path,       Separator3, 0,          0,          0,          0,         }, // Separator2
 
641
        { 0,       User,       User,       Separator3, Pass,       Host,       0,          0,         }, // Separator3
 
642
        { 0,       User,       User,       User,       Pass,       Host,       User,       User,      }, // User
 
643
        { 0,       Pass,       Pass,       Pass,       Pass,       Host,       Pass,       Pass,      }, // Pass
 
644
        { 0,       Host,       Host,       Path,       Port,       Host,       Ref,        Query,     }, // Host
 
645
        { 0,       Path,       Path,       Path,       Path,       Path,       Ref,        Query,     }, // Path
 
646
        { 0,       Ref,        Ref,        Ref,        Ref,        Ref,        Ref,        Query,     }, // Ref
 
647
        { 0,       Query,      Query,      Query,      Query,      Query,      Query,      Query,     }, // Query
 
648
        { 0,       0,          Port,       Path,       0,          0,          0,          0,         }  // Port
 
649
    };
 
650
 
 
651
    bool relPath = false;
 
652
 
 
653
    relPath = false;
 
654
    bool forceRel = false;
 
655
 
 
656
    // If ':' is at pos 1, we have only one letter
 
657
    // before that separator => that's a drive letter!
 
658
    if ( url_.length() >= 2 && url_[1] == ':' )
 
659
        relPath = forceRel = true;
 
660
 
 
661
    int hasNoHost = -1;
 
662
    int cs = url_.find( ":/" );
 
663
    if ( cs != -1 ) // if a protocol is there, find out if there is a host or directly the path after it
 
664
        hasNoHost = url_.find( "///", cs );
 
665
    table[ 4 ][ 1 ] = User;
 
666
    table[ 4 ][ 2 ] = User;
 
667
    if ( cs == -1 || forceRel ) { // we have a relative file
 
668
        if ( url.find( ':' ) == -1 || forceRel ) {
 
669
            table[ 0 ][ 1 ] = Path;
 
670
            // Filenames may also begin with a digit
 
671
            table[ 0 ][ 2 ] = Path;
 
672
        } else {
 
673
            table[ 0 ][ 1 ] = Protocol;
 
674
        }
 
675
        relPath = true;
 
676
    } else { // some checking
 
677
        table[ 0 ][ 1 ] = Protocol;
 
678
 
 
679
        // find the part between the protocol and the path as the meaning
 
680
        // of that part is dependend on some chars
 
681
        ++cs;
 
682
        while ( url_[ cs ] == '/' )
 
683
            ++cs;
 
684
        int slash = url_.find( "/", cs );
 
685
        if ( slash == -1 )
 
686
            slash = url_.length() - 1;
 
687
        QString tmp = url_.mid( cs, slash - cs + 1 );
 
688
 
 
689
        if ( !tmp.isEmpty() ) { // if this part exists
 
690
 
 
691
            // look for the @ in this part
 
692
            int at = tmp.find( "@" );
 
693
            if ( at != -1 )
 
694
                at += cs;
 
695
            // we have no @, which means host[:port], so directly
 
696
            // after the protocol the host starts, or if the protocol
 
697
            // is file or there were more than 2 slashes, itļæ½ļæ½s the
 
698
            // path
 
699
            if ( at == -1 ) {
 
700
                if ( url_.left( 4 ) == "file" || hasNoHost != -1 )
 
701
                    table[ 4 ][ 1 ] = Path;
 
702
                else
 
703
                    table[ 4 ][ 1 ] = Host;
 
704
                table[ 4 ][ 2 ] = table[ 4 ][ 1 ];
 
705
            }
 
706
        }
 
707
    }
 
708
 
 
709
    int state = Init; // parse state
 
710
    int input; // input token
 
711
 
 
712
    QChar c = url_[ 0 ];
 
713
    int i = 0;
 
714
    QString port;
 
715
 
 
716
    for ( ;; ) {
 
717
        switch ( c.latin1() ) {
 
718
        case '?':
 
719
            input = InputQuery;
 
720
            break;
 
721
        case '#':
 
722
            input = InputHash;
 
723
            break;
 
724
        case '@':
 
725
            input = InputAt;
 
726
            break;
 
727
        case ':':
 
728
            input = InputColon;
 
729
            break;
 
730
        case '/':
 
731
            input = InputSlash;
 
732
            break;
 
733
        case '1': case '2': case '3': case '4': case '5':
 
734
        case '6': case '7': case '8': case '9': case '0':
 
735
            input = InputDigit;
 
736
            break;
 
737
        default:
 
738
            input = InputAlpha;
 
739
        }
 
740
 
 
741
        state = table[ state ][ input ];
 
742
 
 
743
        switch ( state ) {
 
744
        case Protocol:
 
745
            d->protocol += c;
 
746
            break;
 
747
        case User:
 
748
            d->user += c;
 
749
            break;
 
750
        case Pass:
 
751
            d->pass += c;
 
752
            break;
 
753
        case Host:
 
754
            d->host += c;
 
755
            break;
 
756
        case Path:
 
757
            d->path += c;
 
758
            break;
 
759
        case Ref:
 
760
            d->refEncoded += c;
 
761
            break;
 
762
        case Query:
 
763
            d->queryEncoded += c;
 
764
            break;
 
765
        case Port:
 
766
            port += c;
 
767
            break;
 
768
        default:
 
769
            break;
 
770
        }
 
771
 
 
772
        ++i;
 
773
        if ( i > (int)url_.length() - 1 || state == Done || state == 0 )
 
774
            break;
 
775
        c = url_[ i ];
 
776
 
 
777
    }
 
778
 
 
779
    if ( !port.isEmpty() ) {
 
780
        port.remove( (uint)0, 1 );
 
781
        d->port = atoi( port.latin1() );
 
782
    }
 
783
 
 
784
    // error
 
785
    if ( i < (int)url_.length() - 1 ) {
 
786
        d->isValid = false;
 
787
        return false;
 
788
    }
 
789
 
 
790
 
 
791
    if ( d->protocol.isEmpty() )
 
792
        d->protocol = oldProtocol;
 
793
 
 
794
    if ( d->path.isEmpty() )
 
795
        d->path = "/";
 
796
 
 
797
    // hack for windows
 
798
    if ( d->path.length() == 2 && d->path[ 1 ] == ':' )
 
799
        d->path += "/";
 
800
 
 
801
    // #### do some corrections, should be done nicer too
 
802
    if ( !d->pass.isEmpty() ) {
 
803
        if ( d->pass[ 0 ] == ':' )
 
804
            d->pass.remove( (uint)0, 1 );
 
805
        decode( d->pass );
 
806
    }
 
807
    if ( !d->user.isEmpty() ) {
 
808
        decode( d->user );
 
809
    }
 
810
    if ( !d->path.isEmpty() ) {
 
811
        if ( d->path[ 0 ] == '@' || d->path[ 0 ] == ':' )
 
812
            d->path.remove( (uint)0, 1 );
 
813
        if ( d->path[ 0 ] != '/' && !relPath && d->path[ 1 ] != ':' )
 
814
            d->path.prepend( "/" );
 
815
    }
 
816
    if ( !d->refEncoded.isEmpty() && d->refEncoded[ 0 ] == '#' )
 
817
        d->refEncoded.remove( (uint)0, 1 );
 
818
    if ( !d->queryEncoded.isEmpty() && d->queryEncoded[ 0 ] == '?' )
 
819
        d->queryEncoded.remove( (uint)0, 1 );
 
820
    if ( !d->host.isEmpty() && d->host[ 0 ] == '@' )
 
821
        d->host.remove( (uint)0, 1 );
 
822
 
 
823
#if defined(Q_OS_WIN32)
 
824
    // hack for windows file://machine/path syntax
 
825
    if ( d->protocol == "file" ) {
 
826
        if ( url.left( 7 ) == "file://" &&
 
827
             d->path.length() > 1 && d->path[ 1 ] != ':' )
 
828
                 d->path.prepend( "/" );
 
829
    }
 
830
#endif
 
831
 
 
832
    decode( d->path );
 
833
    d->cleanPathDirty = true;
 
834
 
 
835
#if 0
 
836
    qDebug( "URL: %s", url.latin1() );
 
837
    qDebug( "protocol: %s", d->protocol.latin1() );
 
838
    qDebug( "user: %s", d->user.latin1() );
 
839
    qDebug( "pass: %s", d->pass.latin1() );
 
840
    qDebug( "host: %s", d->host.latin1() );
 
841
    qDebug( "path: %s", path().latin1() );
 
842
    qDebug( "ref: %s", d->refEncoded.latin1() );
 
843
    qDebug( "query: %s", d->queryEncoded.latin1() );
 
844
    qDebug( "port: %d\n\n----------------------------\n\n", d->port );
 
845
#endif
 
846
 
 
847
    return true;
 
848
}
 
849
 
 
850
/*!
 
851
    \overload
 
852
 
 
853
    Parses \a url and assigns the resulting data to this class.
 
854
 
 
855
    If you pass a string like "/home/qt" the "file" protocol will be
 
856
    assumed.
 
857
*/
 
858
 
 
859
Q3Url& Q3Url::operator=( const QString& url )
 
860
{
 
861
    reset();
 
862
    parse( url );
 
863
 
 
864
    return *this;
 
865
}
 
866
 
 
867
/*!
 
868
    Assigns the data of \a url to this class.
 
869
*/
 
870
 
 
871
Q3Url& Q3Url::operator=( const Q3Url& url )
 
872
{
 
873
    *d = *url.d;
 
874
    return *this;
 
875
}
 
876
 
 
877
/*!
 
878
    Compares this URL with \a url and returns true if they are equal;
 
879
    otherwise returns false.
 
880
*/
 
881
 
 
882
bool Q3Url::operator==( const Q3Url& url ) const
 
883
{
 
884
    if ( !isValid() || !url.isValid() )
 
885
        return false;
 
886
 
 
887
    if ( d->protocol == url.d->protocol &&
 
888
         d->user == url.d->user &&
 
889
         d->pass == url.d->pass &&
 
890
         d->host == url.d->host &&
 
891
         d->path == url.d->path &&
 
892
         d->queryEncoded == url.d->queryEncoded &&
 
893
         d->refEncoded == url.d->refEncoded &&
 
894
         d->isValid == url.d->isValid &&
 
895
         d->port == url.d->port )
 
896
        return true;
 
897
 
 
898
    return false;
 
899
}
 
900
 
 
901
/*!
 
902
    \overload
 
903
 
 
904
    Compares this URL with \a url. \a url is parsed first. Returns
 
905
    true if \a url is equal to this url; otherwise returns false.
 
906
*/
 
907
 
 
908
bool Q3Url::operator==( const QString& url ) const
 
909
{
 
910
    Q3Url u( url );
 
911
    return ( *this == u );
 
912
}
 
913
 
 
914
/*!
 
915
    Sets the file name of the URL to \a name. If this URL contains a
 
916
    fileName(), the original file name is replaced by \a name.
 
917
 
 
918
    See the documentation of fileName() for a more detailed discussion
 
919
    of what is handled as file name and what is handled as a directory
 
920
    path.
 
921
 
 
922
    \sa fileName()
 
923
*/
 
924
 
 
925
void Q3Url::setFileName( const QString& name )
 
926
{
 
927
    QString fn( name );
 
928
    slashify( fn );
 
929
 
 
930
    while ( fn[ 0 ] == '/' )
 
931
        fn.remove( (uint)0, 1 );
 
932
 
 
933
    QString p;
 
934
    if ( path().isEmpty() ) {
 
935
        p = "/";
 
936
    } else {
 
937
        p = path();
 
938
        int slash = p.findRev( QChar( '/' ) );
 
939
        if ( slash == -1 ) {
 
940
            p = "/";
 
941
        } else if ( p[ (int)p.length() - 1 ] != '/' ) {
 
942
            p.truncate( slash + 1 );
 
943
        }
 
944
    }
 
945
 
 
946
    p += fn;
 
947
    if ( !d->queryEncoded.isEmpty() )
 
948
        p += "?" + d->queryEncoded;
 
949
    setEncodedPathAndQuery( p );
 
950
}
 
951
 
 
952
/*!
 
953
    Returns the encoded path and query.
 
954
 
 
955
    \sa decode()
 
956
*/
 
957
 
 
958
QString Q3Url::encodedPathAndQuery()
 
959
{
 
960
    QString p = path();
 
961
    if ( p.isEmpty() )
 
962
        p = "/";
 
963
 
 
964
    encode( p );
 
965
 
 
966
    if ( !d->queryEncoded.isEmpty() ) {
 
967
        p += "?";
 
968
        p += d->queryEncoded;
 
969
    }
 
970
 
 
971
    return p;
 
972
}
 
973
 
 
974
/*!
 
975
    Parses \a pathAndQuery for a path and query and sets those values.
 
976
    The whole string must be encoded.
 
977
 
 
978
    \sa encode()
 
979
*/
 
980
 
 
981
void Q3Url::setEncodedPathAndQuery( const QString& pathAndQuery )
 
982
{
 
983
    d->cleanPathDirty = true;
 
984
    int pos = pathAndQuery.find( '?' );
 
985
    if ( pos == -1 ) {
 
986
        d->path = pathAndQuery;
 
987
        d->queryEncoded = "";
 
988
    } else {
 
989
        d->path = pathAndQuery.left( pos );
 
990
        d->queryEncoded = pathAndQuery.mid( pos + 1 );
 
991
    }
 
992
 
 
993
    decode( d->path );
 
994
    d->cleanPathDirty = true;
 
995
}
 
996
 
 
997
/*!
 
998
    Returns the path of the URL. If \a correct is true, the path is
 
999
    cleaned (deals with too many or too few slashes, cleans things
 
1000
    like "/../..", etc). Otherwise path() returns exactly the path
 
1001
    that was parsed or set.
 
1002
 
 
1003
    \sa setPath() hasPath()
 
1004
*/
 
1005
QString Q3Url::path( bool correct ) const
 
1006
{
 
1007
    if ( !correct )
 
1008
        return d->path;
 
1009
 
 
1010
    if ( d->cleanPathDirty ) {
 
1011
        bool check = true;
 
1012
        if ( QDir::isRelativePath( d->path ) ) {
 
1013
            d->cleanPath = d->path;
 
1014
        } else if ( isLocalFile() ) {
 
1015
#if defined(Q_OS_WIN32)
 
1016
            // hack for stuff like \\machine\path and //machine/path on windows
 
1017
            if ( ( d->path.left( 1 ) == "/" || d->path.left( 1 ) == "\\" ) &&
 
1018
                 d->path.length() > 1 ) {
 
1019
                d->cleanPath = d->path;
 
1020
                bool share = (d->cleanPath[0] == '\\' && d->cleanPath[1] == '\\') ||
 
1021
                             (d->cleanPath[0] == '/' && d->cleanPath[1] == '/');
 
1022
                slashify( d->cleanPath, false );
 
1023
                d->cleanPath = QDir::cleanDirPath( d->cleanPath );
 
1024
                if ( share ) {
 
1025
                    check = false;
 
1026
                    while (d->cleanPath.at(0) != '/' || d->cleanPath.at(1) != '/')
 
1027
                        d->cleanPath.prepend("/");
 
1028
                }
 
1029
            }
 
1030
#endif
 
1031
            if ( check ) {
 
1032
                QFileInfo fi( d->path );
 
1033
                if ( !fi.exists() )
 
1034
                    d->cleanPath = d->path;
 
1035
                else if ( fi.isDir() ) {
 
1036
                    QString canPath = QDir( d->path ).canonicalPath();
 
1037
                    QString dir;
 
1038
                    if ( qt_resolve_symlinks && !canPath.isNull() )
 
1039
                       dir = QDir::cleanDirPath( canPath );
 
1040
                    else
 
1041
                       dir = QDir::cleanDirPath( QDir( d->path ).absPath() );
 
1042
                    dir += "/";
 
1043
                    if ( dir == "//" )
 
1044
                        d->cleanPath = "/";
 
1045
                    else
 
1046
                        d->cleanPath = dir;
 
1047
                } else {
 
1048
                    QString p =
 
1049
                        QDir::cleanDirPath( (qt_resolve_symlinks ?
 
1050
                                            fi.dir().canonicalPath() :
 
1051
                                            fi.dir().absPath()) );
 
1052
                    d->cleanPath = p + "/" + fi.fileName();
 
1053
                }
 
1054
            }
 
1055
        } else {
 
1056
            if ( d->path != "/" && d->path[ (int)d->path.length() - 1 ] == '/' )
 
1057
                d->cleanPath = QDir::cleanDirPath( d->path ) + "/";
 
1058
            else
 
1059
                d->cleanPath = QDir::cleanDirPath( d->path );
 
1060
        }
 
1061
 
 
1062
        if ( check )
 
1063
            slashify( d->cleanPath, false );
 
1064
        d->cleanPathDirty = false;
 
1065
    }
 
1066
 
 
1067
    return d->cleanPath;
 
1068
}
 
1069
 
 
1070
/*!
 
1071
    Returns true if the URL is a local file; otherwise returns false.
 
1072
*/
 
1073
 
 
1074
bool Q3Url::isLocalFile() const
 
1075
{
 
1076
    return d->protocol == "file";
 
1077
}
 
1078
 
 
1079
/*!
 
1080
    Returns the file name of the URL. If the path of the URL doesn't
 
1081
    have a slash at the end, the part between the last slash and the
 
1082
    end of the path string is considered to be the file name. If the
 
1083
    path has a slash at the end, an empty string is returned here.
 
1084
 
 
1085
    \sa setFileName()
 
1086
*/
 
1087
 
 
1088
QString Q3Url::fileName() const
 
1089
{
 
1090
    if ( d->path.isEmpty() || d->path.endsWith( "/" )
 
1091
#ifdef Q_WS_WIN
 
1092
        || d->path.endsWith( "\\" )
 
1093
#endif
 
1094
        )
 
1095
        return QString();
 
1096
 
 
1097
    return QFileInfo( d->path ).fileName();
 
1098
}
 
1099
 
 
1100
/*!
 
1101
    Adds the path \a pa to the path of the URL.
 
1102
 
 
1103
    \sa setPath() hasPath()
 
1104
*/
 
1105
 
 
1106
void Q3Url::addPath( const QString& pa )
 
1107
{
 
1108
    if ( pa.isEmpty() )
 
1109
        return;
 
1110
 
 
1111
    QString p( pa );
 
1112
    slashify( p );
 
1113
 
 
1114
    if ( path().isEmpty() ) {
 
1115
        if ( p[ 0 ] != QChar( '/' ) )
 
1116
            d->path = "/" + p;
 
1117
        else
 
1118
            d->path = p;
 
1119
    } else {
 
1120
        if ( p[ 0 ] != QChar( '/' ) && d->path[ (int)d->path.length() - 1 ] != '/' )
 
1121
            d->path += "/" + p;
 
1122
        else
 
1123
            d->path += p;
 
1124
    }
 
1125
    d->cleanPathDirty = true;
 
1126
}
 
1127
 
 
1128
/*!
 
1129
    Returns the directory path of the URL. This is the part of the
 
1130
    path of the URL without the fileName(). See the documentation of
 
1131
    fileName() for a discussion of what is handled as file name and
 
1132
    what is handled as directory path.
 
1133
 
 
1134
    \sa setPath() hasPath()
 
1135
*/
 
1136
 
 
1137
QString Q3Url::dirPath() const
 
1138
{
 
1139
    if ( path().isEmpty() )
 
1140
        return QString();
 
1141
 
 
1142
    QString s = path();
 
1143
    int pos = s.findRev( '/' );
 
1144
    if ( pos == -1 ) {
 
1145
        return QString::fromLatin1(".");
 
1146
    } else {
 
1147
        if ( pos == 0 )
 
1148
            return QString::fromLatin1( "/" );
 
1149
        return s.left( pos );
 
1150
    }
 
1151
}
 
1152
 
 
1153
/*!
 
1154
    Encodes the \a url in-place into UTF-8.  For example
 
1155
 
 
1156
    \code
 
1157
        QString url = http://www.trolltech.com
 
1158
        Q3Url::encode( url );
 
1159
        // url is now "http%3A//www%20trolltech%20com"
 
1160
    \endcode
 
1161
 
 
1162
  \sa decode()
 
1163
*/
 
1164
 
 
1165
void Q3Url::encode( QString& url )
 
1166
{
 
1167
    if ( url.isEmpty() )
 
1168
        return;
 
1169
 
 
1170
    Q3CString curl = url.utf8();
 
1171
    int oldlen = curl.length();
 
1172
 
 
1173
    const Q3CString special( "+<>#@\"&%$:,;?={}|^~[]\'`\\ \n\t\r" );
 
1174
    QString newUrl;
 
1175
    int newlen = 0;
 
1176
 
 
1177
    for ( int i = 0; i < oldlen ;++i ) {
 
1178
        uchar inCh = (uchar)curl[ i ];
 
1179
 
 
1180
        if ( inCh >= 128 || special.contains(inCh) ) {
 
1181
            newUrl[ newlen++ ] = QChar( '%' );
 
1182
 
 
1183
            ushort c = inCh / 16;
 
1184
            c += c > 9 ? 'A' - 10 : '0';
 
1185
            newUrl[ newlen++ ] = c;
 
1186
 
 
1187
            c = inCh % 16;
 
1188
            c += c > 9 ? 'A' - 10 : '0';
 
1189
            newUrl[ newlen++ ] = c;
 
1190
        } else {
 
1191
            newUrl[ newlen++ ] = inCh;
 
1192
        }
 
1193
    }
 
1194
 
 
1195
    url = newUrl;
 
1196
}
 
1197
 
 
1198
static uchar hex_to_int( uchar c )
 
1199
{
 
1200
    if ( c >= 'A' && c <= 'F' )
 
1201
        return c - 'A' + 10;
 
1202
    if ( c >= 'a' && c <= 'f')
 
1203
        return c - 'a' + 10;
 
1204
    if ( c >= '0' && c <= '9')
 
1205
        return c - '0';
 
1206
    return 0;
 
1207
}
 
1208
 
 
1209
/*!
 
1210
    Decodes the \a url in-place into UTF-8.  For example
 
1211
 
 
1212
    \code
 
1213
        QString url = "http%3A//www%20trolltech%20com"
 
1214
        Q3Url::decode( url );
 
1215
        // url is now "http://www.trolltech.com"
 
1216
    \endcode
 
1217
 
 
1218
    \sa encode()
 
1219
*/
 
1220
 
 
1221
void Q3Url::decode( QString& url )
 
1222
{
 
1223
    if ( url.isEmpty() )
 
1224
        return;
 
1225
 
 
1226
    int newlen = 0;
 
1227
    Q3CString curl = url.utf8();
 
1228
    int oldlen = curl.length();
 
1229
 
 
1230
    Q3CString newUrl(oldlen);
 
1231
 
 
1232
    int i = 0;
 
1233
    while ( i < oldlen ) {
 
1234
        uchar c = (uchar)curl[ i++ ];
 
1235
        if ( c == '%' && i <= oldlen - 2 ) {
 
1236
            c = hex_to_int( (uchar)curl[ i ] ) * 16 + hex_to_int( (uchar)curl[ i + 1 ] );
 
1237
            i += 2;
 
1238
        }
 
1239
        newUrl [ newlen++ ] = c;
 
1240
    }
 
1241
    newUrl.truncate( newlen );
 
1242
 
 
1243
    url = QString::fromUtf8(newUrl.data());
 
1244
}
 
1245
 
 
1246
 
 
1247
/*!
 
1248
    Composes a string version of the URL and returns it. If \a
 
1249
    encodedPath is true the path in the returned string is encoded. If
 
1250
    \a forcePrependProtocol is true and \a encodedPath looks like a
 
1251
    local filename, the "file:/" protocol is also prepended.
 
1252
 
 
1253
    \sa encode() decode()
 
1254
*/
 
1255
 
 
1256
QString Q3Url::toString( bool encodedPath, bool forcePrependProtocol ) const
 
1257
{
 
1258
    QString res, p = path();
 
1259
    if ( encodedPath )
 
1260
        encode( p );
 
1261
 
 
1262
    if ( isLocalFile() ) {
 
1263
        if ( forcePrependProtocol )
 
1264
            res = d->protocol + ":" + p;
 
1265
        else
 
1266
            res = p;
 
1267
    } else if ( d->protocol == "mailto" ) {
 
1268
        res = d->protocol + ":" + p;
 
1269
    } else {
 
1270
        res = d->protocol + "://";
 
1271
        if ( !d->user.isEmpty() || !d->pass.isEmpty() ) {
 
1272
            QString tmp;
 
1273
            if ( !d->user.isEmpty() ) {
 
1274
                tmp = d->user;
 
1275
                encode( tmp );
 
1276
                res += tmp;
 
1277
            }
 
1278
            if ( !d->pass.isEmpty() ) {
 
1279
                tmp = d->pass;
 
1280
                encode( tmp );
 
1281
                res += ":" + tmp;
 
1282
            }
 
1283
            res += "@";
 
1284
        }
 
1285
        res += d->host;
 
1286
        if ( d->port != -1 )
 
1287
            res += ":" + QString( "%1" ).arg( d->port );
 
1288
        if ( !p.isEmpty() ) {
 
1289
            if ( !d->host.isEmpty() && p[0]!='/' )
 
1290
                res += "/";
 
1291
            res += p;
 
1292
        }
 
1293
    }
 
1294
 
 
1295
    if ( !d->refEncoded.isEmpty() )
 
1296
        res += "#" + d->refEncoded;
 
1297
    if ( !d->queryEncoded.isEmpty() )
 
1298
        res += "?" + d->queryEncoded;
 
1299
 
 
1300
    return res;
 
1301
}
 
1302
 
 
1303
/*!
 
1304
    Composes a string version of the URL and returns it.
 
1305
 
 
1306
    \sa Q3Url::toString()
 
1307
*/
 
1308
 
 
1309
Q3Url::operator QString() const
 
1310
{
 
1311
    return toString();
 
1312
}
 
1313
 
 
1314
/*!
 
1315
    Changes the directory to one directory up.
 
1316
 
 
1317
    \sa setPath()
 
1318
*/
 
1319
 
 
1320
bool Q3Url::cdUp()
 
1321
{
 
1322
    d->path += "/..";
 
1323
    d->cleanPathDirty = true;
 
1324
    return true;
 
1325
}
 
1326
 
 
1327
#endif // QT_NO_URL