<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/oauth.py</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <filename>example/block.py</filename>
    </modified>
    <modified>
      <filename>example/dms.py</filename>
    </modified>
    <modified>
      <filename>example/follow.py</filename>
    </modified>
    <modified>
      <filename>example/friends.py</filename>
    </modified>
    <modified>
      <filename>example/leave.py</filename>
    </modified>
    <modified>
      <filename>example/list-followers.py</filename>
    </modified>
    <modified>
      <filename>example/list-friends.py</filename>
    </modified>
    <modified>
      <filename>example/replies.py</filename>
    </modified>
    <modified>
      <filename>example/search.py</filename>
    </modified>
    <modified>
      <filename>example/show-user.py</filename>
    </modified>
    <modified>
      <filename>example/unblock.py</filename>
    </modified>
    <modified>
      <filename>example/update.py</filename>
    </modified>
    <modified>
      <filename>example/user_timeline.py</filename>
    </modified>
    <modified>
      <filename>example/verify_creds.py</filename>
    </modified>
    <modified>
      <diff>@@ -5,18 +5,20 @@ Twisted Twitter interface.
 Copyright (c) 2008  Dustin Sallings &lt;dustin@spy.net&gt;
 &quot;&quot;&quot;
 
-import time
 import base64
 import urllib
 import mimetypes
 import mimetools
 
-from twisted.python import log
-from twisted.internet import reactor, defer
+import oauth
+
+from twisted.internet import defer
 from twisted.web import client
 
 import txml
 
+SIGNATURE_METHOD = oauth.OAuthSignatureMethod_HMAC_SHA1()
+
 BASE_URL=&quot;http://twitter.com&quot;
 SEARCH_URL=&quot;http://search.twitter.com/search.atom&quot;
 
@@ -25,16 +27,35 @@ class Twitter(object):
     agent=&quot;twitty twister&quot;
 
     def __init__(self, user=None, passwd=None,
-        base_url=BASE_URL, search_url=SEARCH_URL):
+        base_url=BASE_URL, search_url=SEARCH_URL, consumer=None, token=None, signature_method=SIGNATURE_METHOD):
 
         self.base_url = BASE_URL
         self.search_Url = SEARCH_URL
+
+
         self.username = user
         self.password = passwd
 
-    def __makeAuthHeader(self, headers=None):
-        if not headers:
-            headers = {}
+        if user and passwd:
+            self.use_oauth = False
+        else:
+            self.use_oauth = True
+            self.consumer = consumer
+            self.token = token
+            self.signature_method = signature_method
+    
+    def __makeOAuthHeader(self, method, url, parameters={}, headers={}):
+        oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
+            token=self.token, http_method=method, http_url=url, parameters=parameters)
+        oauth_request.sign_request(self.signature_method, self.consumer, self.token)
+
+        headers = dict(headers.items() + oauth_request.to_header().items())
+        
+        #for k, v in headers.iteritems():
+        #    headers[k] = v.encode('utf-8')
+        return headers
+    
+    def __makeAuthHeader(self, headers={}):
         authorization = base64.encodestring('%s:%s'
             % (self.username, self.password))[:-1]
         headers['Authorization'] = &quot;Basic %s&quot; % authorization
@@ -54,50 +75,71 @@ class Twitter(object):
         files is a sequence of (name, filename, value) elements for data to be uploaded as files
         Return (content_type, body) ready for httplib.HTTP instance
         &quot;&quot;&quot;
-        BOUNDARY = mimetools.choose_boundary()
-        CRLF = '\r\n'
-        L = []
+        boundary = mimetools.choose_boundary()
+        crlf = '\r\n'
+        l = []
         for k, v in fields:
-            L.append('--' + BOUNDARY)
-            L.append('Content-Disposition: form-data; name=&quot;%s&quot;' % k)
-            L.append('')
-            L.append(v)
+            l.append('--' + boundary)
+            l.append('Content-Disposition: form-data; name=&quot;%s&quot;' % k)
+            l.append('')
+            l.append(v)
         for (k, f, v) in files:
-            L.append('--' + BOUNDARY)
-            L.append('Content-Disposition: form-data; name=&quot;%s&quot;; filename=&quot;%s&quot;' % (k, f))
-            L.append('Content-Type: %s' % self.__getContentType(f))
-            L.append('')
-            L.append(v)
-        L.append('--' + BOUNDARY + '--')
-        L.append('')
-        body = CRLF.join(L)
-        return BOUNDARY, body
+            l.append('--' + boundary)
+            l.append('Content-Disposition: form-data; name=&quot;%s&quot;; filename=&quot;%s&quot;' % (k, f))
+            l.append('Content-Type: %s' % self.__getContentType(f))
+            l.append('')
+            l.append(v)
+        l.append('--' + boundary + '--')
+        l.append('')
+        body = crlf.join(l)
+        return boundary, body
     
     def __getContentType(self, filename):
         return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
     
     def __postMultipart(self, path, fields=(), files=()):
