transport: pass correct channel
[abstract.git] / transport / sqlite / src / aaSqlitePump.cpp
blob09ecbfe73a2b1d787dbf680adc4a80190ef54a34
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: */
3 /*
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"
24 #include "sqlite3.h"
25 #include "nsCOMPtr.h"
26 #include "nsAutoPtr.h"
27 #include "nsComponentManagerUtils.h"
28 #include "nsServiceManagerUtils.h"
29 #include "nsStringAPI.h"
30 #include "nsNetCID.h"
31 #include "nsIAsyncInputStream.h"
32 #include "nsIAsyncOutputStream.h"
33 #include "nsIPipe.h"
34 #include "nsIStreamListener.h"
36 /* Unfrozen API */
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
47 public:
48 aaStartEvent(aaSqliteChannel *channel)
49 :mChannel(channel) {}
50 NS_DECL_ISUPPORTS
51 NS_DECL_NSIRUNNABLE
52 private:
53 nsRefPtr<aaSqliteChannel> mChannel;
54 private:
55 ~aaStartEvent() {}
58 NS_IMPL_THREADSAFE_ISUPPORTS1(aaStartEvent,
59 nsIRunnable)
61 NS_IMETHODIMP
62 aaStartEvent::Run()
64 NS_ENSURE_STATE(mChannel);
65 return mChannel->OnStartRequest(nsnull, nsnull);
68 class aaDataEvent : public nsIRunnable
70 public:
71 aaDataEvent(aaSqliteChannel *channel, nsIInputStream *stream, PRUint32
72 offset, PRUint32 count)
73 :mChannel(channel), mStream(stream), mOffset(offset), mCount(count) {}
74 NS_DECL_ISUPPORTS
75 NS_DECL_NSIRUNNABLE
76 private:
77 nsRefPtr<aaSqliteChannel> mChannel;
78 nsCOMPtr<nsIInputStream> mStream;
79 PRUint32 mOffset;
80 PRUint32 mCount;
81 private:
82 ~aaDataEvent() {}
85 NS_IMPL_THREADSAFE_ISUPPORTS1(aaDataEvent,
86 nsIRunnable)
88 NS_IMETHODIMP
89 aaDataEvent::Run()
91 NS_ENSURE_STATE(mChannel);
92 if (mChannel->IsCancelled())
93 return NS_OK;
95 return mChannel->OnDataAvailable(nsnull, nsnull, mStream,
96 mOffset, mCount);
99 class aaStopEvent : public nsIRunnable
101 public:
102 aaStopEvent(aaSqliteChannel *channel, nsresult status)
103 :mChannel(channel), mStatus(status) {}
104 NS_DECL_ISUPPORTS
105 NS_DECL_NSIRUNNABLE
106 private:
107 nsRefPtr<aaSqliteChannel> mChannel;
108 nsresult mStatus;
109 private:
110 ~aaStopEvent() {}
113 NS_IMPL_THREADSAFE_ISUPPORTS1(aaStopEvent,
114 nsIRunnable)
116 NS_IMETHODIMP
117 aaStopEvent::Run()
119 NS_ENSURE_STATE(mChannel);
120 return mChannel->OnStopRequest(nsnull, nsnull, mStatus);
123 aaSqlitePump::aaSqlitePump(nsIFile *dbFile, const char *query, aaSqliteChannel
124 *channel)
125 :mDBFile(dbFile), mQuery(query), mChannel(channel), mOffset(0), mCount(0),
126 mRowIndex(0)
128 mTarget = do_GetCurrentThread();
131 NS_IMPL_THREADSAFE_ISUPPORTS1(aaSqlitePump,
132 nsIRunnable)
134 NS_IMETHODIMP
135 aaSqlitePump::Run()
137 nsresult rv;
140 nsRefPtr<aaStartEvent> event = new aaStartEvent(mChannel);
141 mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
144 rv = start();
146 PRInt32 code = 0;
147 db.ErrorCode(&code);
148 if (NS_FAILED(rv) && SQLITE_OK != code)
150 feed(kTransportXmlHeader);
151 feedError();
152 feed(kTransportXmlFooter);
155 if (NS_SUCCEEDED(rv))
156 rv = read();
159 nsRefPtr<aaStopEvent> event = new aaStopEvent(mChannel, rv);
160 rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
161 NS_ENSURE_SUCCESS(rv, rv);
164 return NS_OK;
167 nsresult
168 aaSqlitePump::write(const char *data)
170 return writen(data, strlen(data));
173 nsresult
174 aaSqlitePump::writen(const char *data, PRUint32 length)
176 NS_ENSURE_STATE(mSink);
177 nsresult rv;
179 PRUint32 writtenCount;
180 rv = mSink->Write(data, length, &writtenCount);
181 NS_ENSURE_SUCCESS(rv, rv);
183 mCount += writtenCount;
184 return NS_OK;
187 static const char kRaw[] = "<>&";
188 static const char* kEscaped[] =
190 "&lt;"
191 ,"&rt;"
192 ,"&amp;"
195 nsresult
196 aaSqlitePump::writeAndEscape(const char *data)
198 nsresult rv;
200 PRUint32 i = 0;
201 const char *last = data;
203 while (NS_LIKELY(0 != last[i++]));
205 PRUint32 y = 0;
206 for (; y < 3; y++)
208 if (NS_UNLIKELY(kRaw[y] == last[i - 1]))
210 if (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);
217 last = &last[i];
218 i = 0;
223 rv = writen(last, i - 1);
224 NS_ENSURE_SUCCESS(rv, rv);
226 return NS_OK;
229 nsresult
230 aaSqlitePump::notifyListener()
232 NS_ENSURE_STATE(mTarget);
233 nsresult rv;
235 nsRefPtr<aaDataEvent> event = new aaDataEvent(mChannel, mFeeder, mOffset,
236 mCount);
237 NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
239 rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
240 NS_ENSURE_SUCCESS(rv, rv);
242 mOffset += mCount;
243 mCount = 0;
244 return NS_OK;
247 nsresult
248 aaSqlitePump::feed(const char *data)
250 nsresult rv;
252 rv = write(data);
253 NS_ENSURE_SUCCESS(rv, rv);
255 rv = notifyListener();
256 NS_ENSURE_SUCCESS(rv, rv);
258 return NS_OK;
261 nsresult
262 aaSqlitePump::start()
264 NS_ENSURE_STATE(mDBFile);
265 nsresult rv;
267 nsAutoString path;
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());
281 return rv;
284 nsresult
285 aaSqlitePump::read()
287 nsresult rv;
289 rv = feed(kTransportXmlHeader);
290 NS_ENSURE_SUCCESS(rv, rv);
292 rv = feedResult();
293 feed(kTransportXmlFooter);
295 return rv;
298 nsresult
299 aaSqlitePump::feedResult()
301 nsresult rv;
302 int rc;
304 PRBool isItemCollected = PR_FALSE;
305 do {
306 rc = sqlite3_step(db.GetStmt());
307 if (rc != SQLITE_ROW) {
308 if (rc == SQLITE_DONE || rc == SQLITE_OK)
309 break;
310 return feedError();
313 rv = feedItem();
314 NS_ENSURE_SUCCESS(rv, rv);
316 mRowIndex++;
317 isItemCollected = PR_TRUE;
318 } while (PR_TRUE);
319 if (!isItemCollected)
321 feedChanges();
324 return NS_OK;
327 nsresult
328 aaSqlitePump::feedItem()
330 nsresult rv;
331 PRUint32 i, count = sqlite3_column_count(db.GetStmt());
332 nsCAutoString xml(kTransportRowHeader);
334 xml.AppendInt(mRowIndex);
335 xml.Append("\">\n");
336 rv = write(xml.get());
337 NS_ENSURE_SUCCESS(rv, rv);
339 for (i = 0; i < count; i++) {
340 rv = feedColumn(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);
350 return NS_OK;
353 nsresult
354 aaSqlitePump::feedColumn(PRUint32 index)
356 nsresult rv;
357 const char *name = (const char *) sqlite3_column_name(db.GetStmt(), index);
358 const char *text = (const char *) sqlite3_column_text(db.GetStmt(), index);
360 rv = write(" <");
361 NS_ENSURE_SUCCESS(rv, rv);
363 rv = writeAndEscape(name);
364 NS_ENSURE_SUCCESS(rv, rv);
366 rv = write(">");
367 NS_ENSURE_SUCCESS(rv, rv);
369 rv = writeAndEscape(text);
370 NS_ENSURE_SUCCESS(rv, rv);
372 rv = write("</");
373 NS_ENSURE_SUCCESS(rv, rv);
375 rv = writeAndEscape(name);
376 NS_ENSURE_SUCCESS(rv, rv);
378 rv = write(">\n");
379 NS_ENSURE_SUCCESS(rv, rv);
381 return NS_OK;
384 nsresult
385 aaSqlitePump::feedChanges()
387 nsresult rv;
388 nsCAutoString xml(kTransportMessageHeader);
390 xml.AppendLiteral("Query OK: ");
392 PRInt32 changes = 0;
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);
406 return NS_OK;
409 nsresult
410 aaSqlitePump::feedError()
412 nsresult rv;
413 nsCAutoString xml(kTransportErrorHeader);
415 xml.AppendLiteral("ERROR ");
417 PRInt32 code = 0;
418 rv = db.ErrorCode(&code);
419 NS_ENSURE_SUCCESS(rv, rv);
421 xml.AppendInt(code);
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);
437 return NS_OK;