~ubuntu-branches/ubuntu/oneiric/kdelibs/oneiric

« back to all changes in this revision

Viewing changes to .pc/security_05_XMLHttpRequest_vulnerability.diff/khtml/ecma/xmlhttprequest.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2010-10-15 19:20:42 UTC
  • mfrom: (1.1.19 sid)
  • Revision ID: james.westby@ubuntu.com-20101015192042-jv1fhj33n4f5b398
Tags: 4:3.5.10.dfsg.1-5ubuntu1
* Merge from Debian unstable. Remaining Ubuntu changes:
  - make sure control and control.in are in sync
  - --with-distribution="Kubuntu (`lsb_release --codename --short`)
    $(DEB_VERSION)"
  - binary-install/kdelibs-data installs aboutkde-kubuntu.png.uu and
    cr*-device-system.png.uu
  - don't build-dep on libgamin-dev, libfam-dev
  - stop kdelibs4-dev depending on gamin/fam
  - don't install .svgz icons, docs or all_languages in kdelibs-data.install
  - rosetta support in rules common-install-prehook-impl:: [and
    common-post-build-arch:: ?] and include debian/kubuntu-desktop-i18n/
  - build-dep on: gettext-kde, kdesdk-scripts, lsb-release, base-files, sudo
  - cdbs build-dep 0.4.41ubuntu2
  - kdelibs4-dev depends on gettext-kde, kdesdk-scripts
  - copy debian/icons over
  - Make kdelibs4c2a depend on launchpad-integration, sudo.  Recommends on
    xdg-user-dirs
  - Remove 19_debianize_useragent.diff (changed to
    kubuntu_19_debianize_useragent.diff) s/Debian/Kubuntu
  - remove kdelibs4c2a depends on menu-xdg
  - include kubuntu_01_kdepot.diff and kde.pot in debian/patches/common
  - use a local copy of kde.mk without the common-install-prehook-impl::
    rule; edit debian-qt-kde.mk to include debian/cdbs/kde.mk
  - build with --with-sudo-kdesu-backend and build-dep on sudo and make
    kdelibs4c2a depend on sudo
  - kdelibs-data.install : Add nzb mimetype
  - Make kdelibs4-dev replace more recent kdelibs4c2a for overlapping files
  - remove /usr/bin/preparetips, arts files and ksvntopng from
    kdelibs4-dev.install
  - Drop the package kdelibs4-doc completely. It contained API documentation
    which is now obsolete, but still available via api.kde.org.
  - make sure control and control.in are in sync
  - in debian/rule remove .pot files outside .po directory
  - 97_automake_cleanup.diff becomes kubuntu_97_automake_cleanup.diff
  - Remove libarts1-dev from build-depends and kdelibs4-dev depends from
    control.in
* Drop kubuntu_98_fix_khc_invocation.diff, replaced by
  68_support_khelpcenter4.diff
* Drop kubuntu_97_automake_cleanup.diff, replaced by 97_automake_cleanup.diff
* Re-add security_05_XMLHttpRequest_vulnerability.diff which has been
  accidentally dropped
* Fix FTBFS, in debian/rules:
  - Add -Wl,--add-needed to LDFLAGS
  - Disable parallel building

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- c-basic-offset: 2 -*-
 
2
/*
 
3
 *  This file is part of the KDE libraries
 
4
 *  Copyright (C) 2003 Apple Computer, Inc.
 
5
 *
 
6
 *  This library is free software; you can redistribute it and/or
 
7
 *  modify it under the terms of the GNU Lesser General Public
 
8
 *  License as published by the Free Software Foundation; either
 
9
 *  version 2 of the License, or (at your option) any later version.
 
10
 *
 
11
 *  This library is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 *  Lesser General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU Lesser General Public
 
17
 *  License along with this library; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 */
 
20
 
 
21
#include "xmlhttprequest.h"
 
22
#include "xmlhttprequest.lut.h"
 
23
#include "kjs_window.h"
 
24
#include "kjs_events.h"
 
25
 
 
26
#include "dom/dom_doc.h"
 
27
#include "dom/dom_exception.h"
 
28
#include "dom/dom_string.h"
 
29
#include "misc/loader.h"
 
30
#include "html/html_documentimpl.h"
 
31
#include "xml/dom2_eventsimpl.h"
 
32
 
 
33
#include "khtml_part.h"
 
34
#include "khtmlview.h"
 
35
 
 
36
#include <kio/scheduler.h>
 
37
#include <kio/job.h>
 
