Wordpress 2.3.2
[autoinstalls/wordpress.git] / wp-admin / import / blogger.php
index 0772eb1db20aef146f0372ed7aca55f8dcc8703d..b518cd73a8379564aff4ab06027047a37fd7ba89 100644 (file)
 <?php
 
-class Blogger_Import {
+define( 'MAX_RESULTS',        50 ); // How many records per GData query
+define( 'MAX_EXECUTION_TIME', 20 ); // How many seconds to let the script run
+define( 'STATUS_INTERVAL',     3 ); // How many seconds between status bar updates
 
-       var $lump_authors = false;
-       var $import = array();
+class Blogger_Import {
 
-       // Shows the welcome screen and the magic iframe.
+       // Shows the welcome screen and the magic auth link.
        function greet() {
-               $title = __('Import Old Blogger');
-               $welcome = __('Howdy! This importer allows you to import posts and comments from your Old Blogger account into your WordPress blog.');
-               $noiframes = __('This feature requires iframe support.');
-               $warning = __('This will delete everything saved by the Blogger importer except your posts and comments. Are you sure you want to do this?');
-               $reset = __('Reset this importer');
-               $incompat = __('Your web server is not properly configured to use this importer. Please enable the CURL extension for PHP and then reload this page.');
-
-               echo "<div class='wrap'><h2>$title</h2><p>$welcome</p>";
-               echo "<p>" . __('Please note that this importer <em>does not work with Blogger (using your Google account)</em>.') . "</p>";
-               if ( function_exists('curl_init') )
-                       echo "<iframe src='admin.php?import=blogger&amp;noheader=true' height='350px' width = '99%'>$noiframes</iframe><p><a href='admin.php?import=blogger&amp;restart=true&amp;noheader=true' onclick='return confirm(\"$warning\")'>$reset</a></p>";
-               else
-                       echo "<p>$incompat</p>";
-               echo "</div>\n";
+               $next_url = get_option('siteurl') . '/wp-admin/index.php?import=blogger&noheader=true';
+               $auth_url = "https://www.google.com/accounts/AuthSubRequest";
+               $title = __('Import Blogger');
+               $welcome = __('Howdy! This importer allows you to import posts and comments from your Blogger account into your WordPress blog.');
+               $prereqs = __('To use this importer, you must have a Google account, an upgraded (New, was Beta) blog, and it must be on blogspot or a custom domain (not FTP).');
+               $stepone = __('The first thing you need to do is tell Blogger to let WordPress access your account. You will be sent back here after providing authorization.');
+               $auth = __('Authorize');
+
+               echo "
+               <div class='wrap'><h2>$title</h2><p>$welcome</p><p>$prereqs</p><p>$stepone</p>
+                       <form action='$auth_url' method='get'>
+                               <p class='submit' style='text-align:left;'>
+                                       <input type='submit' value='$auth' />
+                                       <input type='hidden' name='scope' value='http://www.blogger.com/feeds/' />
+                                       <input type='hidden' name='session' value='1' />
+                                       <input type='hidden' name='secure' value='0' />
+                                       <input type='hidden' name='next' value='$next_url' />
+                               </p>
+                       </form>
+               </div>\n";
        }
 
-       // Deletes saved data and redirect.
-       function restart() {
-               delete_option('import-blogger');
-               wp_redirect("admin.php?import=blogger");
-               die();
+       function uh_oh($title, $message, $info) {
+               echo "<div class='wrap'><h2>$title</h2><p>$message</p><pre>$info</pre></div>";
        }
 
-       // Generates a string that will make the page reload in a specified interval.
-       function refresher($msec) {
-               if ( $msec )
-                       return "<html><head><script type='text/javascript'>window.onload=setTimeout('window.location.reload()', $msec);</script>\n</head>\n<body>\n";
-               else
-                       return "<html><head><script type='text/javascript'>window.onload=window.location.reload();</script>\n</head>\n<body>\n";
+       function auth() {
+               // We have a single-use token that must be upgraded to a session token.
+               $token = preg_replace( '/[^-_0-9a-zA-Z]/', '', $_GET['token'] );
+               $headers = array(
+                       "GET /accounts/AuthSubSessionToken HTTP/1.0",
+                       "Authorization: AuthSub token=\"$token\""
+               );
+               $request = join( "\r\n", $headers ) . "\r\n\r\n";
+               $sock = $this->_get_auth_sock( );
+               if ( ! $sock ) return false;
+               $response = $this->_txrx( $sock, $request );
+               preg_match( '/token=([-_0-9a-z]+)/i', $response, $matches );
+               if ( empty( $matches[1] ) ) {
+                       $this->uh_oh(
+                               __( 'Authorization failed' ),
+                               __( 'Something went wrong. If the problem persists, send this info to support:' ),
+                               htmlspecialchars($response)
+                       );
+                       return false;
+               }
+               $this->token = $matches[1];
+
+               wp_redirect( remove_query_arg( array( 'token', 'noheader' ) ) );
        }
 
-       // Returns associative array of code, header, cookies, body. Based on code from php.net.
-       function parse_response($this_response) {
-               // Split response into header and body sections
-               list($response_headers, $response_body) = explode("\r\n\r\n", $this_response, 2);
-               $response_header_lines = explode("\r\n", $response_headers);
+       function get_token_info() {
+               $headers = array(
+                       "GET /accounts/AuthSubTokenInfo  HTTP/1.0",
+                       "Authorization: AuthSub token=\"$this->token\""
+               );
+               $request = join( "\r\n", $headers ) . "\r\n\r\n";
+               $sock = $this->_get_auth_sock( );
+               if ( ! $sock ) return;
+               $response = $this->_txrx( $sock, $request );
+               return $this->parse_response($response);
+       }
 
-               // First line of headers is the HTTP response code
-               $http_response_line = array_shift($response_header_lines);
-               if(preg_match('@^HTTP/[0-9]\.[0-9] ([0-9]{3})@',$http_response_line, $matches)) { $response_code = $matches[1]; }
+       function token_is_valid() {
+               $info = $this->get_token_info();
 
-               // put the rest of the headers in an array
-               $response_header_array = array();
-               foreach($response_header_lines as $header_line) {
-                       list($header,$value) = explode(': ', $header_line, 2);
-                       $response_header_array[$header] .= $value."\n";
-               }
-
-               $cookie_array = array();
-               $cookies = explode("\n", $response_header_array["Set-Cookie"]);
-               foreach($cookies as $this_cookie) { array_push($cookie_array, "Cookie: ".$this_cookie); }
+               if ( $info['code'] == 200 )
+                       return true;
 
-               return array("code" => $response_code, "header" => $response_header_array, "cookies" => $cookie_array, "body" => $response_body);
+               return false;
        }
 
-       // Prints a form for the user to enter Blogger creds.
-       function login_form($text='') {
-               echo '<h1>' . __('Log in to Blogger') . "</h1>\n$text\n";
-               echo '<form method="post" action="admin.php?import=blogger&amp;noheader=true&amp;step=0"><table><tr><td>' . __('Username') . ':</td><td><input type="text" name="user" /></td></tr><tr><td>' . __('Password') . ':</td><td><input type="password" name="pass" /></td><td><input type="submit" value="' . __('Start') . '" /></td></tr></table></form>';
-               die;
-       }
-
-       // Sends creds to Blogger, returns the session cookies an array of headers.
-       function login_blogger($user, $pass) {
-               $_url = 'http://www.blogger.com/login.do';
-               $params = "username=$user&password=$pass";
-               $ch = curl_init();
-               curl_setopt($ch, CURLOPT_POST,1);
-               curl_setopt($ch, CURLOPT_POSTFIELDS,$params);
-               curl_setopt($ch, CURLOPT_URL,$_url);
-               curl_setopt($ch, CURLOPT_USERAGENT, 'Blogger Exporter');
-               curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
-               curl_setopt($ch, CURLOPT_HEADER,1);
-               curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
-               $response = curl_exec ($ch);
-
-               $response = $this->parse_response($response);
-
-               sleep(1);
-
-               return $response['cookies'];
-       }
-
-       // Requests page from Blogger, returns the response array.
-       function get_blogger($url, $header = '', $user=false, $pass=false) {
-               $ch = curl_init();
-               if ($user && $pass) curl_setopt($ch, CURLOPT_USERPWD,"{$user}:{$pass}");
-               curl_setopt($ch, CURLOPT_URL,$url);
-               curl_setopt($ch, CURLOPT_TIMEOUT, 10);
-               curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-               curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
-               curl_setopt($ch, CURLOPT_USERAGENT, 'Blogger Exporter');
-               curl_setopt($ch, CURLOPT_HEADER,1);
-               if (is_array($header)) curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
-               $response = curl_exec ($ch);
-
-               $response = $this->parse_response($response);
-               $response['url'] = $url;
-
-               if (curl_errno($ch)) {
-                       print curl_error($ch);
-               } else {
-                       curl_close($ch);
-               }
+       function show_blogs($iter = 0) {
+               if ( empty($this->blogs) ) {
+                       $headers = array(
+                               "GET /feeds/default/blogs HTTP/1.0",
+                               "Host: www.blogger.com",
+                               "Authorization: AuthSub token=\"$this->token\""
+                       );
+                       $request = join( "\r\n", $headers ) . "\r\n\r\n";
+                       $sock = $this->_get_blogger_sock( );
+                       if ( ! $sock ) return;
+                       $response = $this->_txrx( $sock, $request );
+
+                       // Quick and dirty XML mining.
+                       list( $headers, $xml ) = explode( "\r\n\r\n", $response );
+                       $p = xml_parser_create();
+                       xml_parse_into_struct($p, $xml, $vals, $index);
+                       xml_parser_free($p);
+
+                       $this->title = $vals[$index['TITLE'][0]]['value'];
+
+                       // Give it a few retries... this step often flakes out the first time.
+                       if ( empty( $index['ENTRY'] ) ) {
+                               if ( $iter < 3 ) {
+                                       return $this->show_blogs($iter + 1);
+                               } else {
+                                       $this->uh_oh(
+                                               __('Trouble signing in'),
+                                               __('We were not able to gain access to your account. Try starting over.'),
+                                               ''
+                                       );
+                                       return false;
+                               }
+                       }
 
-               return $response;
-       }
+                       foreach ( $index['ENTRY'] as $i ) {
+                               $blog = array();
+                               while ( ( $tag = $vals[$i] ) && ! ( $tag['tag'] == 'ENTRY' && $tag['type'] == 'close' ) ) {
+                                       if ( $tag['tag'] == 'TITLE' ) {
+                                               $blog['title'] = $tag['value'];
+                                       } elseif ( $tag['tag'] == 'SUMMARY' ) {
+                                               $blog['summary'] == $tag['value'];
+                                       } elseif ( $tag['tag'] == 'LINK' ) {
+                                               if ( $tag['attributes']['REL'] == 'alternate' && $tag['attributes']['TYPE'] == 'text/html' ) {
+                                                       $parts = parse_url( $tag['attributes']['HREF'] );
+                                                       $blog['host'] = $parts['host'];
+                                               } elseif ( $tag['attributes']['REL'] == 'edit' )
+                                                       $blog['gateway'] = $tag['attributes']['HREF'];
+                                       }
+                                       ++$i;
+                               }
+                               if ( ! empty ( $blog ) ) {
+                                       $blog['total_posts'] = $this->get_total_results('posts', $blog['host']);
+                                       $blog['total_comments'] = $this->get_total_results('comments', $blog['host']);
+                                       $blog['mode'] = 'init';
+                                       $this->blogs[] = $blog;
+                               }
+                       }
 
-       // Posts data to Blogger, returns response array.
-       function post_blogger($url, $header = false, $paramary = false, $parse=true) {
-               $params = '';
-               if ( is_array($paramary) ) {
-                       foreach($paramary as $key=>$value)
-                               if($key && $value != '')
-                                       $params.=$key."=".urlencode(stripslashes($value))."&";
+                       if ( empty( $this->blogs ) ) {
+                               $this->uh_oh(
+                                       __('No blogs found'),
+                                       __('We were able to log in but there were no blogs. Try a different account next time.'),
+                                       ''
+                               );
+                               return false;
+                       }
                }
-               if ($user && $pass) $params .= "username=$user&password=$pass";
-               $params = trim($params,'&');
-               $ch = curl_init();
-               curl_setopt($ch, CURLOPT_POST,1);
-               curl_setopt($ch, CURLOPT_POSTFIELDS,$params);
-               if ($user && $pass) curl_setopt($ch, CURLOPT_USERPWD,"{$user}:{$pass}");
-               curl_setopt($ch, CURLOPT_URL,$url);
-               curl_setopt($ch, CURLOPT_USERAGENT, 'Blogger Exporter');
-               curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
-               curl_setopt($ch, CURLOPT_HEADER,$parse);
-               curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
-               if ($header) curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
-               $response = curl_exec ($ch);
-
-               if ($parse) {
-                       $response = $this->parse_response($response);
-                       $response['url'] = $url;
-                       return $response;
+//echo '<pre>'.print_r($this,1).'</pre>';
+               $start    = js_escape( __('Import') );
+               $continue = js_escape( __('Continue') );
+               $stop     = js_escape( __('Importing...') );
+               $authors  = js_escape( __('Set Authors') );
+               $loadauth = js_escape( __('Preparing author mapping form...') );
+               $authhead = js_escape( __('Final Step: Author Mapping') );
+               $nothing  = js_escape( __('Nothing was imported. Had you already imported this blog?') );
+               $title    = __('Blogger Blogs');
+               $name     = __('Blog Name');
+               $url      = __('Blog URL');
+               $action   = __('The Magic Button');
+               $posts    = __('Posts');
+               $comments = __('Comments');
+               $noscript = __('This feature requires Javascript but it seems to be disabled. Please enable Javascript and then reload this page. Don\'t worry, you can turn it back off when you\'re done.');
+
+               $interval = STATUS_INTERVAL * 1000;
+
+               foreach ( $this->blogs as $i => $blog ) {
+                       if ( $blog['mode'] == 'init' )
+                               $value = $start;
+                       elseif ( $blog['mode'] == 'posts' || $blog['mode'] == 'comments' )
+                               $value = $continue;
+                       else
+                               $value = $authors;
+                       $blogtitle = js_escape( $blog['title'] );
+                       $pdone = isset($blog['posts_done']) ? (int) $blog['posts_done'] : 0;
+                       $cdone = isset($blog['comments_done']) ? (int) $blog['comments_done'] : 0;
+                       $init .= "blogs[$i]=new blog($i,'$blogtitle','{$blog['mode']}'," . $this->get_js_status($i) . ');';
+                       $pstat = "<div class='ind' id='pind$i'>&nbsp;</div><div id='pstat$i' class='stat'>$pdone/{$blog['total_posts']}</div>";
+                       $cstat = "<div class='ind' id='cind$i'>&nbsp;</div><div id='cstat$i' class='stat'>$cdone/{$blog['total_comments']}</div>";
+                       $rows .= "<tr id='blog$i'><td class='blogtitle'>$blogtitle</td><td class='bloghost'>{$blog['host']}</td><td class='bar'>$pstat</td><td class='bar'>$cstat</td><td class='submit'><input type='submit' id='submit$i' value='$value' /><input type='hidden' name='blog' value='$i' /></td></tr>\n";
                }
 
-               return $response;
+               echo "<div class='wrap'><h2>$title</h2><noscript>$noscript</noscript><table cellpadding='5px'><thead><td>$name</td><td>$url</td><td>$posts</td><td>$comments</td><td>$action</td></thead>\n$rows</table></form></div>";
+               echo "
+               <script type='text/javascript'>
+                       var strings = {cont:'$continue',stop:'$stop',stopping:'$stopping',authors:'$authors',nothing:'$nothing'};
+                       var blogs = {};
+                       function blog(i, title, mode, status){
+                               this.blog   = i;
+                               this.mode   = mode;
+                               this.title  = title;
+                               this.status = status;
+                               this.button = document.getElementById('submit'+this.blog);
+                       };
+                       blog.prototype = {
+                               start: function() {
+                                       this.cont = true;
+                                       this.kick();
+                                       this.check();
+                               },
+                               kick: function() {
+                                       ++this.kicks;
+                                       var i = this.blog;
+                                       jQuery.post('admin.php?import=blogger&noheader=true',{blog:this.blog},function(text,result){blogs[i].kickd(text,result)});
+                               },
+                               check: function() {
+                                       ++this.checks;
+                                       var i = this.blog;
+                                       jQuery.post('admin.php?import=blogger&noheader=true&status=true',{blog:this.blog},function(text,result){blogs[i].checkd(text,result)});
+                               },
+                               kickd: function(text, result) {
+                                       if ( result == 'error' ) {
+                                               // TODO: exception handling
+                                               if ( this.cont )
+                                                       setTimeout('blogs['+this.blog+'].kick()', 1000);
+                                       } else {
+                                               if ( text == 'done' ) {
+                                                       this.stop();
+                                                       this.done();
+                                               } else if ( text == 'nothing' ) {
+                                                       this.stop();
+                                                       this.nothing();
+                                               } else if ( text == 'continue' ) {
+                                                       this.kick();
+                                               } else if ( this.mode = 'stopped' )
+                                                       jQuery(this.button).attr('value', strings.cont);
+                                       }
+                                       --this.kicks;
+                               },
+                               checkd: function(text, result) {
+                                       if ( result == 'error' ) {
+                                               // TODO: exception handling
+                                       } else {
+                                               eval('this.status='+text);
+                                               jQuery('#pstat'+this.blog).empty().append(this.status.p1+'/'+this.status.p2);
+                                               jQuery('#cstat'+this.blog).empty().append(this.status.c1+'/'+this.status.c2);
+                                               this.update();
+                                               if ( this.cont || this.kicks > 0 )
+                                                       setTimeout('blogs['+this.blog+'].check()', $interval);
+                                       }
+                                       --this.checks;
+                               },
+                               update: function() {
+                                       jQuery('#pind'+this.blog).width(((this.status.p1>0&&this.status.p2>0)?(this.status.p1/this.status.p2*jQuery('#pind'+this.blog).parent().width()):1)+'px');
+                                       jQuery('#cind'+this.blog).width(((this.status.c1>0&&this.status.c2>0)?(this.status.c1/this.status.c2*jQuery('#cind'+this.blog).parent().width()):1)+'px');
+                               },
+                               stop: function() {
+                                       this.cont = false;
+                               },
+                               done: function() {
+                                       this.mode = 'authors';
+                                       jQuery(this.button).attr('value', strings.authors);
+                               },
+                               nothing: function() {
+                                       this.mode = 'nothing';
+                                       jQuery(this.button).remove();
+                                       alert(strings.nothing);
+                               },
+                               getauthors: function() {
+                                       if ( jQuery('div.wrap').length > 1 )
+                                               jQuery('div.wrap').gt(0).remove();
+                                       jQuery('div.wrap').empty().append('<h2>$authhead</h2><h3>' + this.title + '</h3>');
+                                       jQuery('div.wrap').append('<p id=\"auth\">$loadauth</p>');
+                                       jQuery('p#auth').load('index.php?import=blogger&noheader=true&authors=1',{blog:this.blog});
+                               },
+                               init: function() {
+                                       this.update();
+                                       var i = this.blog;
+                                       jQuery(this.button).bind('click', function(){return blogs[i].click();});
+                                       this.kicks = 0;
+                                       this.checks = 0;
+                               },
+                               click: function() {
+                                       if ( this.mode == 'init' || this.mode == 'stopped' || this.mode == 'posts' || this.mode == 'comments' ) {
+                                               this.mode = 'started';
+                                               this.start();
+                                               jQuery(this.button).attr('value', strings.stop);
+                                       } else if ( this.mode == 'started' ) {
+                                               return false; // let it run...
+                                               this.mode = 'stopped';
+                                               this.stop();
+                                               if ( this.checks > 0 || this.kicks > 0 ) {
+                                                       this.mode = 'stopping';
+                                                       jQuery(this.button).attr('value', strings.stopping);
+                                               } else {
+                                                       jQuery(this.button).attr('value', strings.cont);
+                                               }
+                                       } else if ( this.mode == 'authors' ) {
+                                               document.location = 'index.php?import=blogger&authors=1&blog='+this.blog;
+                                               //this.mode = 'authors2';
+                                               //this.getauthors();
+                                       }
+                                       return false;
+                               }
+                       };
+                       $init
+                       jQuery.each(blogs, function(i, me){me.init();});
+               </script>\n";
+       }
+
+       // Handy function for stopping the script after a number of seconds.
+       function have_time() {
+               global $importer_started;
+               if ( time() - $importer_started > MAX_EXECUTION_TIME )
+                       die('continue');
+               return true;
+       }
+
+       function get_total_results($type, $host) {
+               $headers = array(
+                       "GET /feeds/$type/default?max-results=1&start-index=2 HTTP/1.0",
+                       "Host: $host",
+                       "Authorization: AuthSub token=\"$this->token\""
+               );
+               $request = join( "\r\n", $headers ) . "\r\n\r\n";
+               $sock = $this->_get_blogger_sock( $host );
+               if ( ! $sock ) return;
+               $response = $this->_txrx( $sock, $request );
+               $response = $this->parse_response( $response );
+               $parser = xml_parser_create();
+               xml_parse_into_struct($parser, $response['body'], $struct, $index);
+               xml_parser_free($parser);
+               $total_results = $struct[$index['OPENSEARCH:TOTALRESULTS'][0]]['value'];
+               return (int) $total_results;
        }
 
-       // Prints the list of blogs for import.
-       function show_blogs() {
-               global $import;
-               echo '<h1>' . __('Selecting a Blog') . "</h1>\n<ul>";
-               foreach ( $this->import['blogs'] as $blog ) {
-                       if (9 == $blog['nextstep']) $status = "100%";
-                       elseif (8 == $blog['nextstep']) $status = "90%";
-                       elseif (7 == $blog['nextstep']) $status = "82.5%";
-                       elseif (6 == $blog['nextstep']) $status = "75%";
-                       elseif (5 == $blog['nextstep']) $status = "57%";
-                       elseif (4 == $blog['nextstep']) $status = "28%";
-                       elseif (3 == $blog['nextstep']) $status = "14%";
-                       else $status = "0%";
-                       echo "\t<li><a href='admin.php?import=blogger&amp;noheader=true&amp;blog={$blog['id']}'>{$blog['title']}</a> $status</li>\n";
+       function import_blog($blogID) {
+               global $importing_blog;
+               $importing_blog = $blogID;
+
+               if ( isset($_GET['authors']) )
+                       return print($this->get_author_form());
+
+               header('Content-Type: text/plain');
+
+               if ( isset($_GET['status']) )
+                       die($this->get_js_status());
+
+               if ( isset($_GET['saveauthors']) )
+                       die($this->save_authors());
+
+               $blog = $this->blogs[$blogID];
+               $total_results = $this->get_total_results('posts', $blog['host']);
+               $this->blogs[$importing_blog]['total_posts'] = $total_results;
+
+               $start_index = $total_results - MAX_RESULTS + 1;
+
+               if ( isset( $this->blogs[$importing_blog]['posts_start_index'] ) )
+                       $start_index = (int) $this->blogs[$importing_blog]['posts_start_index'];
+               elseif ( $total_results > MAX_RESULTS )
+                       $start_index = $total_results - MAX_RESULTS + 1;
+               else
+                       $start_index = 1;
+
+               // This will be positive until we have finished importing posts
+               if ( $start_index > 0 ) {
+                       // Grab all the posts
+                       $this->blogs[$importing_blog]['mode'] = 'posts';
+                       $query = "start-index=$start_index&max-results=" . MAX_RESULTS;
+                       do {
+                               $index = $struct = $entries = array();
+                               $headers = array(
+                                       "GET /feeds/posts/default?$query HTTP/1.0",
+                                       "Host: {$blog['host']}",
+                                       "Authorization: AuthSub token=\"$this->token\""
+                               );
+                               $request = join( "\r\n", $headers ) . "\r\n\r\n";
+                               $sock = $this->_get_blogger_sock( $blog['host'] );
+                               if ( ! $sock ) return; // TODO: Error handling
+                               $response = $this->_txrx( $sock, $request );
+
+                               $response = $this->parse_response( $response );
+
+                               // Extract the entries and send for insertion
+                               preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches );
+                               if ( count( $matches[0] ) ) {
+                                       $entries = array_reverse($matches[0]);
+                                       foreach ( $entries as $entry ) {
+                                               $entry = "<feed>$entry</feed>";
+                                               $AtomParser = new AtomParser();
+                                               $AtomParser->parse( $entry );
+                                               $result = $this->import_post($AtomParser->entry);
+                                               if ( is_wp_error( $result ) ) 
+                                                       return $result;
+                                               unset($AtomParser);
+                                       }
+                               } else break;
+
+                               // Get the 'previous' query string which we'll use on the next iteration
+                               $query = '';
+                               $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches);
+                               if ( count( $matches[1] ) )
+                                       foreach ( $matches[1] as $match )
+                                               if ( preg_match('/rel=.previous./', $match) )
+                                                       $query = html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match) );
+
+                               if ( $query ) {
+                                       parse_str($query, $q);
+                                       $this->blogs[$importing_blog]['posts_start_index'] = (int) $q['start-index'];
+                               } else
+                                       $this->blogs[$importing_blog]['posts_start_index'] = 0;
+                               $this->save_vars();
+                       } while ( !empty( $query ) && $this->have_time() );
                }
-               die("</ul>\n");
-       }
-
-       // Publishes.
-       function publish_blogger($i, $text) {
-               $head = $this->refresher(2000) . "<h1>$text</h1>\n";
-               if ( ! strstr($this->import['blogs'][$_GET['blog']]['publish'][$i], 'http') ) {
-                       // First call. Start the publish process with a fresh set of cookies.
-                       $this->import['cookies'] = $this->login_blogger($this->import['user'], $this->import['pass']);
-                       update_option('import-blogger', $this->import);
-                       $paramary = array('blogID' => $_GET['blog'], 'all' => '1', 'republishAll' => 'Republish Entire Blog', 'publish' => '1', 'redirectUrl' => "/publish.do?blogID={$_GET['blog']}&inprogress=true");
-
-                       $response = $this->post_blogger("http://www.blogger.com/publish.do?blogID={$_GET['blog']}", $this->import['cookies'], $paramary);
-                       if ( $response['code'] == '302' ) {
-                               $url = str_replace('publish.g', 'publish-body.g', $response['header']['Location']);
-                               $this->import['blogs'][$_GET['blog']]['publish'][$i] = $url;
-                               update_option('import-blogger', $this->import);
-                               $response = $this->get_blogger($url, $this->import['cookies']);
-                               preg_match('#<p class="progressIndicator">.*</p>#U', $response['body'], $matches);
-                               $progress = $matches[0];
-                               die($head . $progress);
-                       } else {
-                               $this->import['blogs'][$_GET['blog']]['publish'][$i] = false;
-                               update_option('import-blogger', $this->import);
-                               die($head);
-                       }
-               } else {
-                       // Subsequent call. Keep checking status until Blogger reports publish complete.
-                       $url = $this->import['blogs'][$_GET['blog']]['publish'][$i];
-                       $response = $this->get_blogger($url, $this->import['cookies']);
-                       if ( preg_match('#<p class="progressIndicator">.*</p>#U', $response['body'], $matches) ) {
-                               $progress = $matches[0];
-                               if ( strstr($progress, '100%') ) {
-                                       $this->set_next_step($i);
-                                       $progress .= '<p>'.__('Moving on...').'</p>';
+
+               $total_results = $this->get_total_results( 'comments', $blog['host'] );
+               $this->blogs[$importing_blog]['total_comments'] = $total_results;
+
+               if ( isset( $this->blogs[$importing_blog]['comments_start_index'] ) )
+                       $start_index = (int) $this->blogs[$importing_blog]['comments_start_index'];
+               elseif ( $total_results > MAX_RESULTS )
+                       $start_index = $total_results - MAX_RESULTS + 1;
+               else
+                       $start_index = 1;
+
+               if ( $start_index > 0 ) {
+                       // Grab all the comments
+                       $this->blogs[$importing_blog]['mode'] = 'comments';
+                       $query = "start-index=$start_index&max-results=" . MAX_RESULTS;
+                       do {
+                               $index = $struct = $entries = array();
+                               $headers = array(
+                                       "GET /feeds/comments/default?$query HTTP/1.0",
+                                       "Host: {$blog['host']}",
+                                       "Authorization: AuthSub token=\"$this->token\""
+                               );
+                               $request = join( "\r\n", $headers ) . "\r\n\r\n";
+                               $sock = $this->_get_blogger_sock( $blog['host'] );
+                               if ( ! $sock ) return; // TODO: Error handling
+                               $response = $this->_txrx( $sock, $request );
+
+                               $response = $this->parse_response( $response );
+
+                               // Extract the comments and send for insertion
+                               preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches );
+                               if ( count( $matches[0] ) ) {
+                                       $entries = array_reverse( $matches[0] );
+                                       foreach ( $entries as $entry ) {
+                                               $entry = "<feed>$entry</feed>";
+                                               $AtomParser = new AtomParser();
+                                               $AtomParser->parse( $entry );
+                                               $this->import_comment($AtomParser->entry);
+                                               unset($AtomParser);
+                                       }
                                }
-                               die($head . $progress);
-                       } else {
-                               $this->import['blogs'][$_GET['blog']]['publish'][$i] = false;
-                               update_option('import-blogger', $this->import);
-                               die("$head<p>" . __('Trying again...') . '</p>');
-                       }
+
+                               // Get the 'previous' query string which we'll use on the next iteration
+                               $query = '';
+                               $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches);
+                               if ( count( $matches[1] ) )
+                                       foreach ( $matches[1] as $match )
+                                               if ( preg_match('/rel=.previous./', $match) )
+                                                       $query = html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match) );
+
+                               parse_str($query, $q);
+
+                               $this->blogs[$importing_blog]['comments_start_index'] = (int) $q['start-index'];
+                               $this->save_vars();
+                       } while ( !empty( $query ) && $this->have_time() );
                }
+               $this->blogs[$importing_blog]['mode'] = 'authors';
+               $this->save_vars();
+               if ( !$this->blogs[$importing_blog]['posts_done'] && !$this->blogs[$importing_blog]['comments_done'] )
+                       die('nothing');
+               do_action('import_done', 'blogger');
+               die('done');
        }
 
-       // Sets next step, saves options
-       function set_next_step($step) {
-               $this->import['blogs'][$_GET['blog']]['nextstep'] = $step;
-               update_option('import-blogger', $this->import);
+       function convert_date( $date ) {
+           preg_match('#([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.[0-9]+)?(Z|[\+|\-][0-9]{2,4}){0,1}#', $date, $date_bits);
+           $offset = iso8601_timezone_to_offset( $date_bits[7] );
+               $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
+               $timestamp -= $offset; // Convert from Blogger local time to GMT
+               $timestamp += get_option('gmt_offset') * 3600; // Convert from GMT to WP local time
+               return gmdate('Y-m-d H:i:s', $timestamp);
        }
 
-       // Redirects to next step
-       function do_next_step() {
-               wp_redirect("admin.php?import=blogger&noheader=true&blog={$_GET['blog']}");
-               die();
+       function no_apos( $string ) {
+               return str_replace( '&apos;', "'", $string);
        }
 
-       // Step 0: Do Blogger login, get blogid/title pairs.
-       function do_login() {
-               if ( ( ! $this->import['user'] && ! is_array($this->import['cookies']) ) ) {
-                       // The user must provide a Blogger username and password.
-                       if ( ! ( $_POST['user'] && $_POST['pass'] ) ) {
-                               $this->login_form(__('The script will log into your Blogger account, change some settings so it can read your blog, and restore the original settings when it\'s done. Here\'s what you do:').'</p><ol><li>'.__('Back up your Blogger template.').'</li><li>'.__('Back up any other Blogger settings you might need later.').'</li><li>'.__('Log out of Blogger').'</li><li>'.__('Log in <em>here</em> with your Blogger username and password.').'</li><li>'.__('On the next screen, click one of your Blogger blogs.').'</li><li>'.__('Do not close this window or navigate away until the process is complete.').'</li></ol>');
-                       }
-
-                       // Try logging in. If we get an array of cookies back, we at least connected.
-                       $this->import['cookies'] = $this->login_blogger($_POST['user'], $_POST['pass']);
-                       if ( !is_array( $this->import['cookies'] ) ) {
-                               $this->login_form(__('Login failed. Please enter your credentials again.'));
-                       }
+       function min_whitespace( $string ) {
+               return preg_replace( '|\s+|', ' ', $string );
+       }
 
-                       // Save the password so we can log the browser in when it's time to publish.
-                       $this->import['pass'] = $_POST['pass'];
-                       $this->import['user'] = $_POST['user'];
-
-                       // Get the Blogger welcome page and scrape the blog numbers and names from it
-                       $response = $this->get_blogger('http://www.blogger.com/home', $this->import['cookies']);
-                       if (! stristr($response['body'], 'signed in as') ) $this->login_form(__('Login failed. Please re-enter your username and password.'));
-                       $blogsary = array();
-                       preg_match_all('#posts\.g\?blogID=(\d+)">([^<]+)</a>#U', $response['body'], $blogsary);
-                       if ( ! count( $blogsary[1] < 1 ) )
-                               die(__('No blogs found for this user.'));
-                       $this->import['blogs'] = array();
-                       $template = '<MainPage><br /><br /><br /><p>'.__('Are you looking for %title%? It is temporarily out of service. Please try again in a few minutes. Meanwhile, discover <a href="http://wordpress.org/">a better blogging tool</a>.').'</p><BloggerArchives><a class="archive" href="<$BlogArchiveURL$>"><$BlogArchiveName$></a><br /></BloggerArchives></MainPage><ArchivePage><Blogger><wordpresspost><$BlogItemDateTime$>|W|P|<$BlogItemAuthorNickname$>|W|P|<$BlogItemBody$>|W|P|<$BlogItemNumber$>|W|P|<$BlogItemTitle$>|W|P|<$BlogItemAuthorEmail$><BlogItemCommentsEnabled><BlogItemComments><wordpresscomment><$BlogCommentDateTime$>|W|P|<$BlogCommentAuthor$>|W|P|<$BlogCommentBody$></BlogItemComments></BlogItemCommentsEnabled></Blogger></ArchivePage>';
-                       foreach ( $blogsary[1] as $key => $id ) {
-                               // Define the required Blogger options.
-                               $blog_opts = array(
-                                       'blog-options-basic' => false,
-                                       'blog-options-archiving' => array('archiveFrequency' => 'm'),
-                                       'blog-publishing' => array('publishMode'=>'0', 'blogID' => "$id", 'subdomain' => mt_rand().mt_rand(), 'pingWeblogs' => 'false'),
-                                       'blog-formatting' => array('timeStampFormat' => '0', 'encoding'=>'UTF-8', 'convertLineBreaks'=>'false', 'floatAlignment'=>'false'),
-                                       'blog-comments' => array('commentsTimeStampFormat' => '0'),
-                                       'template-edit' => array( 'templateText' =>  str_replace('%title%', trim($blogsary[2][$key]), $template) )
-                               );
+       function import_post( $entry ) {
+               global $wpdb, $importing_blog;
 
-                               // Build the blog options array template
-                               foreach ($blog_opts as $blog_opt => $modify)
-                                       $new_opts["$blog_opt"] = array('backup'=>false, 'modify' => $modify, 'error'=>false);
-
-                               $this->import['blogs']["$id"] = array(
-                                       'id' => $id,
-                                       'title' => trim($blogsary[2][$key]),
-                                       'options' => $new_opts,
-                                       'url' => false,
-                                       'publish_cookies' => false,
-                                       'published' => false,
-                                       'archives' => false,
-                                       'lump_authors' => false,
-                                       'newusers' => array(),
-                                       'nextstep' => 2
-                               );
+               // The old permalink is all Blogger gives us to link comments to their posts.
+               if ( isset( $entry->draft ) )
+                       $rel = 'self';
+               else
+                       $rel = 'alternate';
+               foreach ( $entry->links as $link ) {
+                       if ( $link['rel'] == $rel ) {
+                               $parts = parse_url( $link['href'] );
+                               $entry->old_permalink = $parts['path'];
+                               break;
                        }
-                       update_option('import-blogger', $this->import);
-                       wp_redirect("admin.php?import=blogger&noheader=true&step=1");
                }
-               die();
-       }
 
-       // Step 1: Select one of the blogs belonging to the user logged in.
-       function select_blog() {
-               if ( is_array($this->import['blogs']) ) {
-                       $this->show_blogs();
-                       die();
+               $post_date    = $this->convert_date( $entry->published );
+               $post_content = trim( addslashes( $this->no_apos( html_entity_decode( $entry->content ) ) ) );
+               $post_title   = trim( addslashes( $this->no_apos( $this->min_whitespace( $entry->title ) ) ) );
+               $post_status  = isset( $entry->draft ) ? 'draft' : 'publish';
+
+               // Clean up content
+               $post_content = preg_replace('|<(/?[A-Z]+)|e', "'<' . strtolower('$1')", $post_content);
+               $post_content = str_replace('<br>', '<br />', $post_content);
+               $post_content = str_replace('<hr>', '<hr />', $post_content);
+
+               // Checks for duplicates
+               if ( isset( $this->blogs[$importing_blog]['posts'][$entry->old_permalink] ) ) {
+                       ++$this->blogs[$importing_blog]['posts_skipped'];
+               } elseif ( $post_id = post_exists( $post_title, $post_content, $post_date ) ) {
+                       $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id;
+                       ++$this->blogs[$importing_blog]['posts_skipped'];
                } else {
-                       $this->restart();
+                       $post = compact('post_date', 'post_content', 'post_title', 'post_status');
+
+                       $post_id = wp_insert_post($post);
+                       if ( is_wp_error( $post_id ) ) 
+                               return $post_id;
+
+                       wp_create_categories( array_map( 'addslashes', $entry->categories ), $post_id );
+
+                       $author = $this->no_apos( strip_tags( $entry->author ) );
+
+                       add_post_meta( $post_id, 'blogger_blog', $this->blogs[$importing_blog]['host'], true );
+                       add_post_meta( $post_id, 'blogger_author', $author, true );
+                       add_post_meta( $post_id, 'blogger_permalink', $entry->old_permalink, true );
+
+                       $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id;
+                       ++$this->blogs[$importing_blog]['posts_done'];
                }
+               $this->save_vars();
+               return;
        }
 
-       // Step 2: Backup the Blogger options pages, updating some of them.
-       function backup_settings() {
-               $output.= '<h1>'.__('Backing up Blogger options')."</h1>\n";
-               $form = false;
-               foreach ($this->import['blogs'][$_GET['blog']]['options'] as $blog_opt => $optary) {
-                       if ( $blog_opt == $_GET['form'] ) {
-                               // Save the posted form data
-                               $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['backup'] = $_POST;
-                               update_option('import-blogger',$this->import);
-
-                               // Post the modified form data to Blogger
-                               if ( $optary['modify'] ) {
-                                       $posturl = "http://www.blogger.com/{$blog_opt}.do";
-                                       $headers = array_merge($this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['cookies'], $this->import['cookies']);
-                                       if ( 'blog-publishing' == $blog_opt ) {
-                                               if ( $_POST['publishMode'] > 0 ) {
-                                                       $response = $this->get_blogger("http://www.blogger.com/blog-publishing.g?blogID={$_GET['blog']}&publishMode=0", $headers);
-                                                       if ( $response['code'] >= 400 )
-                                                               die('<h2>'.__('Failed attempt to change publish mode from FTP to BlogSpot.').'</h2><pre>' . addslashes(print_r($headers, 1)) . addslashes(print_r($response, 1)) . '</pre>');
-                                                       $this->import['blogs'][$_GET['blog']]['url'] = 'http://' . $optary['modify']['subdomain'] . '.blogspot.com/';
-                                                       sleep(2);
-                                               } else {
-                                                       $this->import['blogs'][$_GET['blog']]['url'] = 'http://' . $_POST['subdomain'] . '.blogspot.com/';
-                                                       update_option('import-blogger', $this->import);
-                                                       $output .= "<del><p>$blog_opt</p></del>\n";
-                                                       continue;
-                                               }
-                                               $paramary = $optary['modify'];
-                                       } else {
-                                               $paramary = array_merge($_POST, $optary['modify']);
-                                       }
-                                       $response = $this->post_blogger($posturl, $headers, $paramary);
-                                       if ( $response['code'] >= 400 || strstr($response['body'], 'There are errors on this form') )
-                                               die('<p>'.__('Error on form submission. Retry or reset the importer.').'</p>' . addslashes(print_r($response, 1)));
-                               }
-                               $output .= "<del><p>$blog_opt</p></del>\n";
-                       } elseif ( is_array($this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['backup']) ) {
-                               // This option set has already been backed up.
-                               $output .= "<del><p>$blog_opt</p></del>\n";
-                       } elseif ( ! $form ) {
-                               // This option page needs to be downloaded and given to the browser for submission back to this script.
-                               $response = $this->get_blogger("http://www.blogger.com/{$blog_opt}.g?blogID={$_GET['blog']}", $this->import['cookies']);
-                               $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['cookies'] = $response['cookies'];
-                               update_option('import-blogger',$this->import);
-                               $body = $response['body'];
-                               $body = preg_replace("|\<!DOCTYPE.*\<body[^>]*>|ms","",$body);
-                               $body = preg_replace("|/?{$blog_opt}.do|","admin.php?import=blogger&amp;noheader=true&amp;step=2&amp;blog={$_GET['blog']}&amp;form={$blog_opt}",$body);
-                               $body = str_replace("name='submit'","name='supermit'",$body);
-                               $body = str_replace('name="submit"','name="supermit"',$body);
-                               $body = str_replace('</body>','',str_replace('</html>','',$body));
-                               $form = "<div style='height:0px;width:0px;overflow:hidden;'>";
-                               $form.= $body;
-                               $form.= "</div><script type='text/javascript'>forms=document.getElementsByTagName('form');for(i=0;i<forms.length;i++){if(forms[i].action.search('{$blog_opt}')){forms[i].submit();break;}}</script>";
-                               $output.= '<p>'.sprintf(__('<strong>%s</strong> in progress, please wait...'), $blog_opt)."</p>\n";
-                       } else {
-                               $output.= "<p>$blog_opt</p>\n";
+       function import_comment( $entry ) {
+               global $importing_blog;
+
+               // Drop the #fragment and we have the comment's old post permalink.
+               foreach ( $entry->links as $link ) {
+                       if ( $link['rel'] == 'alternate' ) {
+                               $parts = parse_url( $link['href'] );
+                               $entry->old_permalink = $parts['fragment'];
+                               $entry->old_post_permalink = $parts['path'];
+                               break;
                        }
                }
-               if ( $form )
-                       die($output . $form);
 
-               $this->set_next_step(4);
-               $this->do_next_step();
-       }
+               $comment_post_ID = (int) $this->blogs[$importing_blog]['posts'][$entry->old_post_permalink];
+               preg_match('#<name>(.+?)</name>.*(?:\<uri>(.+?)</uri>)?#', $entry->author, $matches);
+               $comment_author  = addslashes( $this->no_apos( strip_tags( (string) $matches[1] ) ) );
+               $comment_author_url = addslashes( $this->no_apos( strip_tags( (string) $matches[2] ) ) );
+               $comment_date    = $this->convert_date( $entry->updated );
+               $comment_content = addslashes( $this->no_apos( html_entity_decode( $entry->content ) ) );
+
+               // Clean up content
+               $comment_content = preg_replace('|<(/?[A-Z]+)|e', "'<' . strtolower('$1')", $comment_content);
+               $comment_content = str_replace('<br>', '<br />', $comment_content);
+               $comment_content = str_replace('<hr>', '<hr />', $comment_content);
+
+               // Checks for duplicates
+               if (
+                       isset( $this->blogs[$importing_blog]['comments'][$entry->old_permalink] ) ||
+                       comment_exists( $comment_author, $comment_date )
+               ) {
+                       ++$this->blogs[$importing_blog]['comments_skipped'];
+               } else {
+                       $comment = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_date', 'comment_content');
+
+                       $comment_id = wp_insert_comment($comment);
+
+                       $this->blogs[$importing_blog]['comments'][$entry->old_permalink] = $comment_id;
 
-       // Step 3: Cancelled :-)
+                       ++$this->blogs[$importing_blog]['comments_done'];
+               }
+               $this->save_vars();
+       }
 
-       // Step 4: Publish with the new template and settings.
-       function publish_blog() {
-               $this->publish_blogger(5, __('Publishing with new template and options'));
+       function get_js_status($blog = false) {
+               global $importing_blog;
+               if ( $blog === false )
+                       $blog = $this->blogs[$importing_blog];
+               else
+                       $blog = $this->blogs[$blog];
+               $p1 = isset( $blog['posts_done'] ) ? (int) $blog['posts_done'] : 0;
+               $p2 = isset( $blog['total_posts'] ) ? (int) $blog['total_posts'] : 0;
+               $c1 = isset( $blog['comments_done'] ) ? (int) $blog['comments_done'] : 0;
+               $c2 = isset( $blog['total_comments'] ) ? (int) $blog['total_comments'] : 0;
+               return "{p1:$p1,p2:$p2,c1:$c1,c2:$c2}";
        }
 
-       // Step 5: Get the archive URLs from the new blog.
-       function get_archive_urls() {
-               $bloghtml = $this->get_blogger($this->import['blogs'][$_GET['blog']]['url']);
-               if (! strstr($bloghtml['body'], '<a class="archive"') )
-                       die(__('Your Blogger blog did not take the new template or did not respond.'));
-               preg_match_all('#<a class="archive" href="([^"]*)"#', $bloghtml['body'], $archives);
-               foreach ($archives[1] as $archive) {
-                       $this->import['blogs'][$_GET['blog']]['archives'][$archive] = false;
+       function get_author_form($blog = false) {
+               global $importing_blog, $wpdb, $current_user;
+               if ( $blog === false )
+                       $blog = & $this->blogs[$importing_blog];
+               else
+                       $blog = & $this->blogs[$blog];
+
+               if ( !isset( $blog['authors'] ) ) {
+                       $post_ids = array_values($blog['posts']);
+                       $authors = (array) $wpdb->get_col("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN (" . join( ',', $post_ids ) . ")");
+                       $blog['authors'] = array_map(null, $authors, array_fill(0, count($authors), $current_user->ID));
+                       $this->save_vars();
                }
-               $this->set_next_step(6);
-               $this->do_next_step();
+
+               $directions = __('All posts were imported with the current user as author. Use this form to move each Blogger user\'s posts to a different WordPress user. You may <a href="users.php">add users</a> and then return to this page and complete the user mapping. This form may be used as many times as you like until you activate the "Restart" function below.');
+               $heading = __('Author mapping');
+               $blogtitle = "{$blog['title']} ({$blog['host']})";
+               $mapthis = __('Blogger username');
+               $tothis = __('WordPress login');
+               $submit = js_escape( __('Save Changes &raquo;') );
+
+               foreach ( $blog['authors'] as $i => $author )
+                       $rows .= "<tr><td><label for='authors[$i]'>{$author[0]}</label></td><td><select name='authors[$i]' id='authors[$i]'>" . $this->get_user_options($author[1]) . "</select></td></tr>";
+
+               return "<div class='wrap'><h2>$heading</h2><h3>$blogtitle</h3><p>$directions</p><form action='index.php?import=blogger&noheader=true&saveauthors=1' method='post'><input type='hidden' name='blog' value='$importing_blog' /><table cellpadding='5'><thead><td>$mapthis</td><td>$tothis</td></thead>$rows<tr><td></td><td class='submit'><input type='submit' class='authorsubmit' value='$submit' /></td></tr></table></form></div>";
        }
 
-       // Step 6: Get each monthly archive, import it, mark it done.
-       function get_archive() {
-               global $wpdb;
-               $output = '<h2>'.__('Importing Blogger archives into WordPress').'</h2>';
-               $did_one = false;
-               $post_array = $posts = array();
-               foreach ( $this->import['blogs'][$_GET['blog']]['archives'] as $url => $status ) {
-                       $archivename = substr(basename($url),0,7);
-                       if ( $status || $did_one ) {
-                               $foo = 'bar';
-                               // Do nothing.
-                       } else {
-                               // Import the selected month
-                               $postcount = 0;
-                               $skippedpostcount = 0;
-                               $commentcount = 0;
-                               $skippedcommentcount = 0;
-                               $status = __('in progress...');
-                               $this->import['blogs'][$_GET['blog']]['archives']["$url"] = $status;
-                               update_option('import-blogger', $import);
-                               $archive = $this->get_blogger($url);
-                               if ( $archive['code'] > 200 )
-                                       continue;
-                               $posts = explode('<wordpresspost>', $archive['body']);
-                               for ($i = 1; $i < count($posts); $i = $i + 1) {
-                                       $postparts = explode('<wordpresscomment>', $posts[$i]);
-                                       $postinfo = explode('|W|P|', $postparts[0]);
-                                       $post_date = $postinfo[0];
-                                       $post_content = $postinfo[2];
-                                       // Don't try to re-use the original numbers
-                                       // because the new, longer numbers are too
-                                       // big to handle as ints.
-                                       //$post_number = $postinfo[3];
-                                       $post_title = ( $postinfo[4] != '' ) ? $postinfo[4] : $postinfo[3];
-                                       $post_author_name = $wpdb->escape(trim($postinfo[1]));
-                                       $post_author_email = $postinfo[5] ? $postinfo[5] : 'user@wordpress.org';
-
-                                       if ( $this->lump_authors ) {
-                                               // Ignore Blogger authors. Use the current user_ID for all posts imported.
-                                               $post_author = $GLOBALS['user_ID'];
-                                       } else {
-                                               // Add a user for each new author encountered.
-                                               if (! username_exists($post_author_name) ) {
-                                                       $user_login = $wpdb->escape($post_author_name);
-                                                       $user_email = $wpdb->escape($post_author_email);
-                                                       $user_password = substr(md5(uniqid(microtime())), 0, 6);
-                                                       $result = wp_create_user( $user_login, $user_password, $user_email );
-                                                       $status.= sprintf(__('Registered user <strong>%s</strong>.'), $user_login);
-                                                       $this->import['blogs'][$_GET['blog']]['newusers'][] = $user_login;
-                                               }
-                                               $userdata = get_userdatabylogin( $post_author_name );
-                                               $post_author = $userdata->ID;
-                                       }
-                                       $post_date = explode(' ', $post_date);
-                                       $post_date_Ymd = explode('/', $post_date[0]);
-                                       $postyear = $post_date_Ymd[2];
-                                       $postmonth = zeroise($post_date_Ymd[0], 2);
-                                       $postday = zeroise($post_date_Ymd[1], 2);
-                                       $post_date_His = explode(':', $post_date[1]);
-                                       $posthour = zeroise($post_date_His[0], 2);
-                                       $postminute = zeroise($post_date_His[1], 2);
-                                       $postsecond = zeroise($post_date_His[2], 2);
+       function get_user_options($current) {
+               global $wpdb, $importer_users;
+               if ( ! isset( $importer_users ) )
+                       $importer_users = (array) get_users_of_blog();
 
-                                       if (($post_date[2] == 'PM') && ($posthour != '12'))
-                                               $posthour = $posthour + 12;
-                                       else if (($post_date[2] == 'AM') && ($posthour == '12'))
-                                               $posthour = '00';
+               foreach ( $importer_users as $user ) {
+                       $sel = ( $user->user_id == $current ) ? " selected='selected'" : '';
+                       $options .= "<option value='$user->user_id'$sel>$user->display_name</option>";
+               }
 
-                                       $post_date = "$postyear-$postmonth-$postday $posthour:$postminute:$postsecond";
+               return $options;
+       }
 
-                                       $post_content = addslashes($post_content);
-                                       $post_content = str_replace(array('<br>','<BR>','<br/>','<BR/>','<br />','<BR />'), "\n", $post_content); // the XHTML touch... ;)
+       function save_authors() {
+               global $importing_blog, $wpdb;
+               $authors = (array) $_POST['authors'];
 
-                                       $post_title = addslashes($post_title);
+               $host = $this->blogs[$importing_blog]['host'];
 
-                                       $post_status = 'publish';
+               // Get an array of posts => authors
+               $post_ids = (array) $wpdb->get_col("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = '$host'");
+               $post_ids = join( ',', $post_ids );
+               $results = (array) $wpdb->get_results("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN ($post_ids)");
+               foreach ( $results as $row )
+                       $authors_posts[$row->post_id] = $row->meta_value;
 
-                                       if ( $ID = post_exists($post_title, '', $post_date) ) {
-                                               $post_array[$i]['ID'] = $ID;
-                                               $skippedpostcount++;
-                                       } else {
-                                               $post_array[$i]['post'] = compact('post_author', 'post_content', 'post_title', 'post_category', 'post_author', 'post_date', 'post_status');
-                                               $post_array[$i]['comments'] = false;
-                                       }
+               foreach ( $authors as $author => $user_id ) {
+                       $user_id = (int) $user_id;
 
-                                       // Import any comments attached to this post.
-                                       if ($postparts[1]) :
-                                       for ($j = 1; $j < count($postparts); $j = $j + 1) {
-                                               $commentinfo = explode('|W|P|', $postparts[$j]);
-                                               $comment_date = explode(' ', $commentinfo[0]);
-                                               $comment_date_Ymd = explode('/', $comment_date[0]);
-                                               $commentyear = $comment_date_Ymd[2];
-                                               $commentmonth = zeroise($comment_date_Ymd[0], 2);
-                                               $commentday = zeroise($comment_date_Ymd[1], 2);
-                                               $comment_date_His = explode(':', $comment_date[1]);
-                                               $commenthour = zeroise($comment_date_His[0], 2);
-                                               $commentminute = zeroise($comment_date_His[1], 2);
-                                               $commentsecond = '00';
-                                               if (($comment_date[2] == 'PM') && ($commenthour != '12'))
-                                                       $commenthour = $commenthour + 12;
-                                               else if (($comment_date[2] == 'AM') && ($commenthour == '12'))
-                                                       $commenthour = '00';
-                                               $comment_date = "$commentyear-$commentmonth-$commentday $commenthour:$commentminute:$commentsecond";
-                                               $comment_author = addslashes(strip_tags($commentinfo[1]));
-                                               if ( strpos($commentinfo[1], 'a href') ) {
-                                                       $comment_author_parts = explode('&quot;', htmlentities($commentinfo[1]));
-                                                       $comment_author_url = $comment_author_parts[1];
-                                               } else $comment_author_url = '';
-                                               $comment_content = $commentinfo[2];
-                                               $comment_content = str_replace(array('<br>','<BR>','<br/>','<BR/>','<br />','<BR />'), "\n", $comment_content);
-                                               $comment_approved = 1;
-                                               if ( comment_exists($comment_author, $comment_date) ) {
-                                                       $skippedcommentcount++;
-                                               } else {
-                                                       $comment = compact('comment_author', 'comment_author_url', 'comment_date', 'comment_content', 'comment_approved');
-                                                       $post_array[$i]['comments'][$j] = wp_filter_comment($comment);
-                                               }
-                                               $commentcount++;
-                                       }
-                                       endif;
-                                       $postcount++;
-                               }
-                               if ( count($post_array) ) {
-                                       krsort($post_array);
-                                       foreach($post_array as $post) {
-                                               if ( ! $comment_post_ID = $post['ID'] )
-                                                       $comment_post_ID = wp_insert_post($post['post']);
-                                               if ( $post['comments'] ) {
-                                                       foreach ( $post['comments'] as $comment ) {
-                                                               $comment['comment_post_ID'] = $comment_post_ID;
-                                                               wp_insert_comment($comment);
-                                                       }
-                                               }
-                                       }
-                               }
-                               $status = sprintf(__('%s post(s) parsed, %s skipped...'), $postcount,  $skippedpostcount).' '.
-                                       sprintf(__('%s comment(s) parsed, %s skipped...'), $commentcount, $skippedcommentcount).' '.
-                                       ' <strong>'.__('Done').'</strong>';
-                               $import = $this->import;
-                               $import['blogs'][$_GET['blog']]['archives']["$url"] = $status;
-                               update_option('import-blogger', $import);
-                               $did_one = true;
-                       }
-                       $output.= "<p>$archivename $status</p>\n";
-               }
-               if ( ! $did_one )
-                       $this->set_next_step(7);
-               die( $this->refresher(1000) . $output );
-       }
-
-       // Step 7: Restore the backed-up settings to Blogger
-       function restore_settings() {
-               $output = '<h1>'.__('Restoring your Blogger options')."</h1>\n";
-               $did_one = false;
-               // Restore options in reverse order.
-               if ( ! $this->import['reversed'] ) {
-                       $this->import['blogs'][$_GET['blog']]['options'] = array_reverse($this->import['blogs'][$_GET['blog']]['options'], true);
-                       $this->import['reversed'] = true;
-                       update_option('import-blogger', $this->import);
+                       // Skip authors that haven't been changed
+                       if ( $user_id == $this->blogs[$importing_blog]['authors'][$author][1] )
+                               continue;
+
+                       // Get a list of the selected author's posts
+                       $post_ids = (array) array_keys( $authors_posts, $this->blogs[$importing_blog]['authors'][$author][0] );
+                       $post_ids = join( ',', $post_ids);
+
+                       $wpdb->query("UPDATE $wpdb->posts SET post_author = $user_id WHERE id IN ($post_ids)");
+                       $this->blogs[$importing_blog]['authors'][$author][1] = $user_id;
                }
-               foreach ( $this->import['blogs'][$_GET['blog']]['options'] as $blog_opt => $optary ) {
-                       if ( $did_one ) {
-                               $output .= "<p>$blog_opt</p>\n";
-                       } elseif ( $optary['restored'] || ! $optary['modify'] ) {
-                               $output .= "<p><del>$blog_opt</del></p>\n";
-                       } else {
-                               $posturl = "http://www.blogger.com/{$blog_opt}.do";
-                               $headers = array_merge($this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['cookies'], $this->import['cookies']);
-                               if ( 'blog-publishing' == $blog_opt) {
-                                       if ( $optary['backup']['publishMode'] > 0 ) {
-                                               $response = $this->get_blogger("http://www.blogger.com/blog-publishing.g?blogID={$_GET['blog']}&publishMode={$optary['backup']['publishMode']}", $headers);
-                                               sleep(2);
-                                               if ( $response['code'] >= 400 )
-                                                       die('<h1>'.__('Error restoring publishMode').'</h1><p>'.__('Please tell the devs.').'</p>' . addslashes(print_r($response, 1)) );
-                                       }
-                               }
-                               if ( $optary['backup'] != $optary['modify'] ) {
-                                       $response = $this->post_blogger($posturl, $headers, $optary['backup']);
-                                       if ( $response['code'] >= 400 || strstr($response['body'], 'There are errors on this form') ) {
-                                               $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['error'] = true;
-                                               update_option('import-blogger', $this->import);
-                                               $output .= sprintf(__('%s failed. Trying again.'), "<p><strong>$blog_opt</strong> ").'</p>';
-                                       } else {
-                                               $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['restored'] = true;
-                                               update_option('import-blogger', $this->import);
-                                               $output .= sprintf(__('%s restored.'), "<p><strong>$blog_opt</strong> ").'</p>';
-                                       }
-                               }
-                               $did_one = true;
-                       }
+               $this->save_vars();
+
+               wp_redirect('edit.php');
+       }
+
+       function _get_auth_sock() {
+               // Connect to https://www.google.com
+               if ( !$sock = @ fsockopen('ssl://www.google.com', 443, $errno, $errstr) ) {
+                       $this->uh_oh(
+                               __('Could not connect to https://www.google.com'),
+                               __('There was a problem opening a secure connection to Google. This is what went wrong:'),
+                               "$errstr ($errno)"
+                       );
+                       return false;
                }
+               return $sock;
+       }
 
-               if ( $did_one ) {
-                       die( $this->refresher(1000) . $output );
-               } elseif ( $this->import['blogs'][$_GET['blog']]['options']['blog-publishing']['backup']['publishMode'] > 0 ) {
-                       $this->set_next_step(9);
-               } else {
-                       $this->set_next_step(8);
+       function _get_blogger_sock($host = 'www2.blogger.com') {
+               if ( !$sock = @ fsockopen($host, 80, $errno, $errstr) ) {
+                       $this->uh_oh(
+                               sprintf( __('Could not connect to %s'), $host ),
+                               __('There was a problem opening a connection to Blogger. This is what went wrong:'),
+                               "$errstr ($errno)"
+                       );
+                       return false;
                }
+               return $sock;
+       }
 
-               $this->do_next_step();
+       function _txrx( $sock, $request ) {
+               fwrite( $sock, $request );
+               while ( ! feof( $sock ) )
+                       $response .= @ fread ( $sock, 8192 );
+               fclose( $sock );
+               return $response;
        }
 
-       // Step 8: Republish, all back to normal
-       function republish_blog() {
-               $this->publish_blogger(9, __('Publishing with original template and options'));
+       function revoke($token) {
+               $headers = array(
+                       "GET /accounts/AuthSubRevokeToken HTTP/1.0",
+                       "Authorization: AuthSub token=\"$token\""
+               );
+               $request = join( "\r\n", $headers ) . "\r\n\r\n";
+               $sock = $this->_get_auth_sock( );
+               if ( ! $sock ) return false;
+               $this->_txrx( $sock, $request );
+       }
+
+       function restart() {
+               global $wpdb;
+               $options = get_option( 'blogger_importer' );
+
+               if ( isset( $options['token'] ) )
+                       $this->revoke( $options['token'] );
+
+               delete_option('blogger_importer');
+               $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'blogger_author'");
+               wp_redirect('?import=blogger');
+       }
+
+       // Returns associative array of code, header, cookies, body. Based on code from php.net.
+       function parse_response($this_response) {
+               // Split response into header and body sections
+               list($response_headers, $response_body) = explode("\r\n\r\n", $this_response, 2);
+               $response_header_lines = explode("\r\n", $response_headers);
+
+               // First line of headers is the HTTP response code
+               $http_response_line = array_shift($response_header_lines);
+               if(preg_match('@^HTTP/[0-9]\.[0-9] ([0-9]{3})@',$http_response_line, $matches)) { $response_code = $matches[1]; }
+
+               // put the rest of the headers in an array
+               $response_header_array = array();
+               foreach($response_header_lines as $header_line) {
+                       list($header,$value) = explode(': ', $header_line, 2);
+                       $response_header_array[$header] .= $value."\n";
+               }
+
+               $cookie_array = array();
+               $cookies = explode("\n", $response_header_array["Set-Cookie"]);
+               foreach($cookies as $this_cookie) { array_push($cookie_array, "Cookie: ".$this_cookie); }
+
+               return array("code" => $response_code, "header" => $response_header_array, "cookies" => $cookie_array, "body" => $response_body);
        }
 
        // Step 9: Congratulate the user
        function congrats() {
+               $blog = (int) $_GET['blog'];
                echo '<h1>'.__('Congratulations!').'</h1><p>'.__('Now that you have imported your Blogger blog into WordPress, what are you going to do? Here are some suggestions:').'</p><ul><li>'.__('That was hard work! Take a break.').'</li>';
                if ( count($this->import['blogs']) > 1 )
                        echo '<li>'.__('In case you haven\'t done it already, you can import the posts from your other blogs:'). $this->show_blogs() . '</li>';
-               if ( $n = count($this->import['blogs'][$_GET['blog']]['newusers']) )
+               if ( $n = count($this->import['blogs'][$blog]['newusers']) )
                        echo '<li>'.sprintf(__('Go to <a href="%s" target="%s">Authors &amp; Users</a>, where you can modify the new user(s) or delete them. If you want to make all of the imported posts yours, you will be given that option when you delete the new authors.'), 'users.php', '_parent').'</li>';
-               echo '<li>'.__('For security, click the link below to reset this importer. That will clear your Blogger credentials and options from the database.').'</li>';
+               echo '<li>'.__('For security, click the link below to reset this importer.').'</li>';
                echo '</ul>';
        }
 
        // Figures out what to do, then does it.
        function start() {
-               if ( $_GET['restart'] == 'true' ) {
+               if ( isset($_POST['restart']) )
                        $this->restart();
+
+               $options = get_option('blogger_importer');
+
+               if ( is_array($options) )
+                       foreach ( $options as $key => $value )
+                               $this->$key = $value;
+
+               if ( isset( $_REQUEST['blog'] ) ) {
+                       $blog = is_array($_REQUEST['blog']) ? array_shift( array_keys( $_REQUEST['blog'] ) ) : $_REQUEST['blog'];
+                       $blog = (int) $blog;
+                       $result = $this->import_blog( $blog );
+                       if ( is_wp_error( $result ) )
+                               echo $result->get_error_message();
+               } elseif ( isset($_GET['token']) )
+                       $this->auth();
+               elseif ( $this->token && $this->token_is_valid() )
+                       $this->show_blogs();
+               else
+                       $this->greet();
+
+               $saved = $this->save_vars();
+
+               if ( $saved && !isset($_GET['noheader']) ) {
+                       $restart = __('Restart');
+                       $message = __('We have saved some information about your Blogger account in your WordPress database. Clearing this information will allow you to start over. Restarting will not affect any posts you have already imported. If you attempt to re-import a blog, duplicate posts and comments will be skipped.');
+                       $submit = __('Clear account information');
+                       echo "<div class='wrap'><h2>$restart</h2><p>$message</p><form method='post' action='?import=blogger&noheader=true'><p class='submit' style='text-align:left;'><input type='submit' value='$submit' name='restart' /></p></form></div>";
                }
+       }
 
-               if ( isset($_GET['noheader']) ) {
-                       header('Content-Type: text/html; charset=utf-8');
+       function save_vars() {
+               $vars = get_object_vars($this);
+               update_option( 'blogger_importer', $vars );
 
-                       $this->import = get_option('import-blogger');
+               return !empty($vars);
+       }
 
-                       if ( false === $this->import ) {
-                               $step = 0;
-                       } elseif ( isset($_GET['step']) ) {
-                               $step = (int) $_GET['step'];
-                       } elseif ( isset($_GET['blog']) && isset($this->import['blogs'][$_GET['blog']]['nextstep']) ) {
-                               $step = $this->import['blogs'][$_GET['blog']]['nextstep'];
-                       } elseif ( is_array($this->import['blogs']) ) {
-                               $step = 1;
-                       } else {
-                               $step = 0;
+       function admin_head() {
+?>
+<style type="text/css">
+td { text-align: center; line-height: 2em;}
+thead td { font-weight: bold; }
+.bar {
+       width: 200px;
+       text-align: left;
+       line-height: 2em;
+       padding: 0px;
+}
+.ind {
+       position: absolute;
+       background-color: #83B4D8;
+       width: 1px;
+       z-index: 9;
+}
+.stat {
+       z-index: 10;
+       position: relative;
+       text-align: center;
+}
+.submit {
+       text-align: center !important;
+}
+</style>
+<?php
+       }
+
+       function Blogger_Import() {
+               global $importer_started;
+               $importer_started = time();
+               if ( isset( $_GET['import'] ) && $_GET['import'] == 'blogger' ) {
+                       wp_enqueue_script('jquery');
+                       add_action('admin_head', array(&$this, 'admin_head'));
+               }
+       }
+}
+
+$blogger_import = new Blogger_Import();
+
+register_importer('blogger', __('Blogger'), __('Import posts, comments, and users from a Blogger blog'), array ($blogger_import, 'start'));
+
+class AtomEntry {
+       var $links = array();
+       var $categories = array();
+}
+
+class AtomParser {
+
+       var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights');
+       var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft','author');
+
+       var $depth = 0;
+       var $indent = 2;
+       var $in_content;
+       var $ns_contexts = array();
+       var $ns_decls = array();
+       var $is_xhtml = false;
+       var $skipped_div = false;
+
+       var $entry;
+
+       function AtomParser() {
+
+               $this->entry = new AtomEntry();
+               $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";');
+               $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";');
+       }
+
+       function parse($xml) {
+
+               global $app_logging;
+               array_unshift($this->ns_contexts, array());
+
+               $parser = xml_parser_create_ns();
+               xml_set_object($parser, $this);
+               xml_set_element_handler($parser, "start_element", "end_element");
+               xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
+               xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0);
+               xml_set_character_data_handler($parser, "cdata");
+               xml_set_default_handler($parser, "_default");
+               xml_set_start_namespace_decl_handler($parser, "start_ns");
+               xml_set_end_namespace_decl_handler($parser, "end_ns");
+
+               $contents = "";
+
+               xml_parse($parser, $xml);
+
+               xml_parser_free($parser);
+
+               return true;
+       }
+
+       function start_element($parser, $name, $attrs) {
+
+               $tag = array_pop(split(":", $name));
+
+               array_unshift($this->ns_contexts, $this->ns_decls);
+
+               $this->depth++;
+
+               if(!empty($this->in_content)) {
+                       $attrs_prefix = array();
+
+                       // resolve prefixes for attributes
+                       foreach($attrs as $key => $value) {
+                               $attrs_prefix[$this->ns_to_prefix($key)] = $this->xml_escape($value);
                        }
-//echo "Step $step.";
-//die('<pre>'.print_r($this->import,1).'</pre');
-                       switch ($step) {
-                               case 0 :
-                                       $this->do_login();
-                                       break;
-                               case 1 :
-                                       $this->select_blog();
-                                       break;
-                               case 2 :
-                                       $this->backup_settings();
-                                       break;
-                               case 3 :
-                                       $this->wait_for_blogger();
-                                       break;
-                               case 4 :
-                                       $this->publish_blog();
-                                       break;
-                               case 5 :
-                                       $this->get_archive_urls();
-                                       break;
-                               case 6 :
-                                       $this->get_archive();
-                                       break;
-                               case 7 :
-                                       $this->restore_settings();
-                                       break;
-                               case 8 :
-                                       $this->republish_blog();
-                                       break;
-                               case 9 :
-                                       $this->congrats();
-                                       break;
+                       $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix)));
+                       if(strlen($attrs_str) > 0) {
+                               $attrs_str = " " . $attrs_str;
                        }
-                       die;
 
-               } else {
-                       $this->greet();
+                       $xmlns_str = join(' ', array_map($this->map_xmlns_func, array_keys($this->ns_contexts[0]), array_values($this->ns_contexts[0])));
+                       if(strlen($xmlns_str) > 0) {
+                               $xmlns_str = " " . $xmlns_str;
+                       }
+
+                       // handle self-closing tags (case: a new child found right-away, no text node)
+                       if(count($this->in_content) == 2) {
+                               array_push($this->in_content, ">");
+                       }
+
+                       array_push($this->in_content, "<". $this->ns_to_prefix($name) ."{$xmlns_str}{$attrs_str}");
+               } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) {
+                       $this->in_content = array();
+                       $this->is_xhtml = $attrs['type'] == 'xhtml';
+                       array_push($this->in_content, array($tag,$this->depth));
+               } else if($tag == 'link') {
+                       array_push($this->entry->links, $attrs);
+               } else if($tag == 'category') {
+                       array_push($this->entry->categories, $attrs['term']);
+               }
+
+               $this->ns_decls = array();
+       }
+
+       function end_element($parser, $name) {
+
+               $tag = array_pop(split(":", $name));
+
+               if(!empty($this->in_content)) {
+                       if($this->in_content[0][0] == $tag &&
+                       $this->in_content[0][1] == $this->depth) {
+                               array_shift($this->in_content);
+                               if($this->is_xhtml) {
+                                       $this->in_content = array_slice($this->in_content, 2, count($this->in_content)-3);
+                               }
+                               $this->entry->$tag = join('',$this->in_content);
+                               $this->in_content = array();
+                       } else {
+                               $endtag = $this->ns_to_prefix($name);
+                               if (strpos($this->in_content[count($this->in_content)-1], '<' . $endtag) !== false) {
+                                       array_push($this->in_content, "/>");
+                               } else {
+                                       array_push($this->in_content, "</$endtag>");
+                               }
+                       }
                }
+
+               array_shift($this->ns_contexts);
+
+               #print str_repeat(" ", $this->depth * $this->indent) . "end_element('$name')" ."\n";
+
+               $this->depth--;
        }
 
-       function Blogger_Import() {
-               // This space intentionally left blank.
+       function start_ns($parser, $prefix, $uri) {
+               #print str_repeat(" ", $this->depth * $this->indent) . "starting: " . $prefix . ":" . $uri . "\n";
+               array_push($this->ns_decls, array($prefix,$uri));
        }
-}
 
-$blogger_import = new Blogger_Import();
+       function end_ns($parser, $prefix) {
+               #print str_repeat(" ", $this->depth * $this->indent) . "ending: #" . $prefix . "#\n";
+       }
 
-register_importer('blogger', __('Old Blogger'), __('Import <strong>posts and comments</strong> from your Old Blogger account'), array ($blogger_import, 'start'));
+       function cdata($parser, $data) {
+               #print str_repeat(" ", $this->depth * $this->indent) . "data: #" . $data . "#\n";
+               if(!empty($this->in_content)) {
+                       // handle self-closing tags (case: text node found, need to close element started)
+                       if (strpos($this->in_content[count($this->in_content)-1], '<') !== false) {
+                               array_push($this->in_content, ">");
+                       }
+                       array_push($this->in_content, $this->xml_escape($data));
+               }
+       }
+
+       function _default($parser, $data) {
+               # when does this gets called?
+       }
+
+
+       function ns_to_prefix($qname) {
+               $components = split(":", $qname);
+               $name = array_pop($components);
+
+               if(!empty($components)) {
+                       $ns = join(":",$components);
+                       foreach($this->ns_contexts as $context) {
+                               foreach($context as $mapping) {
+                                       if($mapping[1] == $ns && strlen($mapping[0]) > 0) {
+                                               return "$mapping[0]:$name";
+                                       }
+                               }
+                       }
+               }
+               return $name;
+       }
+
+       function xml_escape($string)
+       {
+                        return str_replace(array('&','"',"'",'<','>'),
+                               array('&amp;','&quot;','&apos;','&lt;','&gt;'),
+                               $string );
+       }
+}
 
 ?>