<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>__init__.py</filename>
    </added>
    <added>
      <filename>crypto/number.py</filename>
    </added>
    <added>
      <filename>crypto/randpool.py</filename>
    </added>
    <added>
      <filename>helios/__init__.py</filename>
    </added>
    <added>
      <filename>helios/admin_views.py</filename>
    </added>
    <added>
      <filename>helios/election_urls.py</filename>
    </added>
    <added>
      <filename>helios/json.py</filename>
    </added>
    <added>
      <filename>helios/models.py</filename>
    </added>
    <added>
      <filename>helios/oauth.py</filename>
    </added>
    <added>
      <filename>helios/security.py</filename>
    </added>
    <added>
      <filename>helios/templates/admin_clients.html</filename>
    </added>
    <added>
      <filename>helios/templates/admin_home.html</filename>
    </added>
    <added>
      <filename>helios/templates/ballot_verifier.html</filename>
    </added>
    <added>
      <filename>helios/templates/base.html</filename>
    </added>
    <added>
      <filename>helios/templates/booth.html</filename>
    </added>
    <added>
      <filename>helios/templates/drive_tally.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_bboard.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_build.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_freeze.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_keygenerator.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_new.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_new_2.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_view.html</filename>
    </added>
    <added>
      <filename>helios/templates/election_voters_manage.html</filename>
    </added>
    <added>
      <filename>helios/templates/email_trustees.html</filename>
    </added>
    <added>
      <filename>helios/templates/faq.html</filename>
    </added>
    <added>
      <filename>helios/templates/index.html</filename>
    </added>
    <added>
      <filename>helios/templates/js_api.html</filename>
    </added>
    <added>
      <filename>helios/templates/keyshares_manage.html</filename>
    </added>
    <added>
      <filename>helios/templates/keyshares_tally_manage.html</filename>
    </added>
    <added>
      <filename>helios/templates/learn.html</filename>
    </added>
    <added>
      <filename>helios/templates/login.html</filename>
    </added>
    <added>
      <filename>helios/templates/tally_verifier.html</filename>
    </added>
    <added>
      <filename>helios/templates/trustee_home.html</filename>
    </added>
    <added>
      <filename>helios/templates/trustee_tally.html</filename>
    </added>
    <added>
      <filename>helios/templates/user_home.html</filename>
    </added>
    <added>
      <filename>helios/templates/vote.html</filename>
    </added>
    <added>
      <filename>helios/templates/voters_email.html</filename>
    </added>
    <added>
      <filename>helios/urls.py</filename>
    </added>
    <added>
      <filename>helios/utils.py</filename>
    </added>
    <added>
      <filename>helios/view_utils.py</filename>
    </added>
    <added>
      <filename>helios/views.py</filename>
    </added>
    <added>
      <filename>manage.py</filename>
    </added>
    <added>
      <filename>settings.py</filename>
    </added>
    <added>
      <filename>static/verification-programs/single-ballot-verifier/bigint.class</filename>
    </added>
    <added>
      <filename>static/verification-programs/single-ballot-verifier/index.html</filename>
    </added>
    <added>
      <filename>tests/heliosclient_manyballots.py</filename>
    </added>
    <added>
      <filename>tests/remove_ballot.py</filename>
    </added>
    <added>
      <filename>tests/specific_trustee_key.py</filename>
    </added>
    <added>
      <filename>tests/trustee_election.py</filename>
    </added>
    <added>
      <filename>tests/trustee_keys.py</filename>
    </added>
    <added>
      <filename>urls.py</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -6,34 +6,53 @@ Ben Adida
 &quot;&quot;&quot;
 
 import oauthclient
-from base import utils, oauth
+from helios import utils, oauth
 from crypto import algs, electionalgs
 
 class HeliosClient(object):
-  def __init__(self, auth_info, host, port):
+  def __init__(self, auth_info, host, port, prefix=&quot;&quot;):
     &quot;&quot;&quot;
     auth_info is consumer_key, ....
     &quot;&quot;&quot;
     self.consumer = oauth.OAuthConsumer(auth_info['consumer_key'],auth_info['consumer_secret'])
-    self.token = oauth.OAuthToken(auth_info['access_token'],auth_info['access_token_secret'])
+    self.token = oauth.OAuthToken(auth_info['consumer_key'],auth_info['consumer_secret'])
     self.client = oauthclient.MachineOAuthClient(self.consumer, self.token, host, port)
+    self.prefix = prefix
     
   def get(self, url, parameters = None):
-    return self.client.access_resource(&quot;GET&quot;, url, parameters= parameters)
+    print &quot;getting &quot; + self.prefix + url
+    return self.client.access_resource(&quot;GET&quot;, self.prefix + url, parameters= parameters)
   
   def post(self, url, parameters = None):
-    return self.client.access_resource(&quot;POST&quot;, url, parameters= parameters)
+    print &quot;posting &quot; + self.prefix + url
+    result = self.client.access_resource(&quot;POST&quot;, self.prefix + url, parameters= parameters)
+    return result
 
+  def get_test(self):
+    return self.get(&quot;/helios_test&quot;)
+    
   def params(self):
     params_json = self.get(&quot;/elections/params&quot;)
     return algs.ElGamal.fromJSONDict(utils.from_json(params_json))
     
-  def election_new(self, name, public_key):
-    election_id = self.post(&quot;/elections/new_3&quot;, {&quot;name&quot; : name, &quot;public_key&quot; : utils.to_json(public_key.toJSONDict())})
+  def election_new(self, name, public_key=None, ballot_type = 'homomorphic', tally_type = 'homomorphic', trustee_list=None):
+    args = {&quot;name&quot; : name, &quot;ballot_type&quot;: ballot_type, &quot;tally_type&quot;: tally_type}
+    if public_key:
+      args['public_key'] = utils.to_json(public_key.toJSONDict())
+    if trustee_list:
+      args['trustee_list'] = &quot;,&quot;.join(trustee_list)
+      
+    election_id = self.post(&quot;/elections/new_3&quot;, args)
     return election_id
     