38
#include <qobject.h>
 
39
#include <kdebug.h>
 
40
 
 
41
#ifdef APPLE_CHANGES
 
42
#include "KWQLoader.h"
 
43
#else
 
44
#include <kio/netaccess.h>
 
45
using KIO::NetAccess;
 
46
#endif
 
47
 
 
48
#define BANNED_HTTP_HEADERS "authorization,proxy-authorization,"\
 
49
                            "content-length,host,connect,copy,move,"\
 
50
                            "delete,head,trace,put,propfind,proppatch,"\
 
51
                            "mkcol,lock,unlock,options,via,"\
 
52
                            "accept-charset,accept-encoding,expect,date,"\
 
53
                            "keep-alive,te,trailer,"\
 
54
                            "transfer-encoding,upgrade"
 
55
 
 
56
using khtml::Decoder;
 
57
 
 
58
namespace KJS {
 
59
 
 
60
////////////////////// XMLHttpRequest Object ////////////////////////
 
61
 
 
62
/* Source for XMLHttpRequestProtoTable.
 
63
@begin XMLHttpRequestProtoTable 7
 
64
  abort                 XMLHttpRequest::Abort                   DontDelete|Function 0
 
65
  getAllResponseHeaders XMLHttpRequest::GetAllResponseHeaders   DontDelete|Function 0
 
66
  getResponseHeader     XMLHttpRequest::GetResponseHeader       DontDelete|Function 1
 
67
  open                  XMLHttpRequest::Open                    DontDelete|Function 5
 
68
  overrideMimeType      XMLHttpRequest::OverrideMIMEType        DontDelete|Function 1
 
69
  send                  XMLHttpRequest::Send                    DontDelete|Function 1
 
70
  setRequestHeader      XMLHttpRequest::SetRequestHeader        DontDelete|Function 2
 
71
@end
 
72
*/
 
73
KJS_DEFINE_PROTOTYPE(XMLHttpRequestProto)
 
74
IMPLEMENT_PROTOFUNC_DOM(XMLHttpRequestProtoFunc)
 
75
KJS_IMPLEMENT_PROTOTYPE("XMLHttpRequest", XMLHttpRequestProto,XMLHttpRequestProtoFunc)
 
76
 
 
77
 
 
78
XMLHttpRequestQObject::XMLHttpRequestQObject(XMLHttpRequest *_jsObject)
 
79
{
 
80
  jsObject = _jsObject;
 
81
}
 
82
 
 
83
#ifdef APPLE_CHANGES
 
84
void XMLHttpRequestQObject::slotData( KIO::Job* job, const char *data, int size )
 
85
{
 
86
  jsObject->slotData(job, data, size);
 
87
}
 
88
#else
 
89
void XMLHttpRequestQObject::slotData( KIO::Job* job, const QByteArray &data )
 
90
{
 
91
  jsObject->slotData(job, data);
 
92
}
 
93
#endif
 
94
 
 
95
void XMLHttpRequestQObject::slotFinished( KIO::Job* job )
 
96
{
 
97
  jsObject->slotFinished(job);
 
98
}
 
99
 
 
100
void XMLHttpRequestQObject::slotRedirection( KIO::Job* job, const KURL& url)
 
101
{
 
102
  jsObject->slotRedirection( job, url );
 
103
}
 
104
 
 
105
XMLHttpRequestConstructorImp::XMLHttpRequestConstructorImp(ExecState *, const DOM::Document &d)
 
106
    : ObjectImp(), doc(d)
 
107
{
 
108
}
 
109
 
 
110
bool XMLHttpRequestConstructorImp::implementsConstruct() const
 
111
{
 
112
  return true;
 
113
}
 
114
 
 
115
Object XMLHttpRequestConstructorImp::construct(ExecState *exec, const List &)
 
116
{
 
117
  return Object(new XMLHttpRequest(exec, doc));
 
118
}
 
119
 
 
120
const ClassInfo XMLHttpRequest::info = { "XMLHttpRequest", 0, &XMLHttpRequestTable, 0 };
 
121
 
 
122
 
 
123
/* Source for XMLHttpRequestTable.
 
124
@begin XMLHttpRequestTable 7
 
125
  readyState            XMLHttpRequest::ReadyState              DontDelete|ReadOnly
 
126
  responseText          XMLHttpRequest::ResponseText            DontDelete|ReadOnly
 
127
  responseXML           XMLHttpRequest::ResponseXML             DontDelete|ReadOnly
 
128
  status                XMLHttpRequest::Status                  DontDelete|ReadOnly
 
129
  statusText            XMLHttpRequest::StatusText              DontDelete|ReadOnly
 
130
  onreadystatechange    XMLHttpRequest::Onreadystatechange      DontDelete
 
131
  onload                XMLHttpRequest::Onload                  DontDelete
 
132
@end
 
133
*/
 
134
 
 
135
Value XMLHttpRequest::tryGet(ExecState *exec, const Identifier &propertyName) const
 
136
{
 
137
  return DOMObjectLookupGetValue<XMLHttpRequest,DOMObject>(exec, propertyName, &XMLHttpRequestTable, this);
 
138
}
 
139
 
 
140
Value XMLHttpRequest::getValueProperty(ExecState *exec, int token) const
 
141
{
 
142
  switch (token) {
 
143
  case ReadyState:
 
144
    return Number(state);
 
145
  case ResponseText:
 
146
    return getString(DOM::DOMString(response));
 
147
  case ResponseXML:
 
148
    if (state != Completed) {
 
149
      return Null();
 
150
    }
 
151
    if (!createdDocument) {
 
152
      QString mimeType = "text/xml";
 
153
 
 
154
      if (!m_mimeTypeOverride.isEmpty()) {
 
155
        mimeType = m_mimeTypeOverride;
 
156
      } else {
 
157
          Value header = getResponseHeader("Content-Type");
 
158
          if (header.type() != UndefinedType) {
 
159
            mimeType = QStringList::split(";", header.toString(exec).qstring())[0].stripWhiteSpace();
 
160
          }
 
161
      }
 
162
 
 
163
      if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "application/xhtml+xml") {
 
164
        responseXML = DOM::Document(doc->implementation()->createDocument());
 
165
 
 
166
        DOM::DocumentImpl *docImpl = static_cast<DOM::DocumentImpl *>(responseXML.handle());
 
167
 
 
168
        docImpl->open();
 
169
        docImpl->write(response);
 
170
        docImpl->finishParsing();
 
171
        docImpl->close();
 
172
 
 
173
        typeIsXML = true;
 
174
      } else {
 
175
        typeIsXML = false;
 
176
      }
 
177
      createdDocument = true;
 
178
    }
 
179
 
 
180
    if (!typeIsXML) {
 
181
      return Undefined();
 
182
    }
 
183
 
 
184
    return getDOMNode(exec,responseXML);
 
185
  case Status:
 
186
    return getStatus();
 
187
  case StatusText:
 
188
    return getStatusText();
 
189
  case Onreadystatechange:
 
190
   if (onReadyStateChangeListener && onReadyStateChangeListener->listenerObjImp()) {
 
191
     return onReadyStateChangeListener->listenerObj();
 
192
   } else {
 
193
     return Null();
 
194
   }
 
195
  case Onload:
 
196
   if (onLoadListener && onLoadListener->listenerObjImp()) {
 
197
     return onLoadListener->listenerObj();
 
198
   } else {
 
199
    return Null();
 
200
   }
 
201
  default:
 
202
    kdWarning() << "XMLHttpRequest::getValueProperty unhandled token " << token << endl;
 
203
    return Value();
 
204
  }
 
205
}
 
