engine/src/FeedEngine.cpp
changeset 2 29cda98b007e
child 7 a7a43293ae56
equal deleted inserted replaced
1:5f8e5adbbed9 2:29cda98b007e
       
     1 /*
       
     2 * Copyright (c) 2007-2010 Sebastian Brannstrom, Lars Persson, EmbedDev AB
       
     3 *
       
     4 * All rights reserved.
       
     5 * This component and the accompanying materials are made available
       
     6 * under the terms of the License "Eclipse Public License v1.0"
       
     7 * which accompanies this distribution, and is available
       
     8 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     9 *
       
    10 * Initial Contributors:
       
    11 * EmbedDev AB - initial contribution.
       
    12 *
       
    13 * Contributors:
       
    14 *
       
    15 * Description:
       
    16 *
       
    17 */
       
    18 
       
    19 #include "FeedEngine.h"
       
    20 #include <f32file.h>
       
    21 #include <bautils.h>
       
    22 #include <s32file.h>
       
    23 #include "SettingsEngine.h"
       
    24 #include "ShowEngine.h"
       
    25 #include <e32hashtab.h>
       
    26 #include "OpmlParser.h"
       
    27 #include "PodcastUtils.h"
       
    28 #include <utf.h>
       
    29 
       
    30 // Cleanup stack macro for SQLite3
       
    31 // TODO Move this to some common place.
       
    32 static void Cleanup_sqlite3_finalize_wrapper(TAny* handle)
       
    33 	{
       
    34 	sqlite3_finalize(static_cast<sqlite3_stmt*>(handle));
       
    35 	}
       
    36 #define Cleanup_sqlite3_finalize_PushL(__handle) CleanupStack::PushL(TCleanupItem(&Cleanup_sqlite3_finalize_wrapper, __handle))
       
    37 
       
    38 
       
    39 CFeedEngine* CFeedEngine::NewL(CPodcastModel& aPodcastModel)
       
    40 	{
       
    41 	CFeedEngine* self = new (ELeave) CFeedEngine(aPodcastModel);
       
    42 	CleanupStack::PushL(self);
       
    43 	self->ConstructL();
       
    44 	CleanupStack::Pop(self);
       
    45 	return self;
       
    46 	}
       
    47 
       
    48 void CFeedEngine::ConstructL()
       
    49 	{
       
    50 	iParser = new (ELeave) CFeedParser(*this, iPodcastModel.FsSession());
       
    51 	
       
    52 	iFeedClient = CHttpClient::NewL(iPodcastModel, *this);
       
    53 	iFeedTimer.ConstructL();
       
    54 	
       
    55 	RunFeedTimer();
       
    56 	
       
    57     if (DBGetFeedCount() > 0) 
       
    58     	{
       
    59 		DP("Loading feeds from DB");
       
    60 		DBLoadFeedsL();
       
    61 		} 
       
    62     
       
    63 	if (iPodcastModel.IsFirstStartup()) {
       
    64 		TFileName defaultFile = iPodcastModel.SettingsEngine().DefaultFeedsFileName();
       
    65 		DP1("Loading default feeds from %S", &defaultFile);
       
    66 		if (BaflUtils::FileExists(iPodcastModel.FsSession(), defaultFile)) {
       
    67 			ImportFeedsL(defaultFile);
       
    68 		}
       
    69 	}
       
    70     
       
    71     TFileName importFile = iPodcastModel.SettingsEngine().ImportFeedsFileName();
       
    72     if (BaflUtils::FileExists(iPodcastModel.FsSession(), importFile)) {
       
    73     	DP("Importing feeds");
       
    74     	ImportFeedsL(importFile);
       
    75 		}
       
    76 	}
       
    77 
       
    78 CFeedEngine::CFeedEngine(CPodcastModel& aPodcastModel)
       
    79 		: iClientState(EIdle),
       
    80 		  iFeedTimer(this),
       
    81 		  iPodcastModel(aPodcastModel),
       
    82 		  iDB(*aPodcastModel.DB())
       
    83 	{
       
    84 	}
       
    85 
       
    86 CFeedEngine::~CFeedEngine()
       
    87 	{
       
    88 	iObservers.Close();
       
    89 	
       
    90 	iFeedsUpdating.Close();
       
    91 	iSortedFeeds.ResetAndDestroy();
       
    92 	iSearchResults.ResetAndDestroy();
       
    93 	
       
    94 	delete iParser;
       
    95 	delete iFeedClient;
       
    96 	delete iOpmlParser;
       
    97 	}
       
    98 
       
    99 /**
       
   100  * Returns the current internal state of the feed engine4
       
   101  */
       
   102 EXPORT_C TClientState CFeedEngine::ClientState()
       
   103 	{
       
   104 	return iClientState;
       
   105 	}
       
   106 
       
   107 
       
   108 /**
       
   109  * Returns the current updating client UID if clientstate is != ENotUpdateing
       
   110  * @return TUint
       
   111  */
       
   112 EXPORT_C TUint CFeedEngine::ActiveClientUid()
       
   113 	{
       
   114 	if(iActiveFeed != NULL)
       
   115 		{
       
   116 		return iActiveFeed->Uid();
       
   117 		}
       
   118 	return 0;	
       
   119 	}
       
   120 
       
   121 void CFeedEngine::RunFeedTimer()
       
   122 	{
       
   123 	iFeedTimer.Cancel();
       
   124 
       
   125 	if (iPodcastModel.SettingsEngine().UpdateAutomatically() != EAutoUpdateOff)
       
   126 		{
       
   127 		TInt interval = iPodcastModel.SettingsEngine().UpdateFeedInterval();
       
   128 	
       
   129 		if (interval != 0)
       
   130 			{
       
   131 			iFeedTimer.SetPeriod(interval);
       
   132 			iFeedTimer.RunPeriodically();
       
   133 			}
       
   134 		}
       
   135 	}
       
   136 
       
   137 EXPORT_C void CFeedEngine::UpdateAllFeedsL(TBool aAutoUpdate)
       
   138 	{
       
   139 	iAutoUpdatedInitiator = aAutoUpdate;
       
   140 	if (iFeedsUpdating.Count() > 0)
       
   141 		{
       
   142 		DP("Cancelling update");
       
   143 		iFeedClient->Stop();
       
   144 		iFeedsUpdating.Reset();
       
   145 		return;
       
   146 		}
       
   147 
       
   148 	TInt cnt = iSortedFeeds.Count();
       
   149 	for (int i=0;i<cnt;i++)
       
   150 		{
       
   151 		iFeedsUpdating.Append(iSortedFeeds[i]);
       
   152 		}
       
   153 
       
   154 	UpdateNextFeedL();
       
   155 	}
       
   156 
       
   157 EXPORT_C void CFeedEngine::CancelUpdateAllFeeds()
       
   158 	{
       
   159 	if(iClientState != EIdle)
       
   160 		{
       
   161 		iFeedsUpdating.Reset();
       
   162 		iFeedClient->Stop();
       
   163 		}
       
   164 	}
       
   165 
       
   166 void CFeedEngine::UpdateNextFeedL()
       
   167 	{
       
   168 	DP1("UpdateNextFeed. %d feeds left to update", iFeedsUpdating.Count());
       
   169 	
       
   170 	if (iFeedsUpdating.Count() > 0)
       
   171 		{
       
   172 		CFeedInfo *info = iFeedsUpdating[0];
       
   173 		iFeedsUpdating.Remove(0);
       
   174 		TBool result = EFalse;
       
   175 		//DP2("** UpdateNextFeed: %S, ID: %u", &(info->Url()), info->Uid());
       
   176 		TRAPD(error, result = UpdateFeedL(info->Uid()));
       
   177 		
       
   178 		if (error != KErrNone || !result)
       
   179 			{
       
   180 			DP("Error while updating all feeds");
       
   181 			for (TInt i=0;i<iObservers.Count();i++) 
       
   182 				{
       
   183 				TRAP_IGNORE(iObservers[i]->FeedUpdateAllCompleteL(iAutoUpdatedInitiator?MFeedEngineObserver::EFeedAutoUpdate:MFeedEngineObserver::EFeedManualUpdate));
       
   184 				}
       
   185 			}
       
   186 		}
       
   187 	else
       
   188 		{
       
   189 		iClientState = EIdle;
       
   190 		for (TInt i=0;i<iObservers.Count();i++) 
       
   191 			{
       
   192 			TRAP_IGNORE(iObservers[i]->FeedUpdateAllCompleteL(iAutoUpdatedInitiator?MFeedEngineObserver::EFeedAutoUpdate:MFeedEngineObserver::EFeedManualUpdate));
       
   193 			}
       
   194 		}
       
   195 	}
       
   196 
       
   197 EXPORT_C TBool CFeedEngine::UpdateFeedL(TUint aFeedUid)
       
   198 	{
       
   199 	DP("FeedEngine::UpdateFeedL BEGIN");
       
   200 	iActiveFeed = GetFeedInfoByUid(aFeedUid);
       
   201 	iCatchupCounter = 0;
       
   202 
       
   203 	if (iActiveFeed->LastUpdated() == 0)
       
   204 		{
       
   205 		iCatchupMode = ETrue;
       
   206 		}
       
   207 
       
   208 	iUpdatingFeedFileName.Copy (iPodcastModel.SettingsEngine().PrivatePath ());
       
   209 	_LIT(KFileNameFormat, "%lu.xml");
       
   210 	iUpdatingFeedFileName.AppendFormat(KFileNameFormat, aFeedUid);
       
   211 	
       
   212 	if(iFeedClient->GetL(iActiveFeed->Url(), iUpdatingFeedFileName, iPodcastModel.SettingsEngine().SpecificIAP()))
       
   213 		{
       
   214 		iClientState = EUpdatingFeed;
       
   215 		
       
   216 		for (TInt i=0;i<iObservers.Count();i++)
       
   217 			{
       
   218 			TRAP_IGNORE(iObservers[i]->FeedDownloadStartedL(iAutoUpdatedInitiator?MFeedEngineObserver::EFeedAutoUpdate:MFeedEngineObserver::EFeedManualUpdate, iActiveFeed->Uid()));
       
   219 			}
       
   220 
       
   221 		DP("FeedEngine::UpdateFeedL END, return ETrue");
       
   222 		return ETrue;
       
   223 		}
       
   224 	else
       
   225 		{
       
   226 		DP("FeedEngine::UpdateFeedL END, return EFalse");
       
   227 		return EFalse;
       
   228 		}
       
   229 	}
       
   230 
       
   231 void CFeedEngine::NewShowL(CShowInfo& aItem)
       
   232 	{
       
   233 	//DP4("\nTitle: %S\nURL: %S\nDescription length: %d\nFeed: %d", &(item->Title()), &(item->Url()), item->Description().Length(), item->FeedUid());
       
   234 	
       
   235 	HBufC* description = HBufC::NewLC(KMaxDescriptionLength);
       
   236 	TPtr ptr(description->Des());
       
   237 	ptr.Copy(aItem.Description());
       
   238 	PodcastUtils::CleanHtmlL(ptr);
       
   239 	//DP1("New show has feed ID: %u") item->FeedUid());
       
   240 	TRAP_IGNORE(aItem.SetDescriptionL(*description));
       
   241 	CleanupStack::PopAndDestroy(description);
       
   242 	//DP1("Description: %S", &description);
       
   243 
       
   244 	if (iCatchupMode) {
       
   245 		// in catchup mode, we let one show be unplayed
       
   246 		if (++iCatchupCounter > 1) {
       
   247 			aItem.SetPlayState(EPlayed);
       
   248 		}
       
   249 	}
       
   250 	
       
   251 	TBool isShowAdded = iPodcastModel.ShowEngine().AddShowL(aItem);
       
   252 
       
   253 	if (aItem.PlayState() == ENeverPlayed && isShowAdded && iPodcastModel.SettingsEngine().DownloadAutomatically()) 
       
   254 		{
       
   255 		iPodcastModel.ShowEngine().AddDownloadL(aItem);
       
   256 		}	
       
   257 	}
       
   258 
       
   259 void CFeedEngine::GetFeedImageL(CFeedInfo *aFeedInfo)
       
   260 	{
       
   261 	DP("GetFeedImage");
       
   262 
       
   263 	TFileName filePath;
       
   264 	filePath.Copy(iPodcastModel.SettingsEngine().BaseDir());
       
   265 	
       
   266 	// create relative file name
       
   267 	TFileName relPath;
       
   268 	relPath.Copy(aFeedInfo->Title());
       
   269 	relPath.Append('\\');
       
   270 
       
   271 	TFileName fileName;
       
   272 	PodcastUtils::FileNameFromUrl(aFeedInfo->ImageUrl(), fileName);
       
   273 	relPath.Append(fileName);
       
   274 	PodcastUtils::EnsureProperPathName(relPath);
       
   275 	
       
   276 	// complete file path is base dir + rel path
       
   277 	filePath.Append(relPath);
       
   278 	aFeedInfo->SetImageFileNameL(filePath);
       
   279 
       
   280 	if(iFeedClient->GetL(aFeedInfo->ImageUrl(), filePath, ETrue))
       
   281 		{
       
   282 			iClientState = EUpdatingImage;
       
   283 		}
       
   284 	}
       
   285 
       
   286 EXPORT_C TBool CFeedEngine::AddFeedL(const CFeedInfo&aItem) 
       
   287 	{
       
   288 	DP2("CFeedEngine::AddFeed, title=%S, URL=%S", &aItem.Title(), &aItem.Url());
       
   289 	for (TInt i=0;i<iSortedFeeds.Count();i++) 
       
   290 		{
       
   291 		if (iSortedFeeds[i]->Uid() == aItem.Uid()) 
       
   292 			{
       
   293 			DP1("Already have feed %S, discarding", &aItem.Url());			
       
   294 			return EFalse;
       
   295 			}
       
   296 		}
       
   297 	
       
   298 	TLinearOrder<CFeedInfo> sortOrder( CFeedEngine::CompareFeedsByTitle);
       
   299 	CFeedInfo* newItem = aItem.CopyL();
       
   300 	CleanupStack::PushL(newItem);
       
   301 	User::LeaveIfError(iSortedFeeds.InsertInOrder(newItem, sortOrder));
       
   302 	CleanupStack::Pop(newItem);
       
   303 	
       
   304 
       
   305 	// Save the feeds into DB
       
   306 	DBAddFeedL(aItem);
       
   307 	return ETrue;
       
   308 	}
       
   309 
       
   310 TBool CFeedEngine::DBAddFeedL(const CFeedInfo& aItem)
       
   311 	{
       
   312 	DP2("CFeedEngine::DBAddFeed, title=%S, URL=%S", &aItem.Title(), &aItem.Url());
       
   313 	
       
   314 	CFeedInfo *info = DBGetFeedInfoByUidL(aItem.Uid());
       
   315 	if (info) {
       
   316 		DP("Feed already exists!");
       
   317 		delete info;
       
   318 		return EFalse;
       
   319 	}
       
   320 
       
   321 	HBufC* titleBuf = HBufC::NewLC(KMaxLineLength);
       
   322 	TPtr titlePtr(titleBuf->Des());
       
   323 	titlePtr.Copy(aItem.Title());
       
   324 	PodcastUtils::SQLEncode(titlePtr);
       
   325 	
       
   326 	HBufC* descBuf = HBufC::NewLC(KMaxLineLength);
       
   327 	TPtr descPtr(descBuf->Des());
       
   328 	descPtr.Copy(aItem.Description());
       
   329 	PodcastUtils::SQLEncode(descPtr);
       
   330 	
       
   331 	_LIT(KSqlStatement, "insert into feeds (url, title, description, imageurl, imagefile, link, built, lastupdated, uid, feedtype, customtitle, lasterror)"
       
   332 			" values (\"%S\",\"%S\", \"%S\", \"%S\", \"%S\", \"%S\", \"%Ld\", \"%Ld\", \"%u\", \"%u\", \"%u\", \"%d\")");
       
   333 	iSqlBuffer.Format(KSqlStatement,
       
   334 			&aItem.Url(), titleBuf, descBuf, &aItem.ImageUrl(), &aItem.ImageFileName(), &aItem.Link(),
       
   335 			aItem.BuildDate().Int64(), aItem.LastUpdated().Int64(), aItem.Uid(), EAudioPodcast, aItem.CustomTitle(), aItem.LastError());
       
   336 
       
   337 	CleanupStack::PopAndDestroy(descBuf);
       
   338 	CleanupStack::PopAndDestroy(titleBuf);
       
   339 	
       
   340 	sqlite3_stmt *st;
       
   341 	 
       
   342 	//DP1("SQL statement length=%d", iSqlBuffer.Length());
       
   343 	int rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st, (const void**) NULL);
       
   344 	
       
   345 	if (rc==SQLITE_OK)
       
   346 		{
       
   347 		rc = sqlite3_step(st);
       
   348 
       
   349 		if (rc == SQLITE_DONE)
       
   350 			{
       
   351 			sqlite3_finalize(st);
       
   352 			return ETrue;
       
   353 			}
       
   354 		else {
       
   355 			sqlite3_finalize(st);
       
   356 		}
       
   357 	}
       
   358 
       
   359 	return EFalse;
       
   360 	}
       
   361 
       
   362 EXPORT_C void CFeedEngine::RemoveFeedL(TUint aUid) 
       
   363 	{
       
   364 	for (int i=0;i<iSortedFeeds.Count();i++) 
       
   365 		{
       
   366 		if (iSortedFeeds[i]->Uid() == aUid) 
       
   367 			{
       
   368 			iPodcastModel.ShowEngine().DeleteAllShowsByFeedL(aUid);
       
   369 					
       
   370 			CFeedInfo* feedToRemove = iSortedFeeds[i];
       
   371 			
       
   372 			//delete the image file if it exists
       
   373 			if ((feedToRemove->ImageFileName().Length() >0)
       
   374 					&& BaflUtils::FileExists(iPodcastModel.FsSession(), feedToRemove->ImageFileName()))
       
   375 				{
       
   376 				iPodcastModel.FsSession().Delete(feedToRemove->ImageFileName());
       
   377 				}
       
   378 				
       
   379 			//delete the folder. It has the same name as the title.
       
   380 			TFileName filePath;
       
   381 			filePath.Copy(iPodcastModel.SettingsEngine().BaseDir());
       
   382 			filePath.Append(feedToRemove->Title());
       
   383 			filePath.Append('\\');
       
   384 			iPodcastModel.FsSession().RmDir(filePath);
       
   385 
       
   386 			iSortedFeeds.Remove(i);
       
   387 			delete feedToRemove;
       
   388 			
       
   389 			DP("Removed feed from array");
       
   390 			
       
   391 			// now remove it from DB
       
   392 			DBRemoveFeed(aUid);
       
   393 
       
   394 			return;
       
   395 		}
       
   396 	}
       
   397 }
       
   398 
       
   399 
       
   400 TBool CFeedEngine::DBRemoveFeed(TUint aUid)
       
   401 	{
       
   402 	DP("CFeedEngine::DBRemoveFeed");
       
   403 	_LIT(KSqlStatement, "delete from feeds where uid=%u");
       
   404 	iSqlBuffer.Format(KSqlStatement, aUid);
       
   405 
       
   406 	sqlite3_stmt *st;
       
   407 	 
       
   408 	int rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st,	(const void**) NULL);
       
   409 	
       
   410 	if (rc==SQLITE_OK)
       
   411 		{
       
   412 		rc = sqlite3_step(st);
       
   413 		sqlite3_finalize(st);
       
   414 
       
   415 		if (rc == SQLITE_DONE)
       
   416 			{
       
   417 			DP("Feed removed from DB");
       
   418 			return ETrue;
       
   419 			}
       
   420 		else
       
   421 			{
       
   422 			DP("Error when removing feed from DB");
       
   423 			}
       
   424 		}
       
   425 	return EFalse;	
       
   426 	}
       
   427 
       
   428 TBool CFeedEngine::DBUpdateFeed(const CFeedInfo &aItem)
       
   429 	{
       
   430 	DP2("CFeedEngine::DBUpdateFeed, title=%S, URL=%S", &aItem.Title(), &aItem.Url());
       
   431 	
       
   432 	HBufC* titleBuf = HBufC::NewLC(KMaxLineLength);
       
   433 	TPtr titlePtr(titleBuf->Des());
       
   434 	titlePtr.Copy(aItem.Title());
       
   435 	PodcastUtils::SQLEncode(titlePtr);
       
   436 	
       
   437 	HBufC* descBuf = HBufC::NewLC(KMaxLineLength);
       
   438 	TPtr descPtr(descBuf->Des());
       
   439 	descPtr.Copy(aItem.Description());
       
   440 	PodcastUtils::SQLEncode(descPtr);
       
   441 	
       
   442 	_LIT(KSqlStatement, "update feeds set url=\"%S\", title=\"%S\", description=\"%S\", imageurl=\"%S\", imagefile=\"%S\"," \
       
   443 			"link=\"%S\", built=\"%Lu\", lastupdated=\"%Lu\", feedtype=\"%u\", customtitle=\"%u\", lasterror=\"%d\" where uid=\"%u\"");
       
   444 	iSqlBuffer.Format(KSqlStatement,
       
   445 			&aItem.Url(), titleBuf, descBuf, &aItem.ImageUrl(), &aItem.ImageFileName(), &aItem.Link(),
       
   446 			aItem.BuildDate().Int64(), aItem.LastUpdated().Int64(), EAudioPodcast, aItem.CustomTitle(), aItem.LastError(), aItem.Uid());
       
   447 
       
   448 	CleanupStack::PopAndDestroy(descBuf);
       
   449 	CleanupStack::PopAndDestroy(titleBuf);
       
   450 	
       
   451 	sqlite3_stmt *st;
       
   452 	 
       
   453 	//DP1("SQL statement length=%d", iSqlBuffer.Length());
       
   454 	int rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st, (const void**) NULL);
       
   455 	
       
   456 	if (rc==SQLITE_OK)
       
   457 		{
       
   458 		rc = sqlite3_step(st);
       
   459 		sqlite3_finalize(st);
       
   460 		
       
   461 		if (rc == SQLITE_DONE)
       
   462 			{
       
   463 			return ETrue;
       
   464 			}
       
   465 		}
       
   466 	else
       
   467 		{
       
   468 		DP1("SQLite rc=%d", rc);
       
   469 		}
       
   470 
       
   471 	return EFalse;
       
   472 	}
       
   473 
       
   474 void CFeedEngine::ParsingCompleteL(CFeedInfo *item)
       
   475 	{
       
   476 	TBuf<KMaxLineLength> title;
       
   477 	title.Copy(item->Title());
       
   478 	TRAP_IGNORE(item->SetTitleL(title));
       
   479 	//DBUpdateFeed(*item);
       
   480 	}
       
   481 
       
   482 
       
   483 EXPORT_C void CFeedEngine::AddObserver(MFeedEngineObserver *observer)
       
   484 	{
       
   485 	iObservers.Append(observer);
       
   486 	}
       
   487 
       
   488 EXPORT_C void CFeedEngine::RemoveObserver(MFeedEngineObserver *observer)
       
   489 	{
       
   490 	TInt index = iObservers.Find(observer);
       
   491 	
       
   492 	if (index > KErrNotFound)
       
   493 		{
       
   494 		iObservers.Remove(index);
       
   495 		}
       
   496 	}
       
   497 
       
   498 void CFeedEngine::Connected(CHttpClient* /*aClient*/)
       
   499 	{
       
   500 	}
       
   501 
       
   502 void CFeedEngine::Progress(CHttpClient* /*aHttpClient*/, TInt /*aBytes*/, TInt /*aTotalBytes*/)
       
   503 	{	
       
   504 }
       
   505 
       
   506 void CFeedEngine::CompleteL(CHttpClient* /*aClient*/, TInt aError)
       
   507 	{
       
   508 	DP2("CFeedEngine::CompleteL BEGIN, iClientState=%d, aSuccessful=%d", iClientState, aError);
       
   509 
       
   510 	switch(iClientState)
       
   511 		{		
       
   512 		default:
       
   513 			if(iActiveFeed != NULL)
       
   514 				{
       
   515 				TTime time;
       
   516 				time.HomeTime();
       
   517 				iActiveFeed->SetLastUpdated(time);
       
   518 				iActiveFeed->SetLastError(aError);
       
   519 				NotifyFeedUpdateComplete(aError);
       
   520 				}
       
   521 			break;
       
   522 		case EUpdatingFeed: 
       
   523 		{
       
   524 		// Parse the feed. We need to trap this call since it could leave and then
       
   525 		// the whole update chain will be broken
       
   526 		// change client state
       
   527 		iClientState = EIdle;
       
   528 		switch (aError)
       
   529 			{
       
   530 			case KErrCancel:						
       
   531 				{
       
   532 				iFeedsUpdating.Reset();
       
   533 				}
       
   534 				break;
       
   535 			case KErrCouldNotConnect:
       
   536 				iFeedsUpdating.Reset();
       
   537 				break;
       
   538 			default:
       
   539 				{
       
   540 				iActiveFeed->SetLastError(aError);
       
   541 				TTime time;
       
   542 				time.HomeTime();
       
   543 				iActiveFeed->SetLastUpdated(time);
       
   544 
       
   545 				if( aError == KErrNone)
       
   546 					{			
       
   547 					TRAPD(parserErr, iParser->ParseFeedL(iUpdatingFeedFileName, iActiveFeed, iPodcastModel.SettingsEngine().MaxListItems()));
       
   548 
       
   549 					if(parserErr)
       
   550 						{
       
   551 						// we do not need to any special action on this error.
       
   552 						iActiveFeed->SetLastError(parserErr);
       
   553 						DP1("CFeedEngine::Complete()\t Failed to parse feed. Leave with error code=%d", parserErr);
       
   554 						}
       
   555 					else
       
   556 						{
       
   557 						iPodcastModel.ShowEngine().DeleteOldShowsByFeed(iActiveFeed->Uid());
       
   558 						}
       
   559 
       
   560 					// delete the downloaded XML file as it is no longer needed
       
   561 					BaflUtils::DeleteFile(iPodcastModel.FsSession(),iUpdatingFeedFileName);			
       
   562 
       
   563 					// if the feed has specified a image url. download it if we dont already have it
       
   564 					if((iActiveFeed->ImageUrl().Length() > 0))
       
   565 						{
       
   566 						if ( (iActiveFeed->ImageFileName().Length() == 0) || 
       
   567 								(iActiveFeed->ImageFileName().Length() > 0 && 
       
   568 										!BaflUtils::FileExists(iPodcastModel.FsSession(), 
       
   569 												iActiveFeed->ImageFileName()) )
       
   570 						)
       
   571 							{
       
   572 							TRAPD(error, GetFeedImageL(iActiveFeed));
       
   573 							if (error)
       
   574 								{
       
   575 								// we have failed in a very early stage to fetch the image.
       
   576 								// continue with next Feed update	
       
   577 								iActiveFeed->SetLastError(parserErr);
       
   578 								iClientState = EIdle;							
       
   579 								}
       
   580 							}	
       
   581 						}
       
   582 					}
       
   583 				}break;
       
   584 			}
       
   585 		
       
   586 		NotifyFeedUpdateComplete(aError);
       
   587 
       
   588 		// we will wait until the image has been downloaded to start the next feed update.
       
   589 		if (iClientState == EIdle)
       
   590 			{
       
   591 			UpdateNextFeedL();	
       
   592 			}
       
   593 		}break;
       
   594 	case EUpdatingImage:
       
   595 		{
       
   596 		// change client state to not updating
       
   597 		iClientState = EIdle;
       
   598 
       
   599 		NotifyFeedUpdateComplete(aError);
       
   600 		UpdateNextFeedL();
       
   601 		}break;
       
   602 	case ESearching: 
       
   603 		{
       
   604 		iClientState = EIdle;
       
   605 
       
   606 		DP2("Search complete, results in %S with error %d", &iSearchResultsFileName, aError);
       
   607 		if(aError == KErrNone)
       
   608 			{
       
   609 			if (!iOpmlParser) 
       
   610 				{
       
   611 				iOpmlParser = new COpmlParser(*this, iPodcastModel.FsSession());
       
   612 				}
       
   613 
       
   614 			DP("Parsing OPML");
       
   615 			iOpmlParser->ParseOpmlL(iSearchResultsFileName, ETrue);
       
   616 			}
       
   617 		
       
   618 		BaflUtils::DeleteFile(iPodcastModel.FsSession(), iSearchResultsFileName);
       
   619 		}break;
       
   620 		}
       
   621 	DP("CFeedEngine::CompleteL END");
       
   622 	}
       
   623 
       
   624 void CFeedEngine::NotifyFeedUpdateComplete(TInt aError)
       
   625 	{
       
   626 	DP("CFeedEngine::NotifyFeedUpdateComplete");
       
   627 	DBUpdateFeed(*iActiveFeed);
       
   628 	for (TInt i=0;i<iObservers.Count();i++) 
       
   629 		{
       
   630 		TRAP_IGNORE(iObservers[i]->FeedDownloadFinishedL(iAutoUpdatedInitiator?MFeedEngineObserver::EFeedAutoUpdate:MFeedEngineObserver::EFeedManualUpdate, iActiveFeed->Uid(), aError));
       
   631 		}
       
   632 	}
       
   633 
       
   634 void CFeedEngine::Disconnected(CHttpClient* /*aClient*/)
       
   635 	{
       
   636 	}
       
   637 
       
   638 void CFeedEngine::DownloadInfo(CHttpClient* /*aHttpClient */, int /*aTotalBytes*/)
       
   639 	{	
       
   640 	/*DP1("About to download %d bytes", aTotalBytes);
       
   641 	if(aHttpClient == iShowClient && iShowDownloading != NULL && aTotalBytes != -1) {
       
   642 		iShowDownloading->iShowSize = aTotalBytes;
       
   643 		}*/
       
   644 	}
       
   645 
       
   646 EXPORT_C void CFeedEngine::ImportFeedsL(const TDesC& aFile)
       
   647 	{
       
   648 	TFileName opmlPath;
       
   649 	opmlPath.Copy(aFile);
       
   650 	
       
   651 	if (!iOpmlParser) {
       
   652 		iOpmlParser = new COpmlParser(*this, iPodcastModel.FsSession());
       
   653 	}
       
   654 	
       
   655 	iOpmlParser->ParseOpmlL(opmlPath, EFalse);
       
   656 	}
       
   657 
       
   658 EXPORT_C TBool CFeedEngine::ExportFeedsL(TFileName& aFile)
       
   659 	{
       
   660 	RFile rfile;
       
   661 	TFileName privatePath;
       
   662 	iPodcastModel.FsSession().PrivatePath(privatePath);
       
   663 	TInt error = rfile.Temp(iPodcastModel.FsSession(), privatePath, aFile, EFileWrite);
       
   664 	if (error != KErrNone) 
       
   665 		{
       
   666 		DP("CFeedEngine::ExportFeedsL()\tFailed to open file");
       
   667 		return EFalse;
       
   668 		}
       
   669 	CleanupClosePushL(rfile);
       
   670 	
       
   671 	HBufC* templ = HBufC::NewLC(KMaxLineLength);
       
   672 	templ->Des().Copy(KOpmlFeed());
       
   673 	
       
   674 	HBufC* line = HBufC::NewLC(KMaxLineLength);
       
   675 	HBufC* xmlUrl = HBufC::NewLC(KMaxURLLength);		
       
   676 	HBufC* htmlUrl = HBufC::NewLC(KMaxURLLength);		
       
   677 	HBufC* desc = HBufC::NewLC(KMaxDescriptionLength);
       
   678 	HBufC* title = HBufC::NewLC(KMaxTitleLength);
       
   679 
       
   680 	HBufC8* utf8line = CnvUtfConverter::ConvertFromUnicodeToUtf8L(KOpmlHeader());
       
   681 
       
   682 	rfile.Write(*utf8line);
       
   683 	delete utf8line;
       
   684 	
       
   685 	for (int i=0; i<iSortedFeeds.Count(); i++)
       
   686 		{
       
   687 		CFeedInfo *info = iSortedFeeds[i];
       
   688 		DP1("Exporting feed '%S'", &iSortedFeeds[i]->Title());
       
   689 		
       
   690 	// XML URL
       
   691 		TPtr ptrXml(xmlUrl->Des());
       
   692 		if (info->Url() != KNullDesC)
       
   693 			{
       
   694 			ptrXml.Copy(info->Url());
       
   695 			PodcastUtils::XMLEncode(ptrXml);
       
   696 			}
       
   697 		
       
   698 	// Description	
       
   699 		TPtr ptrDesc(desc->Des());
       
   700 		ptrDesc.Zero();
       
   701 		if (info->Description() != KNullDesC) {
       
   702 			ptrDesc.Copy(info->Description());
       
   703 			PodcastUtils::XMLEncode(ptrDesc);
       
   704 		}
       
   705 		
       
   706 	// Title	
       
   707 		TPtr ptrTitle(title->Des());
       
   708 		ptrTitle.Zero();
       
   709 
       
   710 		if (info->Title() != KNullDesC) {
       
   711 			ptrTitle.Copy(info->Title());
       
   712 			PodcastUtils::XMLEncode(ptrTitle);
       
   713 		}
       
   714 		
       
   715 	// HTML URL	
       
   716 		TPtr ptrHtmlUrl(htmlUrl->Des());
       
   717 		ptrHtmlUrl.Zero();
       
   718 
       
   719 		if (info->Link() != KNullDesC) {
       
   720 			ptrHtmlUrl.Copy(info->Link());
       
   721 			PodcastUtils::XMLEncode(ptrHtmlUrl);
       
   722 		}	
       
   723 	// Write line to OPML file
       
   724 		line->Des().Format(*templ, title, desc, xmlUrl, htmlUrl);
       
   725 		utf8line = CnvUtfConverter::ConvertFromUnicodeToUtf8L(*line);
       
   726 		rfile.Write(*utf8line);
       
   727 		delete utf8line;
       
   728 		}
       
   729 
       
   730 	utf8line = CnvUtfConverter::ConvertFromUnicodeToUtf8L(KOpmlFooter());
       
   731 	rfile.Write(*utf8line);
       
   732 	delete utf8line;
       
   733 		
       
   734 	CleanupStack::PopAndDestroy(7);//destroy 6 bufs & close rfile
       
   735 	
       
   736 	return ETrue;
       
   737 	}
       
   738 
       
   739 EXPORT_C CFeedInfo* CFeedEngine::GetFeedInfoByUid(TUint aFeedUid)
       
   740 	{
       
   741 	TInt cnt = iSortedFeeds.Count();
       
   742 	for (TInt i=0;i<cnt;i++)
       
   743 		{
       
   744 		if (iSortedFeeds[i]->Uid() == aFeedUid)
       
   745 			{
       
   746 			return iSortedFeeds[i];
       
   747 			}
       
   748 		}
       
   749 	
       
   750 	return NULL;
       
   751 	}
       
   752 		
       
   753 EXPORT_C const RFeedInfoArray& CFeedEngine::GetSortedFeeds()
       
   754 {
       
   755 	TLinearOrder<CFeedInfo> sortOrder( CFeedEngine::CompareFeedsByTitle);
       
   756 
       
   757 	iSortedFeeds.Sort(sortOrder);
       
   758 	return iSortedFeeds;
       
   759 }
       
   760 
       
   761 TInt CFeedEngine::CompareFeedsByTitle(const CFeedInfo &a, const CFeedInfo &b)
       
   762 	{
       
   763 		//DP2("Comparing %S to %S", &a.Title(), &b.Title());
       
   764 		return a.Title().CompareF(b.Title());
       
   765 	}
       
   766 
       
   767 EXPORT_C void CFeedEngine::GetDownloadedStats(TUint &aNumShows, TUint &aNumUnplayed)
       
   768 	{
       
   769 	DP("CFeedEngine::GetDownloadedStats");
       
   770 	_LIT(KSqlStatement, "select count(*) from shows where downloadstate=%u");
       
   771 	iSqlBuffer.Format(KSqlStatement, EDownloaded);
       
   772 
       
   773 	sqlite3_stmt *st;
       
   774 	 
       
   775 	int rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st,	(const void**) NULL);
       
   776 	
       
   777 	if( rc==SQLITE_OK ){
       
   778 	  	rc = sqlite3_step(st);
       
   779 	  	
       
   780 	  	if (rc == SQLITE_ROW) {
       
   781 	  		aNumShows = sqlite3_column_int(st, 0);
       
   782 	  	}
       
   783 	}
       
   784 		  
       
   785 	sqlite3_finalize(st);
       
   786 
       
   787 	_LIT(KSqlStatement2, "select count(*) from shows where downloadstate=%u and playstate=%u");
       
   788 	iSqlBuffer.Format(KSqlStatement2, EDownloaded, ENeverPlayed);
       
   789 
       
   790 	rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st,	(const void**) NULL);
       
   791 		
       
   792 	if( rc==SQLITE_OK ){
       
   793 	  	rc = sqlite3_step(st);
       
   794 	  	
       
   795 	  	if (rc == SQLITE_ROW) {
       
   796 	  		aNumUnplayed = sqlite3_column_int(st, 0);
       
   797 	  	}
       
   798 	}
       
   799 		  
       
   800 	sqlite3_finalize(st);
       
   801 	}
       
   802 
       
   803 EXPORT_C void CFeedEngine::GetStatsByFeed(TUint aFeedUid, TUint &aNumShows, TUint &aNumUnplayed)
       
   804 	{
       
   805 	//DP1("CFeedEngine::GetStatsByFeed, aFeedUid=%u", aFeedUid);
       
   806 	DBGetStatsByFeed(aFeedUid, aNumShows, aNumUnplayed);
       
   807 	}
       
   808 
       
   809 void CFeedEngine::DBGetStatsByFeed(TUint aFeedUid, TUint &aNumShows, TUint &aNumUnplayed)
       
   810 	{
       
   811 	//DP1("CFeedEngine::DBGetStatsByFeed, feedUid=%u", aFeedUid);
       
   812 	_LIT(KSqlStatement, "select count(*) from shows where feeduid=%u");
       
   813 	iSqlBuffer.Format(KSqlStatement, aFeedUid);
       
   814 
       
   815 	sqlite3_stmt *st;
       
   816 	 
       
   817 	int rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st,	(const void**) NULL);
       
   818 	
       
   819 	if( rc==SQLITE_OK ){
       
   820 	  	rc = sqlite3_step(st);
       
   821 	  	
       
   822 	  	if (rc == SQLITE_ROW) {
       
   823 	  		aNumShows = sqlite3_column_int(st, 0);
       
   824 	  	}
       
   825 	}
       
   826 		  
       
   827 	sqlite3_finalize(st);
       
   828 
       
   829 	_LIT(KSqlStatement2, "select count(*) from shows where feeduid=%u and playstate=0");
       
   830 	iSqlBuffer.Format(KSqlStatement2, aFeedUid);
       
   831 
       
   832 	rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st,	(const void**) NULL);
       
   833 		
       
   834 	if( rc==SQLITE_OK ){
       
   835 	  	rc = sqlite3_step(st);
       
   836 	  	
       
   837 	  	if (rc == SQLITE_ROW) {
       
   838 	  		aNumUnplayed = sqlite3_column_int(st, 0);
       
   839 	  	}
       
   840 	}
       
   841 		  
       
   842 	sqlite3_finalize(st);
       
   843 }
       
   844 
       
   845 TUint CFeedEngine::DBGetFeedCount()
       
   846 	{
       
   847 	 DP("DBGetFeedCount BEGIN");
       
   848 	 sqlite3_stmt *st;
       
   849 	 int rc = sqlite3_prepare_v2(&iDB,"select count(*) from feeds" , -1, &st, (const char**) NULL);
       
   850 	 TUint size = 0;
       
   851 	 
       
   852 	 if( rc==SQLITE_OK ){
       
   853 	  	rc = sqlite3_step(st);
       
   854 	  	
       
   855 	  	if (rc == SQLITE_ROW) {
       
   856 	  		size = sqlite3_column_int(st, 0);
       
   857 	  	}
       
   858 	  }
       
   859 	  
       
   860 	  sqlite3_finalize(st);
       
   861 	  DP1("DBGetFeedCount END=%d", size);
       
   862 	  return size;
       
   863 }
       
   864 
       
   865 void CFeedEngine::DBLoadFeedsL()
       
   866 	{
       
   867 	DP("DBLoadFeeds BEGIN");
       
   868 	iSortedFeeds.Reset();
       
   869 	CFeedInfo *feedInfo = NULL;
       
   870 	
       
   871 	_LIT(KSqlStatement, "select url, title, description, imageurl, imagefile, link, built, lastupdated, uid, feedtype, customtitle from feeds");
       
   872 	iSqlBuffer.Format(KSqlStatement);
       
   873 
       
   874 	sqlite3_stmt *st;
       
   875 	 
       
   876 	TLinearOrder<CFeedInfo> sortOrder( CFeedEngine::CompareFeedsByTitle);
       
   877 
       
   878 	int rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st,	(const void**) NULL);
       
   879 	Cleanup_sqlite3_finalize_PushL(st);
       
   880 	
       
   881 	if (rc==SQLITE_OK) {
       
   882 		rc = sqlite3_step(st);
       
   883 		while(rc == SQLITE_ROW) {
       
   884 			feedInfo = CFeedInfo::NewLC();
       
   885 			
       
   886 			const void *urlz = sqlite3_column_text16(st, 0);
       
   887 			TPtrC16 url((const TUint16*)urlz);
       
   888 			feedInfo->SetUrlL(url);
       
   889 
       
   890 			const void *titlez = sqlite3_column_text16(st, 1);
       
   891 			TPtrC16 title((const TUint16*)titlez);
       
   892 			feedInfo->SetTitleL(title);
       
   893 
       
   894 			const void *descz = sqlite3_column_text16(st, 2);
       
   895 			TPtrC16 desc((const TUint16*)descz);
       
   896 			feedInfo->SetDescriptionL(desc);
       
   897 
       
   898 			const void *imagez = sqlite3_column_text16(st, 3);
       
   899 			TPtrC16 image((const TUint16*)imagez);
       
   900 			feedInfo->SetImageUrlL(image);
       
   901 
       
   902 			const void *imagefilez = sqlite3_column_text16(st, 4);
       
   903 			TPtrC16 imagefile((const TUint16*)imagefilez);
       
   904 			feedInfo->SetImageFileNameL(imagefile);
       
   905 						
       
   906 			const void *linkz = sqlite3_column_text16(st, 5);
       
   907 			TPtrC16 link((const TUint16*)linkz);
       
   908 			feedInfo->SetDescriptionL(link);
       
   909 					
       
   910 			sqlite3_int64 built = sqlite3_column_int64(st, 6);
       
   911 			TTime buildtime(built);
       
   912 			feedInfo->SetBuildDate(buildtime);
       
   913 
       
   914 			sqlite3_int64 lastupdated = sqlite3_column_int64(st, 7);
       
   915 			TTime lastupdatedtime(lastupdated);
       
   916 			feedInfo->SetLastUpdated(lastupdatedtime);
       
   917 			
       
   918 			sqlite3_int64 customtitle = sqlite3_column_int64(st, 10);
       
   919 			if (customtitle) {
       
   920 				feedInfo->SetCustomTitle();
       
   921 			}
       
   922 			
       
   923 			TInt lasterror = sqlite3_column_int(st, 11);
       
   924 			feedInfo->SetLastError(lasterror);
       
   925 			
       
   926 			TLinearOrder<CFeedInfo> sortOrder( CFeedEngine::CompareFeedsByTitle);
       
   927 
       
   928 			iSortedFeeds.InsertInOrder(feedInfo, sortOrder);
       
   929 			
       
   930 			CleanupStack::Pop(feedInfo);
       
   931 				
       
   932 			rc = sqlite3_step(st);
       
   933 		}
       
   934 	}
       
   935 
       
   936 	CleanupStack::PopAndDestroy();//st
       
   937 
       
   938 	DP("DBLoadFeeds END");
       
   939 	}
       
   940 
       
   941 CFeedInfo* CFeedEngine::DBGetFeedInfoByUidL(TUint aFeedUid)
       
   942 	{
       
   943 	DP("CFeedEngine::DBGetFeedInfoByUid");
       
   944 	CFeedInfo *feedInfo = NULL;
       
   945 	_LIT(KSqlStatement, "select url, title, description, imageurl, imagefile, link, built, lastupdated, uid, feedtype, customtitle, lasterror from feeds where uid=%u");
       
   946 	iSqlBuffer.Format(KSqlStatement, aFeedUid);
       
   947 
       
   948 	sqlite3_stmt *st;
       
   949 	 
       
   950 	//DP1("SQL statement length=%d", iSqlBuffer.Length());
       
   951 
       
   952 	int rc = sqlite3_prepare16_v2(&iDB, (const void*)iSqlBuffer.PtrZ() , -1, &st,	(const void**) NULL);
       
   953 	
       
   954 	if (rc==SQLITE_OK) {
       
   955 		Cleanup_sqlite3_finalize_PushL(st);
       
   956 		rc = sqlite3_step(st);
       
   957 		if (rc == SQLITE_ROW) {
       
   958 			feedInfo = CFeedInfo::NewLC();
       
   959 			
       
   960 			const void *urlz = sqlite3_column_text16(st, 0);
       
   961 			TPtrC16 url((const TUint16*)urlz);
       
   962 			feedInfo->SetUrlL(url);
       
   963 
       
   964 			const void *titlez = sqlite3_column_text16(st, 1);
       
   965 			TPtrC16 title((const TUint16*)titlez);
       
   966 			feedInfo->SetTitleL(title);
       
   967 
       
   968 			const void *descz = sqlite3_column_text16(st, 2);
       
   969 			TPtrC16 desc((const TUint16*)descz);
       
   970 			feedInfo->SetDescriptionL(desc);
       
   971 
       
   972 			const void *imagez = sqlite3_column_text16(st, 3);
       
   973 			TPtrC16 image((const TUint16*)imagez);
       
   974 			feedInfo->SetImageUrlL(image);
       
   975 
       
   976 			const void *imagefilez = sqlite3_column_text16(st, 4);
       
   977 			TPtrC16 imagefile((const TUint16*)imagefilez);
       
   978 			feedInfo->SetDescriptionL(imagefile);
       
   979 			
       
   980 			const void *linkz = sqlite3_column_text16(st, 5);
       
   981 			TPtrC16 link((const TUint16*)linkz);
       
   982 			feedInfo->SetDescriptionL(link);
       
   983 					
       
   984 			sqlite3_int64 built = sqlite3_column_int64(st, 6);
       
   985 			TTime buildtime(built);
       
   986 			feedInfo->SetBuildDate(buildtime);
       
   987 
       
   988 			sqlite3_int64 lastupdated = sqlite3_column_int64(st, 7);
       
   989 			TTime lastupdatedtime(lastupdated);
       
   990 			feedInfo->SetLastUpdated(lastupdatedtime);
       
   991 			
       
   992 			sqlite3_int64 customtitle = sqlite3_column_int64(st, 10);
       
   993 			if (customtitle) {
       
   994 				feedInfo->SetCustomTitle();
       
   995 			}
       
   996 			
       
   997 			TInt lasterror = sqlite3_column_int(st, 11);
       
   998 			feedInfo->SetLastError(lasterror);
       
   999 						
       
  1000 			CleanupStack::Pop(feedInfo);
       
  1001 		}
       
  1002 		CleanupStack::PopAndDestroy();//st	
       
  1003 	}
       
  1004 	
       
  1005 	return feedInfo;
       
  1006 }
       
  1007 
       
  1008 EXPORT_C void CFeedEngine::UpdateFeed(CFeedInfo *aItem)
       
  1009 	{
       
  1010 	DBUpdateFeed(*aItem);
       
  1011 	}
       
  1012 
       
  1013 EXPORT_C void CFeedEngine::SearchForFeedL(TDesC& aSearchString)
       
  1014 	{
       
  1015 	DP1("FeedEngine::SearchForFeedL BEGIN, aSearchString=%S", &aSearchString);
       
  1016 	
       
  1017 	if (iClientState != EIdle) {
       
  1018 		User::Leave(KErrInUse);
       
  1019 	}
       
  1020 	TBuf<KMaxURLLength> ssBuf;
       
  1021 	ssBuf.Copy(aSearchString);
       
  1022 	PodcastUtils::ReplaceString(ssBuf, _L(" "), _L("%20"));
       
  1023 	// prepare search URL
       
  1024 	HBufC* templ = HBufC::NewLC(KMaxLineLength);
       
  1025 	templ->Des().Copy(KSearchUrl());
       
  1026 		
       
  1027 	HBufC* url = HBufC::NewLC(KMaxURLLength);		
       
  1028 	url->Des().Format(*templ, &ssBuf);
       
  1029 
       
  1030 	DP1("SearchURL: %S", url);
       
  1031 	
       
  1032 	// crate path to store OPML search results
       
  1033 	iPodcastModel.FsSession().PrivatePath(iSearchResultsFileName);
       
  1034 	
       
  1035 	iSearchResultsFileName.Append(KSearchResultsFileName);
       
  1036 	iSearchResults.ResetAndDestroy();
       
  1037 	// run search
       
  1038 	if(iFeedClient->GetL(*url, iSearchResultsFileName, iPodcastModel.SettingsEngine().SpecificIAP()))
       
  1039 		{
       
  1040 		iClientState = ESearching;
       
  1041 		}
       
  1042 	else
       
  1043 		{
       
  1044 		iClientState = EIdle;
       
  1045 		User::Leave(KErrAbort);
       
  1046 		}
       
  1047 	
       
  1048 	CleanupStack::PopAndDestroy(url);
       
  1049 	CleanupStack::PopAndDestroy(templ);
       
  1050 		
       
  1051 	DP("FeedEngine::SearchForFeedL END");
       
  1052 	}
       
  1053 
       
  1054 EXPORT_C void CFeedEngine::AddSearchResultL(CFeedInfo *item)
       
  1055 	{
       
  1056 	DP1("CFeedEngine::AddSearchResultL, item->Title()=%S", &(item->Title()));
       
  1057 	iSearchResults.AppendL(item);
       
  1058 	}
       
  1059 
       
  1060 EXPORT_C const RFeedInfoArray& CFeedEngine::GetSearchResults()
       
  1061 	{
       
  1062 	return iSearchResults;
       
  1063 	}
       
  1064 
       
  1065 
       
  1066 EXPORT_C void CFeedEngine::OpmlParsingComplete(TUint aNumFeedsAdded)
       
  1067 	{
       
  1068 	NotifyOpmlParsingComplete(aNumFeedsAdded);
       
  1069 	}
       
  1070 
       
  1071 void CFeedEngine::NotifyOpmlParsingComplete(TUint aNumFeedsAdded)
       
  1072 	{
       
  1073 	for (TInt i=0;i<iObservers.Count();i++) 
       
  1074 		{
       
  1075 		iObservers[i]->OpmlParsingComplete(aNumFeedsAdded);
       
  1076 		}
       
  1077 	}