+  def election_set_pk(self, election_id, public_key):
+    return self.post(&quot;/elections/%s/set_pk&quot; % election_id, {'public_key_json' : utils.to_json(public_key.toJSONDict())})
+    
+  def election_set_trustee_pk(self, election_id, trustee, public_key, pok):
+    return self.post(&quot;/elections/%s/trustees/%s/upload_pk&quot; % (election_id, trustee), {'public_key' : utils.to_json(public_key.toJSONDict()), 'pok': utils.to_json(pok.toJSONDict())})
+    
   def election_get(self, election_id):
-    return electionalgs.Election.fromJSONDict(utils.from_json(self.get(&quot;/elections/%s&quot; % election_id)))
+    return electionalgs.Election.fromJSONDict(utils.from_json(self.get(&quot;/elections/%s/&quot; % election_id)))
     
   def election_set_reg(self, election_id, open_reg=False):
     result = self.post(&quot;/elections/%s/set_reg&quot; % election_id, {'open_p' : str(int(open_reg))})
@@ -44,7 +63,7 @@ class HeliosClient(object):
     return result == &quot;SUCCESS&quot;
     
   def election_freeze(self, election_id):
-    result = self.post(&quot;/elections/%s/freeze_2&quot; % election_id, {})
+    result = self.post(&quot;/elections/%s/freeze&quot; % election_id, {})
     return result == &quot;SUCCESS&quot;
     
   def open_submit(self, election_id, encrypted_vote, email=None, openid_url=None, name=None, category=None):</diff>
      <filename>client/heliosclient.py</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@ Ben Adida
 2008-08-30
 &quot;&quot;&quot;
 
-from base import oauth, utils
+from helios import oauth, utils
 
 import httplib
 </diff>
      <filename>client/oauthclient.py</filename>
    </modified>
    <modified>
      <diff>@@ -8,7 +8,7 @@ ben@adida.net
 &quot;&quot;&quot;
 
 import math, sha, logging
-from base import randpool, number
+import randpool, number
 
 import numtheory
 
@@ -109,6 +109,7 @@ class ElGamal:
       EG.p = Utils.random_safe_prime(n_bits)
 
       # q is the order of the group
+      # FIXME: not always p-1/2
       EG.q = (EG.p-1)/2
   
       # find g that generates the q-order subgroup
@@ -125,7 +126,7 @@ class ElGamal:
       &quot;&quot;&quot;
       
       keypair = EGKeyPair()
-      keypair.generate(self.p, self.g)
+      keypair.generate(self.p, self.q, self.g)
   
       return keypair
       
@@ -145,13 +146,13 @@ class EGKeyPair:
       self.pk = EGPublicKey()
       self.sk = EGSecretKey()
 
-    def generate(self, p, g):
+    def generate(self, p, q, g):
       &quot;&quot;&quot;
       Generate an ElGamal keypair
       &quot;&quot;&quot;
       self.pk.g = g
       self.pk.p = p
-      self.pk.q = (p-1)/2
+      self.pk.q = q
       
       self.sk.x = Utils.random_mpz_lt(p)
       self.pk.y = pow(g, self.sk.x, p)
@@ -209,6 +210,33 @@ class EGPublicKey:
         return {'y' : str(self.y), 'p' : str(self.p), 'g' : str(self.g) , 'q' : str(self.q)}
 
     toJSONDict = to_dict
+    
+    def __mul__(self,other):
+      if other == 0 or other == 1:
+        return self
+        
+      # check p and q
+      if self.p != other.p or self.q != other.q or self.g != other.g:
+        raise Exception(&quot;incompatible public keys&quot;)
+        
+      result = EGPublicKey()
+      result.p = self.p
+      result.q = self.q
+      result.g = self.g
+      result.y = (self.y * other.y) % result.p
+      return result
+      
+    def verify_sk_proof(self, dlog_proof, challenge_generator = None):
+      &quot;&quot;&quot;
+      verify the proof of knowledge of the secret key
+      g^response = commitment * y^challenge
+      &quot;&quot;&quot;
+      left_side = pow(self.g, dlog_proof.response, self.p)
+      right_side = (dlog_proof.commitment * pow(self.y, dlog_proof.challenge, self.p)) % self.p
+      
+      expected_challenge = challenge_generator(dlog_proof.commitment) % self.q
+      
+      return ((left_side == right_side) and (dlog_proof.challenge == expected_challenge))
 
     @classmethod
     def from_dict(cls, d):
@@ -278,10 +306,25 @@ class EGSecretKey:
           }
 
     def to_dict(self):
-        return {'x' : str(self.x), 'pk' : self.pk.to_dict()}
+        return {'x' : str(self.x), 'public_key' : self.pk.to_dict()}
         
     toJSONDict = to_dict
 
+    def prove_sk(self, challenge_generator):
+      &quot;&quot;&quot;
+      Generate a PoK of the secret key
+      Prover generates w, a random integer modulo q, and computes commitment = g^w mod p.
+      Verifier provides challenge modulo q.
+      Prover computes response = w + x*challenge mod q, where x is the secret key.
+      &quot;&quot;&quot;
+      w = Utils.random_mpz_lt(self.pk.q)
+      commitment = pow(self.pk.g, w, self.pk.p)
+      challenge = challenge_generator(commitment) % self.pk.q
+      response = (w + (self.x * challenge)) % self.pk.q
+      
+      return DLogProof(commitment, challenge, response)
+      
+
     @classmethod
     def from_dict(cls, d):
         if not d:
@@ -289,7 +332,10 @@ class EGSecretKey:
           
         sk = cls()
         sk.x = int(d['x'])