206
 
 
207
void XMLHttpRequest::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr)
 
208
{
 
209
  DOMObjectLookupPut<XMLHttpRequest,DOMObject>(exec, propertyName, value, attr, &XMLHttpRequestTable, this );
 
210
}
 
211
 
 
212
void XMLHttpRequest::putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/)
 
213
{
 
214
  JSEventListener* newListener;
 
215
  switch(token) {
 
216
  case Onreadystatechange:
 
217
    newListener = Window::retrieveActive(exec)->getJSEventListener(value, true);
 
218
    if (newListener != onReadyStateChangeListener) {
 
219
      if (onReadyStateChangeListener) onReadyStateChangeListener->deref();
 
220
      onReadyStateChangeListener = newListener;
 
221
      if (onReadyStateChangeListener) onReadyStateChangeListener->ref();
 
222
    }
 
223
    break;
 
224
  case Onload:
 
225
    newListener = Window::retrieveActive(exec)->getJSEventListener(value, true);
 
226
    if (newListener != onLoadListener) {
 
227
      if (onLoadListener) onLoadListener->deref();
 
228
      onLoadListener = newListener;
 
229
      if (onLoadListener) onLoadListener->ref();
 
230
    }
 
231
    break;
 
232
  default:
 
233
    kdWarning() << "XMLHttpRequest::putValue unhandled token " << token << endl;
 
234
  }
 
235
}
 