+        url = BASE_URL + path
+
         (boundary, body) = self.__encodeMultipart(fields, files)
-        h = {'Content-Type': 'multipart/form-data; boundary=%s' % boundary,
+        headers = {'Content-Type': 'multipart/form-data; boundary=%s' % boundary,
             'Content-Length': str(len(body))
             }
+
+        if self.use_oauth:
+            headers = self.__makeOAuthHeader('POST', url, headers=headers)
+        else:
+            headers = self.__makeAuthHeader(h)
         
-        return client.getPage((BASE_URL + &quot;%s&quot;) % path, method='POST',
+        return client.getPage(url, method='POST',
             agent=self.agent,
-            postdata=body, headers=self.__makeAuthHeader(h))
+            postdata=body, headers=headers)
 
     def __post(self, path, args={}):
-        h = {'Content-Type': 'application/x-www-form-urlencoded'}
-        return client.getPage((BASE_URL + &quot;%s&quot;) % path, method='POST',
+        headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}
+
+        url = BASE_URL + path
+
+        if self.use_oauth:
+            headers = self.__makeOAuthHeader('POST', url, args, headers)
+        else:
+            headers = self.__makeAuthHeader(headers)
+        
+        return client.getPage(url, method='POST',
             agent=self.agent,
-            postdata=self.__urlencode(args), headers=self.__makeAuthHeader(h))
+            postdata=self.__urlencode(args), headers=headers)
 
-    def __get(self, path, delegate, params, feed_factory=txml.Feed):
+    def __get(self, path, delegate, params, feed_factory=txml.Feed, extra_args=None):
         url = BASE_URL + path
         if params:
             url += '?' + self.__urlencode(params)
-        return client.downloadPage(url, feed_factory(delegate),
-            agent=self.agent, headers=self.__makeAuthHeader())
+
+        if self.use_oauth:
+            headers = self.__makeOAuthHeader('GET', url)
+        else:
+            headers = self.__makeAuthHeader()
+        
+        return client.downloadPage(url, feed_factory(delegate, extra_args),
+            agent=self.agent, headers=headers)
 
     def verify_credentials(self):
         &quot;Verify a user's credentials.&quot;
@@ -117,14 +159,14 @@ class Twitter(object):
         return self.__parsed_post(self.__post(&quot;/statuses/update.xml&quot;, params),
             txml.parseUpdateResponse)
 
-    def friends(self, delegate, params={}):
+    def friends(self, delegate, params={}, extra_args=None):
         &quot;&quot;&quot;Get updates from friends.
 
         Calls the delgate once for each status object received.&quot;&quot;&quot;
         return self.__get(&quot;/statuses/friends_timeline.xml&quot;, delegate, params,
-            txml.StatusList)
+            txml.StatusList, extra_args=extra_args)
 
-    def user_timeline(self, delegate, user=None, params={}):
+    def user_timeline(self, delegate, user=None, params={}, extra_args=None):
         &quot;&quot;&quot;Get the most recent updates for a user.
 
         If no user is specified, the statuses for the authenticating user are
@@ -133,25 +175,25 @@ class Twitter(object):
         See search for example of how results are returned.&quot;&quot;&quot;
         if user:
             params['id'] = user
-        return self.__get(&quot;/statuses/user_timeline.atom&quot;, delegate, params)
+        return self.__get(&quot;/statuses/user_timeline.xml&quot;, delegate, params, txml.StatusList, extra_args=extra_args)
 
-    def public_timeline(self, delegate, params={}):
+    def public_timeline(self, delegate, params={}, extra_args=None):
         &quot;Get the most recent public timeline.&quot;
 
-        return self.__get(&quot;/statuses/public_timeline.atom&quot;, delegate, params)
+        return self.__get(&quot;/statuses/public_timeline.atom&quot;, delegate, params, extra_args=extra_args)
 
-    def direct_messages(self, delegate, params={}):
+    def direct_messages(self, delegate, params={}, extra_args=None):
         &quot;&quot;&quot;Get direct messages for the authenticating user.
 
         Search results are returned one message at a time a DirectMessage
         objects&quot;&quot;&quot;
-        return self.__get(&quot;/direct_messages.xml&quot;, delegate, params, txml.Direct)
+        return self.__get(&quot;/direct_messages.xml&quot;, delegate, params, txml.Direct, extra_args=extra_args)
 
-    def replies(self, delegate, params={}):
+    def replies(self, delegate, params={}, extra_args=None):
         &quot;&quot;&quot;Get the most recent replies for the authenticating user.
 
         See search for example of how results are returned.&quot;&quot;&quot;