-        sk.pk = EGPublicKey.from_dict(d['pk'])
+        if d.has_key('public_key'):
+          sk.pk = EGPublicKey.from_dict(d['public_key'])
+        else:
+          sk.pk = None
         return sk
         
     fromJSONDict = from_dict
@@ -555,6 +601,22 @@ class EGZKDisjunctiveProof:
   
   toJSONDict = to_dict
 
+class DLogProof(object):
+  def __init__(self, commitment, challenge, response):
+    self.commitment = commitment
+    self.challenge = challenge
+    self.response = response
+    
+  def to_dict(self):
+    return {'challenge': str(self.challenge), 'commitment': str(self.commitment), 'response' : str(self.response)}
+  
+  toJSONDict = to_dict
+  
+  @classmethod
+  def from_dict(cls, d):
+    dlp = cls(int(d['commitment']), int(d['challenge']), int(d['response']))
+    return dlp
+
 def EG_disjunctive_challenge_generator(commitments):
   array_to_hash = []
   for commitment in commitments:
@@ -563,4 +625,8 @@ def EG_disjunctive_challenge_generator(commitments):
 
   string_to_hash = &quot;,&quot;.join(array_to_hash)
   return int(sha.new(string_to_hash).hexdigest(),16)
+  
+def DLog_challenge_generator(commitment):
+  string_to_hash = str(commitment)
+  return int(sha.new(string_to_hash).hexdigest(),16)
 </diff>
      <filename>crypto/algs.py</filename>
    </modified>
    <modified>
      <diff>@@ -6,9 +6,10 @@ Ben Adida
 &quot;&quot;&quot;
 
 import algs
-from base import utils
 import logging
 
+from helios import utils
+
 class EncryptedAnswer(object):
   &quot;&quot;&quot;
   An encrypted answer to a single election question
@@ -19,10 +20,27 @@ class EncryptedAnswer(object):
     self.overall_proof = overall_proof
     self.randomness = randomness
     
-  def verify(self, pk):
-    possible_plaintexts = [algs.EGPlaintext(1, pk), algs.EGPlaintext(pk.g, pk)]
-    homomorphic_sum = 0
+  @classmethod
+  def generate_plaintexts(cls, pk, min=0, max=1):
+    plaintexts = []
+    running_product = 1
+    
+    # run the product up to the min
+    for i in range(max+1):
+      # if we're in the range, add it to the array
+      if i &gt;= min:
+        plaintexts.append(algs.EGPlaintext(running_product, pk))
+        
+      # next value in running product
+      running_product = (running_product * pk.g) % pk.p
+      
+    return plaintexts
 
+    
+  def verify(self, pk, min=0, max=1):
+    possible_plaintexts = self.generate_plaintexts(pk)
+    homomorphic_sum = 0
+      
     for choice_num in range(len(self.choices)):
       choice = self.choices[choice_num]
       choice.pk = pk
@@ -35,8 +53,11 @@ class EncryptedAnswer(object):
       # compute homomorphic sum
       homomorphic_sum = choice * homomorphic_sum
     
+    # determine possible plaintexts for the sum
+    sum_possible_plaintexts = self.generate_plaintexts(pk, min=min, max=max)
+
     # verify the sum