236
 
 
237
XMLHttpRequest::XMLHttpRequest(ExecState *exec, const DOM::Document &d)
 
238
  : DOMObject(XMLHttpRequestProto::self(exec)),
 
239
    qObject(new XMLHttpRequestQObject(this)),
 
240
    doc(static_cast<DOM::DocumentImpl*>(d.handle())),
 
241
    async(true),
 
242
    contentType(QString::null),
 
243
    job(0),
 
244
    state(Uninitialized),
 
245
    onReadyStateChangeListener(0),
 
246
    onLoadListener(0),
 
247
    decoder(0),
 
248
    createdDocument(false),
 
249
    aborted(false)
 
250
{
 
251
}
 
252
 
 
253
XMLHttpRequest::~XMLHttpRequest()
 
254
{
 
255
  if (onReadyStateChangeListener)
 
256
    onReadyStateChangeListener->deref();
 
257
  if (onLoadListener)
 
258
    onLoadListener->deref();
 
259
  delete qObject;
 
260
  qObject = 0;
 
261
  delete decoder;
 
262
  decoder = 0;
 
263
}
 
264
 
 
265
void XMLHttpRequest::changeState(XMLHttpRequestState newState)
 
266
{
 
267
  if (state != newState) {
 
268
    state = newState;
 
269
 
 
270
    ref();
 
271
 
 
272
    if (onReadyStateChangeListener != 0 && doc->view() && doc->view()->part()) {
 
273
      DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents");
 
274
      ev.initEvent("readystatechange", true, true);
 
275
      onReadyStateChangeListener->handleEvent(ev);
 
276
    }
 
277
 
 
278
    if (state == Completed && onLoadListener != 0 && doc->view() && doc->view()->part()) {
 
279
      DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents");
 
280
      ev.initEvent("load", true, true);
 
281
      onLoadListener->handleEvent(ev);
 
282
    }
 
283
 
 
284
    deref();
 
285
  }
 
286
}
 
287
 
 
288
bool XMLHttpRequest::urlMatchesDocumentDomain(const KURL& _url) const
 
289
{
 
290
  // No need to do work if _url is not valid...
 
291
  if (!_url.isValid())
 
292
    return false;
 
293
 
 
294
  KURL documentURL(doc->URL());
 
295
 
 
296
  // a local file can load anything
 
297
  if (documentURL.protocol().lower() == "file") {
 
298
    return true;
 
299
  }
 
300
 
 
301
  // but a remote document can only load from the same port on the server
 
302
  if (documentURL.protocol().lower() == _url.protocol().lower() &&
 
303
      documentURL.host().lower() == _url.host().lower() &&
 
304
      documentURL.port() == _url.port()) {
 
305
    return true;
 
306
  }
 
307
 
 
308
  return false;
 
309
}
 
310
 
 
311
void XMLHttpRequest::open(const QString& _method, const KURL& _url, bool _async)
 
312
{
 
313
  abort();
 
314
  aborted = false;
 
315
 
 
316
  // clear stuff from possible previous load
 
317
  requestHeaders.clear();
 
318
  responseHeaders = QString();
 
319
  response = QString();
 
320
  createdDocument = false;
 
321
  responseXML = DOM::Document();
 
322
 
 
323
  changeState(Uninitialized);
 
324
 
 
325
  if (aborted) {
 
326
    return;
 
327
  }
 
328
 
 
329
  if (!urlMatchesDocumentDomain(_url)) {
 
330
    return;
 
331
  }
 
332
 
 
333
 
 
334
  method = _method.lower();
 
335
  url = _url;
 
336
  async = _async;
 
337
 
 
338
  changeState(Loading);
 
339
}
 
340
 
 
341
void XMLHttpRequest::send(const QString& _body)
 