-        return self.__get(&quot;/statuses/replies.atom&quot;, delegate, params)
+        return self.__get(&quot;/statuses/replies.atom&quot;, delegate, params, extra_args=extra_args)
 
     def follow(self, user):
         &quot;&quot;&quot;Follow the given user.
@@ -165,7 +207,7 @@ class Twitter(object):
         Returns no useful data.&quot;&quot;&quot;
         return self.__post('/friendships/destroy/%s.xml' % user)
 
-    def list_friends(self, delegate, user=None, params=None):
+    def list_friends(self, delegate, user=None, params=None, extra_args=None):
         &quot;&quot;&quot;Get the list of friends for a user.
 
         Calls the delegate with each user object found.&quot;&quot;&quot;
@@ -175,10 +217,10 @@ class Twitter(object):
             url = BASE_URL + '/statuses/friends.xml'
         if params:
             url += '?' + self.__urlencode(params)
-        return client.downloadPage(url, txml.Users(delegate),
+        return client.downloadPage(url, txml.Users(delegate, extra_args),
             headers=self.__makeAuthHeader())
 
-    def list_followers(self, delegate, user=None, params=None):
+    def list_followers(self, delegate, user=None, params=None, extra_args=None):
         &quot;&quot;&quot;Get the list of followers for a user.
 
         Calls the delegate with each user object found.&quot;&quot;&quot;
@@ -188,7 +230,7 @@ class Twitter(object):
             url = BASE_URL + '/statuses/followers.xml'
         if params:
             url += '?' + self.__urlencode(params)
-        return client.downloadPage(url, txml.Users(delegate),
+        return client.downloadPage(url, txml.Users(delegate, extra_args),
             headers=self.__makeAuthHeader())
 
     def show_user(self, user):
@@ -206,7 +248,7 @@ class Twitter(object):
             headers={}).addErrback(lambda e: d.errback(e))
         return d
 
-    def search(self, query, delegate, args=None):
+    def search(self, query, delegate, args=None, extra_args=None):
         &quot;&quot;&quot;Perform a search query.
 
         Results are given one at a time to the delegate.  An example delegate
@@ -218,7 +260,7 @@ class Twitter(object):
             args = {}
         args['q'] = query
         return client.downloadPage(SEARCH_URL + '?' + self.__urlencode(args),
-            txml.Feed(delegate), agent=self.agent)
+            txml.Feed(delegate, extra_args), agent=self.agent)
 
     def block(self, user):
         &quot;&quot;&quot;Block the given user.</diff>
      <filename>lib/twitter.py</filename>
    </modified>
    <modified>
      <diff>@@ -98,8 +98,9 @@ class Parser(sux.XMLParser):
     toplevel_type = None
 
     &quot;&quot;&quot;A file-like thingy that parses a friendfeed feed with SUX.&quot;&quot;&quot;
-    def __init__(self, delegate):
+    def __init__(self, delegate, extra_args=None):
         self.delegate=delegate
+        self.extra_args=extra_args
 
         self.connectionMade()
         self.currentEntry=None
@@ -125,7 +126,10 @@ class Parser(sux.XMLParser):
         if name == self.toplevel_tag:
             self.currentEntry.done = True
             del self.currentEntry.current_ob
-            self.delegate(self.currentEntry)
+            if self.extra_args is None:
+                self.delegate(self.currentEntry)
+            else:
+                self.delegate(self.currentEntry, self.extra_args)
             self.currentEntry = None
         elif self.currentEntry:
             self.currentEntry.gotTagEnd(name, ''.join(self.data).decode('utf8'))</diff>
      <filename>lib/txml.py</filename>
    </modified>
    <modified>
      <filename>test/txml_test.py</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ea7a4173d0f6ab5f738e5ca0b9fdd5cebbc3d628</id>
    </parent>
  </parents>
  <author>
    <name>K&#233;vin Dunglas</name>
    <email>dunglas@macbook-de-kevin-dunglas.local</email>
  </author>
  <url>http://github.com/dunglas/twitty-twister/commit/21aac9b53684d9b860f7d6a62594472aa03ba9c6</url>
  <id>21aac9b53684d9b860f7d6a62594472aa03ba9c6</id>
  <committed-date>2009-06-20T03:46:19-07:00</committed-date>
  <authored-date>2009-06-20T03:46:19-07:00</authored-date>
  <message>Examples.</message>
  <tree>35bd790d3321270d3637e17fe28d61c961af2d61</tree>
  <committer>
    <name>K&#233;vin Dunglas</name>
    <email>dunglas@macbook-de-kevin-dunglas.local</email>
  </committer>
</commit>
