1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
7 *
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9 *
10 * $Id: favicon.cc 11522 2010-12-12 16:43:19Z charles $
11 */
12
13#include <QDesktopServices>
14#include <QDir>
15#include <QNetworkAccessManager>
16#include <QNetworkReply>
17#include <QNetworkRequest>
18
19#include "favicon.h"
20
21/***
22****
23***/
24
25Favicons :: Favicons( )
26{
27    myNAM = new QNetworkAccessManager( );
28    connect( myNAM, SIGNAL(finished(QNetworkReply*)), this, SLOT(onRequestFinished(QNetworkReply*)) );
29}
30
31Favicons :: ~Favicons( )
32{
33    delete myNAM;
34}
35
36/***
37****
38***/
39
40QString
41Favicons :: getCacheDir( )
42{
43    const QString base = QDesktopServices::storageLocation( QDesktopServices::CacheLocation );
44    return QDir( base ).absoluteFilePath( "favicons" );
45}
46
47void
48Favicons :: ensureCacheDirHasBeenScanned( )
49{
50    static bool hasBeenScanned = false;
51
52    if( !hasBeenScanned )
53    {
54        hasBeenScanned = true;
55
56        QDir cacheDir( getCacheDir( ) );
57        cacheDir.mkpath( cacheDir.absolutePath( ) );
58
59        QStringList files = cacheDir.entryList( QDir::Files|QDir::Readable );
60        foreach( QString file, files ) {
61            QPixmap pixmap;
62            pixmap.load( cacheDir.absoluteFilePath( file ) );
63            if( !pixmap.isNull( ) )
64                myPixmaps.insert( file, pixmap );
65        }
66    }
67}
68
69QString
70Favicons :: getHost( const QUrl& url )
71{
72    QString host = url.host( );
73    const int first_dot = host.indexOf( '.' );
74    const int last_dot = host.lastIndexOf( '.' );
75
76    if( ( first_dot != -1 ) && ( last_dot != -1 ) && ( first_dot != last_dot ) )
77        host.remove( 0, first_dot + 1 );
78
79    return host;
80}
81
82QPixmap
83Favicons :: find( const QUrl& url )
84{
85    return findFromHost( getHost( url ) );
86}
87
88namespace
89{
90    const QSize rightSize( 16, 16 );
91};
92
93QPixmap
94Favicons :: findFromHost( const QString& host )
95{
96    ensureCacheDirHasBeenScanned( );
97
98    const QPixmap pixmap = myPixmaps[ host ];
99    return pixmap.size()==rightSize ? pixmap : pixmap.scaled(rightSize);
100}
101
102void
103Favicons :: add( const QUrl& url )
104{
105    ensureCacheDirHasBeenScanned( );
106
107    const QString host = getHost( url );
108
109    if( !myPixmaps.contains( host ) )
110    {
111        // add a placholder s.t. we only ping the server once per session
112        QPixmap tmp( rightSize );
113        tmp.fill( Qt::transparent );
114        myPixmaps.insert( host, tmp );
115
116        // try to download the favicon
117        const QString path = "http://" + host + "/favicon.";
118        QStringList suffixes;
119        suffixes << "ico" << "png" << "gif" << "jpg";
120        foreach( QString suffix, suffixes )
121            myNAM->get( QNetworkRequest( path + suffix ) );
122    }
123}
124
125void
126Favicons :: onRequestFinished( QNetworkReply * reply )
127{
128    const QString host = reply->url().host();
129
130    QPixmap pixmap;
131
132    const QByteArray content = reply->readAll( );
133    if( !reply->error( ) )
134        pixmap.loadFromData( content );
135
136    if( !pixmap.isNull( ) )
137    {
138        // save it in memory...
139        myPixmaps.insert( host, pixmap );
140
141        // save it on disk...
142        QDir cacheDir( getCacheDir( ) );
143        cacheDir.mkpath( cacheDir.absolutePath( ) );
144        QFile file( cacheDir.absoluteFilePath( host ) );
145        file.open( QIODevice::WriteOnly );
146        file.write( content );
147        file.close( );
148
149        // notify listeners
150        emit pixmapReady( host );
151    }
152}
153