1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent tw=79 ft=cpp: */
4 * Copyright (C) 2008 Sergey Yanovich <ynvich@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program 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 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 #include "xpcom-config.h"
26 #include "nsAutoPtr.h"
27 #include "nsComponentManagerUtils.h"
28 #include "nsServiceManagerUtils.h"
29 #include "nsStringAPI.h"
31 #include "nsIAsyncInputStream.h"
32 #include "nsIAsyncOutputStream.h"
34 #include "nsIStreamListener.h"
37 #include "nsThreadUtils.h"
38 #include "nsIRunnable.h"
40 #include "aaSqlitePump.h"
41 #include "aaSqliteChannel.h"
42 #include "aaTransportXML.h"
43 #include "aaTransportUtils.h"
45 class aaStartEvent
: public nsIRunnable
48 aaStartEvent(aaSqliteChannel
*channel
)
53 nsRefPtr
<aaSqliteChannel
> mChannel
;
58 NS_IMPL_THREADSAFE_ISUPPORTS1(aaStartEvent
,
64 NS_ENSURE_STATE(mChannel
);
65 return mChannel
->OnStartRequest(nsnull
, nsnull
);
68 class aaDataEvent
: public nsIRunnable
71 aaDataEvent(aaSqliteChannel
*channel
, nsIInputStream
*stream
, PRUint32
72 offset
, PRUint32 count
)
73 :mChannel(channel
), mStream(stream
), mOffset(offset
), mCount(count
) {}
77 nsRefPtr
<aaSqliteChannel
> mChannel
;
78 nsCOMPtr
<nsIInputStream
> mStream
;
85 NS_IMPL_THREADSAFE_ISUPPORTS1(aaDataEvent
,
91 NS_ENSURE_STATE(mChannel
);
92 if (mChannel
->IsCancelled())
95 return mChannel
->OnDataAvailable(nsnull
, nsnull
, mStream
,
99 class aaStopEvent
: public nsIRunnable
102 aaStopEvent(aaSqliteChannel
*channel
, nsresult status
)
103 :mChannel(channel
), mStatus(status
) {}
107 nsRefPtr
<aaSqliteChannel
> mChannel
;
113 NS_IMPL_THREADSAFE_ISUPPORTS1(aaStopEvent
,
119 NS_ENSURE_STATE(mChannel
);
120 return mChannel
->OnStopRequest(nsnull
, nsnull
, mStatus
);
123 aaSqlitePump::aaSqlitePump(nsIFile
*dbFile
, const char *query
, aaSqliteChannel
125 :mDBFile(dbFile
), mQuery(query
), mChannel(channel
), mOffset(0), mCount(0),
128 mTarget
= do_GetCurrentThread();
131 NS_IMPL_THREADSAFE_ISUPPORTS1(aaSqlitePump
,
140 nsRefPtr
<aaStartEvent
> event
= new aaStartEvent(mChannel
);
141 mTarget
->Dispatch(event
, NS_DISPATCH_NORMAL
);
148 if (NS_FAILED(rv
) && SQLITE_OK
!= code
)
150 feed(kTransportXmlHeader
);
152 feed(kTransportXmlFooter
);
155 if (NS_SUCCEEDED(rv
))
159 nsRefPtr
<aaStopEvent
> event
= new aaStopEvent(mChannel
, rv
);
160 rv
= mTarget
->Dispatch(event
, NS_DISPATCH_NORMAL
);
161 NS_ENSURE_SUCCESS(rv
, rv
);
168 aaSqlitePump::write(const char *data
)
170 return writen(data
, strlen(data
));
174 aaSqlitePump::writen(const char *data
, PRUint32 length
)
176 NS_ENSURE_STATE(mSink
);
179 PRUint32 writtenCount
;
180 rv
= mSink
->Write(data
, length
, &writtenCount
);
181 NS_ENSURE_SUCCESS(rv
, rv
);
183 mCount
+= writtenCount
;
187 static const char kRaw
[] = "<>&";
188 static const char* kEscaped
[] =
196 aaSqlitePump::writeAndEscape(const char *data
)
201 const char *last
= data
;
203 while (NS_LIKELY(0 != last
[i
++]));
208 if (NS_UNLIKELY(kRaw
[y
] == last
[i
- 1]))
211 rv
= writen(last
, i
- 1);
212 NS_ENSURE_SUCCESS(rv
, rv
);
214 rv
= write(kEscaped
[y
]);
215 NS_ENSURE_SUCCESS(rv
, rv
);
223 rv
= writen(last
, i
- 1);
224 NS_ENSURE_SUCCESS(rv
, rv
);
230 aaSqlitePump::notifyListener()
232 NS_ENSURE_STATE(mTarget
);
235 nsRefPtr
<aaDataEvent
> event
= new aaDataEvent(mChannel
, mFeeder
, mOffset
,
237 NS_ENSURE_TRUE(event
, NS_ERROR_OUT_OF_MEMORY
);
239 rv
= mTarget
->Dispatch(event
, NS_DISPATCH_NORMAL
);
240 NS_ENSURE_SUCCESS(rv
, rv
);
248 aaSqlitePump::feed(const char *data
)
253 NS_ENSURE_SUCCESS(rv
, rv
);
255 rv
= notifyListener();
256 NS_ENSURE_SUCCESS(rv
, rv
);
262 aaSqlitePump::start()
264 NS_ENSURE_STATE(mDBFile
);
268 rv
= mDBFile
->GetPath(path
);
269 NS_ENSURE_SUCCESS(rv
, rv
);
271 rv
= AA_CreatePipe(getter_AddRefs(mFeeder
),
272 getter_AddRefs(mSink
));
273 NS_ENSURE_SUCCESS(rv
, rv
);
275 rv
= db
.Connect(NS_ConvertUTF16toUTF8(path
).get());
276 if (NS_SUCCEEDED(rv
))
278 rv
= db
.Create(mQuery
.get());
289 rv
= feed(kTransportXmlHeader
);
290 NS_ENSURE_SUCCESS(rv
, rv
);
293 feed(kTransportXmlFooter
);
299 aaSqlitePump::feedResult()
304 PRBool isItemCollected
= PR_FALSE
;
306 rc
= sqlite3_step(db
.GetStmt());
307 if (rc
!= SQLITE_ROW
) {
308 if (rc
== SQLITE_DONE
|| rc
== SQLITE_OK
)
314 NS_ENSURE_SUCCESS(rv
, rv
);
317 isItemCollected
= PR_TRUE
;
319 if (!isItemCollected
)
328 aaSqlitePump::feedItem()
331 PRUint32 i
, count
= sqlite3_column_count(db
.GetStmt());
332 nsCAutoString
xml(kTransportRowHeader
);
334 xml
.AppendInt(mRowIndex
);
336 rv
= write(xml
.get());
337 NS_ENSURE_SUCCESS(rv
, rv
);
339 for (i
= 0; i
< count
; i
++) {
341 NS_ENSURE_SUCCESS(rv
, rv
);
344 rv
= write(kTransportRowFooter
);
345 NS_ENSURE_SUCCESS(rv
, rv
);
347 rv
= notifyListener();
348 NS_ENSURE_SUCCESS(rv
, rv
);
354 aaSqlitePump::feedColumn(PRUint32 index
)
357 const char *name
= (const char *) sqlite3_column_name(db
.GetStmt(), index
);
358 const char *text
= (const char *) sqlite3_column_text(db
.GetStmt(), index
);
361 NS_ENSURE_SUCCESS(rv
, rv
);
363 rv
= writeAndEscape(name
);
364 NS_ENSURE_SUCCESS(rv
, rv
);
367 NS_ENSURE_SUCCESS(rv
, rv
);
369 rv
= writeAndEscape(text
);
370 NS_ENSURE_SUCCESS(rv
, rv
);
373 NS_ENSURE_SUCCESS(rv
, rv
);
375 rv
= writeAndEscape(name
);
376 NS_ENSURE_SUCCESS(rv
, rv
);
379 NS_ENSURE_SUCCESS(rv
, rv
);
385 aaSqlitePump::feedChanges()
388 nsCAutoString
xml(kTransportMessageHeader
);
390 xml
.AppendLiteral("Query OK: ");
393 rv
= db
.Changes(&changes
);
394 NS_ENSURE_SUCCESS(rv
, rv
);
396 xml
.AppendInt(changes
);
397 xml
.AppendLiteral(" row affected");
398 xml
.AppendLiteral(kTransportMessageFooter
);
400 rv
= write(xml
.get());
401 NS_ENSURE_SUCCESS(rv
, rv
);
403 rv
= notifyListener();
404 NS_ENSURE_SUCCESS(rv
, rv
);
410 aaSqlitePump::feedError()
413 nsCAutoString
xml(kTransportErrorHeader
);
415 xml
.AppendLiteral("ERROR ");
418 rv
= db
.ErrorCode(&code
);
419 NS_ENSURE_SUCCESS(rv
, rv
);
422 xml
.AppendLiteral(": ");
424 const char *msg
= nsnull
;
425 rv
= db
.ErrorMsg(&msg
);
426 NS_ENSURE_SUCCESS(rv
, rv
);
428 xml
.AppendLiteral(msg
);
429 xml
.AppendLiteral(kTransportErrorFooter
);
431 rv
= write(xml
.get());
432 NS_ENSURE_SUCCESS(rv
, rv
);
434 rv
= notifyListener();
435 NS_ENSURE_SUCCESS(rv
, rv
);