Hello!

I just signed up for the distorted view show member's feed (you get extra show 
content, etc). The problem is that the download server is password protected 
which means the podcast client must understand how to do basic http 
authentication.

Attached is a patch against svn revision 351 to add the ability to download 
podcasts from password protected server directories. It stores the username 
and password in the index.xml files in each local podcast directory. The 
passwords are obfuscated using a simple mapping table and the 
string.translate function. I know this isn't secure but I don't think it's 
advisable to store the passwords in plaintext either. The username and 
password are set via the "Advanced" tab in the "Edit channel information" 
dialogue. 

If you would like to test it, I set up a feed and password protected directory 
on my server (with a 5 second "podcast" I made). Here's the info for that:
feed url: http://nikosapi.org/test_protected_feed/feed.xml
username: user01
password: testfeed

Also, I fixed a little bug that would cause gPodder to freeze when loading a 
cover file with 0 length.

nick
--- gpodder-r351/data/gpodder.glade	2007-07-14 14:47:23.000000000 -0400
+++ gpodder-r351-dev/data/gpodder.glade	2007-07-14 19:25:08.000000000 -0400
@@ -1516,6 +1516,131 @@
 	      </child>
 
 	      <child>
+		<widget class="GtkTable" id="table10">
+		  <property name="visible">True</property>
+		  <property name="n_rows">2</property>
+		  <property name="n_columns">2</property>
+		  <property name="homogeneous">False</property>
+		  <property name="row_spacing">0</property>
+		  <property name="column_spacing">0</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="label93">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Feed Username: </property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">0</property>
+		      <property name="bottom_attach">1</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="label94">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Feed Password: </property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">1</property>
+		      <property name="bottom_attach">2</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkEntry" id="FeedUsername">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="editable">True</property>
+		      <property name="visibility">True</property>
+		      <property name="max_length">0</property>
+		      <property name="text" translatable="yes"></property>
+		      <property name="has_frame">True</property>
+		      <property name="invisible_char">●</property>
+		      <property name="activates_default">False</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">0</property>
+		      <property name="bottom_attach">1</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkEntry" id="FeedPassword">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="editable">True</property>
+		      <property name="visibility">False</property>
+		      <property name="max_length">0</property>
+		      <property name="text" translatable="yes"></property>
+		      <property name="has_frame">True</property>
+		      <property name="invisible_char">●</property>
+		      <property name="activates_default">False</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">1</property>
+		      <property name="bottom_attach">2</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHSeparator" id="hseparator13">
+		  <property name="visible">True</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
 		<widget class="GtkTable" id="table3">
 		  <property name="border_width">5</property>
 		  <property name="visible">True</property>
@@ -1639,7 +1764,7 @@
 		</widget>
 		<packing>
 		  <property name="padding">0</property>
-		  <property name="expand">True</property>
+		  <property name="expand">False</property>
 		  <property name="fill">True</property>
 		</packing>
 	      </child>
diff -u gpodder-r351/src/gpodder/gpodder.py gpodder-r351-dev/src/gpodder/gpodder.py
--- gpodder-r351/src/gpodder/gpodder.py	2007-07-14 14:47:17.000000000 -0400
+++ gpodder-r351-dev/src/gpodder/gpodder.py	2007-07-14 17:46:07.000000000 -0400
@@ -1142,6 +1142,8 @@
         self.cbNoSync.set_active( not channel.sync_to_devices)
         self.musicPlaylist.set_text( channel.device_playlist_name)
         self.cbMusicChannel.set_active( channel.is_music_channel)
+        self.FeedUsername.set_text( channel.username)
+        self.FeedPassword.set_text( channel.password)
         gPodderLib().get_image_from_url( channel.image, self.imgCover.set_from_pixbuf, self.labelCoverStatus.set_text, self.labelCoverStatus.hide, channel.cover_file)
         
         b = gtk.TextBuffer()
@@ -1174,6 +1176,8 @@
         self.channel.is_music_channel = self.cbMusicChannel.get_active()
         self.channel.device_playlist_name = self.musicPlaylist.get_text()
         self.channel.set_custom_title( self.entryTitle.get_text())
+        self.channel.username = self.FeedUsername.get_text().strip()
+        self.channel.password = self.FeedPassword.get_text().strip()
         self.channel.save_metadata_to_localdb()
 
         self.gPodderChannel.destroy()
diff -u gpodder-r351/src/gpodder/liblocdbreader.py gpodder-r351-dev/src/gpodder/liblocdbreader.py
--- gpodder-r351/src/gpodder/liblocdbreader.py	2007-07-14 14:47:17.000000000 -0400
+++ gpodder-r351-dev/src/gpodder/liblocdbreader.py	2007-07-14 19:45:25.000000000 -0400
@@ -85,6 +86,8 @@
             if attrs.get('nosync', 'false').lower() == 'true':
                 self.channel.sync_to_devices = False
             self.channel.override_title = attrs.get('title','')
+            self.channel.username = attrs.get('username', '')
+            self.channel.password = self.channel.unobfuscate_password(attrs.get('password', ''))
     
     def endElement( self, name):
         if self.current_item == None:
diff -u gpodder-r351/src/gpodder/liblocdbwriter.py gpodder-r351-dev/src/gpodder/liblocdbwriter.py
--- gpodder-r351/src/gpodder/liblocdbwriter.py	2007-07-14 14:47:17.000000000 -0400
+++ gpodder-r351-dev/src/gpodder/liblocdbwriter.py	2007-07-14 18:07:10.000000000 -0400
@@ -64,6 +64,9 @@
         self.ofile.write( ' playlist="%s"' % (channel.device_playlist_name))
         if channel.override_title:
             self.ofile.write( ' title="%s"' % (channel.override_title))
+        if channel.username or channel.password:
+            self.ofile.write( ' username="%s"' % (channel.username))
+            self.ofile.write( ' password="%s"' % (channel.obfuscate_password(channel.password)))
         self.ofile.write( '/>'+"\n")
 
     def writeEpisodeMetadata( self, episode):
diff -u gpodder-r351/src/gpodder/libpodcasts.py gpodder-r351-dev/src/gpodder/libpodcasts.py
--- gpodder-r351/src/gpodder/libpodcasts.py	2007-07-14 14:47:17.000000000 -0400
+++ gpodder-r351-dev/src/gpodder/libpodcasts.py	2007-07-14 20:40:27.000000000 -0400
@@ -64,6 +64,7 @@
 
 import md5
 
+import string
 
 class podcastChannel(ListType):
     """holds data for a complete channel"""
@@ -87,6 +88,11 @@
         self.device_playlist_name = 'gPodder'
         # if set, this overrides the channel-provided title
         self.override_title = ''
+        self.username = ''
+        self.password = ''
+        # mapping table for maketrans
+        self.map_from = 'abcdefghijklmnopqrstuvwxyz0123456789'
+        self.map_to =   'qazwsxedcrfvtgbyhnujmikolp9514738062'
         
     def get_filename( self):
         """Return the MD5 sum of the channel URL"""
@@ -155,6 +161,8 @@
         self.is_music_channel = ch.is_music_channel
         self.device_playlist_name = ch.device_playlist_name
         self.override_title = ch.override_title
+        self.username = ch.username
+        self.password = ch.password
 
     def newest_pubdate_downloaded( self):
         gl = libgpodder.gPodderLib()
@@ -493,6 +501,20 @@
 
         libgpodder.releaseLock()
 
+    def obfuscate_password(self, password):
+        translation_table = string.maketrans(self.map_from + self.map_from.upper(), self.map_to + self.map_to.upper())
+        return string.translate(password, translation_table)
+        
+    def unobfuscate_password(self, password_obfus):
+        if password_obfus == '':
+            return
+        translation_table = string.maketrans(self.map_to + self.map_to.upper(), self.map_from + self.map_from.upper())
+        try:
+            # For now at least, only ascii passwords will work
+            return string.translate(password_obfus.encode('ascii'), translation_table)
+        except:
+            return
+
 class podcastItem(object):
     """holds data for one object in a channel"""
     def __init__( self,
@@ -686,7 +708,7 @@
 
         new_model.set( new_iter, 7, '%s\n<small>%s</small>' % ( saxutils.escape( channel.title), saxutils.escape( channel.description.split('\n')[0]), ))
 
-        if os.path.exists( channel.cover_file):
+        if os.path.exists( channel.cover_file) and os.path.getsize(channel.cover_file) > 0:
             new_model.set( new_iter, 8, gtk.gdk.pixbuf_new_from_file_at_size( channel.cover_file, 32, 32))
         else:
             iconsize = gtk.icon_size_from_name('channel-icon')
diff -u gpodder-r351/src/gpodder/libwget.py gpodder-r351-dev/src/gpodder/libwget.py
--- gpodder-r351/src/gpodder/libwget.py	2007-07-14 14:47:17.000000000 -0400
+++ gpodder-r351-dev/src/gpodder/libwget.py	2007-07-14 18:53:55.000000000 -0400
@@ -85,7 +85,10 @@
             limit_str = '--limit-rate=%.1fk' % ( libgpodder.gPodderLib().limit_rate_value, )
             libgpodder.gPodderLib().deleteFilename( self.tempname)
         # TODO use config file for number of retries (wget --tries)?
-        command = 'wget --timeout=120 --continue %s --output-document="%s" "%s"' % ( limit_str, self.tempname, self.url )
+        if self.channelitem and ( self.channelitem.username or self.channelitem.password ):
+            command = 'wget --user "%s" --password "%s" --timeout=120 --continue %s --output-document="%s" "%s"' % ( self.channelitem.username, self.channelitem.password, limit_str, self.tempname, self.url )
+        else:
+            command = 'wget --timeout=120 --continue %s --output-document="%s" "%s"' % ( limit_str, self.tempname, self.url )
         log( 'Command: %s', command)
         if self.statusmgr:
             self.statusmgr.updateInfo( self.statusmgr_id, { 'episode':self.cutename, 'speed':_('Queued'), 'progress':0, 'url':self.url})
_______________________________________________
gpodder-devel mailing list
gpodder-devel@lists.berlios.de
https://lists.berlios.de/mailman/listinfo/gpodder-devel

Reply via email to