342
{
 
343
  aborted = false;
 
344
 
 
345
  if (method == "post") {
 
346
    QString protocol = url.protocol().lower();
 
347
 
 
348
    // Abondon the request when the protocol is other than "http",
 
349
    // instead of blindly changing it to a "get" request.
 
350
    if (!protocol.startsWith("http") && !protocol.startsWith("webdav"))
 
351
    {
 
352
      abort();
 
353
      return;
 
354
    }
 
355
 
 
356
    // FIXME: determine post encoding correctly by looking in headers
 
357
    // for charset.
 
358
    QByteArray buf;
 
359
    QCString str = _body.utf8();
 
360
    buf.duplicate(str.data(), str.size() - 1);
 
361
 
 
362
    job = KIO::http_post( url, buf, false );
 
363
    if(contentType.isNull())
 
364
      job->addMetaData( "content-type", "Content-type: text/plain" );
 
365
    else
 
366
      job->addMetaData( "content-type", contentType );
 
367
  }
 
368
  else {
 
369
    job = KIO::get( url, false, false );
 
370
  }
 
371
 
 
372
  if (!requestHeaders.isEmpty()) {
 
373
    QString rh;
 
374
    QMap<QString, QString>::ConstIterator begin = requestHeaders.begin();
 
375
    QMap<QString, QString>::ConstIterator end = requestHeaders.end();
 
376
    for (QMap<QString, QString>::ConstIterator i = begin; i != end; ++i) {
 
377
      QString key = i.key();
 
378
      QString value = i.data();
 
379
      if (key == "accept") {
 
380
        // The HTTP KIO slave supports an override this way
 
381
        job->addMetaData("accept", value);
 
382
      } else {
 
383
        if (i != begin)
 
384
          rh += "\r\n";
 
385
        rh += key + ": " + value;
 
386
      }
 
387
    }
 
388
 
 
389
    job->addMetaData("customHTTPHeader", rh);
 
390
  }
 
391
 
 
392
  job->addMetaData("PropagateHttpHeader", "true");
 
393
 
 
394
  // Set the default referrer if one is not already supplied
 
395
  // through setRequestHeader. NOTE: the user can still disable
 
396
  // this feature at the protocol level (kio_http).
 
397
  // ### does find() ever succeed? the headers are stored in lower case!
 
398
  if (requestHeaders.find("Referer") == requestHeaders.end()) {
 
399
    KURL documentURL(doc->URL());
 
400
    documentURL.setPass(QString::null);
 
401
    documentURL.setUser(QString::null);
 
402
    job->addMetaData("referrer", documentURL.url());
 
403
    // kdDebug() << "Adding referrer: " << documentURL << endl;
 
404
  }
 
405
 
 
406
  if (!async) {
 
407
    QByteArray data;
 
408
    KURL finalURL;
 
409
    QString headers;
 
410
 
 
411
#ifdef APPLE_CHANGES
 
412
    data = KWQServeSynchronousRequest(khtml::Cache::loader(), doc->docLoader(), job, finalURL, headers);
 
413
#else
 
414
    QMap<QString, QString> metaData;
 
415
    if ( NetAccess::synchronousRun( job, 0, &data, &finalURL, &metaData ) ) {
 
416
      headers = metaData[ "HTTP-Headers" ];
 
417
    }
 
418
#endif
 
419
    job = 0;
 
420
    processSyncLoadResults(data, finalURL, headers);
 
421
    return;
 
422
  }
 
423
 
 
424
  qObject->connect( job, SIGNAL( result( KIO::Job* ) ),
 
425
                    SLOT( slotFinished( KIO::Job* ) ) );
 
426
#ifdef APPLE_CHANGES
 
427
  qObject->connect( job, SIGNAL( data( KIO::Job*, const char*, int ) ),
 
428
                    SLOT( slotData( KIO::Job*, const char*, int ) ) );
 
429
#else
 
430
  qObject->connect( job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
 
431
                    SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
 
432
#endif
 
433
  qObject->connect( job, SIGNAL(redirection(KIO::Job*, const KURL& ) ),
 
434
                    SLOT( slotRedirection(KIO::Job*, const KURL&) ) );
 
435
 
 
436
#ifdef APPLE_CHANGES
 
437
  KWQServeRequest(khtml::Cache::loader(), doc->docLoader(), job);
 
438
#else
 
439
  KIO::Scheduler::scheduleJob( job );
 
440
#endif
 
441
}
 
442
 
 
443
void XMLHttpRequest::abort()
 
444
{
 
445
  if (job) {
 
446
    job->kill();
 
447
    job = 0;
 
448
  }
 
449
  delete decoder;
 
450
  decoder = 0;
 
451
  aborted = true;
 
452
}
 
453
 
 
454
void XMLHttpRequest::overrideMIMEType(const QString& override)
 
455
{
 
456
    m_mimeTypeOverride = override;
 
457
}
 
458
 
 
459
void XMLHttpRequest::setRequestHeader(const QString& _name, const QString &value)
 
460
{
 
461
  QString name = _name.lower().stripWhiteSpace();
 
462
 
 
463
  // Content-type needs to be set seperately from the other headers
 
464
  if(name == "content-type") {
 
465
    contentType = "Content-type: " + value;
 
466
    return;
 
467
  }
 
468
 
 
469
  // Sanitize the referrer header to protect against spoofing...
 
470
  if(name == "referer") {
 
471
    KURL referrerURL(value);
 
472
    if (urlMatchesDocumentDomain(referrerURL))
 
473
      requestHeaders[name] = referrerURL.url();
 
474
    return;
 
475
  }
 
476
 
 
477
  // Sanitize the request headers below and handle them as if they are
 
478
  // calls to open. Otherwise, we will end up ignoring them all together!
 
479
  // TODO: Do something about "put" which kio_http sort of supports and
 
480
  // the webDAV headers such as PROPFIND etc...
 
481
  if (name == "get"  || name == "post") {
 
482
    KURL reqURL (doc->URL(), value.stripWhiteSpace());
 
483
    open(name, reqURL, async);
 
484
    return;
 
485
  }
 
486
 
 
487
  // Reject all banned headers. See BANNED_HTTP_HEADERS above.
 
488
  // kdDebug() << "Banned HTTP Headers: " << BANNED_HTTP_HEADERS << endl;
 
489
  QStringList bannedHeaders = QStringList::split(',',
 
490
                                  QString::fromLatin1(BANNED_HTTP_HEADERS));
 
491
 
 
492
  if (bannedHeaders.contains(name))
 
493
    return;   // Denied
 
494
 
 
495
  requestHeaders[name] = value.stripWhiteSpace();
 
496
}
 
497
 
 
498
Value XMLHttpRequest::getAllResponseHeaders() const
 
499
{
 
500
  if (responseHeaders.isEmpty()) {
 
501
    return Undefined();
 
502
  }
 
503
 
 
504
  int endOfLine = responseHeaders.find("\n");
 
505
 
 
506
  if (endOfLine == -1) {
 
507
    return Undefined();
 
508
  }
 
509
 
 
510
  return String(responseHeaders.mid(endOfLine + 1) + "\n");
 
511
}
 
512
 
 
513
Value XMLHttpRequest::getResponseHeader(const QString& name) const
 
514
{
 
515
  if (responseHeaders.isEmpty()) {
 
516
    return Undefined();
 
517
  }
 
518
 
 
519
  QRegExp headerLinePattern(name + ":", false);
 
520
 
 
521
  int matchLength;
 
522
  int headerLinePos = headerLinePattern.search(responseHeaders, 0);
 
523
  matchLength = headerLinePattern.matchedLength();
 
524
  while (headerLinePos != -1) {
 
525
    if (headerLinePos == 0 || responseHeaders[headerLinePos-1] == '\n') {
 
526
      break;
 
527
    }
 
528
 
 
529
    headerLinePos = headerLinePattern.search(responseHeaders, headerLinePos + 1);
 
530
    matchLength = headerLinePattern.matchedLength();
 
531
  }
 
532
 
 
533
 
 
534
  if (headerLinePos == -1) {
 
535
    return Undefined();
 
536
  }
 
537
 
 
538
  int endOfLine = responseHeaders.find("\n", headerLinePos + matchLength);
 
539
 
 
540
  return String(responseHeaders.mid(headerLinePos + matchLength, endOfLine - (headerLinePos + matchLength)).stripWhiteSpace());
 
541
}
 
542
 
 
543
static Value httpStatus(const QString& response, bool textStatus = false)
 
544
{
 
545
  if (response.isEmpty()) {
 
546
    return Undefined();
 
547
  }
 
548
 
 
549
  int endOfLine = response.find("\n");
 
550
  QString firstLine = (endOfLine == -1) ? response : response.left(endOfLine);
 
551
  int codeStart = firstLine.find(" ");
 
552
  int codeEnd = firstLine.find(" ", codeStart + 1);
 
553
 
 
554
  if (codeStart == -1 || codeEnd == -1) {
 
555
    return Undefined();
 
556
  }
 
557
 
 
558
  if (textStatus) {
 
559
    QString statusText = firstLine.mid(codeEnd + 1, endOfLine - (codeEnd + 1)).stripWhiteSpace();
 
560
    return String(statusText);
 
561
  }
 
562
 
 
563
  QString number = firstLine.mid(codeStart + 1, codeEnd - (codeStart + 1));
 
564
 
 
565
  bool ok = false;
 
566
  int code = number.toInt(&ok);
 
567
  if (!ok) {
 
568
    return Undefined();
 
569
  }
 
570
 
 
571
  return Number(code);
 
572
}
 
573
 
 
574
Value XMLHttpRequest::getStatus() const
 
575
{
 
576
  return httpStatus(responseHeaders);
 
577
}
 
578
 
 
579
Value XMLHttpRequest::getStatusText() const
 
580
{
 
581
  return httpStatus(responseHeaders, true);
 
582
}
 
583
 
 
584
void XMLHttpRequest::processSyncLoadResults(const QByteArray &data, const KURL &finalURL, const QString &headers)
 
585
{
 
586
  if (!urlMatchesDocumentDomain(finalURL)) {
 
587
    abort();
 
588
    return;
 
589
  }
 
590
 
 
591
  responseHeaders = headers;
 
592
  changeState(Loaded);
 
593
  if (aborted) {
 
594
    return;
 
595
  }
 
596
 
 
597
#ifdef APPLE_CHANGES
 
598
  const char *bytes = (const char *)data.data();
 
599
  int len = (int)data.size();
 
600
 
 
601
  slotData(0, bytes, len);
 
602
#else
 
603
  slotData(0, data);
 
604
#endif
 
605
 
 
606
  if (aborted) {
 
607
    return;
 
608
  }
 
609
 
 
610
  slotFinished(0);
 
611
}
 
612
 
 
613
void XMLHttpRequest::slotFinished(KIO::Job *)
 
614
{
 
615
  if (decoder) {
 
616
    response += decoder->flush();
 
617
  }
 
618
 
 
619
  // make sure to forget about the job before emitting completed,
 
620
  // since changeState triggers JS code, which might e.g. call abort.
 
621
  job = 0;
 
622
  changeState(Completed);
 
623
 
 
624
  delete decoder;
 
625
  decoder = 0;
 
626
}
 
627
 
 
628
void XMLHttpRequest::slotRedirection(KIO::Job*, const KURL& url)
 
629
{
 
630
  if (!urlMatchesDocumentDomain(url)) {
 
631
    abort();
 
632
  }
 
633
}
 
634
 
 
635
#ifdef APPLE_CHANGES
 
636
void XMLHttpRequest::slotData( KIO::Job*, const char *data, int len )
 
637
#else
 
638
void XMLHttpRequest::slotData(KIO::Job*, const QByteArray &_data)
 
639
#endif
 
640
{
 
641
  if (state < Loaded ) {
 
642
    responseHeaders = job->queryMetaData("HTTP-Headers");
 
643
 
 
644
    // NOTE: Replace a 304 response with a 200! Both IE and Mozilla do this.
 
645
    // Problem first reported through bug# 110272.
 
646
    int codeStart = responseHeaders.find("304");
 
647
    if ( codeStart != -1) {
 
648
      int codeEnd = responseHeaders.find("\n", codeStart+3);
 
649
      if (codeEnd != -1)
 
650
        responseHeaders.replace(codeStart, (codeEnd-codeStart), "200 OK");
 
651
    }
 
652
 
 
653
    changeState(Loaded);
 
654
  }
 
655
 
 
656
#ifndef APPLE_CHANGES
 
657
  const char *data = (const char *)_data.data();
 
658
  int len = (int)_data.size();
 
659
#endif
 
660
 
 
661
  if ( decoder == NULL ) {
 
662
    int pos = responseHeaders.find("content-type:", 0, false);
 
663
 
 
664
    if ( pos > -1 ) {
 
665
      pos += 13;
 
666
      int index = responseHeaders.find('\n', pos);
 
667
      QString type = responseHeaders.mid(pos, (index-pos));
 
668
      index = type.find (';');
 
669
      if (index > -1)
 
670
        encoding = type.mid( index+1 ).remove(QRegExp("charset[ ]*=[ ]*", false)).stripWhiteSpace();
 
671
    }
 
672
 
 
673
    decoder = new Decoder;
 
674
    if (!encoding.isNull())
 
675
      decoder->setEncoding(encoding.latin1(), Decoder::EncodingFromHTTPHeader);
 
676
    else {
 
677
      // Per section 2 of W3C working draft spec, fall back to "UTF-8".
 
678
      decoder->setEncoding("UTF-8", Decoder::DefaultEncoding);
 
679
    }
 
680
  }
 
681
  if (len == 0)
 
682
    return;
 
683
 
 
684
  if (len == -1)
 
685
    len = strlen(data);
 
686
 
 
687
  QString decoded = decoder->decode(data, len);
 
688
 
 
689
  response += decoded;
 
690
 
 
691
  if (!aborted) {
 
692
    changeState(Interactive);
 
693
  }
 
694
}
 
695
 
 
696
Value XMLHttpRequestProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
 
697
{
 
698
  if (!thisObj.inherits(&XMLHttpRequest::info)) {
 
699
    Object err = Error::create(exec,TypeError);
 
700
    exec->setException(err);
 
701
    return err;
 
702
  }
 
703
 
 
704
  XMLHttpRequest *request = static_cast<XMLHttpRequest *>(thisObj.imp());
 
705
  switch (id) {
 
706
  case XMLHttpRequest::Abort:
 
707
    request->abort();
 
708
    return Undefined();
 
709
  case XMLHttpRequest::GetAllResponseHeaders:
 
710
    if (args.size() != 0) {
 
711
    return Undefined();
 
712
    }
 
713
 
 
714
    return request->getAllResponseHeaders();
 
715
  case XMLHttpRequest::GetResponseHeader:
 
716
    if (args.size() != 1) {
 
717
    return Undefined();
 
718
    }
 
719
 
 
720
    return request->getResponseHeader(args[0].toString(exec).qstring());
 
721
  case XMLHttpRequest::Open:
 
722
    {
 
723
      if (args.size() < 2 || args.size() > 5) {
 
724
        return Undefined();
 
725
      }
 
726
 
 
727
      QString method = args[0].toString(exec).qstring();
 
728
      KHTMLPart *part = ::qt_cast<KHTMLPart *>(Window::retrieveActive(exec)->part());
 
729
      if (!part)
 
730
        return Undefined();
 
731
      KURL url = KURL(part->document().completeURL(args[1].toString(exec).qstring()).string());
 
732
 
 
733
      bool async = true;
 
734
      if (args.size() >= 3) {
 
735
        async = args[2].toBoolean(exec);
 
736
      }
 
737
 
 
738
      if (args.size() >= 4) {
 
739
        url.setUser(args[3].toString(exec).qstring());
 
740
      }
 
741
 
 
742
      if (args.size() >= 5) {
 
743
        url.setPass(args[4].toString(exec).qstring());
 
744
      }
 
745
 
 
746
      request->open(method, url, async);
 
747
 
 
748
      return Undefined();
 
749
    }
 
750
  case XMLHttpRequest::Send:
 
751
    {
 
752
      if (args.size() > 1) {
 
753
        return Undefined();
 
754
      }
 
755
 
 
756
      if (request->state != Loading) {
 
757
        return Undefined();
 
758
      }
 
759
 
 
760
      QString body;
 
761
      if (args.size() >= 1) {
 
762
        Object obj = Object::dynamicCast(args[0]);
 
763
        if (obj.isValid() && obj.inherits(&DOMDocument::info)) {
 
764
          DOM::Node docNode = static_cast<KJS::DOMDocument *>(obj.imp())->toNode();
 
765
          DOM::DocumentImpl *doc = static_cast<DOM::DocumentImpl *>(docNode.handle());
 
766
          
 
767
          try {
 
768
            body = doc->toString().string();
 
769
            // FIXME: also need to set content type, including encoding!
 
770
  
 
771
          } catch(DOM::DOMException& e) {
 
772
            Object err = Error::create(exec, GeneralError, "Exception serializing document");
 
773
            exec->setException(err);
 
774
          }
 
775
        } else {
 
776
          body = args[0].toString(exec).qstring();
 
777
        }
 
778
      }
 
779
 
 
780
      request->send(body);
 
781
 
 
782
      return Undefined();
 
783
    }
 
784
  case XMLHttpRequest::SetRequestHeader:
 
785
    if (args.size() != 2) {
 
786
      return Undefined();
 
787
    }
 
788
 
 
789
    request->setRequestHeader(args[0].toString(exec).qstring(), args[1].toString(exec).qstring());
 
790
 
 
791
    return Undefined();
 
792
 
 
793
  case XMLHttpRequest::OverrideMIMEType:
 
794
    if (args.size() < 1) {
 
795
       Object err = Error::create(exec, SyntaxError, "Not enough arguments");
 
796
       exec->setException(err);
 
797
       return err;
 
798
    }
 
799
 
 
800
    request->overrideMIMEType(args[0].toString(exec).qstring());
 
801
    return Undefined();
 
802
  }
 
803
 
 
804
  return Undefined();
 
805
}
 
806
 
 
807
} // end namespace
 
808
 
 
809
 
 
810
#include "xmlhttprequest.moc"