-    return homomorphic_sum.verify_disjunctive_encryption_proof(possible_plaintexts, self.overall_proof, algs.EG_disjunctive_challenge_generator)
+    return homomorphic_sum.verify_disjunctive_encryption_proof(sum_possible_plaintexts, self.overall_proof, algs.EG_disjunctive_challenge_generator)
         
   def toJSONDict(self):
     return {
@@ -77,7 +98,7 @@ class EncryptedAnswer(object):
     randomness = [None for a in range(len(answers))]
     
     # possible plaintexts [0, 1]
-    plaintexts = [algs.EGPlaintext(1, pk), algs.EGPlaintext(pk.g, pk)]
+    plaintexts = cls.generate_plaintexts(pk)
     
     # keep track of number of options selected.
     num_selected_answers = 0;
@@ -109,7 +130,18 @@ class EncryptedAnswer(object):
 
     # prove that the sum is 0 or 1 (can be &quot;blank vote&quot; for this answer)
     # num_selected_answers is 0 or 1, which is the index into the plaintext that is actually encoded
-    overall_proof = homomorphic_sum.generate_disjunctive_encryption_proof(plaintexts, num_selected_answers, randomness_sum, algs.EG_disjunctive_challenge_generator);
+    min_answers = 0
+    if question.has_key('min'):
+      min_answers = question['min']
+    max_answers = question['max']
+    
+    if num_selected_answers &lt; min_answers:
+      raise Exception(&quot;Need to select at least %s answer(s)&quot; % min_answers)
+    
+    sum_plaintexts = cls.generate_plaintexts(pk, min=min_answers, max=max_answers)
+    
+    # need to subtract the min from the offset
+    overall_proof = homomorphic_sum.generate_disjunctive_encryption_proof(sum_plaintexts, num_selected_answers - min_answers, randomness_sum, algs.EG_disjunctive_challenge_generator);
     
     return cls(choices, individual_proofs, overall_proof, randomness)
     
@@ -137,8 +169,14 @@ class EncryptedVote(object):
       return False
       
     # check proofs on all of answers
-    for ea in self.encrypted_answers:
-      if not ea.verify(election.pk):
+    for question_num in range(len(election.questions)):
+      ea = self.encrypted_answers[question_num]
+      question = election.questions[question_num]
+      min_answers = 0
+      if question.has_key('min'):
+        min_answers = question['min']
+        
+      if not ea.verify(election.pk, min=min_answers, max=question['max']):
         return False
         
     return True
@@ -182,6 +220,8 @@ class Election(object):
     self.voting_starts_at = None
     self.voting_ends_at = None
     self.openreg = False
+    self.ballot_type = False
+    self.tally_type = False
     
   def init_tally(self):
     return Tally(self)
@@ -193,10 +233,12 @@ class Election(object):
 
   def toJSONDict(self):
     return_value = {
+      'ballot_type' : self.ballot_type,
       'election_id' : self.election_id,
       'name' : self.name,
-      'pk' : self.pk.toJSONDict(),
+      'public_key' : self.pk.toJSONDict(),
       'questions' : self.questions,
+      'tally_type' : self.tally_type,
       'voting_starts_at' : self.voting_starts_at,
       'voting_ends_at' : self.voting_ends_at
     }
@@ -218,9 +260,11 @@ class Election(object):
     el.voting_starts_at = d['voting_starts_at']
     el.voting_ends_at = d['voting_ends_at']
     el.questions = d['questions']
+    el.ballot_type = d['ballot_type']
+    el.tally_type = d['tally_type']
     
-    if d['pk']:
-      el.pk = algs.EGPublicKey.fromJSONDict(d['pk'])
+    if d['public_key']:
+      el.pk = algs.EGPublicKey.fromJSONDict(d['public_key'])
     else:
       el.pk = None
 </diff>
      <filename>crypto/electionalgs.py</filename>
    </modified>
    <modified>
      <diff>@@ -13,7 +13,6 @@
 BigInt = Class.extend({
   init: function(value, radix) {
     if (value == null) {
-      debugger;
       throw &quot;null value!&quot;;
     }
       
@@ -109,20 +108,26 @@ function check_applet() {
     alert(&quot;Java support required for Helios&quot;);
   }
   
+  // HACK
+  // use_applet = true;
+  
+  // add the applet
+  if (use_applet) {
+    var applet_base = '/static';
+    
+    var applet_html = '&lt;applet codebase=&quot;' + applet_base + '&quot; mayscript name=&quot;bigint&quot; code=&quot;bigint.class&quot; width=1 height=1&gt;&lt;/applet&gt;';
+    // var applet_html = '&lt;object classid=&quot;clsid:8AD9C840-044E-11D1-B3E9-00805F499D93&quot; name=&quot;bigint&quot; width=&quot;1&quot; height=&quot;1&quot; codebase=&quot;http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0&quot;&gt; &lt;param name=&quot;code&quot; value=&quot;bigint.class&quot;&gt; &lt;param name=&quot;codebase&quot; value=&quot;' + applet_base + '&quot;&gt; &lt;param name=&quot;archive&quot; value=&quot;myapplet.jar&quot;&gt; &lt;param name=&quot;type&quot; value=&quot;application/x-java-applet;version=1.5.0&quot;&gt; &lt;param name=&quot;scriptable&quot; value=&quot;true&quot;&gt; &lt;param name=&quot;mayscript&quot; value=&quot;false&quot;&gt; &lt;comment&gt; &lt;embed code=&quot;bigint.class&quot; name=&quot;bigint&quot; java_codebase=&quot;' + applet_base + '&quot; width=&quot;1&quot; height=&quot;1&quot; scriptable=&quot;true&quot; mayscript=&quot;false&quot; type=&quot;application/x-java-applet;version=1.5.0&quot; pluginspage=&quot;http://java.sun.com/j2se/1.5.0/download.html&quot;&gt; &lt;noembed&gt;No Java Support.&lt;/noembed&gt; &lt;/embed&gt; &lt;/comment&gt; &lt;/object&gt;';
+    $(&quot;#applet_div&quot;).html(applet_html);
+  }
+  
   return use_applet;
 };
 
-BigInt.use_applet = check_applet();
-
 // Set up the pointer to the applet if necessary, and some
 // basic Big Ints that everyone needs (0, 1, 2, and 42)
 BigInt.setup = function() {
   if(BigInt.use_applet) {
       BigInt.APPLET = document.applets[&quot;bigint&quot;];
-      if (BigInt.APPLET == null) {
-        //setTimeout(&quot;BigInt.setup();&quot;, 2000);
-        //return;
-      }
   }
 
   BigInt.ZERO = new BigInt(&quot;0&quot;,10);
@@ -134,6 +139,7 @@ BigInt.setup = function() {
 // .onload instead of .ready, as I don't think the applet is ready until onload.
 // FIXME: something wrong here in the first load
 $(document).ready(function() {
+    BigInt.use_applet = check_applet();
     BigInt.setup();
 });
 </diff>
      <filename>static/bigint.js</filename>
    </modified>
    <modified>
      <diff>@@ -103,7 +103,7 @@ ElGamal.SecretKey = Class.extend({
   },
   
   toJSONObject: function() {
-    return {pk: this.pk.toJSONObject(), x: this.x.toJSONObject()};
+    return {public_key: this.pk.toJSONObject(), x: this.x.toJSONObject()};
   },
   
   // a decryption factor is *not yet* mod-inverted, because it needs to be part of the proof.
@@ -165,7 +165,7 @@ ElGamal.SecretKey = Class.extend({
 
 ElGamal.SecretKey.fromJSONObject = function(d) {
   var sk = new ElGamal.SecretKey();
-  sk.pk = ElGamal.PublicKey.fromJSONObject(d.pk);
+  sk.pk = ElGamal.PublicKey.fromJSONObject(d.public_key);
   sk.x = BigInt.fromJSONObject(d.x);
   return sk;
 }
@@ -246,6 +246,7 @@ ElGamal.Ciphertext = Class.extend({
     // go through all plaintexts and simulate the ones that must be simulated.
     // note how the interface is as such so that the result does not reveal which is the real proof.
     var self = this;
+    
     var proofs = $(list_of_plaintexts).map(function(p_num, plaintext) {
       if (p_num == real_index) {
         // no real proof yet
@@ -268,6 +269,7 @@ ElGamal.Ciphertext = Class.extend({
       var commitments = $(proofs).map(function(proof_num, proof) {
         return proof.commitment;
       });
+      
       var disjunctive_challenge = challenge_generator(commitments);
       
       // now we must subtract all of the other challenges from this challenge.
@@ -283,7 +285,7 @@ ElGamal.Ciphertext = Class.extend({
     
     // set the real proof
     proofs[real_index] = real_proof;
-    
+        
     return new ElGamal.DisjunctiveProof(proofs);
   },
   
@@ -378,8 +380,8 @@ ElGamal.Proof = Class.extend({
   
   toJSONObject: function() {
     return {
-      commitment : {A: this.commitment.A.toJSONObject(), B: this.commitment.B.toJSONObject()},
       challenge : this.challenge.toJSONObject(),
+      commitment : {A: this.commitment.A.toJSONObject(), B: this.commitment.B.toJSONObject()},
       response : this.response.toJSONObject()
     }
   },</diff>
      <filename>static/elgamal.js</filename>
    </modified>
    <modified>
      <diff>@@ -33,28 +33,35 @@ _Helios_SameSite = Class.extend({
     }
   },
   
+  getJSON: function(url, callback) {
+    // get the JSON manually so that the raw source is still accessible
+    $.get(API_PREFIX + url, function(json_str) {
+      callback($.secureEvalJSON(json_str), json_str);
+    });
+  },
+  
   get_election: function(params, callback) {
-    $.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;?date=&quot; + new Date().getTime(), callback);
+    this.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;?date=&quot; + new Date().getTime(), callback);
   },
   
   get_election_voters: function(params, callback) {
-    $.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/voters?date=&quot; + new Date().getTime(), callback);
+    this.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/voters?date=&quot; + new Date().getTime(), callback);
   },
   
   get_election_voter: function(params, callback) {
-    $.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/voters/&quot; + params['voter_id'] + &quot;?date=&quot; + new Date().getTime(), callback);
+    this.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/voters/&quot; + params['voter_id'] + &quot;?date=&quot; + new Date().getTime(), callback);
   },
   
   get_election_trustees: function(params, callback) {
-    $.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/trustees/?date=&quot; + new Date().getTime(), callback);
+    this.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/trustees/?date=&quot; + new Date().getTime(), callback);
   },
   
   get_election_result: function(params, callback) {
-    $.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/result?date=&quot; + new Date().getTime(), callback);
+    this.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/result?date=&quot; + new Date().getTime(), callback);
   },
   
   get_election_result_proof: function(params, callback) {
-    $.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/result_proof?date=&quot; + new Date().getTime(), callback);
+    this.getJSON(&quot;/elections/&quot; + params['election_id'] + &quot;/result_proof?date=&quot; + new Date().getTime(), callback);
   }
 });
 
@@ -76,7 +83,7 @@ _Helios_CrossSite = Class.extend({
     }
       
     this.API_FRAME = document.createElement('iframe');
-    this.API_FRAME.src = &quot;http://&quot; + API_HOST + &quot;/elections/api&quot;;
+    this.API_FRAME.src = &quot;http://&quot; + API_HOST + API_PREFIX + &quot;/elections/api&quot;;
     this.API_FRAME.style.width=0;
     this.API_FRAME.style.height=0;
     this.API_FRAME.style.border=0;</diff>
      <filename>static/helios-api.js</filename>
    </modified>
    <modified>
      <diff>@@ -72,18 +72,24 @@ HELIOS.Election = Class.extend({
     // in terms of ordering the keys. FIXME: get a JSON library that orders keys properly.
     if (this.openreg) {
       return {
-        election_id : this.election_id, name : this.name, openreg: true, pk: this.pk.toJSONObject(), questions : this.questions,
-        voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at
+        ballot_type: this.ballot_type, election_id : this.election_id,
+        name : this.name, openreg: true, public_key: this.pk.toJSONObject(), questions : this.questions,
+        tally_type: this.tally_type, voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at
       };
     } else {
       return {
-        election_id : this.election_id, name : this.name, pk: this.pk.toJSONObject(), questions : this.questions,
-        voters_hash : this.voters_hash, voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at
+        ballot_type: this.ballot_type, election_id : this.election_id,
+        name : this.name, public_key: this.pk.toJSONObject(), questions : this.questions,
+        tally_type: this.tally_type, voters_hash : this.voters_hash, voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at
       };      
     }
   },
   
   get_hash: function() {
+    if (this.election_hash)
+      return this.election_hash;
+    
+    // otherwise  
     return b64_sha1(this.toJSON());
   },
   
@@ -93,14 +99,32 @@ HELIOS.Election = Class.extend({
   }
 });
 
+HELIOS.Election.fromJSONString = function(raw_json) {
+  var json_object = $.secureEvalJSON(raw_json);
+  
+  // hash fix for the issue with re-json'ifying unicode chars
+  var election = HELIOS.Election.fromJSONObject(json_object);
+  election.election_hash = b64_sha1(raw_json);
+  
+  return election;
+};
+
 HELIOS.Election.fromJSONObject = function(d) {
   var el = new HELIOS.Election();
   el.election_id = d.election_id;
   el.name = d.name; el.voters_hash = d.voters_hash; el.voting_starts_at = d.voting_starts_at; el.voting_ends_at = d.voting_ends_at;
   el.questions = d.questions;
   
-  if (d.pk)
-    el.pk = ElGamal.PublicKey.fromJSONObject(d.pk);
+  // stuff about the election
+  el.ballot_type = d.ballot_type;
+  el.tally_type = d.tally_type;
+  
+  // empty questions
+  if (!el.questions)
+    el.questions = [];
+  
+  if (d.public_key)
+    el.pk = ElGamal.PublicKey.fromJSONObject(d.public_key);
     
   el.openreg = d.openreg;
   return el;
@@ -142,15 +166,19 @@ UTILS.open_window_with_content = function(content) {
 };
 
 // generate an array of the first few plaintexts
-UTILS.generate_plaintexts = function(pk, num) {
+UTILS.generate_plaintexts = function(pk, min, max) {
   var last_plaintext = BigInt.ONE;
 
   // an array of plaintexts
-  var plaintexts = []
+  var plaintexts = [];
+  
+  if (min == null)
+    min = 0;
   
   // questions with more than one possible answer, add to the array.
-  for (var i=0; i&lt;=num; i++) {
-    plaintexts[i] = new ElGamal.Plaintext(last_plaintext, pk, false);
+  for (var i=0; i&lt;=max; i++) {
+    if (i &gt;= min)
+      plaintexts.push(new ElGamal.Plaintext(last_plaintext, pk, false));
     last_plaintext = last_plaintext.multiply(pk.g).mod(pk.p);
   }
   
@@ -187,8 +215,9 @@ HELIOS.EncryptedAnswer = Class.extend({
     var individual_proofs = [];
     var overall_proof = null;
     
-    // possible plaintexts [0, 1, .. , question.max]
-    var plaintexts = UTILS.generate_plaintexts(pk, question.max);
+    // possible plaintexts [question.min .. , question.max]
+    var plaintexts = UTILS.generate_plaintexts(pk, question.min, question.max);
+    var zero_one_plaintexts = UTILS.generate_plaintexts(pk, 0, 1);
     
     // keep track of whether we need to generate new randomness
     var generate_new_randomness = false;    
@@ -216,12 +245,12 @@ HELIOS.EncryptedAnswer = Class.extend({
         randomness[i] = Random.getRandomInteger(pk.q);        
       }
 
-      choices[i] = ElGamal.encrypt(pk, plaintexts[plaintext_index], randomness[i]);
+      choices[i] = ElGamal.encrypt(pk, zero_one_plaintexts[plaintext_index], randomness[i]);
       
       // generate proof
       if (generate_new_randomness) {
         // generate proof that this ciphertext is a 0 or a 1
-        individual_proofs[i] = choices[i].generateDisjunctiveProof(plaintexts, plaintext_index, randomness[i], ElGamal.disjunctive_challenge_generator);        
+        individual_proofs[i] = choices[i].generateDisjunctiveProof(zero_one_plaintexts, plaintext_index, randomness[i], ElGamal.disjunctive_challenge_generator);        
       }
       
       if (progress)
@@ -241,7 +270,14 @@ HELIOS.EncryptedAnswer = Class.extend({
     
       // prove that the sum is 0 or 1 (can be &quot;blank vote&quot; for this answer)
       // num_selected_answers is 0 or 1, which is the index into the plaintext that is actually encoded
-      overall_proof = hom_sum.generateDisjunctiveProof(plaintexts, num_selected_answers, rand_sum, ElGamal.disjunctive_challenge_generator);
+      //
+      // now that &quot;plaintexts&quot; only contains the array of plaintexts that are possible starting with min
+      // and going to max, the num_selected_answers needs to be reduced by min to be the proper index
+      var overall_plaintext_index = num_selected_answers;
+      if (question.min)
+        overall_plaintext_index -= question.min;
+        
+      overall_proof = hom_sum.generateDisjunctiveProof(plaintexts, overall_plaintext_index, rand_sum, ElGamal.disjunctive_challenge_generator);
       if (progress)
         progress.tick();
     }
@@ -405,7 +441,7 @@ HELIOS.EncryptedVote = Class.extend({
   },
   
   verifyProofs: function(pk, outcome_callback) {
-    var zero_or_one = UTILS.generate_plaintexts(pk, 1);
+    var zero_or_one = UTILS.generate_plaintexts(pk, 0, 1);
 
     var VALID_P = true;
     
@@ -427,7 +463,7 @@ HELIOS.EncryptedVote = Class.extend({
         });
         
         // possible plaintexts [0, 1, .. , question.max]
-        var plaintexts = UTILS.generate_plaintexts(pk, self.election.questions[ea_num].max);
+        var plaintexts = UTILS.generate_plaintexts(pk, self.election.questions[ea_num].min, self.election.questions[ea_num].max);
         
         // check the proof on the overall product
         var overall_check = overall_result.verifyDisjunctiveProof(plaintexts, enc_answer.overall_proof, ElGamal.disjunctive_challenge_generator);
@@ -476,10 +512,10 @@ HELIOS.dejsonify_list_of_lists = function(lol, item_dejsonifier) {
 }
 
 HELIOS.Trustee = Class.extend({
-  init: function(email, name, pk, pok, decryption_factors, decryption_proofs) {
+  init: function(email, name, public_key, pok, decryption_factors, decryption_proofs) {
     this.email = email;
     this.name = name;
-    this.pk = pk;
+    this.public_key = public_key;
     this.pok = pok;
     this.decryption_factors = decryption_factors;
     this.decryption_proofs = decryption_proofs;
@@ -489,14 +525,14 @@ HELIOS.Trustee = Class.extend({
     return {
       'decryption_factors' : HELIOS.jsonify_list_of_lists(this.decryption_factors),
       'decryption_proofs' : HELIOS.jsonify_list_of_list(this.decryption_proofs),
-      'email' : this.email, 'name' : this.name, 'pk' : this.pk.toJSONObject(), 'pok' : this.pok.toJSONObject()
+      'email' : this.email, 'name' : this.name, 'pok' : this.pok.toJSONObject(), 'public_key' : this.pk.toJSONObject()
     }
   }
 });
 
 HELIOS.Trustee.fromJSONObject = function(d) {
   return new HELIOS.Trustee(d.email, d.name, 
-    ElGamal.PublicKey.fromJSONObject(d.pk), ElGamal.DLogProof.fromJSONObject(d.pok),
+    ElGamal.PublicKey.fromJSONObject(d.public_key), ElGamal.DLogProof.fromJSONObject(d.pok),
     HELIOS.dejsonify_list_of_lists(d.decryption_factors, BigInt.fromJSONObject),
     HELIOS.dejsonify_list_of_lists(d.decryption_proofs, ElGamal.Proof.fromJSONObject));
 };
\ No newline at end of file</diff>
      <filename>static/helios.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,13 +1,18 @@
 
-&lt;h3 style=&quot;border-bottom: 1px solid black;&quot;&gt;Question #{$T.question_num + 1} &lt;/h3&gt;
+&lt;h3 style=&quot;border-bottom: 1px solid black;&quot;&gt;Question #{$T.question_num + 1} of {$T.last_question_num + 1}&lt;/h3&gt;
 
 &lt;form onsubmit=&quot;return false;&quot; class=&quot;prettyform&quot; id=&quot;answer_form&quot;&gt;
 &lt;input type=&quot;hidden&quot; name=&quot;question_num&quot; value=&quot;{$T.question_num}&quot; /&gt;
 
 &lt;p&gt;
 &lt;em&gt;{$T.question.question}&lt;/em&gt;
-
- (select {$T.question.max} answer{#if $T.question.max &gt; 1}s{#/if})
+&lt;br /&gt;
+&lt;span style=&quot;font-size: 0.6em;&quot;&gt;(select 
+{#if $T.question.min &amp;&amp; $T.question.min &gt; 0}
+at least {$T.question.min} answer{#if $T.question.min &gt; 1}s{#/if}, 
+{#/if} 
+up to {$T.question.max} answer{#if $T.question.max &gt; 1}s{#/if})
+&lt;/span&gt;
 &lt;/p&gt;
 
 
@@ -24,16 +29,16 @@
 {#/for}
 
 &lt;div style=&quot;float: right;&quot;&gt;
-&lt;input type=&quot;button&quot; onclick=&quot;BOOTH.show_confirm();&quot; value=&quot;Review all Choices&quot; /&gt;
+&lt;input type=&quot;button&quot; onclick=&quot;BOOTH.validate_and_confirm({$T.question_num});&quot; value=&quot;Review all Choices&quot; /&gt;
 &lt;/div&gt;
 
 {#if $T.question_num != 0}
-&lt;input type=&quot;button&quot; onclick=&quot;BOOTH.show_question({$T.question_num -1});&quot; value=&quot;Previous&quot; /&gt;
+&lt;input type=&quot;button&quot; onclick=&quot;if (BOOTH.validate_question({$T.question_num})) {BOOTH.show_question({$T.question_num -1});}&quot; value=&quot;Previous&quot; /&gt;
 &amp;nbsp;
 {#/if}
 
 {#if $T.question_num &lt; $T.last_question_num}
-&lt;input type=&quot;button&quot; onclick=&quot;BOOTH.show_question({$T.question_num +1});&quot; value=&quot;Next&quot; /&gt;
+&lt;input type=&quot;button&quot; onclick=&quot;if (BOOTH.validate_question({$T.question_num})) {BOOTH.show_question({$T.question_num +1});}&quot; value=&quot;Next&quot; /&gt;
 &amp;nbsp;
 {#/if}
 </diff>
      <filename>static/templates/booth/question.html</filename>
    </modified>
    <modified>
      <diff>@@ -15,6 +15,9 @@
 &lt;label for=&quot;question&quot;&gt;Full Question:&lt;/label&gt;
 &lt;textarea name=&quot;question&quot; id=&quot;question&quot; cols=&quot;40&quot; rows=&quot;3&quot; wrap=&quot;soft&quot;&gt;{$T.question.question}&lt;/textarea&gt;&lt;br /&gt;
 
+&lt;label for=&quot;max&quot;&gt;Min # of Answers:&lt;/label&gt;
+&lt;input type=&quot;text&quot; name=&quot;min&quot; id=&quot;min&quot; size=&quot;3&quot; value=&quot;{$T.question.min || 0}&quot; /&gt;&lt;br /&gt;
+
 &lt;label for=&quot;max&quot;&gt;Max # of Answers:&lt;/label&gt;
 &lt;input type=&quot;text&quot; name=&quot;max&quot; id=&quot;max&quot; size=&quot;3&quot; value=&quot;{$T.question.max || 1}&quot; /&gt;&lt;br /&gt;
 &lt;!--&lt;input type=&quot;hidden&quot; name=&quot;max&quot; id=&quot;max&quot; value=&quot;1&quot; /&gt;--&gt;</diff>
      <filename>static/templates/builder/question.html</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@ Some tests of Elections Algs
 import sys
 sys.path.append('../')
 
-from base import utils
+from helios import utils
 from crypto import algs, electionalgs
 
 # parse an election</diff>
      <filename>tests/electionalgs.py</filename>
    </modified>
    <modified>
      <diff>@@ -8,16 +8,22 @@ Variables at the beginning
 import sys
 sys.path.append('../')
 
-from base import utils
+from helios import utils
 from crypto import algs, electionalgs
 from client import heliosclient
 
 # instantiate the client
 # modify variables here
-helios = heliosclient.HeliosClient({'consumer_key': 'test', 'consumer_secret': 'test',
-                        'access_token': '123', 'access_token_secret' : '123'},
-                        host = 'localhost',
-                        port = 8082)
+helios = heliosclient.HeliosClient({'consumer_key': 'test', 'consumer_secret': '123'},
+#                        host = '174.129.241.146',
+                        host = &quot;localhost&quot;,
+                        port = 8000,
+#                         port = 80,
+#                        prefix = &quot;/helios&quot;
+                        )
+
+print &quot;headers:\n&quot;
+print helios.get_test()
 
 # get the El Gamal Parameters
 params = helios.params()
@@ -34,7 +40,7 @@ print &quot;election id is: &quot; + election_id
 helios.election_set_reg(election_id, open_reg= True)
 
 # set questions
-questions = [{&quot;answers&quot;: [&quot;ice-cream&quot;, &quot;cake&quot;], &quot;max&quot;: 1, &quot;question&quot;: &quot;ice-cream or cake?&quot;, &quot;short_name&quot;: &quot;dessert&quot;}]
+questions = [{&quot;answers&quot;: [&quot;ice-cream&quot;, &quot;cake&quot;], &quot;min&quot;: 1, &quot;max&quot;: 1, &quot;question&quot;: &quot;ice-cream or cake?&quot;, &quot;short_name&quot;: &quot;dessert&quot;}]
 helios.election_questions_save(election_id, questions)
 
 # freeze it
@@ -48,7 +54,9 @@ print &quot;election hash is %s&quot; % election.hash
 
 # create three ballots
 ballot_1 = electionalgs.EncryptedVote.fromElectionAndAnswers(election, [[1]])
+print &quot;one ballot&quot;
 ballot_2 = electionalgs.EncryptedVote.fromElectionAndAnswers(election, [[1]])
+print &quot;two ballots&quot;
 ballot_3 = electionalgs.EncryptedVote.fromElectionAndAnswers(election, [[0]])
 
 print &quot;created 3 ballots&quot;
@@ -74,4 +82,4 @@ result, proof = tally.decrypt_and_prove(sk)
 helios.set_tally(election_id, result, proof)
 
 print &quot;tally is: &quot;
-print result
\ No newline at end of file
+print result</diff>
      <filename>tests/heliosclient.py</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>Cheetah/CacheRegion.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/CacheStore.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/CheetahWrapper.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Compiler.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/DummyTransaction.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/ErrorCatchers.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/FileUtils.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Filters.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/ImportHooks.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/ImportManager.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Macros/I18n.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Macros/__init__.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/NameMapper.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Parser.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Servlet.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/SettingsManager.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/SourceReader.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Template.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/TemplateCmdLineIface.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Templates/SkeletonPage.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Templates/SkeletonPage.tmpl</filename>
    </removed>
    <removed>
      <filename>Cheetah/Templates/_SkeletonPage.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Templates/__init__.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Tools/CGITemplate.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Tools/MondoReport.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Tools/MondoReportDoc.txt</filename>
    </removed>
    <removed>
      <filename>Cheetah/Tools/RecursiveNull.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Tools/SiteHierarchy.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Tools/__init__.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Unspecified.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/Indenter.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/Misc.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/VerifyType.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/WebInputMixin.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/__init__.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/htmlDecode.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/htmlEncode.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/memcache.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/optik/__init__.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/optik/errors.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/optik/option.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Utils/optik/option_parser.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/Version.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/__init__.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/_namemapper.py</filename>
    </removed>
    <removed>
      <filename>Cheetah/convertTmplPathToModuleName.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/LICENSE.txt</filename>
    </removed>
    <removed>
      <filename>cherrypy/__init__.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpcgifs.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpchecker.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpconfig.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpdispatch.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cperror.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cplogging.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpmodpy.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cprequest.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpserver.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpthreadinglocal.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cptools.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cptree.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpwsgi.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/_cpwsgi_server.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/cherryd</filename>
    </removed>
    <removed>
      <filename>cherrypy/favicon.ico</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/__init__.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/auth.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/caching.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/covercp.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/cptools.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/encoding.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/http.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/httpauth.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/profiler.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/safemime.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/sessions.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/static.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/tidy.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/wsgiapp.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/lib/xmlrpc.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/process/__init__.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/process/plugins.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/process/servers.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/process/win32.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/process/wspbus.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/scaffold/example.conf</filename>
    </removed>
    <removed>
      <filename>cherrypy/scaffold/site.conf</filename>
    </removed>
    <removed>
      <filename>cherrypy/scaffold/static/made_with_cherrypy_small.png</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/__init__.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/benchmark.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/checkerdemo.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/helper.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/logtest.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/modpy.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/modwsgi.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/py25.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/static/dirback.jpg</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/static/index.html</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/style.css</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test.pem</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_caching.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_config.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_conn.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_core.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_encoding.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_etags.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_http.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_httpauth.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_httplib.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_logging.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_misc_tools.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_objectmapping.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_proxy.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_refleaks.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_routes.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_safe_multipart.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_session.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_sessionauthenticate.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_states.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_states_demo.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_static.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_tidy.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_tools.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_tutorials.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_virtualhost.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_wsgi_ns.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_wsgi_vhost.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_wsgiapps.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/test_xmlrpc.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/test/webtest.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/README.txt</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/__init__.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/bonus-sqlobject.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/custom_error.html</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/pdf_file.pdf</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut01_helloworld.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut02_expose_methods.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut03_get_and_post.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut04_complex_site.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut05_derived_objects.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut06_default_method.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut07_sessions.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut08_generators_and_yield.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut09_files.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tut10_http_errors.py</filename>
    </removed>
    <removed>
      <filename>cherrypy/tutorial/tutorial.conf</filename>
    </removed>
    <removed>
      <filename>cherrypy/wsgiserver/__init__.py</filename>
    </removed>
    <removed>
      <filename>testdata.txt</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>102dc6e50013d6606e9b128b0512c5d84853b503</id>
    </parent>
    <parent>
      <id>02797be206b7eff9c66d9796319cb3a4b6109595</id>
    </parent>
  </parents>
  <author>
    <name>Ben Adida</name>
    <email>ben@adida.net</email>
  </author>
  <url>http://github.com/benadida/helios/commit/34f81b586dd133f8d92f8045a57753372b27ea50</url>
  <id>34f81b586dd133f8d92f8045a57753372b27ea50</id>
  <committed-date>2009-04-16T15:17:05-07:00</committed-date>
  <authored-date>2009-04-16T15:17:05-07:00</authored-date>
  <message>Merge branch 'move-to-django'</message>
  <tree>165188cfd6d570bb1510af704707ace6f0ccda1e</tree>
  <committer>
    <name>Ben Adida</name>
    <email>ben@adida.net</email>
  </committer>
</commit>
