]> git.sesse.net Git - vlc/blob - modules/gui/wxwindows/updatevlc.cpp
VLC update checker in the wxWidgets interface (in help menu)
[vlc] / modules / gui / wxwindows / updatevlc.cpp
1 /*****************************************************************************
2  * updatevlc.cpp : Check for VLC updates dialog
3  *****************************************************************************
4  * Copyright (C) 2000-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc/intf.h>
29
30 #include <wx/progdlg.h>
31
32 #include "wxwindows.h"
33
34 #include "vlc_block.h"
35 #include "vlc_stream.h"
36 #include "vlc_xml.h"
37
38 /* define UPDATE_VLC_OS and UPDATE_VLC_ARCH */
39 /* todo : move this somewhere else (isn't wx specific) */
40
41 #ifdef WIN32
42 #   define UPDATE_VLC_OS "windows"
43 #   define UPDATE_VLC_ARCH "i386"
44 #else
45 #ifdef SYS_DARWIN
46 #   define UPDATE_VLC_OS "macosx"
47 #   define UPDATE_VLC_ARCH "ppc"
48 #else
49 #   define UPDATE_VLC_OS "windows"
50 #   define UPDATE_VLC_ARCH "i386"
51 #endif
52 #endif
53
54 /* arch == "*" and os == "*" concern non OS or arch specific stuff */
55
56 #define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status"
57 #define UPDATE_VLC_MIRRORS_URL "http://update.videolan.org/mirrors"
58
59 #define UPDATE_VLC_DOWNLOAD_BUFFER_SIZE 2048
60
61 class UpdatesTreeItem : public wxTreeItemData
62 {
63     public:
64         UpdatesTreeItem( wxString _url ):wxTreeItemData()
65         {
66             url = _url;
67         }
68         wxString url;
69 };
70
71 /*****************************************************************************
72  * Event Table.
73  *****************************************************************************/
74
75 /* IDs for the controls and the menu commands */
76 enum
77 {
78     Close_Event,
79     CheckForUpdate_Event,
80     MirrorChoice_Event,
81     UpdatesTreeActivate_Event
82 };
83
84 BEGIN_EVENT_TABLE(UpdateVLC, wxFrame)
85     /* Button events */
86     EVT_BUTTON(wxID_OK, UpdateVLC::OnButtonClose)
87     EVT_BUTTON(CheckForUpdate_Event, UpdateVLC::OnCheckForUpdate)
88
89     /* Choice events */
90     EVT_CHOICE(MirrorChoice_Event, UpdateVLC::OnMirrorChoice)
91
92     /* Tree events */
93     EVT_TREE_ITEM_ACTIVATED(UpdatesTreeActivate_Event, UpdateVLC::OnUpdatesTreeActivate)
94
95     /* Hide the window when the user closes the window */
96     EVT_CLOSE(UpdateVLC::OnClose)
97
98 END_EVENT_TABLE()
99
100 /*****************************************************************************
101  * Constructor.
102  *****************************************************************************/
103 UpdateVLC::UpdateVLC( intf_thread_t *_p_intf, wxWindow *p_parent ):
104     wxFrame( p_parent, -1, wxU(_("Check for updates ...")), wxDefaultPosition,
105              wxDefaultSize, wxDEFAULT_FRAME_STYLE )
106 {
107     /* Initializations */
108     p_intf = _p_intf;
109     release_type = wxT( "stable" );
110     SetIcon( *p_intf->p_sys->p_icon );
111     SetAutoLayout( TRUE );
112
113     /* Create a panel to put everything in */
114     wxPanel *panel = new wxPanel( this, -1 );
115     panel->SetAutoLayout( TRUE );
116
117     updates_tree =
118         new wxTreeCtrl( panel, UpdatesTreeActivate_Event, wxDefaultPosition,
119                         wxSize( 400, 200 ),
120                         wxTR_HAS_BUTTONS | wxTR_HIDE_ROOT | wxSUNKEN_BORDER );
121
122     /* Place everything in sizers */
123     wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
124     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
125     wxBoxSizer *subpanel_sizer = new wxBoxSizer( wxHORIZONTAL );
126     panel_sizer->Add( updates_tree, 0, wxGROW | wxALL, 5 );
127     wxButton *update_button =
128         new wxButton( panel, CheckForUpdate_Event,
129                       wxU(_("Check for updates now !")) );
130     subpanel_sizer->Add( update_button, 0, wxALL, 5 );
131     wxArrayString *choices_array = new wxArrayString();
132     choices_array->Add( wxT("") );
133     mirrors_choice =
134         new wxChoice( panel, MirrorChoice_Event, wxDefaultPosition,
135                       wxSize( 200, -1 ), *choices_array );
136     subpanel_sizer->Add( mirrors_choice, 0, wxALL, 5 );
137     subpanel_sizer->Layout();
138     panel_sizer->Add( subpanel_sizer, 0, wxALL , 0 );
139     panel_sizer->Layout();
140     panel->SetSizerAndFit( panel_sizer );
141     main_sizer->Add( panel, 0, wxALL | wxGROW, 0 );
142     main_sizer->Layout();
143     SetSizerAndFit( main_sizer );
144
145     UpdateMirrorsChoice();
146     UpdateUpdatesTree();
147 }
148
149
150 UpdateVLC::~UpdateVLC()
151 {
152 }
153
154 /* this function gets all the info from the xml files hosted on
155 http://update.videolan.org/ and stores it in appropriate lists */
156 void UpdateVLC::GetData()
157 {
158     stream_t *p_stream = NULL;
159     char *psz_eltname = NULL;
160     char *psz_name = NULL;
161     char *psz_value = NULL;
162     char *psz_eltvalue = NULL;
163     xml_t *p_xml = NULL;
164     xml_reader_t *p_xml_reader = NULL;
165     bool b_os = false;
166     bool b_arch = false;
167
168     struct update_file_t tmp_file;
169     struct update_version_t tmp_version;
170     std::list<update_version_t>::iterator it;
171     std::list<update_file_t>::iterator it_files;
172
173     struct update_mirror_t tmp_mirror;
174
175     p_xml = xml_Create( p_intf );
176     if( !p_xml )
177     {
178         msg_Err( p_intf, "Failed to open XML parser" );
179         // FIXME : display error message in dialog
180         return;
181     }
182
183     p_stream = stream_UrlNew( p_intf, UPDATE_VLC_STATUS_URL );
184     if( !p_stream )
185     {
186         msg_Err( p_intf, "Failed to open %s for reading",
187                  UPDATE_VLC_STATUS_URL );
188         // FIXME : display error message in dialog
189         return;
190     }
191
192     p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
193
194     if( !p_xml_reader )
195     {
196         msg_Err( p_intf, "Failed to open %s for parsing",
197                  UPDATE_VLC_STATUS_URL );
198         // FIXME : display error message in dialog
199         return;
200     }
201
202     /* empty tree */
203     m_versions.clear();
204
205     /* build tree */
206     while( xml_ReaderRead( p_xml_reader ) == 1 )
207     {
208         switch( xml_ReaderNodeType( p_xml_reader ) )
209         {
210             // Error
211             case -1:
212                 // TODO : print message
213                 return;
214
215             case XML_READER_STARTELEM:
216                 psz_eltname = xml_ReaderName( p_xml_reader );
217                 if( !psz_eltname )
218                 {
219                     // TODO : print message
220                     return;
221                 }
222                 msg_Dbg( p_intf, "element name : %s", psz_eltname );
223                 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
224                 {
225                     psz_name = xml_ReaderName( p_xml_reader );
226                     psz_value = xml_ReaderValue( p_xml_reader );
227                     if( !psz_name || !psz_value )
228                     {
229                         // TODO : print message
230                         free( psz_eltname );
231                         return;
232                     }
233                     msg_Dbg( p_intf, "  attribute %s = %s",
234                              psz_name, psz_value );
235                     if( b_os && b_arch )
236                     {
237                         if( strcmp( psz_eltname, "version" ) == 0 )
238                         {
239                             if( !strcmp( psz_name, "type" ) )
240                                 tmp_version.type = wxU( psz_value );
241                             if( !strcmp( psz_name, "major" ) )
242                                 tmp_version.major = wxU( psz_value );
243                             if( !strcmp( psz_name, "minor" ) )
244                                 tmp_version.minor = wxU( psz_value );
245                             if( !strcmp( psz_name, "revision" ) )
246                                 tmp_version.revision = wxU( psz_value );
247                             if( !strcmp( psz_name, "extra" ) )
248                                 tmp_version.extra = wxU( psz_value );
249                         }
250                         if( !strcmp( psz_eltname, "file" ) )
251                         {
252                             if( !strcmp( psz_name, "type" ) )
253                                 tmp_file.type = wxU( psz_value );
254                             if( !strcmp( psz_name, "md5" ) )
255                                 tmp_file.md5 = wxU( psz_value );
256                             if( !strcmp( psz_name, "size" ) )
257                                 tmp_file.size = wxU( psz_value );
258                             if( !strcmp( psz_name, "url" ) )
259                                 tmp_file.url = wxU( psz_value );
260                             if( !strcmp( psz_name, "desciption" ) )
261                                 tmp_file.description = wxU( psz_value );
262                         }
263                     }
264                     if( !strcmp( psz_name, "name" )
265                         && ( !strcmp( psz_value, UPDATE_VLC_OS )
266                            || !strcmp( psz_value, "*" ) )
267                         && !strcmp( psz_eltname, "os" ) )
268                     {
269                         b_os = true;
270                     }
271                     if( b_os && !strcmp( psz_name, "name" )
272                         && ( !strcmp( psz_value, UPDATE_VLC_ARCH )
273                            || !strcmp( psz_value, "*" ) )
274                         && !strcmp( psz_eltname, "arch" ) )
275                     {
276                         b_arch = true;
277                     }
278                     free( psz_name );
279                     free( psz_value );
280                 }
281                 if( ( b_os && b_arch && strcmp( psz_eltname, "arch" ) ) )
282                 {
283                     if( !strcmp( psz_eltname, "version" ) )
284                     {
285                         it = m_versions.begin();
286                         while( it != m_versions.end() )
287                         {
288                             if( it->type == tmp_version.type
289                                 && it->major == tmp_version.major
290                                 && it->minor == tmp_version.minor
291                                 && it->revision == tmp_version.revision
292                                 && it->extra == tmp_version.extra )
293                             {
294                                 break;
295                             }
296                             it++;
297                         }
298                         if( it == m_versions.end() )
299                         {
300                             m_versions.push_back( tmp_version );
301                             it = m_versions.begin();
302                             while( it != m_versions.end() )
303                             {
304                                 if( it->type == tmp_version.type
305                                     && it->major == tmp_version.major
306                                     && it->minor == tmp_version.minor
307                                     && it->revision == tmp_version.revision
308                                     && it->extra == tmp_version.extra )
309                                 {
310                                     break;
311                                 }
312                                 it++;
313                             }
314                         }
315                         tmp_version.type = wxT( "" );
316                         tmp_version.major = wxT( "" );
317                         tmp_version.minor = wxT( "" );
318                         tmp_version.revision = wxT( "" );
319                         tmp_version.extra = wxT( "" );
320                     }
321                     if( !strcmp( psz_eltname, "file" ) )
322                     {
323                         it->m_files.push_back( tmp_file );
324                         tmp_file.type = wxT( "" );
325                         tmp_file.md5 = wxT( "" );
326                         tmp_file.size = wxT( "" );
327                         tmp_file.url = wxT( "" );
328                         tmp_file.description = wxT( "" );
329                     }
330                 }
331                 free( psz_eltname );
332                 break;
333
334             case XML_READER_ENDELEM:
335                 psz_eltname = xml_ReaderName( p_xml_reader );
336                 if( !psz_eltname )
337                 {
338                     // TODO : print message
339                     return;
340                 }
341                 msg_Dbg( p_intf, "element end : %s", psz_eltname );
342                 if( !strcmp( psz_eltname, "os" ) )
343                     b_os = false;
344                 if( !strcmp( psz_eltname, "arch" ) )
345                     b_arch = false;
346                 free( psz_eltname );
347                 break;
348
349             case XML_READER_TEXT:
350                 psz_eltvalue = xml_ReaderValue( p_xml_reader );
351                 msg_Dbg( p_intf, "  text : %s", psz_eltvalue );
352                 /* This doesn't look safe ... but it works */
353                 it->m_files.back().description = wxU( psz_eltvalue );
354                 free( psz_eltvalue );
355                 break;
356         }
357     }
358
359     if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
360     if( p_stream ) stream_Delete( p_stream );
361
362     p_stream = stream_UrlNew( p_intf, UPDATE_VLC_MIRRORS_URL );
363     if( !p_stream )
364     {
365         msg_Err( p_intf, "Failed to open %s for reading",
366                  UPDATE_VLC_MIRRORS_URL );
367         // FIXME : display error message in dialog
368         return;
369     }
370
371     p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
372
373     if( !p_xml_reader )
374     {
375         msg_Err( p_intf, "Failed to open %s for parsing",
376                  UPDATE_VLC_MIRRORS_URL );
377         // FIXME : display error message in dialog
378         return;
379     }
380     /* empty list */
381     m_mirrors.clear();
382
383     /* build list */
384     while( xml_ReaderRead( p_xml_reader ) == 1 )
385     {
386         switch( xml_ReaderNodeType( p_xml_reader ) )
387         {
388             // Error
389             case -1:
390                 // TODO : print message
391                 return;
392
393             case XML_READER_STARTELEM:
394                 psz_eltname = xml_ReaderName( p_xml_reader );
395                 if( !psz_eltname )
396                 {
397                     // TODO : print message
398                     return;
399                 }
400                 msg_Dbg( p_intf, "element name : %s", psz_eltname );
401                 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
402                 {
403                     psz_name = xml_ReaderName( p_xml_reader );
404                     psz_value = xml_ReaderValue( p_xml_reader );
405                     if( !psz_name || !psz_value )
406                     {
407                         // TODO : print message
408                         free( psz_eltname );
409                         return;
410                     }
411                     msg_Dbg( p_intf, "  attribute %s = %s",
412                              psz_name, psz_value );
413                     if( !strcmp( psz_eltname, "mirror" ) )
414                     {
415                         if( !strcmp( psz_name, "name" ) )
416                             tmp_mirror.name = wxU( psz_value );
417                         if( !strcmp( psz_name, "location" ) )
418                             tmp_mirror.location = wxU( psz_value );
419                     }
420                     if( !strcmp( psz_eltname, "url" ) )
421                     {
422                         if( !strcmp( psz_name, "type" ) )
423                             tmp_mirror.type = wxU( psz_value );
424                         if( !strcmp( psz_name, "base" ) )
425                             tmp_mirror.base_url = wxU( psz_value );
426                     }
427                     free( psz_name );
428                     free( psz_value );
429                 }
430                 if( !strcmp( psz_eltname, "url" ) )
431                 {
432                     m_mirrors.push_back( tmp_mirror );
433                     tmp_mirror.type = wxT( "" );
434                     tmp_mirror.base_url = wxT( "" );
435                 }
436                 free( psz_eltname );
437                 break;
438
439             case XML_READER_ENDELEM:
440                 psz_eltname = xml_ReaderName( p_xml_reader );
441                 if( !psz_eltname )
442                 {
443                     // TODO : print message
444                     return;
445                 }
446                 msg_Dbg( p_intf, "element end : %s", psz_eltname );
447                 if( !strcmp( psz_eltname, "mirror" ) )
448                 {
449                     tmp_mirror.name = wxT( "" );
450                     tmp_mirror.location = wxT( "" );
451                 }
452                 free( psz_eltname );
453                 break;
454
455             case XML_READER_TEXT:
456                 psz_eltvalue = xml_ReaderValue( p_xml_reader );
457                 msg_Dbg( p_intf, "  text : %s", psz_eltvalue );
458                 free( psz_eltvalue );
459                 break;
460         }
461     }
462
463
464     if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
465     if( p_stream ) stream_Delete( p_stream );
466     if( p_xml ) xml_Delete( p_xml );
467 }
468
469 void UpdateVLC::UpdateUpdatesTree()
470 {
471     wxTreeItemId parent;
472     std::list<update_version_t>::iterator it;
473     std::list<update_file_t>::iterator it_files;
474     std::list<update_mirror_t>::iterator it_mirrors;
475
476     int selection = mirrors_choice->GetSelection();
477     wxString base_url = wxT( "" );
478
479     if( selection-- )
480     {
481         it_mirrors = m_mirrors.begin();
482         while( it_mirrors != m_mirrors.end() && selection )
483         {
484             it_mirrors++;
485             selection--;
486         }
487         if( it_mirrors != m_mirrors.end() ) base_url = it_mirrors->base_url;
488     }
489
490     /* empty tree */
491     updates_tree->DeleteAllItems();
492
493     /* build tree */
494     parent = updates_tree->AppendItem( updates_root, wxT( "" ) );
495     updates_tree->AppendItem( parent,
496                              wxT( "Current version : "PACKAGE_VERSION ),
497                              -1, -1, new UpdatesTreeItem( wxT( "" ) ));
498     it = m_versions.begin();
499     while( it != m_versions.end() )
500     {
501         wxTreeItemId cat = updates_tree->AppendItem( parent,
502                          wxT("VLC media player ")+ it->major + wxT(".")
503                          + it->minor + wxT(".") + it->revision + wxT("-")
504                          + it->extra + wxT(" (") + it->type + wxT(")"),
505                          -1, -1, new UpdatesTreeItem( wxT( "" ) ));
506         it_files = it->m_files.begin();
507         while( it_files != it->m_files.end() )
508         {
509             wxString url = (it_files->url[0]=='/' ? base_url : wxT( "" ) )
510                            + it_files->url;
511             wxTreeItemId file =
512                 updates_tree->AppendItem( cat, it_files->description,
513                    -1, -1, new UpdatesTreeItem( url ) );
514             updates_tree->AppendItem( file,
515                 wxU(_("type : ")) + it_files->type,
516                 -1, -1, new UpdatesTreeItem( url ));
517             updates_tree->AppendItem( file, wxU(_("URL : ")) + url,
518                                       -1, -1, new UpdatesTreeItem( url ));
519             if( it_files->size != wxT( "" ) )
520                 updates_tree->AppendItem( file,
521                     wxU(_("file size : ")) + it_files->size,
522                     -1, -1, new UpdatesTreeItem( url ));
523             if( it_files->md5 != wxT( "" ) )
524                 updates_tree->AppendItem( file,
525                     wxU(_("file md5 hash : ")) + it_files->md5,
526                     -1, -1, new UpdatesTreeItem( url ));
527             it_files ++;
528         }
529         it ++;
530         updates_tree->Expand( cat );
531     }
532 }
533
534 void UpdateVLC::UpdateMirrorsChoice()
535 {
536     std::list<update_mirror_t>::iterator it_mirrors;
537
538     mirrors_choice->Clear();
539     mirrors_choice->Append( wxU(_("Choose a mirror")) );
540     it_mirrors = m_mirrors.begin();
541     while( it_mirrors != m_mirrors.end() )
542     {
543         mirrors_choice->Append( it_mirrors->name + wxT(" (")
544                                 + it_mirrors->location + wxT(") [")
545                                 + it_mirrors->type + wxT("]") );
546         it_mirrors++;
547     }
548     mirrors_choice->SetSelection( 0 );
549 }
550
551 /*void UpdateVLC::UpdateUpdateVLC()
552 {
553     UpdateUpdatesTree();
554     UpdateMirrorsChoice();
555 }*/
556
557 void UpdateVLC::OnButtonClose( wxCommandEvent& event )
558 {
559     wxCloseEvent cevent;
560     OnClose(cevent);
561 }
562
563 void UpdateVLC::OnClose( wxCloseEvent& WXUNUSED(event) )
564 {
565     Hide();
566 }
567
568 void UpdateVLC::OnCheckForUpdate( wxCommandEvent& event )
569 {
570     GetData();
571     UpdateMirrorsChoice();
572     UpdateUpdatesTree();
573 }
574
575 void UpdateVLC::OnMirrorChoice( wxCommandEvent& event )
576 {
577     UpdateUpdatesTree();
578 }
579
580 void UpdateVLC::OnUpdatesTreeActivate( wxTreeEvent& event )
581 {
582     wxString url =
583       ((UpdatesTreeItem *)(updates_tree->GetItemData(event.GetItem())))->url;
584     if( url != wxT( "" ) ? url[0] != '/' : false )
585     {
586         wxFileDialog *filedialog =
587             new wxFileDialog( updates_tree, wxU(_("Save file ...")),
588                               wxT(""), url.AfterLast( '/' ), wxT("*.*"),
589                               wxSAVE | wxOVERWRITE_PROMPT );
590         if( filedialog->ShowModal() == wxID_OK )
591         {
592             DownloadFile( url, filedialog->GetPath() );
593         }
594     }
595 }
596
597 void UpdateVLC::DownloadFile( wxString url, wxString dst )
598 {
599     msg_Dbg( p_intf, "Downloading %s to %s",
600              (const char *)url.mb_str(), (const char *)dst.mb_str() );
601
602     stream_t *p_stream = NULL;
603     p_stream = stream_UrlNew( p_intf, (const char *)url.mb_str() );
604     if( !p_stream )
605     {
606         msg_Err( p_intf, "Failed to open %s for reading", (const char *)url.mb_str() );
607         // FIXME : display error message in dialog
608         return;
609     }
610
611     FILE *p_file = NULL;
612     p_file = fopen( (const char *)dst.mb_str(), "w" );
613     if( !p_file )
614     {
615         msg_Err( p_intf, "Failed to open %s for writing", (const char *)dst.mb_str() );
616         // FIXME : display error message in dialog
617         return;
618     }
619
620     int i_progress = 0;
621     wxProgressDialog *progressdialog =
622         new wxProgressDialog( wxU(_("Downloading...")),
623         wxU(wxT("Src: ") +url + wxT("\nDst: ") +dst ),
624         (int)(stream_Size(p_stream)/UPDATE_VLC_DOWNLOAD_BUFFER_SIZE), NULL,
625         wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_AUTO_HIDE
626         | wxPD_CAN_ABORT );
627
628     void *buffer = (void *)malloc( UPDATE_VLC_DOWNLOAD_BUFFER_SIZE );
629     while( stream_Read( p_stream, buffer, UPDATE_VLC_DOWNLOAD_BUFFER_SIZE ) )
630     {
631         fwrite( buffer, UPDATE_VLC_DOWNLOAD_BUFFER_SIZE, 1, p_file);
632         if( !progressdialog->Update(++i_progress) )
633         {
634             free( buffer );
635             fclose( p_file );
636             if( p_stream ) stream_Delete( p_stream );
637             progressdialog->Destroy();
638             msg_Warn( p_intf, "User aborted download" );
639             return;
640         }
641     }
642     progressdialog->Destroy();
643     msg_Dbg( p_intf, "Download finished" );
644     free( buffer );
645     fclose( p_file );
646     if( p_stream ) stream_Delete( p_stream );
647 }